Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.71% covered (warning)
88.71%
55 / 62
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiZObjectFetcher
88.71% covered (warning)
88.71%
55 / 62
50.00% covered (danger)
50.00%
1 / 2
14.28
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 run
88.33% covered (warning)
88.33%
53 / 60
0.00% covered (danger)
0.00%
0 / 1
10.16
 getAllowedParams
n/a
0 / 0
n/a
0 / 0
2
 getExamplesMessages
n/a
0 / 0
n/a
0 / 0
1
1<?php
2/**
3 * WikiLambda ZObject fetching API
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\ActionAPI;
12
13use MediaWiki\Api\ApiMain;
14use MediaWiki\Extension\WikiLambda\HttpStatus;
15use MediaWiki\Extension\WikiLambda\Registry\ZErrorTypeRegistry;
16use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
17use MediaWiki\Extension\WikiLambda\ZErrorException;
18use MediaWiki\Extension\WikiLambda\ZErrorFactory;
19use MediaWiki\Extension\WikiLambda\ZObjectContentHandler;
20use MediaWiki\Extension\WikiLambda\ZObjectUtils;
21use MediaWiki\MediaWikiServices;
22use MediaWiki\Title\Title;
23use Wikimedia\ParamValidator\ParamValidator;
24use Wikimedia\Telemetry\SpanInterface;
25
26class ApiZObjectFetcher extends WikiLambdaApiBase {
27
28    public function __construct( ApiMain $mainModule, string $moduleName ) {
29        parent::__construct( $mainModule, $moduleName );
30
31        $this->setUp();
32    }
33
34    /**
35     * @inheritDoc
36     */
37    protected function run(): void {
38        // Exit if we're running in non-repo mode (e.g. on a client wiki)
39        if ( !$this->getConfig()->get( 'WikiLambdaEnableRepoMode' ) ) {
40            WikiLambdaApiBase::dieWithZError(
41                ZErrorFactory::createZErrorInstance(
42                    ZErrorTypeRegistry::Z_ERROR_USER_CANNOT_RUN,
43                    []
44                ),
45                HttpStatus::BAD_REQUEST
46            );
47        }
48
49        $params = $this->extractRequestParams();
50
51        $ZIDs = $params[ 'zids' ];
52
53        $revisions = $params[ 'revisions' ];
54
55        $tracer = MediaWikiServices::getInstance()->getTracer();
56        $span = $tracer->createSpan( 'WikiLambda ApiZObjectFetcher' )
57            ->setSpanKind( SpanInterface::SPAN_KIND_CLIENT )
58            ->start();
59        $span->activate();
60
61        if (
62            $revisions &&
63            count( $revisions ) !== count( $ZIDs )
64        ) {
65            $errorMessage = "You must specify a revision for each ZID, or none at all.";
66            $zErrorObject = ZErrorFactory::createZErrorInstance(
67                ZErrorTypeRegistry::Z_ERROR_UNKNOWN,
68                [
69                    'message' => $errorMessage
70                ]
71            );
72            WikiLambdaApiBase::dieWithZError( $zErrorObject, HttpStatus::BAD_REQUEST );
73        }
74
75        $language = $params[ 'language' ];
76
77        foreach ( $ZIDs as $index => $ZID ) {
78            if ( !ZObjectUtils::isValidZObjectReference( mb_strtoupper( $ZID ) ) ) {
79                $zErrorObject = ZErrorFactory::createZErrorInstance(
80                    ZErrorTypeRegistry::Z_ERROR_INVALID_REFERENCE,
81                    [ 'data' => $ZID ]
82                );
83                WikiLambdaApiBase::dieWithZError( $zErrorObject, HttpStatus::NOT_FOUND );
84            } else {
85                $title = Title::newFromText( $ZID, NS_MAIN );
86
87                if ( !$title || !$title->exists() ) {
88                    $zErrorObject = ZErrorFactory::createZErrorInstance(
89                        ZErrorTypeRegistry::Z_ERROR_UNKNOWN_REFERENCE,
90                        [ 'data' => $ZID ]
91                    );
92                    WikiLambdaApiBase::dieWithZError( $zErrorObject, HttpStatus::NOT_FOUND );
93                } else {
94                    $revision = $revisions ? $revisions[ $index ] : null;
95                    try {
96                        $fetchedContent = ZObjectContentHandler::getExternalRepresentation(
97                            $title,
98                            $language,
99                            $revision
100                        );
101                        $span->setSpanStatus( SpanInterface::SPAN_STATUS_OK );
102                    } catch ( ZErrorException $e ) {
103                        // This probably means that the requested revision is not known; return
104                        // null for this entry rather than throwing or returning a ZError instance.
105                        $fetchedContent = null;
106                        $span->setSpanStatus( SpanInterface::SPAN_STATUS_ERROR )
107                            ->setAttributes( [
108                                'exception.message' => $e->getMessage()
109                            ] );
110                    } finally {
111                        $span->end();
112                    }
113
114                    $this->getResult()->addValue(
115                        $ZID,
116                        $this->getModuleName(),
117                        $fetchedContent
118                    );
119                }
120            }
121        }
122    }
123
124    /**
125     * @inheritDoc
126     * @codeCoverageIgnore
127     */
128    protected function getAllowedParams(): array {
129        // Don't try to read the supported languages from the DB on client wikis, we can't.
130        $supportedLanguageCodes =
131            ( MediaWikiServices::getInstance()->getMainConfig()->get( 'WikiLambdaEnableRepoMode' ) ) ?
132            WikiLambdaServices::getZObjectStore()->fetchAllZLanguageCodes() :
133            [];
134
135        return [
136            'zids' => [
137                ParamValidator::PARAM_TYPE => 'string',
138                ParamValidator::PARAM_REQUIRED => true,
139                ParamValidator::PARAM_ISMULTI => true,
140            ],
141            'revisions' => [
142                ParamValidator::PARAM_TYPE => 'string',
143                ParamValidator::PARAM_ISMULTI => true,
144            ],
145            'language' => [
146                ParamValidator::PARAM_TYPE => $supportedLanguageCodes,
147                ParamValidator::PARAM_REQUIRED => false,
148            ]
149        ];
150    }
151
152    /**
153     * @see ApiBase::getExamplesMessages()
154     * @return array
155     * @codeCoverageIgnore
156     */
157    protected function getExamplesMessages() {
158        return [
159            'action=wikilambda_fetch&zids=Z1'
160                => 'apihelp-wikilambda_fetch-example-single',
161            'action=wikilambda_fetch&zids=Z1&revisions=12'
162                => 'apihelp-wikilambda_fetch-example-single-old',
163            'action=wikilambda_fetch&zids=Z1|Z2|Z3'
164                => 'apihelp-wikilambda_fetch-example-multiple',
165            'action=wikilambda_fetch&zids=Z1|Z2|Z3&revisions=12|14|25'
166                => 'apihelp-wikilambda_fetch-example-multiple-old',
167        ];
168    }
169}