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