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