Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 65
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 / 65
0.00% covered (danger)
0.00%
0 / 4
420
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 / 46
0.00% covered (danger)
0.00%
0 / 1
306
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     * @param RevisionRecord $revRecord
58     */
59    private function markCacheable( RevisionRecord $revRecord ) {
60        $main = $this->getMain();
61        $main->setCacheMode( 'public' );
62        $main->setCacheMaxAge( 300 );
63
64        $lastModified = wfTimestamp( TS_RFC2822, $revRecord->getTimestamp() );
65        $main->getRequest()->response()->header( "Last-Modified: $lastModified" );
66    }
67
68    public function execute() {
69        $params = $this->extractRequestParams();
70
71        if ( !isset( $params['revid'] ) && !isset( $params['title'] ) ) {
72            $this->dieWithError(
73                [ 'apierror-missingparam-at-least-one-of', 'revid', 'title' ],
74                null, null, 400
75            );
76        }
77
78        $revLookup = MediaWikiServices::getInstance()->getRevisionLookup();
79        // If we are given revid, then look up Revision and
80        // verify that $params['title'] (if given) matches.
81        if ( isset( $params['revid'] ) ) {
82            $revRecord = $revLookup->getRevisionById( $params['revid'] );
83            if ( !$revRecord ) {
84                $this->dieWithError(
85                    [ 'apierror-nosuchrevid', $params['revid'] ], null, null, 400
86                );
87            }
88            $title = Title::newFromLinkTarget( $revRecord->getPageAsLinkTarget() );
89            if ( !$title || !$title->inNamespace( NS_SCHEMA ) ) {
90                $this->dieWithError(
91                    [ 'apierror-invalidtitle', wfEscapeWikiText( $title ?: '' ) ], null, null, 400
92                );
93            }
94
95            // If we use the revid param for lookup; the 'title' parameter is
96            // optional. If present, it is used to assert that the specified
97            // revision ID is indeed a revision of a page with the specified
98            // title. (Bug 46174)
99            if (
100                $params['title'] &&
101                !$title->equals( Title::newFromText( $params['title'], NS_SCHEMA ) )
102            ) {
103                $this->dieWithError(
104                    [ 'apierror-revwrongpage', $params['revid'], wfEscapeWikiText( $params['title'] ) ],
105                    null, null, 400
106                );
107            }
108
109        // Else use $params['title'] and get the latest revision
110        } else {
111            $title = Title::newFromText( $params['title'], NS_SCHEMA );
112            if ( !$title || !$title->exists() || !$title->inNamespace( NS_SCHEMA ) ) {
113                $this->dieWithError(
114                    [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ], null, null, 400
115                );
116            }
117
118            $revRecord = $revLookup->getRevisionById( $title->getLatestRevID() );
119        }
120
121        /** @var JsonSchemaContent $content */
122        $content = $revRecord->getContent( SlotRecord::MAIN );
123        if ( !$content ) {
124            $this->dieWithError(
125                [ 'apierror-nosuchrevid', $revRecord->getId() ],
126                null, null, 400
127            );
128        }
129
130        // @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141
131        $this->markCacheable( $revRecord );
132        '@phan-var JsonSchemaContent $content';
133        $schema = $content->getJsonData();
134
135        $result = $this->getResult();
136        $result->addValue( null, 'title', $title->getText() );
137        foreach ( $schema as $k => &$v ) {
138            if ( $k === 'properties' ) {
139                foreach ( $v as &$properties ) {
140                    $properties[ApiResult::META_BC_BOOLS] = [ 'required' ];
141                }
142            }
143            $result->addValue( null, $k, $v );
144        }
145    }
146}