• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 14403159177

11 Apr 2025 12:30PM UTC coverage: 8.183% (-0.3%) from 8.488%
14403159177

Pull #7082

github

web-flow
Merge 2381bf145 into 83d005a2b
Pull Request #7082: feat(openapi): manage error resources in global

23 of 27 new or added lines in 12 files covered. (85.19%)

187 existing lines in 14 files now uncovered.

12919 of 157884 relevant lines covered (8.18%)

13.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Metadata/Tests/Extractor/ResourceMetadataCompatibilityTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Metadata\Tests\Extractor;
15

16
use ApiPlatform\Metadata\ApiResource;
17
use ApiPlatform\Metadata\Delete;
18
use ApiPlatform\Metadata\Error;
19
use ApiPlatform\Metadata\Extractor\XmlResourceExtractor;
20
use ApiPlatform\Metadata\Extractor\YamlResourceExtractor;
21
use ApiPlatform\Metadata\Get;
22
use ApiPlatform\Metadata\GetCollection;
23
use ApiPlatform\Metadata\GraphQl\DeleteMutation;
24
use ApiPlatform\Metadata\GraphQl\Mutation;
25
use ApiPlatform\Metadata\GraphQl\Query;
26
use ApiPlatform\Metadata\GraphQl\QueryCollection;
27
use ApiPlatform\Metadata\GraphQl\Subscription;
28
use ApiPlatform\Metadata\HttpOperation;
29
use ApiPlatform\Metadata\Operations;
30
use ApiPlatform\Metadata\Patch;
31
use ApiPlatform\Metadata\Post;
32
use ApiPlatform\Metadata\QueryParameter;
33
use ApiPlatform\Metadata\Resource\Factory\ExtractorResourceMetadataCollectionFactory;
34
use ApiPlatform\Metadata\Resource\Factory\OperationDefaultsTrait;
35
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
36
use ApiPlatform\Metadata\Tests\Extractor\Adapter\ResourceAdapterInterface;
37
use ApiPlatform\Metadata\Tests\Extractor\Adapter\XmlResourceAdapter;
38
use ApiPlatform\Metadata\Tests\Extractor\Adapter\YamlResourceAdapter;
39
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Comment;
40
use ApiPlatform\Metadata\Util\CamelCaseToSnakeCaseNameConverter;
41
use ApiPlatform\OpenApi\Model\ExternalDocumentation;
42
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
43
use ApiPlatform\OpenApi\Model\RequestBody;
44
use PHPUnit\Framework\AssertionFailedError;
45
use PHPUnit\Framework\TestCase;
46
use Symfony\Component\WebLink\Link;
47

48
/**
49
 * Ensures XML and YAML mappings are fully compatible with ApiResource.
50
 *
51
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
52
 */
53
final class ResourceMetadataCompatibilityTest extends TestCase
54
{
55
    use OperationDefaultsTrait;
56
    private const RESOURCE_CLASS = Comment::class;
57
    private const SHORT_NAME = 'Comment';
58
    private const DEFAULTS = [
59
        'route_prefix' => '/v1',
60
    ];
61
    private const FIXTURES = [
62
        null,
63
        [
64
            'uriTemplate' => '/users/{userId}/comments',
65
            'shortName' => self::SHORT_NAME,
66
            'description' => 'A list of Comments from User',
67
            'routePrefix' => '/api',
68
            'stateless' => true,
69
            'sunset' => '2021-01-01',
70
            'acceptPatch' => 'application/merge-patch+json',
71
            'status' => 200,
72
            'host' => 'example.com',
73
            'condition' => 'request.headers.get(\'User-Agent\') matches \{/firefox/i\'',
74
            'controller' => 'App\Controller\CommentController',
75
            'urlGenerationStrategy' => 1,
76
            'deprecationReason' => 'This resource is deprecated',
77
            'elasticsearch' => true,
78
            'messenger' => true,
79
            'input' => 'App\Dto\CommentInput',
80
            'output' => 'App\Dto\CommentOutut',
81
            'fetchPartial' => true,
82
            'forceEager' => true,
83
            'paginationClientEnabled' => true,
84
            'paginationClientItemsPerPage' => true,
85
            'paginationClientPartial' => true,
86
            'paginationEnabled' => true,
87
            'paginationFetchJoinCollection' => true,
88
            'paginationUseOutputWalkers' => true,
89
            'paginationItemsPerPage' => 42,
90
            'paginationMaximumItemsPerPage' => 200,
91
            'paginationPartial' => true,
92
            'paginationType' => 'page',
93
            'security' => 'is_granted(\'ROLE_USER\')',
94
            'securityMessage' => 'Sorry, you can\'t access this resource.',
95
            'securityPostDenormalize' => 'is_granted(\'ROLE_ADMIN\')',
96
            'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this resource.',
97
            'securityPostValidation' => 'is_granted(\'ROLE_OWNER\')',
98
            'securityPostValidationMessage' => 'Sorry, you must the owner of this resource to access it.',
99
            'queryParameterValidationEnabled' => true,
100
            'strictQueryParameterValidation' => false,
101
            'hideHydraOperation' => false,
102
            'types' => ['someirischema', 'anotheririschema'],
103
            'formats' => [
104
                'json' => null,
105
                'jsonld' => null,
106
                'xls' => 'application/vnd.ms-excel',
107
            ],
108
            'inputFormats' => [
109
                'json' => 'application/merge-patch+json',
110
            ],
111
            'outputFormats' => [
112
                'json' => 'application/merge-patch+json',
113
            ],
114
            'uriVariables' => [
115
                'userId' => [
116
                    'fromClass' => Comment::class,
117
                    'fromProperty' => 'author',
118
                    'compositeIdentifier' => true,
119
                ],
120
            ],
121
            'defaults' => [
122
                'prout' => 'pouet',
123
            ],
124
            'requirements' => [
125
                'id' => '\d+',
126
            ],
127
            'options' => [
128
                'foo' => 'bar',
129
            ],
130
            'schemes' => ['http', 'https'],
131
            'cacheHeaders' => [
132
                'max_age' => 60,
133
                'shared_max_age' => 120,
134
                'vary' => ['Authorization', 'Accept-Language'],
135
            ],
136
            'normalizationContext' => [
137
                'groups' => 'comment:read',
138
            ],
139
            'denormalizationContext' => [
140
                'groups' => ['comment:write', 'comment:custom'],
141
            ],
142
            'collectDenormalizationErrors' => true,
143
            'hydraContext' => [
144
                'foo' => ['bar' => 'baz'],
145
            ],
146
            'openapi' => [
147
                'extensionProperties' => [
148
                    'bar' => 'baz',
149
                ],
150
            ],
151
            'validationContext' => [
152
                'foo' => 'bar',
153
            ],
154
            'filters' => ['comment.custom_filter'],
155
            'order' => ['foo', 'bar'],
156
            'paginationViaCursor' => [
157
                'id' => 'DESC',
158
            ],
159
            'exceptionToStatus' => [
160
                'Symfony\Component\Serializer\Exception\ExceptionInterface' => 400,
161
            ],
162
            'extraProperties' => [
163
                'custom_property' => 'Lorem ipsum dolor sit amet',
164
                'another_custom_property' => [
165
                    'Lorem ipsum' => 'Dolor sit amet',
166
                ],
167
            ],
168
            'mercure' => true,
169
            'stateOptions' => [
170
                'elasticsearchOptions' => [
171
                    'index' => 'foo_index',
172
                    'type' => 'foo_type',
173
                ],
174
            ],
175
            'errors' => [
176
               Error::class,
177
            ],
178
            'graphQlOperations' => [
179
                [
180
                    'args' => [
181
                        'foo' => [
182
                            'type' => 'custom',
183
                            'bar' => 'baz',
184
                        ],
185
                    ],
186
                    'extraArgs' => [
187
                        'bar' => [
188
                            'type' => 'custom',
189
                            'baz' => 'qux',
190
                        ],
191
                    ],
192
                    'queryParameterValidationEnabled' => true,
193
                    'shortName' => self::SHORT_NAME,
194
                    'description' => 'Creates a Comment.',
195
                    'class' => Mutation::class,
196
                    'name' => 'create',
197
                    'urlGenerationStrategy' => 0,
198
                    'deprecationReason' => 'I don\'t know',
199
                    'normalizationContext' => [
200
                        'groups' => 'comment:read_collection',
201
                    ],
202
                    'denormalizationContext' => [
203
                        'groups' => ['comment:write'],
204
                    ],
205
                    'validationContext' => [
206
                        'foo' => 'bar',
207
                    ],
208
                    'filters' => ['comment.another_custom_filter'],
209
                    'elasticsearch' => false,
210
                    'mercure' => [
211
                        'private' => true,
212
                    ],
213
                    'messenger' => 'input',
214
                    'input' => 'App\Dto\CreateCommentInput',
215
                    'output' => 'App\Dto\CommentCollectionOutut',
216
                    'order' => ['userId'],
217
                    'fetchPartial' => false,
218
                    'forceEager' => false,
219
                    'paginationClientEnabled' => false,
220
                    'paginationClientItemsPerPage' => false,
221
                    'paginationClientPartial' => false,
222
                    'paginationEnabled' => false,
223
                    'paginationFetchJoinCollection' => false,
224
                    'paginationUseOutputWalkers' => false,
225
                    'paginationItemsPerPage' => 54,
226
                    'paginationMaximumItemsPerPage' => 200,
227
                    'paginationPartial' => false,
228
                    'paginationType' => 'page',
229
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
230
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
231
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
232
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
233
                    'read' => true,
234
                    'deserialize' => false,
235
                    'validate' => false,
236
                    'write' => false,
237
                    'serialize' => true,
238
                    'priority' => 200,
239
                    'extraProperties' => [
240
                        'custom_property' => 'Lorem ipsum dolor sit amet',
241
                        'another_custom_property' => [
242
                            'Lorem ipsum' => 'Dolor sit amet',
243
                        ],
244
                        'foo' => 'bar',
245
                        'route_prefix' => '/v1', // from defaults
246
                    ],
247
                    'stateOptions' => [
248
                        'elasticsearchOptions' => [
249
                            'index' => 'foo_index',
250
                            'type' => 'foo_type',
251
                        ],
252
                    ],
253
                ],
254
                [
255
                    'class' => Query::class,
256
                    'queryParameterValidationEnabled' => true,
257
                    'extraProperties' => [
258
                        'route_prefix' => '/v1',
259
                        'custom_property' => 'Lorem ipsum dolor sit amet',
260
                        'another_custom_property' => [
261
                            'Lorem ipsum' => 'Dolor sit amet',
262
                        ],
263
                    ],
264
                    'stateOptions' => [
265
                        'elasticsearchOptions' => [
266
                            'index' => 'foo_index',
267
                            'type' => 'foo_type',
268
                        ],
269
                    ],
270
                ],
271
                [
272
                    'class' => QueryCollection::class,
273
                    'queryParameterValidationEnabled' => true,
274
                    'extraProperties' => [
275
                        'route_prefix' => '/v1',
276
                        'custom_property' => 'Lorem ipsum dolor sit amet',
277
                        'another_custom_property' => [
278
                            'Lorem ipsum' => 'Dolor sit amet',
279
                        ],
280
                    ],
281
                    'stateOptions' => [
282
                        'elasticsearchOptions' => [
283
                            'index' => 'foo_index',
284
                            'type' => 'foo_type',
285
                        ],
286
                    ],
287
                ],
288
                [
289
                    'class' => Subscription::class,
290
                    'queryParameterValidationEnabled' => true,
291
                    'extraProperties' => [
292
                        'route_prefix' => '/v1',
293
                        'custom_property' => 'Lorem ipsum dolor sit amet',
294
                        'another_custom_property' => [
295
                            'Lorem ipsum' => 'Dolor sit amet',
296
                        ],
297
                    ],
298
                    'stateOptions' => [
299
                        'elasticsearchOptions' => [
300
                            'index' => 'foo_index',
301
                            'type' => 'foo_type',
302
                        ],
303
                    ],
304
                ],
305
            ],
306
            'operations' => [
307
                [
308
                    'name' => 'custom_operation_name',
309
                    'method' => 'GET',
310
                    'uriTemplate' => '/users/{userId}/comments{._format}',
311
                    'shortName' => self::SHORT_NAME,
312
                    'description' => 'A list of Comments',
313
                    'types' => ['Comment'],
314
                    'formats' => [
315
                        'json' => null,
316
                        'jsonld' => null,
317
                        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
318
                    ],
319
                    'inputFormats' => [
320
                        'jsonld' => 'application/merge-patch+json+ld',
321
                    ],
322
                    'outputFormats' => [
323
                        'jsonld' => 'application/merge-patch+json+ld',
324
                    ],
325
                    'uriVariables' => [
326
                        'userId' => [
327
                            'fromClass' => Comment::class,
328
                            'fromProperty' => 'author',
329
                            'compositeIdentifier' => true,
330
                        ],
331
                    ],
332
                    'routePrefix' => '/foo/api',
333
                    'defaults' => [
334
                        '_bar' => '_foo',
335
                    ],
336
                    'requirements' => [
337
                        'userId' => '\d+',
338
                    ],
339
                    'options' => [
340
                        'bar' => 'baz',
341
                    ],
342
                    'stateless' => false,
343
                    'sunset' => '2021-12-01',
344
                    'acceptPatch' => 'text/example;charset=utf-8',
345
                    'status' => 204,
346
                    'host' => 'api-platform.com',
347
                    'schemes' => ['https'],
348
                    'headers' => ['key' => 'value'],
349
                    'condition' => 'request.headers.has(\'Accept\')',
350
                    'controller' => 'App\Controller\CustomController',
351
                    'class' => GetCollection::class,
352
                    'urlGenerationStrategy' => 0,
353
                    'deprecationReason' => 'I don\'t know',
354
                    'cacheHeaders' => [
355
                        'max_age' => 60,
356
                        'shared_max_age' => 120,
357
                        'vary' => ['Authorization', 'Accept-Language', 'Accept'],
358
                    ],
359
                    'normalizationContext' => [
360
                        'groups' => 'comment:read_collection',
361
                    ],
362
                    'denormalizationContext' => [
363
                        'groups' => ['comment:write'],
364
                    ],
365
                    'hydraContext' => [
366
                        'foo' => ['bar' => 'baz'],
367
                    ],
368
                    'openapi' => [
369
                        'extensionProperties' => [
370
                            'bar' => 'baz',
371
                        ],
372
                    ],
373
                    'validationContext' => [
374
                        'foo' => 'bar',
375
                    ],
376
                    'filters' => ['comment.another_custom_filter'],
377
                    'elasticsearch' => false,
378
                    'mercure' => [
379
                        'private' => true,
380
                    ],
381
                    'messenger' => 'input',
382
                    'input' => 'App\Dto\CreateCommentInput',
383
                    'output' => 'App\Dto\CommentCollectionOutut',
384
                    'order' => ['userId'],
385
                    'fetchPartial' => false,
386
                    'forceEager' => false,
387
                    'paginationClientEnabled' => false,
388
                    'paginationClientItemsPerPage' => false,
389
                    'paginationClientPartial' => false,
390
                    'paginationViaCursor' => [
391
                        'userId' => 'DESC',
392
                    ],
393
                    'paginationEnabled' => false,
394
                    'paginationFetchJoinCollection' => false,
395
                    'paginationUseOutputWalkers' => false,
396
                    'paginationItemsPerPage' => 54,
397
                    'paginationMaximumItemsPerPage' => 200,
398
                    'paginationPartial' => false,
399
                    'paginationType' => 'page',
400
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
401
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
402
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
403
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
404
                    'exceptionToStatus' => [
405
                        'Symfony\Component\Serializer\Exception\ExceptionInterface' => 404,
406
                    ],
407
                    'queryParameterValidationEnabled' => false,
408
                    'strictQueryParameterValidation' => false,
409
                    'hideHydraOperation' => false,
410
                    'read' => true,
411
                    'deserialize' => false,
412
                    'validate' => false,
413
                    'write' => false,
414
                    'serialize' => true,
415
                    'priority' => 200,
416
                    'extraProperties' => [
417
                        'custom_property' => 'Lorem ipsum dolor sit amet',
418
                        'another_custom_property' => [
419
                            'Lorem ipsum' => 'Dolor sit amet',
420
                        ],
421
                        'foo' => 'bar',
422
                    ],
423
                    'links' => [
424
                        ['rel' => 'http://www.w3.org/ns/json-ld#error', 'href' => 'http://www.w3.org/ns/hydra/error'],
425
                    ],
426
                    'parameters' => [
427
                        'author' => ['key' => 'author', 'required' => true, 'schema' => ['type' => 'string']],
428
                    ],
429
                ],
430
                [
431
                    'uriTemplate' => '/users/{userId}/comments/{commentId}{._format}',
432
                    'class' => Get::class,
433
                    'uriVariables' => [
434
                        'userId' => [
435
                            'fromClass' => Comment::class,
436
                            'fromProperty' => 'author',
437
                            'compositeIdentifier' => true,
438
                        ],
439
                        'commentId' => [Comment::class, 'id'],
440
                    ],
441
                    'links' => [
442
                        ['rel' => 'http://www.w3.org/ns/json-ld#error', 'href' => 'http://www.w3.org/ns/hydra/error'],
443
                    ],
444
                    'parameters' => [
445
                        'date' => ['key' => 'date'],
446
                    ],
447
                ],
448
            ],
449
        ],
450
    ];
451
    private const BASE = [
452
        'shortName',
453
        'description',
454
        'urlGenerationStrategy',
455
        'deprecationReason',
456
        'elasticsearch',
457
        'messenger',
458
        'mercure',
459
        'input',
460
        'output',
461
        'fetchPartial',
462
        'forceEager',
463
        'paginationClientEnabled',
464
        'paginationClientItemsPerPage',
465
        'paginationClientPartial',
466
        'paginationEnabled',
467
        'paginationFetchJoinCollection',
468
        'paginationUseOutputWalkers',
469
        'paginationItemsPerPage',
470
        'paginationMaximumItemsPerPage',
471
        'paginationPartial',
472
        'paginationType',
473
        'processor',
474
        'provider',
475
        'security',
476
        'securityMessage',
477
        'securityPostDenormalize',
478
        'securityPostDenormalizeMessage',
479
        'securityPostValidation',
480
        'securityPostValidationMessage',
481
        'normalizationContext',
482
        'denormalizationContext',
483
        'collectDenormalizationErrors',
484
        'validationContext',
485
        'filters',
486
        'order',
487
        'extraProperties',
488
    ];
489
    private const EXTENDED_BASE = [
490
        'uriTemplate',
491
        'routePrefix',
492
        'stateless',
493
        'sunset',
494
        'acceptPatch',
495
        'status',
496
        'host',
497
        'condition',
498
        'controller',
499
        'queryParameterValidationEnabled',
500
        'strictQueryParameterValidation',
501
        'hideHydraOperation',
502
        'exceptionToStatus',
503
        'types',
504
        'formats',
505
        'inputFormats',
506
        'outputFormats',
507
        'uriVariables',
508
        'defaults',
509
        'requirements',
510
        'options',
511
        'schemes',
512
        'cacheHeaders',
513
        'hydraContext',
514
        'openapi',
515
        'paginationViaCursor',
516
        'stateOptions',
517
        'errors',
518
        'links',
519
        'rules',
520
        'headers',
521
        'parameters',
522
    ];
523

524
    #[\PHPUnit\Framework\Attributes\DataProvider('getExtractors')]
525
    public function testValidMetadata(string $extractorClass, ResourceAdapterInterface $adapter): void
526
    {
UNCOV
527
        $reflClass = new \ReflectionClass(ApiResource::class);
×
528
        $parameters = $reflClass->getConstructor()->getParameters();
×
529
        $this->defaults = self::DEFAULTS;
×
530
        $this->camelCaseToSnakeCaseNameConverter = new CamelCaseToSnakeCaseNameConverter();
×
531

532
        try {
UNCOV
533
            $extractor = new $extractorClass($adapter(self::RESOURCE_CLASS, $parameters, self::FIXTURES));
×
UNCOV
534
            $factory = new ExtractorResourceMetadataCollectionFactory($extractor, null, self::DEFAULTS, null, true);
×
535
            $collection = $factory->create(self::RESOURCE_CLASS);
×
536
        } catch (\Exception $exception) {
×
UNCOV
537
            throw new AssertionFailedError('Failed asserting that the schema is valid according to '.ApiResource::class, 0, $exception);
×
538
        }
539

UNCOV
540
        $resources = $this->buildApiResources();
×
541
        $this->assertEquals(new ResourceMetadataCollection(self::RESOURCE_CLASS, $resources), $collection);
×
542
    }
543

544
    public static function getExtractors(): array
545
    {
UNCOV
546
        return [
×
UNCOV
547
            [XmlResourceExtractor::class, new XmlResourceAdapter()],
×
UNCOV
548
            [YamlResourceExtractor::class, new YamlResourceAdapter()],
×
UNCOV
549
        ];
×
550
    }
551

552
    /**
553
     * @return ApiResource[]
554
     */
555
    private function buildApiResources(): array
556
    {
557
        $resources = [];
×
558

559
        foreach (self::FIXTURES as $fixtures) {
×
560
            $resource = (new ApiResource())->withClass(self::RESOURCE_CLASS)->withShortName(self::SHORT_NAME);
×
561

562
            if (null === $fixtures) {
×
563
                // Build default operations
UNCOV
564
                $operations = [];
×
565
                foreach ([new Get(), new GetCollection(), new Post(), new Patch(), new Delete()] as $operation) {
×
UNCOV
566
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $operation);
×
UNCOV
567
                    $operations[$name] = $operation;
×
568
                }
569

570
                $resource = $resource->withOperations(new Operations($operations));
×
571

572
                // Build default GraphQL operations
UNCOV
573
                $graphQlOperations = [];
×
UNCOV
574
                foreach ([new QueryCollection(), new Query(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')] as $graphQlOperation) {
×
575
                    $description = $graphQlOperation instanceof Mutation ? ucfirst("{$graphQlOperation->getName()}s a {$resource->getShortName()}.") : null;
×
UNCOV
576
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $graphQlOperation);
×
577
                    $graphQlOperations[$name] = $operation->withDescription($description);
×
578
                }
579

580
                $resources[] = $resource->withGraphQlOperations($graphQlOperations);
×
581

582
                continue;
×
583
            }
584

585
            foreach ($fixtures as $parameter => $value) {
×
586
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
587
                    $value = $this->{'with'.ucfirst($parameter)}($value, $fixtures);
×
588
                }
589

590
                if (method_exists($resource, 'with'.ucfirst($parameter))) {
×
UNCOV
591
                    $resource = $resource->{'with'.ucfirst($parameter)}($value, $fixtures);
×
UNCOV
592
                    continue;
×
593
                }
594

UNCOV
595
                throw new \RuntimeException(\sprintf('Unknown ApiResource parameter "%s".', $parameter));
×
596
            }
597

UNCOV
598
            $resources[] = $resource;
×
599
        }
600

601
        return $resources;
×
602
    }
603

604
    private function withOpenapi(array|bool $values): bool|OpenApiOperation
605
    {
606
        if (\is_bool($values)) {
×
607
            return $values;
×
608
        }
609

610
        $allowedProperties = array_map(fn (\ReflectionProperty $reflProperty): string => $reflProperty->getName(), (new \ReflectionClass(OpenApiOperation::class))->getProperties());
×
611
        foreach ($values as $key => $value) {
×
612
            $values[$key] = match ($key) {
×
UNCOV
613
                'externalDocs' => new ExternalDocumentation(description: $value['description'] ?? '', url: $value['url'] ?? ''),
×
614
                'requestBody' => new RequestBody(description: $value['description'] ?? '', content: isset($value['content']) ? new \ArrayObject($value['content']) : null, required: $value['required'] ?? false),
×
615
                'callbacks' => new \ArrayObject($value),
×
UNCOV
616
                default => $value,
×
UNCOV
617
            };
×
618

619
            if (\in_array($key, $allowedProperties, true)) {
×
UNCOV
620
                continue;
×
621
            }
622

UNCOV
623
            $values['extensionProperties'][$key] = $value;
×
UNCOV
624
            unset($values[$key]);
×
625
        }
626

627
        return new OpenApiOperation(...$values);
×
628
    }
629

630
    private function withUriVariables(array $values): array
631
    {
UNCOV
632
        $uriVariables = [];
×
UNCOV
633
        foreach ($values as $parameterName => $value) {
×
634
            if (\is_string($value)) {
×
635
                $uriVariables[$value] = $value;
×
UNCOV
636
                continue;
×
637
            }
638

UNCOV
639
            if (isset($value['fromClass']) || isset($value[0])) {
×
640
                $uriVariables[$parameterName]['from_class'] = $value['fromClass'] ?? $value[0];
×
641
            }
UNCOV
642
            if (isset($value['fromProperty']) || isset($value[1])) {
×
643
                $uriVariables[$parameterName]['from_property'] = $value['fromProperty'] ?? $value[1];
×
644
            }
UNCOV
645
            if (isset($value['toClass'])) {
×
646
                $uriVariables[$parameterName]['to_class'] = $value['toClass'];
×
647
            }
UNCOV
648
            if (isset($value['toProperty'])) {
×
649
                $uriVariables[$parameterName]['to_property'] = $value['toProperty'];
×
650
            }
UNCOV
651
            if (isset($value['identifiers'])) {
×
UNCOV
652
                $uriVariables[$parameterName]['identifiers'] = $value['identifiers'];
×
653
            }
654
            if (isset($value['compositeIdentifier'])) {
×
UNCOV
655
                $uriVariables[$parameterName]['composite_identifier'] = $value['compositeIdentifier'];
×
656
            }
657
        }
658

659
        return $uriVariables;
×
660
    }
661

662
    private function withOperations(array $values, ?array $fixtures): Operations
663
    {
UNCOV
664
        $operations = [];
×
665
        foreach ($values as $value) {
×
666
            $class = $value['class'] ?? HttpOperation::class;
×
667
            unset($value['class']);
×
UNCOV
668
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
669

UNCOV
670
            foreach (array_merge(self::BASE, self::EXTENDED_BASE) as $parameter) {
×
671
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
672
                    $value[$parameter] = $fixtures[$parameter];
×
673
                }
674
            }
675

676
            foreach ($value as $parameter => $parameterValue) {
×
677
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
678
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
679
                }
680

681
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
UNCOV
682
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
UNCOV
683
                    continue;
×
684
                }
685

UNCOV
686
                throw new \RuntimeException(\sprintf('Unknown Operation parameter "%s".', $parameter));
×
687
            }
688

UNCOV
689
            $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, self::RESOURCE_CLASS);
×
UNCOV
690
            $operations[$operationName] = $operation;
×
691
        }
692

693
        return new Operations($operations);
×
694
    }
695

696
    private function withGraphQlOperations(array $values, ?array $fixtures): array
697
    {
UNCOV
698
        $operations = [];
×
699
        foreach ($values as $value) {
×
700
            $class = $value['class'];
×
701
            unset($value['class']);
×
UNCOV
702
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
703

UNCOV
704
            foreach (self::BASE as $parameter) {
×
705
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
706
                    $value[$parameter] = $fixtures[$parameter];
×
707
                }
708
            }
709

710
            foreach ($value as $parameter => $parameterValue) {
×
711
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
712
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
713
                }
714

715
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
UNCOV
716
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
UNCOV
717
                    continue;
×
718
                }
719

UNCOV
720
                throw new \RuntimeException(\sprintf('Unknown GraphQlOperation parameter "%s".', $parameter));
×
721
            }
722

UNCOV
723
            $operationName = $operation->getName();
×
UNCOV
724
            $operations[$operationName] = $operation;
×
725
        }
726

727
        return $operations;
×
728
    }
729

730
    private function withStateOptions(array $values)
731
    {
732
        if (!$values) {
×
UNCOV
733
            return null;
×
734
        }
735

736
        if (1 !== \count($values)) {
×
737
            throw new \InvalidArgumentException('Only one options can be configured at a time.');
×
738
        }
739

UNCOV
740
        $configuration = reset($values);
×
741
        switch (key($values)) {
×
UNCOV
742
            case 'elasticsearchOptions':
×
UNCOV
743
                return null;
×
744
        }
745

746
        throw new \LogicException(\sprintf('Unsupported "%s" state options.', key($values)));
×
747
    }
748

749
    private function withLinks(array $values): ?array
750
    {
UNCOV
751
        if (!$values) {
×
UNCOV
752
            return null;
×
753
        }
754

755
        return [new Link($values[0]['rel'] ?? null, $values[0]['href'] ?? null)];
×
756
    }
757

758
    private function withParameters(array $values): ?array
759
    {
760
        if (!$values) {
×
761
            return null;
×
762
        }
763

764
        $parameters = [];
×
UNCOV
765
        foreach ($values as $k => $value) {
×
UNCOV
766
            $parameters[$k] = new QueryParameter(key: $value['key'] ?? $k, required: $value['required'] ?? null, schema: $value['schema'] ?? null);
×
767
        }
768

UNCOV
769
        return $parameters;
×
770
    }
771
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc