1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace WikibaseQuality\ConstraintReport\Api; |
4
|
|
|
|
5
|
|
|
use ApiBase; |
6
|
|
|
use ApiMain; |
7
|
|
|
use Config; |
8
|
|
|
use IBufferingStatsdDataFactory; |
9
|
|
|
use RequestContext; |
10
|
|
|
use ValueFormatters\FormatterOptions; |
11
|
|
|
use Wikibase\DataModel\Entity\EntityId; |
12
|
|
|
use Wikibase\DataModel\Entity\EntityIdParser; |
13
|
|
|
use Wikibase\DataModel\Entity\EntityIdParsingException; |
14
|
|
|
use Wikibase\DataModel\Services\Statement\StatementGuidValidator; |
15
|
|
|
use Wikibase\Lib\Formatters\SnakFormatter; |
16
|
|
|
use Wikibase\Repo\Api\ApiErrorReporter; |
17
|
|
|
use Wikibase\Repo\Api\ApiHelperFactory; |
18
|
|
|
use Wikibase\Repo\Api\ResultBuilder; |
19
|
|
|
use Wikibase\Repo\EntityIdLabelFormatterFactory; |
20
|
|
|
use Wikibase\Repo\WikibaseRepo; |
21
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\MultilingualTextViolationMessageRenderer; |
22
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult; |
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
|
|
|
*/ |
30
|
|
|
class CheckConstraints extends ApiBase { |
31
|
|
|
|
32
|
|
|
const PARAM_ID = 'id'; |
33
|
|
|
const PARAM_CLAIM_ID = 'claimid'; |
34
|
|
|
const PARAM_CONSTRAINT_ID = 'constraintid'; |
35
|
|
|
const PARAM_STATUS = 'status'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var EntityIdParser |
39
|
|
|
*/ |
40
|
|
|
private $entityIdParser; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var StatementGuidValidator |
44
|
|
|
*/ |
45
|
|
|
private $statementGuidValidator; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var ResultBuilder |
49
|
|
|
*/ |
50
|
|
|
private $resultBuilder; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var ApiErrorReporter |
54
|
|
|
*/ |
55
|
|
|
private $errorReporter; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var ResultsSource |
59
|
|
|
*/ |
60
|
|
|
private $resultsSource; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var CheckResultsRenderer |
64
|
|
|
*/ |
65
|
|
|
private $checkResultsRenderer; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var IBufferingStatsdDataFactory |
69
|
|
|
*/ |
70
|
|
|
private $dataFactory; |
71
|
|
|
|
72
|
|
|
public static function factory( |
73
|
|
|
ApiMain $main, |
74
|
|
|
string $name, |
75
|
|
|
Config $config, |
76
|
|
|
IBufferingStatsdDataFactory $dataFactory, |
77
|
|
|
EntityIdParser $entityIdParser, |
78
|
|
|
ResultsSource $resultsSource |
79
|
|
|
): self { |
80
|
|
|
$repo = WikibaseRepo::getDefaultInstance(); |
81
|
|
|
|
82
|
|
|
$language = $repo->getUserLanguage(); |
83
|
|
|
$formatterOptions = new FormatterOptions(); |
84
|
|
|
$formatterOptions->setOption( SnakFormatter::OPT_LANG, $language->getCode() ); |
85
|
|
|
$valueFormatterFactory = $repo->getValueFormatterFactory(); |
86
|
|
|
$valueFormatter = $valueFormatterFactory->getValueFormatter( SnakFormatter::FORMAT_HTML, $formatterOptions ); |
87
|
|
|
|
88
|
|
|
$entityIdHtmlLinkFormatterFactory = $repo->getEntityIdHtmlLinkFormatterFactory(); |
89
|
|
|
$entityIdHtmlLinkFormatter = $entityIdHtmlLinkFormatterFactory->getEntityIdFormatter( $language ); |
90
|
|
|
$entityIdLabelFormatterFactory = new EntityIdLabelFormatterFactory(); |
91
|
|
|
$entityIdLabelFormatter = $entityIdLabelFormatterFactory->getEntityIdFormatter( $language ); |
92
|
|
|
|
93
|
|
|
$checkResultsRenderer = new CheckResultsRenderer( |
94
|
|
|
$repo->getEntityTitleLookup(), |
95
|
|
|
$entityIdLabelFormatter, |
96
|
|
|
new MultilingualTextViolationMessageRenderer( |
97
|
|
|
$entityIdHtmlLinkFormatter, |
98
|
|
|
$valueFormatter, |
99
|
|
|
$main, |
100
|
|
|
$config |
101
|
|
|
) |
102
|
|
|
); |
103
|
|
|
|
104
|
|
|
return new self( |
105
|
|
|
$main, |
106
|
|
|
$name, |
107
|
|
|
$entityIdParser, |
108
|
|
|
$repo->getStatementGuidValidator(), |
109
|
|
|
$repo->getApiHelperFactory( RequestContext::getMain() ), |
110
|
|
|
$resultsSource, |
111
|
|
|
$checkResultsRenderer, |
112
|
|
|
$dataFactory |
113
|
|
|
); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param ApiMain $main |
118
|
|
|
* @param string $name |
119
|
|
|
* @param EntityIdParser $entityIdParser |
120
|
|
|
* @param StatementGuidValidator $statementGuidValidator |
121
|
|
|
* @param ApiHelperFactory $apiHelperFactory |
122
|
|
|
* @param ResultsSource $resultsSource |
123
|
|
|
* @param CheckResultsRenderer $checkResultsRenderer |
124
|
|
|
* @param IBufferingStatsdDataFactory $dataFactory |
125
|
|
|
*/ |
126
|
|
|
public function __construct( |
127
|
|
|
ApiMain $main, |
128
|
|
|
$name, |
129
|
|
|
EntityIdParser $entityIdParser, |
130
|
|
|
StatementGuidValidator $statementGuidValidator, |
131
|
|
|
ApiHelperFactory $apiHelperFactory, |
132
|
|
|
ResultsSource $resultsSource, |
133
|
|
|
CheckResultsRenderer $checkResultsRenderer, |
134
|
|
|
IBufferingStatsdDataFactory $dataFactory |
135
|
|
|
) { |
136
|
|
|
parent::__construct( $main, $name ); |
137
|
|
|
$this->entityIdParser = $entityIdParser; |
138
|
|
|
$this->statementGuidValidator = $statementGuidValidator; |
139
|
|
|
$this->resultBuilder = $apiHelperFactory->getResultBuilder( $this ); |
140
|
|
|
$this->errorReporter = $apiHelperFactory->getErrorReporter( $this ); |
141
|
|
|
$this->resultsSource = $resultsSource; |
142
|
|
|
$this->checkResultsRenderer = $checkResultsRenderer; |
143
|
|
|
$this->dataFactory = $dataFactory; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Evaluates the parameters, runs the requested constraint check, and sets up the result |
148
|
|
|
*/ |
149
|
|
|
public function execute() { |
150
|
|
|
$this->dataFactory->increment( |
151
|
|
|
'wikibase.quality.constraints.api.checkConstraints.execute' |
152
|
|
|
); |
153
|
|
|
|
154
|
|
|
$params = $this->extractRequestParams(); |
155
|
|
|
|
156
|
|
|
$this->validateParameters( $params ); |
157
|
|
|
$entityIds = $this->parseEntityIds( $params ); |
158
|
|
|
$claimIds = $this->parseClaimIds( $params ); |
159
|
|
|
$constraintIDs = $params[self::PARAM_CONSTRAINT_ID]; |
160
|
|
|
$statuses = $params[self::PARAM_STATUS]; |
161
|
|
|
|
162
|
|
|
$this->getResult()->addValue( |
163
|
|
|
null, |
164
|
|
|
$this->getModuleName(), |
165
|
|
|
$this->checkResultsRenderer->render( |
166
|
|
|
$this->resultsSource->getResults( |
167
|
|
|
$entityIds, |
168
|
|
|
$claimIds, |
169
|
|
|
$constraintIDs, |
170
|
|
|
$statuses |
171
|
|
|
) |
172
|
|
|
)->getArray() |
173
|
|
|
); |
174
|
|
|
$this->resultBuilder->markSuccess( 1 ); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param array $params |
179
|
|
|
* |
180
|
|
|
* @return EntityId[] |
181
|
|
|
*/ |
182
|
|
|
private function parseEntityIds( array $params ) { |
183
|
|
|
$ids = $params[self::PARAM_ID]; |
184
|
|
|
|
185
|
|
View Code Duplication |
if ( $ids === null ) { |
|
|
|
|
186
|
|
|
return []; |
187
|
|
|
} elseif ( $ids === [] ) { |
188
|
|
|
$this->errorReporter->dieError( |
189
|
|
|
'If ' . self::PARAM_ID . ' is specified, it must be nonempty.', 'no-data' ); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return array_map( function ( $id ) { |
193
|
|
|
try { |
194
|
|
|
return $this->entityIdParser->parse( $id ); |
195
|
|
|
} catch ( EntityIdParsingException $e ) { |
196
|
|
|
$this->errorReporter->dieError( |
197
|
|
|
"Invalid id: $id", 'invalid-entity-id', 0, [ self::PARAM_ID => $id ] ); |
198
|
|
|
} |
199
|
|
|
}, $ids ); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @param array $params |
204
|
|
|
* |
205
|
|
|
* @return string[] |
206
|
|
|
*/ |
207
|
|
|
private function parseClaimIds( array $params ) { |
208
|
|
|
$ids = $params[self::PARAM_CLAIM_ID]; |
209
|
|
|
|
210
|
|
View Code Duplication |
if ( $ids === null ) { |
|
|
|
|
211
|
|
|
return []; |
212
|
|
|
} elseif ( $ids === [] ) { |
213
|
|
|
$this->errorReporter->dieError( |
214
|
|
|
'If ' . self::PARAM_CLAIM_ID . ' is specified, it must be nonempty.', 'no-data' ); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
foreach ( $ids as $id ) { |
218
|
|
|
if ( !$this->statementGuidValidator->validate( $id ) ) { |
219
|
|
|
$this->errorReporter->dieError( |
220
|
|
|
"Invalid claim id: $id", 'invalid-guid', 0, [ self::PARAM_CLAIM_ID => $id ] ); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
return $ids; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
private function validateParameters( array $params ) { |
228
|
|
|
if ( $params[self::PARAM_CONSTRAINT_ID] !== null |
229
|
|
|
&& empty( $params[self::PARAM_CONSTRAINT_ID] ) |
230
|
|
|
) { |
231
|
|
|
$paramConstraintId = self::PARAM_CONSTRAINT_ID; |
232
|
|
|
$this->errorReporter->dieError( |
233
|
|
|
"If $paramConstraintId is specified, it must be nonempty.", 'no-data' ); |
234
|
|
|
} |
235
|
|
|
if ( $params[self::PARAM_ID] === null && $params[self::PARAM_CLAIM_ID] === null ) { |
236
|
|
|
$paramId = self::PARAM_ID; |
237
|
|
|
$paramClaimId = self::PARAM_CLAIM_ID; |
238
|
|
|
$this->errorReporter->dieError( |
239
|
|
|
"At least one of $paramId, $paramClaimId must be specified.", 'no-data' ); |
240
|
|
|
} |
241
|
|
|
// contents of PARAM_ID and PARAM_CLAIM_ID are validated by parse{Entity,Claim}Ids() |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @return array[] |
246
|
|
|
* @codeCoverageIgnore |
247
|
|
|
*/ |
248
|
|
|
public function getAllowedParams() { |
249
|
|
|
return [ |
250
|
|
|
self::PARAM_ID => [ |
251
|
|
|
ApiBase::PARAM_TYPE => 'string', |
252
|
|
|
ApiBase::PARAM_ISMULTI => true, |
253
|
|
|
], |
254
|
|
|
self::PARAM_CLAIM_ID => [ |
255
|
|
|
ApiBase::PARAM_TYPE => 'string', |
256
|
|
|
ApiBase::PARAM_ISMULTI => true, |
257
|
|
|
], |
258
|
|
|
self::PARAM_CONSTRAINT_ID => [ |
259
|
|
|
ApiBase::PARAM_TYPE => 'string', |
260
|
|
|
ApiBase::PARAM_ISMULTI => true, |
261
|
|
|
], |
262
|
|
|
self::PARAM_STATUS => [ |
263
|
|
|
ApiBase::PARAM_TYPE => [ |
264
|
|
|
CheckResult::STATUS_COMPLIANCE, |
265
|
|
|
CheckResult::STATUS_VIOLATION, |
266
|
|
|
CheckResult::STATUS_WARNING, |
267
|
|
|
CheckResult::STATUS_SUGGESTION, |
268
|
|
|
CheckResult::STATUS_EXCEPTION, |
269
|
|
|
CheckResult::STATUS_NOT_IN_SCOPE, |
270
|
|
|
CheckResult::STATUS_DEPRECATED, |
271
|
|
|
CheckResult::STATUS_BAD_PARAMETERS, |
272
|
|
|
CheckResult::STATUS_TODO, |
273
|
|
|
], |
274
|
|
|
ApiBase::PARAM_ISMULTI => true, |
275
|
|
|
ApiBase::PARAM_ALL => true, |
276
|
|
|
ApiBase::PARAM_DFLT => implode( '|', CachingResultsSource::CACHED_STATUSES ), |
277
|
|
|
ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
278
|
|
|
], |
279
|
|
|
]; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Returns usage examples for this module |
284
|
|
|
* |
285
|
|
|
* @return string[] |
286
|
|
|
* @codeCoverageIgnore |
287
|
|
|
*/ |
288
|
|
|
public function getExamplesMessages() { |
289
|
|
|
return [ |
290
|
|
|
'action=wbcheckconstraints&id=Q5|Q42' |
291
|
|
|
=> 'apihelp-wbcheckconstraints-example-1', |
292
|
|
|
'action=wbcheckconstraints&claimid=q42%248419C20C-8EF8-4EC0-80D6-AF1CA55E7557' |
293
|
|
|
=> 'apihelp-wbcheckconstraints-example-2', |
294
|
|
|
'action=wbcheckconstraints&format=json&id=Q2&constraintid=P1082%24DA39C2DA-47DA-48FB-8A9A-DA80200FB2DB' |
295
|
|
|
=> 'apihelp-wbcheckconstraints-example-3', |
296
|
|
|
]; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
} |
300
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.