Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
60.49% covered (warning)
60.49%
49 / 81
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
CheckConstraints
60.49% covered (warning)
60.49%
49 / 81
33.33% covered (danger)
33.33%
2 / 6
41.26
0.00% covered (danger)
0.00%
0 / 1
 factory
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
1
 parseEntityIds
58.33% covered (warning)
58.33%
7 / 12
0.00% covered (danger)
0.00%
0 / 1
5.16
 parseClaimIds
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
6.20
 validateParameters
30.00% covered (danger)
30.00%
3 / 10
0.00% covered (danger)
0.00%
0 / 1
13.57
 getAllowedParams
n/a
0 / 0
n/a
0 / 0
1
 getExamplesMessages
n/a
0 / 0
n/a
0 / 0
1
1<?php
2
3declare( strict_types = 1 );
4
5namespace WikibaseQuality\ConstraintReport\Api;
6
7use ApiBase;
8use ApiMain;
9use IBufferingStatsdDataFactory;
10use Wikibase\DataModel\Entity\EntityId;
11use Wikibase\DataModel\Entity\EntityIdParser;
12use Wikibase\DataModel\Entity\EntityIdParsingException;
13use Wikibase\DataModel\Services\Statement\StatementGuidValidator;
14use Wikibase\Lib\LanguageFallbackChainFactory;
15use Wikibase\Lib\Store\EntityTitleLookup;
16use Wikibase\Repo\Api\ApiErrorReporter;
17use Wikibase\Repo\Api\ApiHelperFactory;
18use Wikibase\Repo\Api\ResultBuilder;
19use Wikibase\Repo\EntityIdLabelFormatterFactory;
20use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessageRendererFactory;
21use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
22use Wikimedia\ParamValidator\ParamValidator;
23
24/**
25 * API module that performs constraint check of entities, claims and constraint ID
26 *
27 * @author Olga Bode
28 * @license GPL-2.0-or-later
29 */
30class CheckConstraints extends ApiBase {
31
32    public const PARAM_ID = 'id';
33    public const PARAM_CLAIM_ID = 'claimid';
34    public const PARAM_CONSTRAINT_ID = 'constraintid';
35    public const PARAM_STATUS = 'status';
36
37    private EntityIdParser $entityIdParser;
38    private StatementGuidValidator $statementGuidValidator;
39    private ResultBuilder $resultBuilder;
40    private ApiErrorReporter $errorReporter;
41    private ResultsSource $resultsSource;
42    private CheckResultsRendererFactory $checkResultsRendererFactory;
43    private IBufferingStatsdDataFactory $dataFactory;
44
45    public static function factory(
46        ApiMain $main,
47        string $name,
48        IBufferingStatsdDataFactory $dataFactory,
49        ApiHelperFactory $apiHelperFactory,
50        EntityIdLabelFormatterFactory $entityIdLabelFormatterFactory,
51        EntityIdParser $entityIdParser,
52        EntityTitleLookup $entityTitleLookup,
53        LanguageFallbackChainFactory $languageFallbackChainFactory,
54        StatementGuidValidator $statementGuidValidator,
55        ResultsSource $resultsSource,
56        ViolationMessageRendererFactory $violationMessageRendererFactory
57    ): self {
58        $checkResultsRendererFactory = new CheckResultsRendererFactory(
59            $entityTitleLookup,
60            $entityIdLabelFormatterFactory,
61            $languageFallbackChainFactory,
62            $violationMessageRendererFactory
63        );
64
65        return new self(
66            $main,
67            $name,
68            $entityIdParser,
69            $statementGuidValidator,
70            $apiHelperFactory,
71            $resultsSource,
72            $checkResultsRendererFactory,
73            $dataFactory
74        );
75    }
76
77    public function __construct(
78        ApiMain $main,
79        string $name,
80        EntityIdParser $entityIdParser,
81        StatementGuidValidator $statementGuidValidator,
82        ApiHelperFactory $apiHelperFactory,
83        ResultsSource $resultsSource,
84        CheckResultsRendererFactory $checkResultsRendererFactory,
85        IBufferingStatsdDataFactory $dataFactory
86    ) {
87        parent::__construct( $main, $name );
88        $this->entityIdParser = $entityIdParser;
89        $this->statementGuidValidator = $statementGuidValidator;
90        $this->resultBuilder = $apiHelperFactory->getResultBuilder( $this );
91        $this->errorReporter = $apiHelperFactory->getErrorReporter( $this );
92        $this->resultsSource = $resultsSource;
93        $this->checkResultsRendererFactory = $checkResultsRendererFactory;
94        $this->dataFactory = $dataFactory;
95    }
96
97    /**
98     * Evaluates the parameters, runs the requested constraint check, and sets up the result
99     */
100    public function execute() {
101        $this->dataFactory->increment(
102            'wikibase.quality.constraints.api.checkConstraints.execute'
103        );
104
105        $params = $this->extractRequestParams();
106
107        $this->validateParameters( $params );
108        $entityIds = $this->parseEntityIds( $params );
109        $claimIds = $this->parseClaimIds( $params );
110        $constraintIDs = $params[self::PARAM_CONSTRAINT_ID];
111        $statuses = $params[self::PARAM_STATUS];
112
113        $checkResultsRenderer = $this->checkResultsRendererFactory
114            ->getCheckResultsRenderer( $this->getLanguage(), $this );
115
116        $this->getResult()->addValue(
117            null,
118            $this->getModuleName(),
119            $checkResultsRenderer->render(
120                $this->resultsSource->getResults(
121                    $entityIds,
122                    $claimIds,
123                    $constraintIDs,
124                    $statuses
125                )
126            )->getArray()
127        );
128        $this->resultBuilder->markSuccess( 1 );
129    }
130
131    /**
132     * @param array $params
133     *
134     * @return EntityId[]
135     */
136    private function parseEntityIds( array $params ): array {
137        $ids = $params[self::PARAM_ID];
138
139        if ( $ids === null ) {
140            return [];
141        } elseif ( $ids === [] ) {
142            $this->errorReporter->dieError(
143                'If ' . self::PARAM_ID . ' is specified, it must be nonempty.', 'no-data' );
144        }
145
146        return array_map( function ( $id ) {
147            try {
148                return $this->entityIdParser->parse( $id );
149            } catch ( EntityIdParsingException $e ) {
150                $this->errorReporter->dieError(
151                    "Invalid id: $id", 'invalid-entity-id', 0, [ self::PARAM_ID => $id ] );
152            }
153        }, $ids );
154    }
155
156    /**
157     * @param array $params
158     *
159     * @return string[]
160     */
161    private function parseClaimIds( array $params ): array {
162        $ids = $params[self::PARAM_CLAIM_ID];
163
164        if ( $ids === null ) {
165            return [];
166        } elseif ( $ids === [] ) {
167            $this->errorReporter->dieError(
168                'If ' . self::PARAM_CLAIM_ID . ' is specified, it must be nonempty.', 'no-data' );
169        }
170
171        foreach ( $ids as $id ) {
172            if ( !$this->statementGuidValidator->validate( $id ) ) {
173                $this->errorReporter->dieError(
174                    "Invalid claim id: $id", 'invalid-guid', 0, [ self::PARAM_CLAIM_ID => $id ] );
175            }
176        }
177
178        return $ids;
179    }
180
181    private function validateParameters( array $params ): void {
182        if ( $params[self::PARAM_CONSTRAINT_ID] !== null
183             && empty( $params[self::PARAM_CONSTRAINT_ID] )
184        ) {
185            $paramConstraintId = self::PARAM_CONSTRAINT_ID;
186            $this->errorReporter->dieError(
187                "If $paramConstraintId is specified, it must be nonempty.", 'no-data' );
188        }
189        if ( $params[self::PARAM_ID] === null && $params[self::PARAM_CLAIM_ID] === null ) {
190            $paramId = self::PARAM_ID;
191            $paramClaimId = self::PARAM_CLAIM_ID;
192            $this->errorReporter->dieError(
193                "At least one of $paramId$paramClaimId must be specified.", 'no-data' );
194        }
195        // contents of PARAM_ID and PARAM_CLAIM_ID are validated by parse{Entity,Claim}Ids()
196    }
197
198    /**
199     * @return array[]
200     * @codeCoverageIgnore
201     */
202    public function getAllowedParams() {
203        return [
204            self::PARAM_ID => [
205                ParamValidator::PARAM_TYPE => 'string',
206                ParamValidator::PARAM_ISMULTI => true,
207            ],
208            self::PARAM_CLAIM_ID => [
209                ParamValidator::PARAM_TYPE => 'string',
210                ParamValidator::PARAM_ISMULTI => true,
211            ],
212            self::PARAM_CONSTRAINT_ID => [
213                ParamValidator::PARAM_TYPE => 'string',
214                ParamValidator::PARAM_ISMULTI => true,
215            ],
216            self::PARAM_STATUS => [
217                ParamValidator::PARAM_TYPE => [
218                    CheckResult::STATUS_COMPLIANCE,
219                    CheckResult::STATUS_VIOLATION,
220                    CheckResult::STATUS_WARNING,
221                    CheckResult::STATUS_SUGGESTION,
222                    CheckResult::STATUS_EXCEPTION,
223                    CheckResult::STATUS_NOT_IN_SCOPE,
224                    CheckResult::STATUS_DEPRECATED,
225                    CheckResult::STATUS_BAD_PARAMETERS,
226                    CheckResult::STATUS_TODO,
227                ],
228                ParamValidator::PARAM_ISMULTI => true,
229                ParamValidator::PARAM_ALL => true,
230                ParamValidator::PARAM_DEFAULT => implode( '|', CachingResultsSource::CACHED_STATUSES ),
231                ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
232            ],
233        ];
234    }
235
236    /**
237     * Returns usage examples for this module
238     *
239     * @return string[]
240     * @codeCoverageIgnore
241     */
242    public function getExamplesMessages() {
243        return [
244            'action=wbcheckconstraints&id=Q5|Q42'
245                => 'apihelp-wbcheckconstraints-example-1',
246            'action=wbcheckconstraints&claimid=q42%248419C20C-8EF8-4EC0-80D6-AF1CA55E7557'
247                => 'apihelp-wbcheckconstraints-example-2',
248            'action=wbcheckconstraints&format=json&id=Q2&constraintid=P1082%24DA39C2DA-47DA-48FB-8A9A-DA80200FB2DB'
249                => 'apihelp-wbcheckconstraints-example-3',
250        ];
251    }
252
253}