Completed
Push — master ( c5da41...e1d486 )
by Pieter
22s queued 14s
created

convertSubActionToRequestBody()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 20
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 36
rs 9.6
1
<?php
2
3
namespace W2w\Lib\Apie\OpenApiSchema;
4
5
use erasys\OpenApi\Spec\v3 as OASv3;
6
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
7
use W2w\Lib\Apie\Core\ApiResourceMetadataFactory;
8
use W2w\Lib\Apie\Core\ClassResourceConverter;
9
use W2w\Lib\Apie\Core\IdentifierExtractor;
10
use W2w\Lib\Apie\Core\PluginContainer;
11
use W2w\Lib\Apie\Core\Resources\ApiResourcesInterface;
12
use W2w\Lib\Apie\Interfaces\SearchFilterProviderInterface;
13
use W2w\Lib\Apie\OpenApiSchema\SubActions\SubAction;
14
use W2w\Lib\Apie\OpenApiSchema\SubActions\SubActionContainer;
15
16
/**
17
 * Class that generated an OpenAPI spec from a list of API resources.
18
 */
19
class OpenApiSpecGenerator
20
{
21
    private $apiResources;
22
23
    private $converter;
24
25
    private $info;
26
27
    private $schemaGenerator;
28
29
    private $apiResourceMetadataFactory;
30
31
    private $identifierExtractor;
32
33
    private $baseUrl;
34
35
    private $subActionContainer;
36
37
    private $addSpecsHook;
38
39
    public function __construct(
40
        ApiResourcesInterface $apiResources,
41
        ClassResourceConverter $converter,
42
        OASv3\Info $info,
43
        SchemaGenerator $schemaGenerator,
44
        ApiResourceMetadataFactory $apiResourceMetadataFactory,
45
        IdentifierExtractor $identifierExtractor,
46
        string $baseUrl,
47
        SubActionContainer $subActionContainer,
48
        ?callable $addSpecsHook = null
49
    ) {
50
        $this->apiResources = $apiResources;
51
        $this->converter = $converter;
52
        $this->info = $info;
53
        $this->schemaGenerator = $schemaGenerator;
54
        $this->apiResourceMetadataFactory = $apiResourceMetadataFactory;
55
        $this->identifierExtractor = $identifierExtractor;
56
        $this->baseUrl = $baseUrl;
57
        $this->subActionContainer = $subActionContainer;
58
        $this->addSpecsHook = $addSpecsHook;
59
    }
60
61
    private function getIdentifierKey(string $className): ?string
62
    {
63
        $context = $this->apiResourceMetadataFactory->getMetadata($className)->getContext();
64
        return $this->identifierExtractor->getIdentifierKeyOfClass($className, $context);
65
    }
66
67
    /**
68
     * Gets an OpenAPI spec document.
69
     *
70
     * @return OASv3\Document
71
     */
72
    public function getOpenApiSpec(): OASv3\Document
73
    {
74
        $paths = [];
75
        foreach ($this->apiResources->getApiResources() as $apiResourceClass) {
76
            $path = $this->converter->normalize($apiResourceClass);
77
            $identifierName = $this->getIdentifierKey($apiResourceClass);
78
            $paths['/' . $path] = $this->convertAllToPathItem($apiResourceClass, $path);
79
            if ($identifierName) {
80
                $paths['/' . $path . '/{' . $identifierName . '}'] = $this->convertToPathItem($apiResourceClass, $path, $identifierName);
81
                foreach ($this->subActionContainer->getSubActionsForResourceClass($apiResourceClass) as $key => $subAction) {
82
                    $paths['/' . $path . '/{' . $identifierName . '}/' . $key] = $this->convertSubActionToPathItem($subAction, $path, $identifierName);
83
                }
84
            }
85
        }
86
87
        $stringSchema = new OASv3\Schema(['type' => 'string']);
88
        $stringOrIntSchema = new OASv3\Schema(['oneOf' => [$stringSchema, new OASv3\Schema(['type' => 'integer'])]]);
89
        $stringArraySchema = new OASv3\Schema(['type' => 'array', 'items' => $stringSchema]);
90
91
        $errorSchema = new OASv3\Reference('#/components/schemas/Error');
92
93
        $validationErrorSchema = new OASv3\Schema([
94
            'type'       => 'object',
95
            'properties' => [
96
                'type'    => $stringSchema,
97
                'message' => $stringSchema,
98
                'code'    => $stringOrIntSchema,
99
                'trace'   => $stringSchema,
100
                'errors'  => new OASv3\Schema([
101
                    'type'       => 'object',
102
                    'additionalProperties' => $stringArraySchema
103
                ]),
104
            ],
105
            'xml' => new OASv3\Xml(['name' => 'response']),
106
        ]);
107
108
        $doc = new OASv3\Document(
109
            $this->info,
110
            $paths,
111
            '3.0.1',
112
            [
113
                'servers' => [
114
                    new OASv3\Server($this->baseUrl),
115
                ],
116
                'components' => new OASv3\Components([
117
                    'schemas' => [
118
                        'Error' => new OASv3\Schema([
119
                            'type'       => 'object',
120
                            'properties' => [
121
                                'type'    => $stringSchema,
122
                                'message' => $stringSchema,
123
                                'code'    => $stringOrIntSchema,
124
                                'trace'   => $stringSchema,
125
                            ],
126
                            'xml' => new OASv3\Xml(['name' => 'response']),
127
                        ]),
128
                    ],
129
                    'headers' => [
130
                        'x-ratelimit-limit' => new Oasv3\Header(
131
                            'Request limit per hour',
132
                            [
133
                                'example' => 100,
134
                                'schema'  => new OASv3\Schema([
135
                                    'type' => 'integer',
136
                                ]),
137
                            ]
138
                        ),
139
                        'x-ratelimit-remaining' => new Oasv3\Header(
140
                            'Request limit per hour',
141
                            [
142
                                'example' => 94,
143
                                'schema'  => new OASv3\Schema([
144
                                    'type' => 'integer',
145
                                ]),
146
                            ]
147
                        ),
148
                    ],
149
                    'responses' => [
150
                        'InvalidFormat' => new OASv3\Response(
151
                            'The body input could not be parsed',
152
                            [
153
                                'application/json' => new OASv3\MediaType(
154
                                    [
155
                                        'schema' => $errorSchema,
156
                                    ]
157
                                ),
158
                                'application/xml' => new OASv3\MediaType(
159
                                    [
160
                                        'schema' => $errorSchema,
161
                                    ]
162
                                ),
163
                            ]
164
                        ),
165
                        'ValidationError' => new OASv3\Response(
166
                            'The body input was in a proper format, but the input values were not valid',
167
                            [
168
                                'application/json' => new OASv3\MediaType(
169
                                    [
170
                                        'schema' => $validationErrorSchema,
171
                                    ]
172
                                ),
173
                                'application/xml' => new OASv3\MediaType(
174
                                    [
175
                                        'schema' => $validationErrorSchema,
176
                                    ]
177
                                ),
178
                            ]
179
                        ),
180
                        'TooManyRequests' => new OASv3\Response(
181
                            'Too many requests per seconds were sent',
182
                            [
183
                                'application/json' => new OASv3\MediaType(
184
                                    [
185
                                        'schema' => $errorSchema,
186
                                    ]
187
                                ),
188
                                'application/xml' => new OASv3\MediaType(
189
                                    [
190
                                        'schema' => $errorSchema,
191
                                    ]
192
                                ),
193
                            ]
194
                        ),
195
                        'MaintenanceMode' => new OASv3\Response(
196
                            'App is in maintenance mode',
197
                            [
198
                                'application/json' => new OASv3\MediaType(
199
                                    [
200
                                        'schema' => $errorSchema,
201
                                    ]
202
                                ),
203
                                'application/xml' => new OASv3\MediaType(
204
                                    [
205
                                        'schema' => $errorSchema,
206
                                    ]
207
                                ),
208
                            ]
209
                        ),
210
                        'NotFound' => new OASv3\Response(
211
                            'Response when resource could not be found',
212
                            [
213
                                'application/json' => new OASv3\MediaType(
214
                                    [
215
                                        'schema' => $errorSchema,
216
                                    ]
217
                                ),
218
                                'application/xml' => new OASv3\MediaType(
219
                                    [
220
                                        'schema' => $errorSchema,
221
                                    ]
222
                                ),
223
                            ]
224
                        ),
225
                        'NotAuthorized' => new OASv3\Response(
226
                            'You have no permission to do this call',
227
                            [
228
                                'application/json' => new OASv3\MediaType(
229
                                    [
230
                                        'schema' => $errorSchema,
231
                                    ]
232
                                ),
233
                                'application/xml' => new OASv3\MediaType(
234
                                    [
235
                                        'schema' => $errorSchema,
236
                                    ]
237
                                ),
238
                            ]
239
                        ),
240
                        'InternalError' => new OASv3\Response(
241
                            'An internal error occured',
242
                            [
243
                                'application/json' => new OASv3\MediaType(
244
                                    [
245
                                        'schema' => $errorSchema,
246
                                    ]
247
                                ),
248
                                'application/xml' => new OASv3\MediaType(
249
                                    [
250
                                        'schema' => $errorSchema,
251
                                    ]
252
                                ),
253
                            ]
254
                        ),
255
                        'ServerDependencyError' => new OASv3\Response(
256
                            'The server required an external response which threw an error',
257
                            [
258
                                'application/json' => new OASv3\MediaType(
259
                                    [
260
                                        'schema' => $errorSchema,
261
                                    ]
262
                                ),
263
                                'application/xml' => new OASv3\MediaType(
264
                                    [
265
                                        'schema' => $errorSchema,
266
                                    ]
267
                                ),
268
                            ]
269
                        ),
270
                    ],
271
                ]),
272
            ]
273
        );
274
        if (is_callable($this->addSpecsHook)) {
275
            $res = call_user_func($this->addSpecsHook, $doc);
276
            if ($res instanceof OASv3\Document) {
277
                return $res;
278
            }
279
        }
280
281
        return $doc;
282
    }
283
284
    /**
285
     * Returns the default HTTP headers we generated for every REST api call.
286
     *
287
     * @return array
288
     */
289
    private function getDefaultHeaders(): array
290
    {
291
        return [
292
            'x-ratelimit-limit'     => new OASv3\Reference('#/components/headers/x-ratelimit-limit'),
293
            'x-ratelimit-remaining' => new OASv3\Reference('#/components/headers/x-ratelimit-remaining'),
294
        ];
295
    }
296
297
    private function convertSubActionToRequestBody(SubAction $subAction): ?OASv3\RequestBody
298
    {
299
        $properties = [];
300
        foreach ($subAction->getArguments() as $fieldName => $type) {
301
            //TODO typehint string etc.
302
            if ($type === null || !$type->getClassName()) {
303
                $properties[$fieldName] = new OASv3\Schema([
304
                    'type' => 'object',
305
                    'additionalProperties' => true,
306
                ]);
307
                continue;
308
            }
309
            $properties[$fieldName] = $this->schemaGenerator->createSchema($type->getClassName(), 'post', ['write', 'post']);
310
        }
311
        $jsonSchema = new OASv3\Schema([
312
            'type' => 'object',
313
            'properties' => $properties,
314
        ]);
315
        $xmlSchema = unserialize(serialize($jsonSchema));
316
        $xmlSchema->xml = new OASv3\Xml(['name' => 'item']);
317
318
        return new OASv3\RequestBody(
319
            [
320
                'application/json' => new OASv3\MediaType(
321
                    [
322
                        'schema' => $jsonSchema,
323
                    ]
324
                ),
325
                'application/xml' => new OASv3\MediaType(
326
                    [
327
                        'schema' => $xmlSchema,
328
                    ]
329
                ),
330
            ],
331
            'the resource as JSON to persist',
332
            true
333
        );
334
    }
335
336
    /**
337
     * Returns the content OpenAPI spec for a resource class and a certain operation.
338
     *
339
     * @param string $apiResourceClass
340
     * @param string $operation
341
     * @return OASv3\MediaType[]
342
     */
343
    private function convertToContent(string $apiResourceClass, string $operation): array
344
    {
345
        $readWrite = $this->determineReadWrite($operation);
346
        $jsonSchema = $this->schemaGenerator->createSchema($apiResourceClass, $operation, [$operation, $readWrite]);
347
        $xmlSchema = unserialize(serialize($jsonSchema));
348
        $xmlSchema->xml = new OASv3\Xml(['name' => 'item']);
349
350
        return [
351
            'application/json' => new OASv3\MediaType(
352
                [
353
                    'schema' => $jsonSchema,
354
                ]
355
            ),
356
            'application/xml' => new OASv3\MediaType(
357
                [
358
                    'schema' => $xmlSchema,
359
                ]
360
            ),
361
        ];
362
    }
363
364
    /**
365
     * Returns the content OpenAPI spec for a resource class when it returns an array of resources.
366
     *
367
     * @param string $apiResourceClass
368
     * @param string $operation
369
     * @return OASv3\MediaType[]
370
     */
371
    private function convertToContentArray(string $apiResourceClass, string $operation): array
372
    {
373
        $readWrite = $this->determineReadWrite($operation);
374
        $jsonSchema = $this->schemaGenerator->createSchema($apiResourceClass, $operation, [$operation, $readWrite]);
375
        $xmlSchema = $this->schemaGenerator->createSchema($apiResourceClass, $operation, [$operation, $readWrite]);
376
        $xmlSchema->xml = new OASv3\Xml(['name' => 'item']);
377
378
        return [
379
            'application/json' => new OASv3\MediaType(
380
                [
381
                    'schema' => new OASv3\Schema([
382
                        'type'  => 'array',
383
                        'items' => $jsonSchema,
384
                    ]),
385
                ]
386
            ),
387
            'application/xml' => new OASv3\MediaType(
388
                [
389
                    'schema' => new OASv3\Schema([
390
                        'type'  => 'array',
391
                        'items' => $xmlSchema,
392
                        'xml'   => new OASv3\Xml(['name' => 'response']),
393
                    ]),
394
                ]
395
            ),
396
        ];
397
    }
398
399
    /**
400
     * Determine if the operation is a read or a write.
401
     *
402
     * @param string $operation
403
     * @return string
404
     */
405
    private function determineReadWrite(string $operation): string
406
    {
407
        if ($operation === 'post' || $operation === 'put') {
408
            return 'write';
409
        }
410
411
        return 'read';
412
    }
413
414
    /**
415
     * Sluggify resource name for the operation id.
416
     *
417
     * @param string $resourceName
418
     * @return string|string[]|\Symfony\Component\Serializer\NameConverter\string|null
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Serializer\NameConverter\string was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
419
     */
420
    private function sluggify(string $resourceName)
421
    {
422
        return (new CamelCaseToSnakeCaseNameConverter(null, false))->denormalize($resourceName);
423
    }
424
425
    /**
426
     * Returns all paths of an api resource without an id in the url.
427
     *
428
     * @param string $apiResourceClass
429
     * @param string $resourceName
430
     * @return OASv3\PathItem
431
     */
432
    private function convertAllToPathItem(string $apiResourceClass, string $resourceName): OASv3\PathItem
433
    {
434
        $paths = [];
435
436
        if ($this->allowed($apiResourceClass, 'all')) {
437
            $paths['get'] = new OASv3\Operation(
438
                [
439
                    '200' => new OASv3\Response(
440
                        'Retrieves all instances of ' . $resourceName,
441
                        $this->convertToContentArray($apiResourceClass, 'get'),
442
                        $this->getDefaultHeaders()
443
                    ),
444
                    '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
445
                    '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
446
                    '500' => new OASv3\Reference('#/components/responses/InternalError'),
447
                    '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
448
                    '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
449
                ],
450
                'resourceGetAll' . $this->sluggify($resourceName),
451
                'get/search all instances of ' . $resourceName,
452
                [
453
                    'tags'       => [$resourceName],
454
                    'parameters' => [
455
                        new OASv3\Parameter('page', 'query', 'pagination index counting from 0', ['schema' => new OASv3\Schema(['type' => 'integer', 'minimum' => 0])]),
456
                        new OASv3\Parameter('limit', 'query', 'number of results', ['schema' => new OASv3\Schema(['type' => 'integer', 'minimum' => 1])]),
457
                    ],
458
                ]
459
            );
460
            $metadata = $this->apiResourceMetadataFactory->getMetadata($apiResourceClass);
461
            $retriever = $metadata->hasResourceRetriever() ? $metadata->getResourceRetriever() : null;
462
            if ($retriever instanceof SearchFilterProviderInterface) {
463
                foreach ($retriever->getSearchFilter($metadata)->getAllPrimitiveSearchFilter() as $name => $filter) {
464
                    $schema = $filter->getSchemaForFilter();
465
                    $paths['get']->parameters[] = new OASv3\Parameter(
466
                        $name,
467
                        'query',
468
                        'search filter ' . $name,
469
                        ['schema' => $schema]
470
                    );
471
                }
472
            }
473
        }
474
475
        if ($this->allowed($apiResourceClass, 'post')) {
476
            $paths['post'] = new OASv3\Operation(
477
                [
478
                    '200' => new OASv3\Response(
479
                        'Creates a new instance of ' . $resourceName,
480
                        $this->convertToContent($apiResourceClass, 'get'),
481
                        $this->getDefaultHeaders()
482
                    ),
483
                    '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
484
                    '415' => new OASv3\Reference('#/components/responses/InvalidFormat'),
485
                    '422' => new OASv3\Reference('#/components/responses/ValidationError'),
486
                    '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
487
                    '500' => new OASv3\Reference('#/components/responses/InternalError'),
488
                    '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
489
                    '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
490
                ],
491
                'resourcePostSingle' . $this->sluggify($resourceName),
492
                'create a new single instance of ' . $resourceName,
493
                [
494
                    'tags'        => [$resourceName],
495
                    'requestBody' => new OASv3\RequestBody(
496
                        $this->convertToContent($apiResourceClass, 'post'),
497
                        'the resource as JSON to persist',
498
                        true
499
                    ),
500
                ]
501
            );
502
        }
503
504
        return new OASv3\PathItem($paths);
505
    }
506
507
    /**
508
     * Creates PathItem for sub actions.
509
     *
510
     * @param SubAction $subAction
511
     * @param string $resourceName
512
     * @param string $identifierName
513
     * @return OASv3\PathItem
514
     */
515
    private function convertSubActionToPathItem(SubAction $subAction, string $resourceName, string $identifierName): OASv3\PathItem
516
    {
517
        $paths = [
518
            'parameters' => [
519
                new OASv3\Parameter($identifierName, 'path', 'the id of the resource', ['required' => true, 'schema' => new OASv3\Schema(['type' => 'string'])]),
520
            ],
521
        ];
522
        $paths['post'] = new OASv3\Operation(
523
            [
524
                '200' => new OASv3\Response(
525
                    'Retrieves return value of ' . $subAction->getName(),
526
                    $subAction->getReturnTypehint()->getClassName() ? $this->convertToContent($subAction->getReturnTypehint()->getClassName(), 'get') : null,
527
                    $this->getDefaultHeaders()
528
                ),
529
                '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
530
                '404' => new OASv3\Reference('#/components/responses/NotFound'),
531
                '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
532
                '500' => new OASv3\Reference('#/components/responses/InternalError'),
533
                '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
534
                '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
535
            ],
536
            'resourcePostSubAction' . $this->sluggify($resourceName . '_' . $subAction->getName()),
537
            null,
538
            [
539
                'tags' => [$resourceName],
540
                'requestBody' => $this->convertSubActionToRequestBody($subAction),
541
            ]
542
        );
543
        return new OASv3\PathItem($paths);
544
    }
545
546
    /**
547
     * Returns all paths of an api resource with an id in the url.
548
     * @param string $apiResourceClass
549
     * @param string $resourceName
550
     * @return OASv3\PathItem
551
     */
552
    private function convertToPathItem(string $apiResourceClass, string $resourceName, string $identifierName): OASv3\PathItem
553
    {
554
        $paths = [
555
            'parameters' => [
556
                new OASv3\Parameter($identifierName, 'path', 'the id of the resource', ['required' => true, 'schema' => new OASv3\Schema(['type' => 'string'])]),
557
            ],
558
        ];
559
        if ($this->allowed($apiResourceClass, 'get')) {
560
            $paths['get'] = new OASv3\Operation(
561
                [
562
                    '200' => new OASv3\Response(
563
                        'Retrieves a single instance of ' . $resourceName,
564
                        $this->convertToContent($apiResourceClass, 'get'),
565
                        $this->getDefaultHeaders()
566
                    ),
567
                    '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
568
                    '404' => new OASv3\Reference('#/components/responses/NotFound'),
569
                    '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
570
                    '500' => new OASv3\Reference('#/components/responses/InternalError'),
571
                    '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
572
                    '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
573
                ],
574
                'resourceGetSingle' . $this->sluggify($resourceName),
575
                'retrieve a single instance of ' . $resourceName,
576
                [
577
                    'tags' => [$resourceName],
578
                ]
579
            );
580
        }
581
        if ($this->allowed($apiResourceClass, 'delete')) {
582
            $paths['delete'] = new OASv3\Operation(
583
                [
584
                    '204' => new OASv3\Response(
585
                        'Deletes a single instance of ' . $resourceName,
586
                        null,
587
                        $this->getDefaultHeaders()
588
                    ),
589
                    '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
590
                    '404' => new OASv3\Reference('#/components/responses/NotFound'),
591
                    '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
592
                    '500' => new OASv3\Reference('#/components/responses/InternalError'),
593
                    '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
594
                    '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
595
                ],
596
                'resourceDeleteSingle' . $this->sluggify($resourceName),
597
                'delete a single instance of ' . $resourceName,
598
                [
599
                    'tags' => [$resourceName],
600
                ]
601
            );
602
        }
603
        if ($this->allowed($apiResourceClass, 'put')) {
604
            $paths['put'] = new OASv3\Operation(
605
                [
606
                    '200' => new OASv3\Response(
607
                        'Retrieves and update a single instance of ' . $resourceName,
608
                        $this->convertToContent($apiResourceClass, 'get'),
609
                        $this->getDefaultHeaders()
610
                    ),
611
                    '401' => new OASv3\Reference('#/components/responses/NotAuthorized'),
612
                    '404' => new OASv3\Reference('#/components/responses/NotFound'),
613
                    '415' => new OASv3\Reference('#/components/responses/InvalidFormat'),
614
                    '422' => new OASv3\Reference('#/components/responses/ValidationError'),
615
                    '429' => new OASv3\Reference('#/components/responses/TooManyRequests'),
616
                    '500' => new OASv3\Reference('#/components/responses/InternalError'),
617
                    '502' => new OASv3\Reference('#/components/responses/ServerDependencyError'),
618
                    '503' => new OASv3\Reference('#/components/responses/MaintenanceMode'),
619
                ],
620
                'resourcePutSingle' . $this->sluggify($resourceName),
621
                'modify a single instance of ' . $resourceName,
622
                [
623
                    'tags'        => [$resourceName],
624
                    'requestBody' => new OASv3\RequestBody(
625
                        $this->convertToContent($apiResourceClass, 'put'),
626
                        'the resource as JSON to persist',
627
                        true
628
                    ),
629
                ]
630
            );
631
        }
632
633
        return new OASv3\PathItem($paths);
634
    }
635
636
    /**
637
     * Returns if a specific REST API call is an allowed method.
638
     *
639
     * @param string $apiResourceClass
640
     * @param string $operation
641
     * @return bool
642
     */
643
    private function allowed(string $apiResourceClass, string $operation): bool
644
    {
645
        $metadata = $this->apiResourceMetadataFactory->getMetadata($apiResourceClass);
646
        switch ($operation) {
647
            case 'all':
648
                return $metadata->allowGetAll();
649
            case 'get':
650
                return $metadata->allowGet();
651
            case 'post':
652
                return $metadata->allowPost();
653
            case 'put':
654
                return $metadata->allowPut();
655
            case 'delete':
656
                return $metadata->allowDelete();
657
        }
658
        // @codeCoverageIgnoreStart
659
        return false;
660
        // @codeCoverageIgnoreEnd
661
    }
662
}
663