OpenApiSchemasFormatter   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 623
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 204
dl 0
loc 623
rs 3.44
c 0
b 0
f 0
wmc 62

26 Methods

Rating   Name   Duplication   Size   Complexity  
A addDefaultRelationshipsSchema() 0 4 1
A addSchemaData() 0 3 1
A getRelationships() 0 9 2
A getIsSnakeCased() 0 12 3
A getRelationshipResourceAttributesClassNames() 0 19 4
A validateTransfer() 0 5 2
A addResponseDataAttributesSchemaFromTransfer() 0 22 4
A processResourceContexts() 0 21 5
A addAttributesSchemasFromResourceRelationshipAnnotations() 0 14 3
A processCustomRouteContexts() 0 21 6
A __construct() 0 12 1
A addDefaultLinksSchema() 0 3 1
A getAnnotationsWithRequest() 0 5 1
A fillRelationsMap() 0 8 3
A addRequestSchema() 0 22 3
A addRequestDataAttributesSchemaFromTransfer() 0 22 5
A format() 0 13 1
A addRelationshipSchemas() 0 11 2
A addResponseCollectionSchema() 0 26 2
A addResponseResourceSchema() 0 26 2
A addDefaultSchemas() 0 5 1
A addDefaultErrorMessageSchema() 0 6 1
A resolveTransferClassName() 0 5 1
A getNotCollectionPathAnnotations() 0 7 1
A addIncludeSchemaData() 0 14 3
A addIncludeSchemas() 0 27 3

How to fix   Complexity   

Complex Class

Complex classes like OpenApiSchemasFormatter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OpenApiSchemasFormatter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Glue\DocumentationGeneratorOpenApi\Formatter;
9
10
use Generated\Shared\Transfer\AnnotationTransfer;
11
use Generated\Shared\Transfer\ApiApplicationSchemaContextTransfer;
12
use Generated\Shared\Transfer\GlueResourceMethodCollectionTransfer;
13
use Generated\Shared\Transfer\PathAnnotationTransfer;
14
use Generated\Shared\Transfer\ResourceContextTransfer;
15
use Generated\Shared\Transfer\RestErrorMessageTransfer;
16
use Generated\Shared\Transfer\SchemaComponentTransfer;
17
use Generated\Shared\Transfer\SchemaDataTransfer;
18
use Generated\Shared\Transfer\SchemaItemsComponentTransfer;
19
use Spryker\Glue\DocumentationGeneratorOpenApi\Analyzer\ResourceTransferAnalyzerInterface;
20
use Spryker\Glue\DocumentationGeneratorOpenApi\Exception\InvalidTransferClassException;
21
use Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Builder\SchemaBuilderInterface;
22
use Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Processor\ResourceRelationshipProcessorInterface;
23
use Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Renderer\SchemaRendererInterface;
24
use Spryker\Shared\Kernel\Transfer\AbstractTransfer;
25
26
class OpenApiSchemasFormatter implements OpenApiSchemaFormatterInterface
27
{
28
    /**
29
     * @var string
30
     */
31
    protected const KEY_IS_TRANSFER = 'is_transfer';
32
33
    /**
34
     * @var string
35
     */
36
    protected const KEY_TYPE = 'type';
37
38
    /**
39
     * @var string
40
     */
41
    protected const PATTERN_SCHEMA_REFERENCE = '#/components/schemas/%s';
42
43
    /**
44
     * @var string
45
     */
46
    protected const METHOD_GET = 'get';
47
48
    /**
49
     * @var string
50
     */
51
    protected const METHOD_GET_COLLECTION = 'getCollection';
52
53
    /**
54
     * @var string
55
     */
56
    protected const METHOD_POST = 'post';
57
58
    /**
59
     * @var string
60
     */
61
    protected const METHOD_PATCH = 'patch';
62
63
    /**
64
     * @var string
65
     */
66
    protected const METHOD_DELETE = 'delete';
67
68
    /**
69
     * @var array<mixed>
70
     */
71
    protected $schemas = [];
72
73
    /**
74
     * @var \Spryker\Glue\DocumentationGeneratorOpenApi\Analyzer\ResourceTransferAnalyzerInterface
75
     */
76
    protected $resourceTransferAnalyzer;
77
78
    /**
79
     * @var \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Builder\SchemaBuilderInterface
80
     */
81
    protected $schemaBuilder;
82
83
    /**
84
     * @var \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Renderer\SchemaRendererInterface
85
     */
86
    protected $schemaRenderer;
87
88
    /**
89
     * @var \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Processor\ResourceRelationshipProcessorInterface
90
     */
91
    protected $resourceRelationshipProcessor;
92
93
    /**
94
     * @var array<string, array<string>>
95
     */
96
    protected $relationshipMap;
97
98
    /**
99
     * @var array<string, array<string>>
100
     */
101
    protected $resourceResponsesMap;
102
103
    /**
104
     * @param \Spryker\Glue\DocumentationGeneratorOpenApi\Analyzer\ResourceTransferAnalyzerInterface $resourceTransferAnalyzer
105
     * @param \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Builder\SchemaBuilderInterface $schemaBuilder
106
     * @param \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Renderer\SchemaRendererInterface $schemaRenderer
107
     * @param \Spryker\Glue\DocumentationGeneratorOpenApi\Formatter\Schema\Processor\ResourceRelationshipProcessorInterface $resourceRelationshipProcessor
108
     */
109
    public function __construct(
110
        ResourceTransferAnalyzerInterface $resourceTransferAnalyzer,
111
        SchemaBuilderInterface $schemaBuilder,
112
        SchemaRendererInterface $schemaRenderer,
113
        ResourceRelationshipProcessorInterface $resourceRelationshipProcessor
114
    ) {
115
        $this->resourceTransferAnalyzer = $resourceTransferAnalyzer;
116
        $this->schemaBuilder = $schemaBuilder;
117
        $this->schemaRenderer = $schemaRenderer;
118
        $this->resourceRelationshipProcessor = $resourceRelationshipProcessor;
119
120
        $this->addDefaultSchemas();
121
    }
122
123
    /**
124
     * @param array<mixed> $formattedData
125
     * @param \Generated\Shared\Transfer\ApiApplicationSchemaContextTransfer $apiApplicationSchemaContextTransfer
126
     *
127
     * @return array<mixed>
128
     */
129
    public function format(
130
        array $formattedData,
131
        ApiApplicationSchemaContextTransfer $apiApplicationSchemaContextTransfer
132
    ): array {
133
        $this->fillRelationsMap($apiApplicationSchemaContextTransfer->getRelationshipPluginsContexts()->getArrayCopy());
134
135
        $this->processResourceContexts($apiApplicationSchemaContextTransfer->getResourceContexts()->getArrayCopy());
136
        $this->processCustomRouteContexts($apiApplicationSchemaContextTransfer->getCustomRoutesContexts()->getArrayCopy());
137
138
        ksort($this->schemas);
139
        $formattedData['components']['schemas'] = $this->schemas;
140
141
        return $formattedData;
142
    }
143
144
    /**
145
     * @param \Generated\Shared\Transfer\AnnotationTransfer $annotationTransfer
146
     * @param bool $isSnakeCased
147
     *
148
     * @return string
149
     */
150
    protected function addRequestSchema(
151
        AnnotationTransfer $annotationTransfer,
152
        bool $isSnakeCased
153
    ): string {
154
        if ($annotationTransfer->getRequestAttributesClassName() === null) {
155
            return '';
156
        }
157
        /** @phpstan-var class-string<\Spryker\Shared\Kernel\Transfer\AbstractTransfer> $transferClassName */
158
        $transferClassName = $this->resolveTransferClassName($annotationTransfer->getRequestAttributesClassName());
159
        if (!$this->resourceTransferAnalyzer->isRequestSchemaRequired($transferClassName)) {
160
            return '';
161
        }
162
163
        $requestSchemaName = $this->resourceTransferAnalyzer->createRequestSchemaNameFromTransferClassName($transferClassName);
164
        $requestDataSchemaName = $this->resourceTransferAnalyzer->createRequestDataSchemaNameFromTransferClassName($transferClassName);
165
        $requestAttributesSchemaName = $this->resourceTransferAnalyzer->createRequestAttributesSchemaNameFromTransferClassName($transferClassName);
166
167
        $this->addSchemaData($this->schemaBuilder->createRequestBaseSchema($requestSchemaName, $requestDataSchemaName));
168
        $this->addSchemaData($this->schemaBuilder->createRequestDataSchema($requestDataSchemaName, $requestAttributesSchemaName));
169
        $this->addRequestDataAttributesSchemaFromTransfer(new $transferClassName(), $requestAttributesSchemaName, $isSnakeCased);
170
171
        return sprintf(static::PATTERN_SCHEMA_REFERENCE, $requestSchemaName);
172
    }
173
174
    /**
175
     * @param \Generated\Shared\Transfer\AnnotationTransfer $annotationTransfer
176
     * @param bool $isSnakeCased
177
     * @param \Generated\Shared\Transfer\ResourceContextTransfer|null $resourceContextTransfer
178
     *
179
     * @return string
180
     */
181
    protected function addResponseResourceSchema(
182
        AnnotationTransfer $annotationTransfer,
183
        bool $isSnakeCased,
184
        ?ResourceContextTransfer $resourceContextTransfer
185
    ): string {
186
        /** @phpstan-var class-string<\Spryker\Shared\Kernel\Transfer\AbstractTransfer> $transferClassName */
187
        $transferClassName = $this->resolveTransferClassName($annotationTransfer->getResponseAttributesClassNameOrFail());
188
189
        $responseSchemaName = $this->resourceTransferAnalyzer->createResponseResourceSchemaNameFromTransferClassName($transferClassName);
190
        $responseDataSchemaName = $this->resourceTransferAnalyzer->createResponseResourceDataSchemaNameFromTransferClassName($transferClassName);
191
        $responseAttributesSchemaName = $this->resourceTransferAnalyzer->createResponseAttributesSchemaNameFromTransferClassName($transferClassName);
192
193
        $isIdNullable = (bool)$annotationTransfer->getIsIdNullable();
194
        $this->addSchemaData($this->schemaBuilder->createResponseBaseSchema($responseSchemaName, $responseDataSchemaName));
195
        $this->addSchemaData($this->schemaBuilder->createResponseDataSchema($responseDataSchemaName, $responseAttributesSchemaName, $isIdNullable));
196
        $this->addResponseDataAttributesSchemaFromTransfer(new $transferClassName(), $responseAttributesSchemaName, $isSnakeCased);
197
198
        if ($resourceContextTransfer) {
199
            $relationShipResourceAttributesClassNames = $this->getRelationshipResourceAttributesClassNames($resourceContextTransfer);
200
            $relationships = $this->getRelationships($resourceContextTransfer);
201
            $this->addAttributesSchemasFromResourceRelationshipAnnotations($relationShipResourceAttributesClassNames);
202
            $this->addRelationshipSchemas($relationships, $transferClassName, $responseDataSchemaName);
203
            $this->addIncludeSchemas($relationShipResourceAttributesClassNames, $transferClassName, $responseSchemaName);
204
        }
205
206
        return sprintf(static::PATTERN_SCHEMA_REFERENCE, $responseSchemaName);
207
    }
208
209
    /**
210
     * @param \Generated\Shared\Transfer\AnnotationTransfer $annotationTransfer
211
     * @param bool $isSnakeCased
212
     * @param \Generated\Shared\Transfer\ResourceContextTransfer|null $resourceContextTransfer
213
     *
214
     * @return string
215
     */
216
    protected function addResponseCollectionSchema(
217
        AnnotationTransfer $annotationTransfer,
218
        bool $isSnakeCased,
219
        ?ResourceContextTransfer $resourceContextTransfer
220
    ): string {
221
        /** @phpstan-var class-string<\Spryker\Shared\Kernel\Transfer\AbstractTransfer> $transferClassName */
222
        $transferClassName = $this->resolveTransferClassName($annotationTransfer->getResponseAttributesClassNameOrFail());
223
224
        $responseSchemaName = $this->resourceTransferAnalyzer->createResponseCollectionSchemaNameFromTransferClassName($transferClassName);
225
        $responseDataSchemaName = $this->resourceTransferAnalyzer->createResponseCollectionDataSchemaNameFromTransferClassName($transferClassName);
226
        $responseAttributesSchemaName = $this->resourceTransferAnalyzer->createResponseAttributesSchemaNameFromTransferClassName($transferClassName);
227
228
        $isIdNullable = (bool)$annotationTransfer->getIsIdNullable();
229
        $this->addSchemaData($this->schemaBuilder->createCollectionResponseBaseSchema($responseSchemaName, $responseDataSchemaName));
230
        $this->addSchemaData($this->schemaBuilder->createResponseDataSchema($responseDataSchemaName, $responseAttributesSchemaName, $isIdNullable));
231
        $this->addResponseDataAttributesSchemaFromTransfer(new $transferClassName(), $responseAttributesSchemaName, $isSnakeCased);
232
233
        if ($resourceContextTransfer) {
234
            $relationShipResourceAttributesClassNames = $this->getRelationshipResourceAttributesClassNames($resourceContextTransfer);
235
            $relationships = $this->getRelationships($resourceContextTransfer);
236
            $this->addAttributesSchemasFromResourceRelationshipAnnotations($relationShipResourceAttributesClassNames);
237
            $this->addRelationshipSchemas($relationships, $transferClassName, $responseDataSchemaName);
238
            $this->addIncludeSchemas($relationShipResourceAttributesClassNames, $transferClassName, $responseSchemaName);
239
        }
240
241
        return sprintf(static::PATTERN_SCHEMA_REFERENCE, $responseSchemaName);
242
    }
243
244
    /**
245
     * @param array<string> $resourceAttributesClassNames
246
     *
247
     * @return void
248
     */
249
    protected function addAttributesSchemasFromResourceRelationshipAnnotations(array $resourceAttributesClassNames): void
250
    {
251
        if ($resourceAttributesClassNames === []) {
252
            return;
253
        }
254
255
        foreach ($resourceAttributesClassNames as $resourceAttributesClassName) {
256
            $responseDataSchemaName = $this->resourceTransferAnalyzer->createResponseResourceDataSchemaNameFromTransferClassName($resourceAttributesClassName);
257
            $responseAttributesSchemaName = $this->resourceTransferAnalyzer->createResponseAttributesSchemaNameFromTransferClassName($resourceAttributesClassName);
258
259
            $this->addSchemaData($this->schemaBuilder->createResponseDataSchema($responseDataSchemaName, $responseAttributesSchemaName, false));
260
            /** @var \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer */
261
            $transfer = new $resourceAttributesClassName();
262
            $this->addResponseDataAttributesSchemaFromTransfer($transfer, $responseAttributesSchemaName, false);
263
        }
264
    }
265
266
    /**
267
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer
268
     * @param string $attributesSchemaName
269
     * @param bool $isSnakeCased
270
     *
271
     * @return void
272
     */
273
    protected function addResponseDataAttributesSchemaFromTransfer(
274
        AbstractTransfer $transfer,
275
        string $attributesSchemaName,
276
        bool $isSnakeCased
277
    ): void {
278
        if (array_key_exists($attributesSchemaName, $this->schemas)) {
279
            return;
280
        }
281
        $this->schemas[$attributesSchemaName] = [];
282
283
        $transferMetadata = $this->resourceTransferAnalyzer->getTransferMetadata($transfer);
284
        foreach ($transferMetadata as $property) {
285
            if ($property[static::KEY_IS_TRANSFER]) {
286
                $this->validateTransfer($property[static::KEY_TYPE]);
287
                $schemaName = $this->resourceTransferAnalyzer->createResponseAttributesSchemaNameFromTransferClassName($property[static::KEY_TYPE]);
288
                /** @var \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer */
289
                $transfer = new $property[static::KEY_TYPE]();
290
                $this->addResponseDataAttributesSchemaFromTransfer($transfer, $schemaName, $isSnakeCased);
291
            }
292
        }
293
294
        $this->addSchemaData($this->schemaBuilder->createResponseDataAttributesSchema($attributesSchemaName, $transferMetadata, $isSnakeCased));
295
    }
296
297
    /**
298
     * @param \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer
299
     * @param string $attributesSchemaName
300
     * @param bool $isSnakeCased
301
     *
302
     * @return void
303
     */
304
    protected function addRequestDataAttributesSchemaFromTransfer(
305
        AbstractTransfer $transfer,
306
        string $attributesSchemaName,
307
        bool $isSnakeCased
308
    ): void {
309
        if (array_key_exists($attributesSchemaName, $this->schemas)) {
310
            return;
311
        }
312
        $this->schemas[$attributesSchemaName] = [];
313
314
        $transferMetadata = $this->resourceTransferAnalyzer->getTransferMetadata($transfer);
315
        foreach ($transferMetadata as $property) {
316
            if ($property[static::KEY_IS_TRANSFER] && $this->resourceTransferAnalyzer->isRequestParameterRequired($property)) {
317
                $this->validateTransfer($property[static::KEY_TYPE]);
318
                $schemaName = $this->resourceTransferAnalyzer->createRequestAttributesSchemaNameFromTransferClassName($property[static::KEY_TYPE]);
319
                /** @var \Spryker\Shared\Kernel\Transfer\AbstractTransfer $transfer */
320
                $transfer = new $property[static::KEY_TYPE]();
321
                $this->addRequestDataAttributesSchemaFromTransfer($transfer, $schemaName, $isSnakeCased);
322
            }
323
        }
324
325
        $this->addSchemaData($this->schemaBuilder->createRequestDataAttributesSchema($attributesSchemaName, $transferMetadata, $isSnakeCased));
326
    }
327
328
    /**
329
     * @param array<string> $relationships
330
     * @param string $transferClassName
331
     * @param string $responseDataSchemaName
332
     *
333
     * @return void
334
     */
335
    protected function addRelationshipSchemas(
336
        array $relationships,
337
        string $transferClassName,
338
        string $responseDataSchemaName
339
    ): void {
340
        $relationshipSchemaDataTransfers = $this
341
            ->resourceRelationshipProcessor
342
            ->getRelationshipSchemaDataTransfers($relationships, $transferClassName, $responseDataSchemaName);
343
344
        foreach ($relationshipSchemaDataTransfers as $relationshipSchemaDataTransfer) {
345
            $this->addSchemaData($relationshipSchemaDataTransfer);
346
        }
347
    }
348
349
    /**
350
     * @return void
351
     */
352
    protected function addDefaultSchemas(): void
353
    {
354
        $this->addDefaultErrorMessageSchema();
355
        $this->addDefaultLinksSchema();
356
        $this->addDefaultRelationshipsSchema();
357
    }
358
359
    /**
360
     * @return void
361
     */
362
    protected function addDefaultErrorMessageSchema(): void
363
    {
364
        $this->addResponseDataAttributesSchemaFromTransfer(
365
            new RestErrorMessageTransfer(),
366
            $this->resourceTransferAnalyzer->createResponseAttributesSchemaNameFromTransferClassName(RestErrorMessageTransfer::class),
367
            false,
368
        );
369
    }
370
371
    /**
372
     * @return void
373
     */
374
    protected function addDefaultLinksSchema(): void
375
    {
376
        $this->addSchemaData($this->schemaBuilder->createDefaultLinksSchema());
377
    }
378
379
    /**
380
     * @return void
381
     */
382
    protected function addDefaultRelationshipsSchema(): void
383
    {
384
        $this->addSchemaData($this->schemaBuilder->createDefaultRelationshipDataAttributesSchema());
385
        $this->addSchemaData($this->schemaBuilder->createDefaultRelationshipDataCollectionAttributesSchema());
386
    }
387
388
    /**
389
     * @param \Generated\Shared\Transfer\SchemaDataTransfer $schemaData
390
     *
391
     * @return void
392
     */
393
    protected function addSchemaData(SchemaDataTransfer $schemaData): void
394
    {
395
        $this->schemas = array_replace_recursive($this->schemas, $this->schemaRenderer->render($schemaData));
396
    }
397
398
    /**
399
     * @param \Generated\Shared\Transfer\SchemaDataTransfer $schemaData
400
     *
401
     * @return void
402
     */
403
    protected function addIncludeSchemaData(SchemaDataTransfer $schemaData): void
404
    {
405
        $renderData = $this->schemaRenderer->render($schemaData);
406
        foreach ($renderData as $key => $item) {
407
            if (!isset($this->schemas[$key])) {
408
                $this->schemas = array_replace_recursive($this->schemas, $renderData);
409
410
                continue;
411
            }
412
            $oneOfs = array_merge(
413
                $this->schemas[$key][SchemaComponentTransfer::ITEMS][SchemaItemsComponentTransfer::ONE_OF],
414
                $item[SchemaComponentTransfer::ITEMS][SchemaItemsComponentTransfer::ONE_OF],
415
            );
416
            $this->schemas[$key][SchemaComponentTransfer::ITEMS][SchemaItemsComponentTransfer::ONE_OF] = array_unique($oneOfs, SORT_REGULAR);
417
        }
418
    }
419
420
    /**
421
     * @param string $transferClassName
422
     *
423
     * @return string
424
     */
425
    protected function resolveTransferClassName(string $transferClassName): string
426
    {
427
        $this->validateTransfer($transferClassName);
428
429
        return $transferClassName;
430
    }
431
432
    /**
433
     * @param string $transferClassName
434
     *
435
     * @throws \Spryker\Glue\DocumentationGeneratorOpenApi\Exception\InvalidTransferClassException
436
     *
437
     * @return void
438
     */
439
    protected function validateTransfer(string $transferClassName): void
440
    {
441
        if (!$this->resourceTransferAnalyzer->isTransferValid($transferClassName)) {
442
            throw new InvalidTransferClassException(
443
                sprintf('Invalid transfer %s', $transferClassName),
444
            );
445
        }
446
    }
447
448
    /**
449
     * @param array<string> $resourceAttributesClassNames
450
     * @param string $transferClassName
451
     * @param string $responseSchemaName
452
     *
453
     * @return void
454
     */
455
    protected function addIncludeSchemas(
456
        array $resourceAttributesClassNames,
457
        string $transferClassName,
458
        string $responseSchemaName
459
    ): void {
460
        if (!$resourceAttributesClassNames) {
461
            return;
462
        }
463
464
        $this->addSchemaData(
465
            $this
466
                ->resourceRelationshipProcessor
467
                ->getIncludeBaseSchema($transferClassName, $responseSchemaName),
468
        );
469
470
        $relationshipResponses = [];
471
472
        foreach ($resourceAttributesClassNames as $resourceAttributesClassName) {
473
            $relationshipResponses[] = $this->resourceTransferAnalyzer->createResponseResourceDataSchemaNameFromTransferClassName(
474
                $resourceAttributesClassName,
475
            );
476
        }
477
478
        $this->addIncludeSchemaData(
479
            $this
480
                ->resourceRelationshipProcessor
481
                ->getIncludeDataSchema($transferClassName, $relationshipResponses),
482
        );
483
    }
484
485
    /**
486
     * @param array<\Generated\Shared\Transfer\RelationshipPluginsContextTransfer> $relationshipPluginsContextTransfers
487
     *
488
     * @return void
489
     */
490
    protected function fillRelationsMap(array $relationshipPluginsContextTransfers): void
491
    {
492
        foreach ($relationshipPluginsContextTransfers as $relationshipPluginsContextTransfer) {
493
            $resourceType = $relationshipPluginsContextTransfer->getResourceTypeOrFail();
494
            $relationship = $relationshipPluginsContextTransfer->getRelationshipOrFail();
495
            if ($relationshipPluginsContextTransfer->getRelationshipPluginAnnotationsContext()) {
496
                $resourceAttributesClassName = $relationshipPluginsContextTransfer->getRelationshipPluginAnnotationsContextOrFail()->getResourceAttributesClassNameOrFail();
497
                $this->relationshipMap[$resourceType][$relationship] = $resourceAttributesClassName;
498
            }
499
        }
500
    }
501
502
    /**
503
     * @param \Generated\Shared\Transfer\ResourceContextTransfer $resourceContextTransfer
504
     *
505
     * @return array<string, string>
506
     */
507
    protected function getRelationshipResourceAttributesClassNames(ResourceContextTransfer $resourceContextTransfer): array
508
    {
509
        if (!$resourceContextTransfer->getRelationships()) {
510
            return [];
511
        }
512
513
        $resourceAttributesClassName = [];
514
        $resourceTypeOrFail = $resourceContextTransfer->getResourceTypeOrFail();
515
        $relationships = $this->getRelationships($resourceContextTransfer);
516
517
        foreach ($relationships as $relationship) {
518
            if (!isset($this->relationshipMap[$resourceTypeOrFail][$relationship])) {
519
                continue;
520
            }
521
522
            $resourceAttributesClassName[$relationship] = $this->relationshipMap[$resourceTypeOrFail][$relationship];
523
        }
524
525
        return $resourceAttributesClassName;
526
    }
527
528
    /**
529
     * @param \Generated\Shared\Transfer\ResourceContextTransfer $resourceContextTransfer
530
     *
531
     * @return array<string>
532
     */
533
    protected function getRelationships(ResourceContextTransfer $resourceContextTransfer): array
534
    {
535
        $relationships = $resourceContextTransfer->getRelationships();
536
537
        if (!$relationships) {
538
            return [];
539
        }
540
541
        return explode(',', $relationships);
542
    }
543
544
    /**
545
     * @param \Generated\Shared\Transfer\PathAnnotationTransfer $pathAnnotationTransfer
546
     *
547
     * @return array<\Generated\Shared\Transfer\AnnotationTransfer>
548
     */
549
    protected function getAnnotationsWithRequest(PathAnnotationTransfer $pathAnnotationTransfer): array
550
    {
551
        return array_filter([
552
            static::METHOD_POST => $pathAnnotationTransfer->getPost(),
553
            static::METHOD_PATCH => $pathAnnotationTransfer->getPatch(),
554
        ]);
555
    }
556
557
    /**
558
     * @param \Generated\Shared\Transfer\PathAnnotationTransfer $pathAnnotationTransfer
559
     *
560
     * @return array<\Generated\Shared\Transfer\AnnotationTransfer>
561
     */
562
    protected function getNotCollectionPathAnnotations(PathAnnotationTransfer $pathAnnotationTransfer): array
563
    {
564
        return array_filter([
565
            static::METHOD_GET => $pathAnnotationTransfer->getGetResourceById(),
566
            static::METHOD_POST => $pathAnnotationTransfer->getPost(),
567
            static::METHOD_PATCH => $pathAnnotationTransfer->getPatch(),
568
            static::METHOD_DELETE => $pathAnnotationTransfer->getDelete(),
569
        ]);
570
    }
571
572
    /**
573
     * @param array<\Generated\Shared\Transfer\ResourceContextTransfer> $resourceContextTransfers
574
     *
575
     * @return void
576
     */
577
    protected function processResourceContexts(array $resourceContextTransfers): void
578
    {
579
        foreach ($resourceContextTransfers as $resourceContextTransfer) {
580
            $pathAnnotationTransfer = $resourceContextTransfer->getPathAnnotationOrFail();
581
            $declaredMethods = $resourceContextTransfer->getDeclaredMethods();
582
583
            foreach ($this->getAnnotationsWithRequest($pathAnnotationTransfer) as $method => $annotationTransfer) {
584
                $isSnakeCased = $this->getIsSnakeCased($method, $declaredMethods);
585
                $this->addRequestSchema($annotationTransfer, $isSnakeCased);
586
            }
587
            foreach ($this->getNotCollectionPathAnnotations($pathAnnotationTransfer) as $method => $annotationTransfer) {
588
                $isSnakeCased = $this->getIsSnakeCased($method, $declaredMethods);
589
                $this->addResponseResourceSchema($annotationTransfer, $isSnakeCased, $resourceContextTransfer);
590
            }
591
592
            if ($resourceContextTransfer->getPathAnnotationOrFail()->getGetCollection()) {
593
                $isSnakeCased = $this->getIsSnakeCased(static::METHOD_GET_COLLECTION, $declaredMethods);
594
                $this->addResponseCollectionSchema(
595
                    $resourceContextTransfer->getPathAnnotationOrFail()->getGetCollectionOrFail(),
596
                    $isSnakeCased,
597
                    $resourceContextTransfer,
598
                );
599
            }
600
        }
601
    }
602
603
    /**
604
     * @param string $method
605
     * @param \Generated\Shared\Transfer\GlueResourceMethodCollectionTransfer|null $declaredMethods
606
     *
607
     * @return bool
608
     */
609
    protected function getIsSnakeCased(
610
        string $method,
611
        ?GlueResourceMethodCollectionTransfer $declaredMethods
612
    ): bool {
613
        if (
614
            $declaredMethods === null ||
615
            !$declaredMethods->offsetExists($method)
616
        ) {
617
            return false;
618
        }
619
620
        return (bool)$declaredMethods->offsetGet($method)->getIsSnakeCased();
621
    }
622
623
    /**
624
     * @param array<\Generated\Shared\Transfer\CustomRoutesContextTransfer> $customRoutesContextTransfers
625
     *
626
     * @return void
627
     */
628
    protected function processCustomRouteContexts(array $customRoutesContextTransfers): void
629
    {
630
        foreach ($customRoutesContextTransfers as $customRoutesContextTransfer) {
631
            if (isset($customRoutesContextTransfer->getDefaults()['_resourceName'])) {
632
                continue;
633
            }
634
635
            $pathAnnotationTransfer = $customRoutesContextTransfer->getPathAnnotationOrFail();
636
637
            foreach ($this->getAnnotationsWithRequest($pathAnnotationTransfer) as $annotationTransfer) {
638
                $this->addRequestSchema($annotationTransfer, false);
639
            }
640
            foreach ($this->getNotCollectionPathAnnotations($pathAnnotationTransfer) as $annotationTransfer) {
641
                $this->addResponseResourceSchema($annotationTransfer, false, null);
642
            }
643
644
            if ($pathAnnotationTransfer->getGetCollection()) {
645
                $this->addResponseCollectionSchema(
646
                    $customRoutesContextTransfer->getPathAnnotationOrFail()->getGetCollectionOrFail(),
647
                    false,
648
                    null,
649
                );
650
            }
651
        }
652
    }
653
}
654