Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiJsonSchema
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 4
380
0.00% covered (danger)
0.00%
0 / 1
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 markCacheable
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 1
272
1<?php
2/**
3 * API module for retrieving JSON Schema.
4 *
5 * @file
6 * @ingroup EventLogging
7 * @ingroup Extensions
8 *
9 * @author Ori Livneh <ori@wikimedia.org>
10 */
11
12namespace MediaWiki\Extension\EventLogging;
13
14use MediaWiki\Api\ApiBase;
15use MediaWiki\Api\ApiResult;
16use MediaWiki\MediaWikiServices;
17use MediaWiki\Revision\RevisionRecord;
18use MediaWiki\Revision\SlotRecord;
19use MediaWiki\Title\Title;
20use Wikimedia\ParamValidator\ParamValidator;
21
22/**
23 * API module for retrieving JSON Schema.
24 * This avoids API result paths and returns HTTP error codes in order to
25 * act like a request for the raw page content.
26 * @ingroup API
27 */
28class ApiJsonSchema extends ApiBase {
29
30    /** @inheritDoc */
31    public function getAllowedParams() {
32        return [
33            'revid' => [
34                ParamValidator::PARAM_TYPE => 'integer',
35            ],
36            'title' => [
37                ParamValidator::PARAM_TYPE => 'string',
38            ],
39        ];
40    }
41
42    /**
43     * @see ApiBase::getExamplesMessages()
44     * @return array
45     */
46    protected function getExamplesMessages() {
47        return [
48            'action=jsonschema&revid=1234'
49                => 'apihelp-jsonschema-example-1',
50            'action=jsonschema&title=Test'
51                => 'apihelp-jsonschema-example-2',
52        ];
53    }
54
55    /**
56     * Set headers on the pending HTTP response.
57     */
58    private function markCacheable( RevisionRecord $revRecord ) {
59        $main = $this->getMain();
60        $main->setCacheMode( 'public' );
61        $main->setCacheMaxAge( 300 );
62
63        $lastModified = wfTimestamp( TS_RFC2822, $revRecord->getTimestamp() );
64        $main->getRequest()->response()->header( "Last-Modified: $lastModified" );
65    }
66
67    public function execute() {
68        $params = $this->extractRequestParams();
69
70        if ( !isset( $params['revid'] ) && !isset( $params['title'] ) ) {
71            $this->dieWithError(
72                [ 'apierror-missingparam-at-least-one-of', 'revid', 'title' ],
73                null, null, 400
74            );
75        }
76
77        $revLookup = MediaWikiServices::getInstance()->getRevisionLookup();
78        // If we are given revid, then look up Revision and
79        // verify that $params['title'] (if given) matches.
80        if ( isset( $params['revid'] ) ) {
81            $revRecord = $revLookup->getRevisionById( $params['revid'] );
82            if ( !$revRecord ) {
83                $this->dieWithError(
84                    [ 'apierror-nosuchrevid', $params['revid'] ], null, null, 400
85                );
86            }
87            $title = Title::newFromPageIdentity( $revRecord->getPage() );
88            if ( !$title->inNamespace( NS_SCHEMA ) ) {
89                $this->dieWithError(
90                    [ 'apierror-invalidtitle', wfEscapeWikiText( $title->getPrefixedText() ) ], null, null, 400
91                );
92            }
93
94            // If we use the revid param for lookup; the 'title' parameter is
95            // optional. If present, it is used to assert that the specified
96            // revision ID is indeed a revision of a page with the specified
97            // title. (Bug 46174)
98            if (
99                $params['title'] &&
100                !$title->equals( Title::newFromText( $params['title'], NS_SCHEMA ) )
101            ) {
102                $this->dieWithError(
103                    [ 'apierror-revwrongpage', $params['revid'], wfEscapeWikiText( $params['title'] ) ],
104                    null, null, 400
105                );
106            }
107
108        // Else use $params['title'] and get the latest revision
109        } else {
110            $title = Title::newFromText( $params['title'], NS_SCHEMA );
111            if ( !$title || !$title->exists() || !$title->inNamespace( NS_SCHEMA ) ) {
112                $this->dieWithError(
113                    [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ], null, null, 400
114                );
115            }
116
117            $revRecord = $revLookup->getRevisionById( $title->getLatestRevID() );
118            if ( !$revRecord ) {
119                $this->dieWithError(
120                    [ 'apierror-nosuchrevid', $title->getLatestRevID() ], null, null, 400
121                );
122            }
123        }
124
125        /** @var JsonSchemaContent $content */
126        $content = $revRecord->getContent( SlotRecord::MAIN );
127        if ( !$content ) {
128            $this->dieWithError(
129                [ 'apierror-nosuchrevid', $revRecord->getId() ],
130                null, null, 400
131            );
132        }
133
134        $this->markCacheable( $revRecord );
135        '@phan-var JsonSchemaContent $content';
136        $schema = $content->getJsonData();
137
138        $result = $this->getResult();
139        $result->addValue( null, 'title', $title->getText() );
140        foreach ( $schema as $k => &$v ) {
141            if ( $k === 'properties' ) {
142                foreach ( $v as &$properties ) {
143                    $properties[ApiResult::META_BC_BOOLS] = [ 'required' ];
144                }
145            }
146            $result->addValue( null, $k, $v );
147        }
148    }
149}