SetReference::getAllowedParams()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 9.344
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Wikibase\Repo\Api;
6
7
use ApiBase;
8
use ApiMain;
9
use Deserializers\Exceptions\DeserializationException;
10
use Wikibase\DataModel\DeserializerFactory;
11
use Wikibase\DataModel\Reference;
12
use Wikibase\DataModel\Services\Statement\StatementGuidParser;
13
use Wikibase\DataModel\Snak\SnakList;
14
use Wikibase\DataModel\Statement\Statement;
15
use Wikibase\Repo\ChangeOp\ChangeOp;
16
use Wikibase\Repo\ChangeOp\StatementChangeOpFactory;
17
use Wikibase\Repo\WikibaseRepo;
18
19
/**
20
 * API module for creating a reference or setting the value of an existing one.
21
 *
22
 * @license GPL-2.0-or-later
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author Tobias Gritschacher < [email protected] >
25
 */
26
class SetReference extends ApiBase {
27
28
	use FederatedPropertyApiValidatorTrait;
29
30
	/**
31
	 * @var StatementChangeOpFactory
32
	 */
33
	private $statementChangeOpFactory;
34
35
	/**
36
	 * @var ApiErrorReporter
37
	 */
38
	protected $errorReporter;
39
40
	/**
41
	 * @var DeserializerFactory
42
	 */
43
	private $deserializerFactory;
44
45
	/**
46
	 * @var StatementModificationHelper
47
	 */
48
	private $modificationHelper;
49
50
	/**
51
	 * @var StatementGuidParser
52
	 */
53
	private $guidParser;
54
55
	/**
56
	 * @var ResultBuilder
57
	 */
58
	private $resultBuilder;
59
60
	/**
61
	 * @var EntitySavingHelper
62
	 */
63
	private $entitySavingHelper;
64
65
	public function __construct(
66
		ApiMain $mainModule,
67
		string $moduleName,
68
		DeserializerFactory $deserializerFactory,
69
		ApiErrorReporter $errorReporter,
70
		StatementChangeOpFactory $statementChangeOpFactory,
71
		StatementModificationHelper $modificationHelper,
72
		StatementGuidParser $guidParser,
73
		callable $resultBuilderInstantiator,
74
		callable $entitySavingHelperInstantiator,
75
		bool $federatedPropertiesEnabled
76
	) {
77
		parent::__construct( $mainModule, $moduleName );
78
79
		$this->deserializerFactory = $deserializerFactory;
80
		$this->errorReporter = $errorReporter;
81
		$this->statementChangeOpFactory = $statementChangeOpFactory;
82
		$this->modificationHelper = $modificationHelper;
83
		$this->guidParser = $guidParser;
84
		$this->resultBuilder = $resultBuilderInstantiator( $this );
85
		$this->entitySavingHelper = $entitySavingHelperInstantiator( $this );
86
		$this->federatedPropertiesEnabled = $federatedPropertiesEnabled;
87
	}
88
89
	public static function factory( ApiMain $mainModule, string $moduleName ): self {
90
		$wikibaseRepo = WikibaseRepo::getDefaultInstance();
91
		$apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $mainModule->getContext() );
92
		$changeOpFactoryProvider = $wikibaseRepo->getChangeOpFactoryProvider();
93
94
		$modificationHelper = new StatementModificationHelper(
95
			$wikibaseRepo->getSnakFactory(),
96
			$wikibaseRepo->getEntityIdParser(),
97
			$wikibaseRepo->getStatementGuidValidator(),
98
			$apiHelperFactory->getErrorReporter( $mainModule )
99
		);
100
101
		return new self(
102
			$mainModule,
103
			$moduleName,
104
			$wikibaseRepo->getBaseDataModelDeserializerFactory(),
105
			$apiHelperFactory->getErrorReporter( $mainModule ),
106
			$changeOpFactoryProvider->getStatementChangeOpFactory(),
107
			$modificationHelper,
108
			$wikibaseRepo->getStatementGuidParser(),
109
			function ( $module ) use ( $apiHelperFactory ) {
110
				return $apiHelperFactory->getResultBuilder( $module );
111
			},
112
			function ( $module ) use ( $apiHelperFactory ) {
113
				return $apiHelperFactory->getEntitySavingHelper( $module );
114
			},
115
			$wikibaseRepo->inFederatedPropertyMode()
116
		);
117
	}
118
119
	/**
120
	 * @inheritDoc
121
	 */
122
	public function execute(): void {
123
		$params = $this->extractRequestParams();
124
		$this->validateParameters( $params );
125
126
		$entityId = $this->guidParser->parse( $params['statement'] )->getEntityId();
127
		$this->validateAlteringEntityById( $entityId );
128
129
		$entity = $this->entitySavingHelper->loadEntity( $entityId );
130
131
		$summary = $this->modificationHelper->createSummary( $params, $this );
132
133
		$claim = $this->modificationHelper->getStatementFromEntity( $params['statement'], $entity );
134
135
		if ( isset( $params['reference'] ) ) {
136
			$this->validateReferenceHash( $claim, $params['reference'] );
137
		}
138
139
		if ( isset( $params['snaks-order' ] ) ) {
140
			$snaksOrder = $this->getArrayFromParam( $params['snaks-order'], 'snaks-order' );
141
		} else {
142
			$snaksOrder = [];
143
		}
144
145
		$deserializer = $this->deserializerFactory->newSnakListDeserializer();
146
		/** @var SnakList $snakList */
147
		try {
148
			$snakList = $deserializer->deserialize( $this->getArrayFromParam( $params['snaks'], 'snaks' ) );
149
		} catch ( DeserializationException $e ) {
150
			$this->errorReporter->dieError(
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
151
				'Failed to get reference from reference Serialization ' . $e->getMessage(),
152
				'snak-instantiation-failure'
153
			);
154
		}
155
		$snakList->orderByProperty( $snaksOrder );
156
157
		$newReference = new Reference( $snakList );
158
159
		$changeOp = $this->getChangeOp( $newReference );
160
		$this->modificationHelper->applyChangeOp( $changeOp, $entity, $summary );
161
162
		$status = $this->entitySavingHelper->attemptSaveEntity( $entity, $summary );
163
		$this->resultBuilder->addRevisionIdFromStatusToResult( $status, 'pageinfo' );
164
		$this->resultBuilder->markSuccess();
165
		$this->resultBuilder->addReference( $newReference );
166
	}
167
168
	private function validateParameters( array $params ): void {
169
		if ( !( $this->modificationHelper->validateStatementGuid( $params['statement'] ) ) ) {
170
			$this->errorReporter->dieError( 'Invalid claim guid', 'invalid-guid' );
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
171
		}
172
	}
173
174
	private function validateReferenceHash( Statement $statement, string $referenceHash ): void {
175
		if ( !$statement->getReferences()->hasReferenceHash( $referenceHash ) ) {
176
			$this->errorReporter->dieError(
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
177
				'Statement does not have a reference with the given hash',
178
				'no-such-reference'
179
			);
180
		}
181
	}
182
183
	private function getArrayFromParam( string $arrayParam, string $parameter ): array {
184
		$rawArray = json_decode( $arrayParam, true );
185
186
		if ( !is_array( $rawArray ) || !count( $rawArray ) ) {
187
			$this->errorReporter->dieError(
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
188
				'No array or invalid JSON given for parameter: ' . $parameter,
189
				'invalid-json'
190
			);
191
		}
192
193
		return $rawArray;
194
	}
195
196
	private function getChangeOp( Reference $reference ): ChangeOp {
197
		$params = $this->extractRequestParams();
198
199
		$guid = $params['statement'];
200
		$hash = $params['reference'] ?? '';
201
		$index = $params['index'] ?? null;
202
203
		return $this->statementChangeOpFactory->newSetReferenceOp( $guid, $reference, $hash, $index );
204
	}
205
206
	/**
207
	 * @inheritDoc
208
	 */
209
	public function isWriteMode(): bool {
210
		return true;
211
	}
212
213
	/**
214
	 * @see ApiBase::needsToken
215
	 *
216
	 * @return string
217
	 */
218
	public function needsToken(): string {
219
		return 'csrf';
220
	}
221
222
	/**
223
	 * @inheritDoc
224
	 */
225
	protected function getAllowedParams(): array {
226
		return array_merge(
227
			[
228
				'statement' => [
229
					self::PARAM_TYPE => 'string',
230
					self::PARAM_REQUIRED => true,
231
				],
232
				'snaks' => [
233
					self::PARAM_TYPE => 'text',
234
					self::PARAM_REQUIRED => true,
235
				],
236
				'snaks-order' => [
237
					self::PARAM_TYPE => 'string',
238
				],
239
				'reference' => [
240
					self::PARAM_TYPE => 'string',
241
				],
242
				'index' => [
243
					self::PARAM_TYPE => 'integer',
244
				],
245
				'summary' => [
246
					self::PARAM_TYPE => 'string',
247
				],
248
				'tags' => [
249
					self::PARAM_TYPE => 'tags',
250
					self::PARAM_ISMULTI => true,
251
				],
252
				'token' => null,
253
				'baserevid' => [
254
					self::PARAM_TYPE => 'integer',
255
				],
256
				'bot' => false,
257
			],
258
			parent::getAllowedParams()
259
		);
260
	}
261
262
	/**
263
	 * @inheritDoc
264
	 */
265
	protected function getExamplesMessages(): array {
266
		return [
267
			'action=wbsetreference&statement=Q76$D4FDE516-F20C-4154-ADCE-7C5B609DFDFF&snaks='
268
				. '{"P212":[{"snaktype":"value","property":"P212","datavalue":{"type":"string",'
269
				. '"value":"foo"}}]}&baserevid=7201010&token=foobar'
270
				=> 'apihelp-wbsetreference-example-1',
271
			'action=wbsetreference&statement=Q76$D4FDE516-F20C-4154-ADCE-7C5B609DFDFF'
272
				. '&reference=1eb8793c002b1d9820c833d234a1b54c8e94187e&snaks='
273
				. '{"P212":[{"snaktype":"value","property":"P212","datavalue":{"type":"string",'
274
				. '"value":"bar"}}]}&baserevid=7201010&token=foobar'
275
				=> 'apihelp-wbsetreference-example-2',
276
			'action=wbsetreference&statement=Q76$D4FDE516-F20C-4154-ADCE-7C5B609DFDFF&snaks='
277
				. '{"P212":[{"snaktype":"novalue","property":"P212"}]}'
278
				. '&index=0&baserevid=7201010&token=foobar'
279
				=> 'apihelp-wbsetreference-example-3',
280
		];
281
	}
282
283
}
284