ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
RequestProcessor.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
28use ILIAS\MetaData\OERExposer\OAIPMH\DateHelper;
31
33{
34 use DateHelper;
35
38 protected ExposedRecordsRepository $records_repository;
40
41 protected readonly string $valid_md_prefix;
42 protected readonly string $default_set;
43 protected readonly int $max_list_size;
44
45 public function __construct(
48 ExposedRecordsRepository $resource_status_repository,
50 ) {
51 $this->writer = $writer;
52 $this->settings = $settings;
53 $this->records_repository = $resource_status_repository;
54 $this->token_handler = $token_handler;
55
56 $this->valid_md_prefix = 'oai_dc';
57 $this->default_set = 'default';
58 $this->max_list_size = 100;
59 }
60
61 public function getResponseToRequest(RequestInterface $request): \DomDocument
62 {
63 if ($request->verb() === Verb::NULL) {
64 return $this->writer->writeErrorResponse(
65 $request,
66 $this->writer->writeError(
67 Error::BAD_VERB,
68 'No valid OAI-PMH verb in request.'
69 )
70 );
71 }
72
73 return match ($request->verb()) {
74 Verb::GET_RECORD => $this->getRecord($request),
75 Verb::IDENTIFY => $this->identify($request),
76 Verb::LIST_IDENTIFIERS, Verb::LIST_RECORDS => $this->listRecordsOrIdentifiers($request),
77 Verb::LIST_MD_FORMATS => $this->listMetadataFormats($request),
78 Verb::LIST_SETS => $this->listSets($request),
79 default => $this->writer->writeErrorResponse(
80 $request,
81 $this->writer->writeError(
82 Error::BAD_VERB,
83 'No valid OAI-PMH verb in request.'
84 )
85 )
86 };
87 }
88
89 protected function getRecord(RequestInterface $request): \DomDocument
90 {
91 $errors = [];
92 if (!$request->hasCorrectArguments([Argument::IDENTIFIER, Argument::MD_PREFIX], [], [])) {
93 $errors[] = $this->writeBadArgumentError(
94 Verb::GET_RECORD,
95 ...$request->argumentKeys()
96 );
97 }
98
99 if (
100 $request->hasArgument(Argument::MD_PREFIX) &&
101 $request->argumentValue(Argument::MD_PREFIX) !== $this->valid_md_prefix
102 ) {
103 $errors[] = $this->writer->writeError(
104 Error::CANNOT_DISSEMINATE_FORMAT,
105 'This repository only supports oai_dc as metadata format.'
106 );
107 }
108
109 $record = null;
110 if ($request->hasArgument(Argument::IDENTIFIER)) {
111 $identifier = $request->argumentValue(Argument::IDENTIFIER);
112 if (!$this->isIdentifierValid($identifier)) {
113 $errors[] = $this->writer->writeError(
114 Error::ID_DOES_NOT_EXIST,
115 'Identifier "' . $identifier . '" is invalid for this repository.'
116 );
117 } elseif (is_null($record = $this->records_repository->getRecordByIdentifier(
118 $this->removePrefixFromIdentifier($identifier)
119 ))) {
120 $errors[] = $this->writer->writeError(
121 Error::ID_DOES_NOT_EXIST,
122 'This repository does not have a record with identifier "' . $identifier . '".'
123 );
124 }
125 }
126
127 if (!empty($errors)) {
128 return $this->writer->writeErrorResponse($request, ...$errors);
129 }
130 return $this->writer->writeResponse(
131 $request,
132 $this->writeRecord($record)
133 );
134 }
135
136 protected function identify(RequestInterface $request): \DomDocument
137 {
138 if (!$request->hasCorrectArguments([], [], [])) {
139 return $this->writer->writeErrorResponse(
140 $request,
142 Verb::IDENTIFY,
143 ...$request->argumentKeys()
144 )
145 );
146 }
147
148 return $this->writer->writeResponse(
149 $request,
150 ...$this->writer->writeIdentifyElements(
151 $this->settings->getOAIRepositoryName(),
152 $request->baseURL(),
153 $this->records_repository->getEarliestDatestamp(),
154 $this->settings->getOAIContactMail()
155 )
156 );
157 }
158
159 protected function listMetadataFormats(RequestInterface $request): \DomDocument
160 {
161 $errors = [];
162 if (!$request->hasCorrectArguments([], [Argument::IDENTIFIER], [])) {
163 $errors[] = $this->writeBadArgumentError(
164 Verb::LIST_MD_FORMATS,
165 ...$request->argumentKeys()
166 );
167 }
168
169 if ($request->hasArgument(Argument::IDENTIFIER)) {
170 $identifier = $request->argumentValue(Argument::IDENTIFIER);
171 if (!$this->isIdentifierValid($identifier)) {
172 $errors[] = $this->writer->writeError(
173 Error::ID_DOES_NOT_EXIST,
174 'Identifier "' . $identifier . '" is invalid for this repository.'
175 );
176 } elseif (!$this->records_repository->doesRecordWithIdentifierExist(
177 $this->removePrefixFromIdentifier($identifier)
178 )) {
179 $errors[] = $this->writer->writeError(
180 Error::ID_DOES_NOT_EXIST,
181 'This repository does not have a record with identifier "' . $identifier . '".'
182 );
183 }
184 }
185
186 if (!empty($errors)) {
187 return $this->writer->writeErrorResponse($request, ...$errors);
188 }
189 return $this->writer->writeResponse(
190 $request,
191 $this->writer->writeMetadataFormat()
192 );
193 }
194
195 protected function listSets(RequestInterface $request): \DomDocument
196 {
197 $errors = [];
198 if (!$request->hasCorrectArguments([], [], [Argument::RESUMPTION_TOKEN])) {
199 $errors[] = $this->writeBadArgumentError(
201 ...$request->argumentKeys()
202 );
203 }
204
205 if ($request->hasArgument(Argument::RESUMPTION_TOKEN)) {
206 $errors[] = $this->writer->writeError(
207 Error::BAD_RESUMTPION_TOKEN,
208 'ListSets does not issue resumption tokens.'
209 );
210 }
211
212 if (!empty($errors)) {
213 return $this->writer->writeErrorResponse($request, ...$errors);
214 }
215 return $this->writer->writeResponse(
216 $request,
217 $this->writer->writeSet($this->default_set, $this->default_set)
218 );
219 }
220
221 protected function listRecordsOrIdentifiers(
222 RequestInterface $request
223 ): \DomDocument {
224 $errors = [];
225 if (!$request->hasCorrectArguments(
226 [Argument::MD_PREFIX],
227 [Argument::FROM_DATE, Argument::UNTIL_DATE, Argument::SET],
228 [Argument::RESUMPTION_TOKEN]
229 )) {
230 $errors[] = $this->writeBadArgumentError(
231 Verb::LIST_IDENTIFIERS,
232 ...$request->argumentKeys()
233 );
234 }
235
236 if (
237 $request->hasArgument(Argument::SET) &&
238 $request->argumentValue(Argument::SET) !== $this->default_set
239 ) {
240 $errors[] = $this->writer->writeError(
241 Error::NO_RECORDS_MATCH,
242 "This repository only supports a trivial set named 'default'."
243 );
244 }
245
246 if (
247 $request->hasArgument(Argument::MD_PREFIX) &&
248 $request->argumentValue(Argument::MD_PREFIX) !== $this->valid_md_prefix
249 ) {
250 $errors[] = $this->writer->writeError(
251 Error::CANNOT_DISSEMINATE_FORMAT,
252 'This repository only supports oai_dc as metadata format.'
253 );
254 }
255
256 $effective_request = clone $request;
257 $offset = 0;
258 if ($request->hasArgument(Argument::RESUMPTION_TOKEN)) {
259 $token = $request->argumentValue(Argument::RESUMPTION_TOKEN);
260 if (!$this->token_handler->isTokenValid($token)) {
261 $errors[] = $this->writer->writeError(
262 Error::BAD_RESUMTPION_TOKEN,
263 'Invalid resumption token for this repository.'
264 );
265 return $this->writer->writeErrorResponse($effective_request, ...$errors);
266 }
267 $effective_request = $this->token_handler->appendArgumentsFromTokenToRequest($effective_request, $token);
268 $offset = $this->token_handler->getOffsetFromToken($token);
269 }
270
271 $from_date = null;
272 if ($effective_request->hasArgument(Argument::FROM_DATE)) {
273 $from_date_string = $effective_request->argumentValue(Argument::FROM_DATE);
274 if ($this->isStringValidAsDate($from_date_string)) {
275 $from_date = $this->getDateFromString($from_date_string);
276 } else {
277 $errors[] = $this->writer->writeError(
278 Error::BAD_ARGUMENT,
279 'The date "' . $from_date_string . '" is invalid for this repository.'
280 );
281 }
282 }
283 $until_date = null;
284 if ($effective_request->hasArgument(Argument::UNTIL_DATE)) {
285 $until_date_string = $effective_request->argumentValue(Argument::UNTIL_DATE);
286 if ($this->isStringValidAsDate($until_date_string)) {
287 $until_date = $this->getDateFromString($until_date_string);
288 } else {
289 $errors[] = $this->writer->writeError(
290 Error::BAD_ARGUMENT,
291 'The date "' . $until_date_string . '" is invalid for this repository.'
292 );
293 }
294 }
295
296 $content_xmls = [];
297 if ($effective_request->verb() === Verb::LIST_IDENTIFIERS) {
298 $record_infos = $this->records_repository->getRecordInfos(
299 $from_date,
300 $until_date,
301 $this->max_list_size,
302 $offset
303 );
304 foreach ($record_infos as $info) {
305 $content_xmls[] = $this->writer->writeRecordHeader(
306 $this->settings->getOAIIdentifierPrefix() . $info->identfifier(),
307 $info->datestamp(),
308 $info->isDeleted()
309 );
310 }
311 } elseif ($effective_request->verb() === Verb::LIST_RECORDS) {
312 $records = $this->records_repository->getRecords(
313 $from_date,
314 $until_date,
315 $this->max_list_size,
316 $offset
317 );
318 foreach ($records as $record) {
319 $content_xmls[] = $this->writeRecord($record);
320 }
321 } else {
322 throw new \ilMDOERExposerException('Invalid verb handling.');
323 }
324
325 if (empty($content_xmls)) {
326 $errors[] = $this->writer->writeError(
327 Error::NO_RECORDS_MATCH,
328 'No matching records found.'
329 );
330 }
331
332 $count = $this->records_repository->getRecordCount($from_date, $until_date);
333 if (
334 $request->hasArgument(Argument::RESUMPTION_TOKEN) ||
335 $this->max_list_size < $count
336 ) {
337 $new_token = '';
338 if ($offset + $this->max_list_size < $count) {
339 $new_token = $this->token_handler->generateToken(
340 $offset + $this->max_list_size,
341 $from_date,
342 $until_date
343 );
344 }
345 $content_xmls[] = $this->writer->writeResumptionToken(
346 $new_token,
347 $count,
348 $offset
349 );
350 }
351
352 if (!empty($errors)) {
353 return $this->writer->writeErrorResponse($request, ...$errors);
354 }
355 return $this->writer->writeResponse(
356 $request,
357 ...$content_xmls
358 );
359 }
360
361 protected function writeRecord(RecordInterface $record): \DomDocument
362 {
363 if ($record->infos()->isDeleted()) {
364 return $this->writer->writeDeletedRecord(
365 $this->settings->getOAIIdentifierPrefix() . $record->infos()->identfifier(),
366 $record->infos()->datestamp()
367 );
368 }
369 return $this->writer->writeRecord(
370 $this->settings->getOAIIdentifierPrefix() . $record->infos()->identfifier(),
371 $record->infos()->datestamp(),
372 $record->metadata()
373 );
374 }
375
376 protected function writeBadArgumentError(Verb $verb, Argument ...$arguments): \DomDocument
377 {
378 if (empty($arguments)) {
379 $message = $verb->value . ' must come with additional arguments.';
380 } else {
381 $arg_strings = [];
382 foreach ($arguments as $argument) {
383 $arg_strings[] = $argument->value;
384 }
385 $message = implode(', ', $arg_strings) .
386 ' is not a valid set of arguments for ' . $verb->value . '.';
387 }
388 return $this->writer->writeError(
389 Error::BAD_ARGUMENT,
390 $message
391 );
392 }
393
394 protected function isIdentifierValid(string $identifier): bool
395 {
396 return str_starts_with($identifier, $this->settings->getOAIIdentifierPrefix()) &&
397 substr($identifier, strlen($this->settings->getOAIIdentifierPrefix())) !== '';
398 }
399
400 protected function removePrefixFromIdentifier(string $identifier): string
401 {
402 if (str_starts_with($identifier, $this->settings->getOAIIdentifierPrefix())) {
403 $identifier = substr($identifier, strlen($this->settings->getOAIIdentifierPrefix()));
404 }
405 return $identifier;
406 }
407}
__construct(WriterInterface $writer, SettingsInterface $settings, ExposedRecordsRepository $resource_status_repository, TokenHandlerInterface $token_handler)
writeBadArgumentError(Verb $verb, Argument ... $arguments)
$info
Definition: entry_point.php:21
hasCorrectArguments(array $required, array $optional, array $exclusive)
Returns true if this either has all required arguments, any subset of the optional arguments,...
Processes OAI PMH requests according to https://www.openarchives.org/OAI/openarchivesprotocol....
metadata()
Deleted records don't carry metadata.
$token
Definition: xapitoken.php:67