Completed
Push — master ( a718f1...259f04 )
by
unknown
01:49
created

CheckConstraintParameters::newFromGlobalState()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 9.472
c 0
b 0
f 0
cc 1
nc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\Api;
4
5
use ApiBase;
6
use ApiMain;
7
use ApiResult;
8
use Config;
9
use IBufferingStatsdDataFactory;
10
use InvalidArgumentException;
11
use Wikibase\DataModel\Entity\PropertyId;
12
use Wikibase\DataModel\Services\Statement\StatementGuidParser;
13
use Wikibase\DataModel\Services\Statement\StatementGuidParsingException;
14
use Wikibase\Lib\Formatters\OutputFormatValueFormatterFactory;
15
use Wikibase\Repo\Api\ApiErrorReporter;
16
use Wikibase\Repo\Api\ApiHelperFactory;
17
use Wikibase\View\EntityIdFormatterFactory;
18
use WikibaseQuality\ConstraintReport\ConstraintCheck\DelegatingConstraintChecker;
19
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
20
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessageRendererFactory;
21
22
/**
23
 * API module that checks whether the parameters of a constraint statement are valid.
24
 *
25
 * @author Lucas Werkmeister
26
 * @license GPL-2.0-or-later
27
 */
28
class CheckConstraintParameters extends ApiBase {
29
30
	public const PARAM_PROPERTY_ID = 'propertyid';
31
	public const PARAM_CONSTRAINT_ID = 'constraintid';
32
	public const KEY_STATUS = 'status';
33
	public const STATUS_OKAY = 'okay';
34
	public const STATUS_NOT_OKAY = 'not-okay';
35
	public const STATUS_NOT_FOUND = 'not-found';
36
	public const KEY_PROBLEMS = 'problems';
37
	public const KEY_MESSAGE_HTML = 'message-html';
38
39
	/**
40
	 * @var ApiErrorReporter
41
	 */
42
	private $apiErrorReporter;
43
44
	/**
45
	 * @var DelegatingConstraintChecker
46
	 */
47
	private $delegatingConstraintChecker;
48
49
	/**
50
	 * @var ViolationMessageRendererFactory
51
	 */
52
	private $violationMessageRendererFactory;
53
54
	/**
55
	 * @var StatementGuidParser
56
	 */
57
	private $statementGuidParser;
58
59
	/**
60
	 * @var IBufferingStatsdDataFactory
61
	 */
62
	private $dataFactory;
63
64
	/**
65
	 * Creates new instance from global state.
66
	 */
67
	public static function newFromGlobalState(
68
		ApiMain $main,
69
		string $name,
70
		Config $config,
71
		IBufferingStatsdDataFactory $dataFactory,
72
		ApiHelperFactory $apiHelperFactory,
73
		EntityIdFormatterFactory $entityIdFormatterFactory,
74
		StatementGuidParser $statementGuidParser,
75
		OutputFormatValueFormatterFactory $valueFormatterFactory,
76
		DelegatingConstraintChecker $delegatingConstraintChecker
77
	): self {
78
		$violationMessageRendererFactory = new ViolationMessageRendererFactory(
79
			$config,
80
			$main,
81
			$entityIdFormatterFactory,
82
			$valueFormatterFactory
83
		);
84
85
		return new self(
86
			$main,
87
			$name,
88
			$apiHelperFactory,
89
			$delegatingConstraintChecker,
90
			$violationMessageRendererFactory,
91
			$statementGuidParser,
92
			$dataFactory
93
		);
94
	}
95
96
	public function __construct(
97
		ApiMain $main,
98
		string $name,
99
		ApiHelperFactory $apiHelperFactory,
100
		DelegatingConstraintChecker $delegatingConstraintChecker,
101
		ViolationMessageRendererFactory $violationMessageRendererFactory,
102
		StatementGuidParser $statementGuidParser,
103
		IBufferingStatsdDataFactory $dataFactory
104
	) {
105
		parent::__construct( $main, $name );
106
107
		$this->apiErrorReporter = $apiHelperFactory->getErrorReporter( $this );
108
		$this->delegatingConstraintChecker = $delegatingConstraintChecker;
109
		$this->violationMessageRendererFactory = $violationMessageRendererFactory;
110
		$this->statementGuidParser = $statementGuidParser;
111
		$this->dataFactory = $dataFactory;
112
	}
113
114
	public function execute() {
115
		$this->dataFactory->increment(
116
			'wikibase.quality.constraints.api.checkConstraintParameters.execute'
117
		);
118
119
		$params = $this->extractRequestParams();
120
		$result = $this->getResult();
121
122
		$propertyIds = $this->parsePropertyIds( $params[self::PARAM_PROPERTY_ID] );
123
		$constraintIds = $this->parseConstraintIds( $params[self::PARAM_CONSTRAINT_ID] );
124
125
		$this->checkPropertyIds( $propertyIds, $result );
126
		$this->checkConstraintIds( $constraintIds, $result );
127
128
		$result->addValue( null, 'success', 1 );
129
	}
130
131
	/**
132
	 * @param array|null $propertyIdSerializations
133
	 * @return PropertyId[]
134
	 */
135
	private function parsePropertyIds( $propertyIdSerializations ) {
136
		if ( $propertyIdSerializations === null ) {
137
			return [];
138
		} elseif ( empty( $propertyIdSerializations ) ) {
139
			$this->apiErrorReporter->dieError(
140
				'If ' . self::PARAM_PROPERTY_ID . ' is specified, it must be nonempty.',
141
				'no-data'
142
			);
143
		}
144
145
		return array_map(
146
			function ( $propertyIdSerialization ) {
147
				try {
148
					return new PropertyId( $propertyIdSerialization );
149
				} catch ( InvalidArgumentException $e ) {
150
					$this->apiErrorReporter->dieError(
151
						"Invalid id: $propertyIdSerialization",
152
						'invalid-property-id',
153
						0, // default argument
154
						[ self::PARAM_PROPERTY_ID => $propertyIdSerialization ]
155
					);
156
				}
157
			},
158
			$propertyIdSerializations
159
		);
160
	}
161
162
	/**
163
	 * @param array|null $constraintIds
164
	 * @return string[]
165
	 */
166
	private function parseConstraintIds( $constraintIds ) {
167
		if ( $constraintIds === null ) {
168
			return [];
169
		} elseif ( empty( $constraintIds ) ) {
170
			$this->apiErrorReporter->dieError(
171
				'If ' . self::PARAM_CONSTRAINT_ID . ' is specified, it must be nonempty.',
172
				'no-data'
173
			);
174
		}
175
176
		return array_map(
177
			function ( $constraintId ) {
178
				try {
179
					$propertyId = $this->statementGuidParser->parse( $constraintId )->getEntityId();
180
					if ( !$propertyId instanceof PropertyId ) {
181
						$this->apiErrorReporter->dieError(
182
							"Invalid property ID: {$propertyId->getSerialization()}",
183
							'invalid-property-id',
184
							0, // default argument
185
							[ self::PARAM_CONSTRAINT_ID => $constraintId ]
186
						);
187
					}
188
					return $constraintId;
189
				} catch ( StatementGuidParsingException $e ) {
190
					$this->apiErrorReporter->dieError(
191
						"Invalid statement GUID: $constraintId",
192
						'invalid-guid',
193
						0, // default argument
194
						[ self::PARAM_CONSTRAINT_ID => $constraintId ]
195
					);
196
				}
197
			},
198
			$constraintIds
199
		);
200
	}
201
202
	/**
203
	 * @param PropertyId[] $propertyIds
204
	 * @param ApiResult $result
205
	 */
206
	private function checkPropertyIds( array $propertyIds, ApiResult $result ) {
207
		foreach ( $propertyIds as $propertyId ) {
208
			$result->addArrayType( $this->getResultPathForPropertyId( $propertyId ), 'assoc' );
209
			$allConstraintExceptions = $this->delegatingConstraintChecker
210
				->checkConstraintParametersOnPropertyId( $propertyId );
211
			foreach ( $allConstraintExceptions as $constraintId => $constraintParameterExceptions ) {
212
				$this->addConstraintParameterExceptionsToResult(
213
					$constraintId,
214
					$constraintParameterExceptions,
215
					$result
216
				);
217
			}
218
		}
219
	}
220
221
	/**
222
	 * @param string[] $constraintIds
223
	 * @param ApiResult $result
224
	 */
225
	private function checkConstraintIds( array $constraintIds, ApiResult $result ) {
226
		foreach ( $constraintIds as $constraintId ) {
227
			if ( $result->getResultData( $this->getResultPathForConstraintId( $constraintId ) ) ) {
228
				// already checked as part of checkPropertyIds()
229
				continue;
230
			}
231
			$constraintParameterExceptions = $this->delegatingConstraintChecker
232
				->checkConstraintParametersOnConstraintId( $constraintId );
233
			$this->addConstraintParameterExceptionsToResult( $constraintId, $constraintParameterExceptions, $result );
234
		}
235
	}
236
237
	/**
238
	 * @param PropertyId $propertyId
239
	 * @return string[]
240
	 */
241
	private function getResultPathForPropertyId( PropertyId $propertyId ) {
242
		return [ $this->getModuleName(), $propertyId->getSerialization() ];
243
	}
244
245
	/**
246
	 * @param string $constraintId
247
	 * @return string[]
248
	 */
249
	private function getResultPathForConstraintId( $constraintId ) {
250
		$propertyId = $this->statementGuidParser->parse( $constraintId )->getEntityId();
251
		'@phan-var PropertyId $propertyId';
252
		return array_merge( $this->getResultPathForPropertyId( $propertyId ), [ $constraintId ] );
253
	}
254
255
	/**
256
	 * Add the ConstraintParameterExceptions for $constraintId to the API result.
257
	 *
258
	 * @param string $constraintId
259
	 * @param ConstraintParameterException[]|null $constraintParameterExceptions
260
	 * @param ApiResult $result
261
	 */
262
	private function addConstraintParameterExceptionsToResult(
263
		$constraintId,
264
		$constraintParameterExceptions,
265
		ApiResult $result
266
	) {
267
		$path = $this->getResultPathForConstraintId( $constraintId );
268
		if ( $constraintParameterExceptions === null ) {
269
			$result->addValue(
270
				$path,
271
				self::KEY_STATUS,
272
				self::STATUS_NOT_FOUND
273
			);
274
		} else {
275
			$result->addValue(
276
				$path,
277
				self::KEY_STATUS,
278
				empty( $constraintParameterExceptions ) ? self::STATUS_OKAY : self::STATUS_NOT_OKAY
279
			);
280
281
			$violationMessageRenderer = $this->violationMessageRendererFactory
282
				->getViolationMessageRenderer( $this->getLanguage() );
283
			$problems = [];
284
			foreach ( $constraintParameterExceptions as $constraintParameterException ) {
285
				$problems[] = [
286
					self::KEY_MESSAGE_HTML => $violationMessageRenderer->render(
287
						$constraintParameterException->getViolationMessage() ),
288
				];
289
			}
290
			$result->addValue(
291
				$path,
292
				self::KEY_PROBLEMS,
293
				$problems
294
			);
295
		}
296
	}
297
298
	/**
299
	 * @return array[]
300
	 * @codeCoverageIgnore
301
	 */
302
	public function getAllowedParams() {
303
		return [
304
			self::PARAM_PROPERTY_ID => [
305
				ApiBase::PARAM_TYPE => 'string',
306
				ApiBase::PARAM_ISMULTI => true
307
			],
308
			self::PARAM_CONSTRAINT_ID => [
309
				ApiBase::PARAM_TYPE => 'string',
310
				ApiBase::PARAM_ISMULTI => true
311
			]
312
		];
313
	}
314
315
	/**
316
	 * @return string[]
317
	 * @codeCoverageIgnore
318
	 */
319
	public function getExamplesMessages() {
320
		return [
321
			'action=wbcheckconstraintparameters&propertyid=P247'
322
				=> 'apihelp-wbcheckconstraintparameters-example-propertyid-1',
323
			'action=wbcheckconstraintparameters&' .
324
			'constraintid=P247$0fe1711e-4c0f-82ce-3af0-830b721d0fba|' .
325
			'P225$cdc71e4a-47a0-12c5-dfb3-3f6fc0b6613f'
326
				=> 'apihelp-wbcheckconstraintparameters-example-constraintid-2',
327
		];
328
	}
329
330
}
331