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

api-platform / core / 19438754788

17 Nov 2025 05:34PM UTC coverage: 0.0%. Remained the same
19438754788

push

github

soyuka
Merge 4.2

0 of 95 new or added lines in 28 files covered. (0.0%)

19 existing lines in 18 files now uncovered.

0 of 57020 relevant lines covered (0.0%)

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

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

512
    #[\PHPUnit\Framework\Attributes\DataProvider('getExtractors')]
513
    public function testValidMetadata(string $extractorClass, ResourceAdapterInterface $adapter): void
514
    {
515
        $reflClass = new \ReflectionClass(ApiResource::class);
×
516
        $parameters = $reflClass->getConstructor()->getParameters();
×
517
        $this->defaults = self::DEFAULTS;
×
518
        $this->camelCaseToSnakeCaseNameConverter = new CamelCaseToSnakeCaseNameConverter();
×
519

520
        try {
521
            $extractor = new $extractorClass($adapter(self::RESOURCE_CLASS, $parameters, self::FIXTURES));
×
522
            $factory = new ExtractorResourceMetadataCollectionFactory($extractor, null, self::DEFAULTS, null, true);
×
523
            $collection = $factory->create(self::RESOURCE_CLASS);
×
524
        } catch (\Exception $exception) {
×
525
            throw new AssertionFailedError('Failed asserting that the schema is valid according to '.ApiResource::class, 0, $exception);
×
526
        }
527

528
        $resources = $this->buildApiResources();
×
529
        $this->assertEquals(new ResourceMetadataCollection(self::RESOURCE_CLASS, $resources), $collection);
×
530
    }
531

532
    public static function getExtractors(): array
533
    {
534
        return [
×
535
            [XmlResourceExtractor::class, new XmlResourceAdapter()],
×
536
            [YamlResourceExtractor::class, new YamlResourceAdapter()],
×
537
        ];
×
538
    }
539

540
    /**
541
     * @return ApiResource[]
542
     */
543
    private function buildApiResources(): array
544
    {
545
        $resources = [];
×
546

547
        foreach (self::FIXTURES as $fixtures) {
×
548
            $resource = (new ApiResource())->withClass(self::RESOURCE_CLASS)->withShortName(self::SHORT_NAME);
×
549

550
            if (null === $fixtures) {
×
551
                // Build default operations
552
                $operations = [];
×
553
                foreach ([new Get(), new GetCollection(), new Post(), new Patch(), new Delete()] as $operation) {
×
554
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $operation);
×
555
                    $operations[$name] = $operation;
×
556
                }
557

558
                $resource = $resource->withOperations(new Operations($operations));
×
559

560
                // Build default GraphQL operations
561
                $graphQlOperations = [];
×
562
                foreach ([new QueryCollection(), new Query(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')] as $graphQlOperation) {
×
563
                    $description = $graphQlOperation instanceof Mutation ? ucfirst("{$graphQlOperation->getName()}s a {$resource->getShortName()}.") : null;
×
564
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $graphQlOperation);
×
565
                    $graphQlOperations[$name] = $operation->withDescription($description);
×
566
                }
567

568
                $resources[] = $resource->withGraphQlOperations($graphQlOperations);
×
569

570
                continue;
×
571
            }
572

573
            foreach ($fixtures as $parameter => $value) {
×
574
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
575
                    $value = $this->{'with'.ucfirst($parameter)}($value, $fixtures); // @phpstan-ignore-line
×
576
                }
577

578
                if (method_exists($resource, 'with'.ucfirst($parameter))) {
×
579
                    $resource = $resource->{'with'.ucfirst($parameter)}($value, $fixtures); // @phpstan-ignore-line
×
580
                    continue;
×
581
                }
582

583
                throw new \RuntimeException(\sprintf('Unknown ApiResource parameter "%s".', $parameter));
×
584
            }
585

586
            $resources[] = $resource;
×
587
        }
588

589
        return $resources;
×
590
    }
591

592
    private function withOpenapi(array|bool $values): bool|OpenApiOperation
593
    {
594
        if (\is_bool($values)) {
×
595
            return $values;
×
596
        }
597

598
        $allowedProperties = array_map(fn (\ReflectionProperty $reflProperty): string => $reflProperty->getName(), (new \ReflectionClass(OpenApiOperation::class))->getProperties());
×
599
        foreach ($values as $key => $value) {
×
600
            $values[$key] = match ($key) {
×
601
                'externalDocs' => new ExternalDocumentation(description: $value['description'] ?? '', url: $value['url'] ?? ''),
×
602
                'requestBody' => new RequestBody(description: $value['description'] ?? '', content: isset($value['content']) ? new \ArrayObject($value['content']) : null, required: $value['required'] ?? false),
×
603
                'callbacks' => new \ArrayObject($value),
×
604
                default => $value,
×
605
            };
×
606

607
            if (\in_array($key, $allowedProperties, true)) {
×
608
                continue;
×
609
            }
610

611
            $values['extensionProperties'][$key] = $value;
×
612
            unset($values[$key]);
×
613
        }
614

615
        return new OpenApiOperation(...$values);
×
616
    }
617

618
    private function withUriVariables(array $values): array
619
    {
620
        $uriVariables = [];
×
621
        foreach ($values as $parameterName => $value) {
×
622
            if (\is_string($value)) {
×
623
                $uriVariables[$value] = $value;
×
624
                continue;
×
625
            }
626

627
            if (isset($value['fromClass']) || isset($value[0])) {
×
628
                $uriVariables[$parameterName]['from_class'] = $value['fromClass'] ?? $value[0];
×
629
            }
630
            if (isset($value['fromProperty']) || isset($value[1])) {
×
631
                $uriVariables[$parameterName]['from_property'] = $value['fromProperty'] ?? $value[1];
×
632
            }
633
            if (isset($value['toClass'])) {
×
634
                $uriVariables[$parameterName]['to_class'] = $value['toClass'];
×
635
            }
636
            if (isset($value['toProperty'])) {
×
637
                $uriVariables[$parameterName]['to_property'] = $value['toProperty'];
×
638
            }
639
            if (isset($value['identifiers'])) {
×
640
                $uriVariables[$parameterName]['identifiers'] = $value['identifiers'];
×
641
            }
642
            if (isset($value['compositeIdentifier'])) {
×
643
                $uriVariables[$parameterName]['composite_identifier'] = $value['compositeIdentifier'];
×
644
            }
645
        }
646

647
        return $uriVariables;
×
648
    }
649

650
    private function withOperations(array $values, ?array $fixtures): Operations
651
    {
652
        $operations = [];
×
653
        foreach ($values as $value) {
×
654
            $class = $value['class'] ?? HttpOperation::class;
×
655
            unset($value['class']);
×
656
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
657

658
            foreach (array_merge(self::BASE, self::EXTENDED_BASE) as $parameter) {
×
659
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
660
                    $value[$parameter] = $fixtures[$parameter];
×
661
                }
662
            }
663

664
            foreach ($value as $parameter => $parameterValue) {
×
665
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
666
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
667
                }
668

669
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
670
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
671
                    continue;
×
672
                }
673

674
                throw new \RuntimeException(\sprintf('Unknown Operation parameter "%s".', $parameter));
×
675
            }
676

677
            $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, self::RESOURCE_CLASS);
×
678
            $operations[$operationName] = $operation;
×
679
        }
680

681
        return new Operations($operations);
×
682
    }
683

684
    private function withGraphQlOperations(array $values, ?array $fixtures): array
685
    {
686
        $operations = [];
×
687
        foreach ($values as $value) {
×
688
            $class = $value['class'];
×
689
            unset($value['class']);
×
690
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
691

692
            foreach (self::BASE as $parameter) {
×
693
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
694
                    $value[$parameter] = $fixtures[$parameter];
×
695
                }
696
            }
697

698
            foreach ($value as $parameter => $parameterValue) {
×
699
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
700
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
701
                }
702

703
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
704
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
705
                    continue;
×
706
                }
707

708
                throw new \RuntimeException(\sprintf('Unknown GraphQlOperation parameter "%s".', $parameter));
×
709
            }
710

711
            $operationName = $operation->getName();
×
712
            $operations[$operationName] = $operation;
×
713
        }
714

715
        return $operations;
×
716
    }
717

718
    private function withStateOptions(array $values)
719
    {
720
        if (!$values) {
×
721
            return null;
×
722
        }
723

724
        if (1 !== \count($values)) {
×
725
            throw new \InvalidArgumentException('Only one options can be configured at a time.');
×
726
        }
727

728
        $configuration = reset($values);
×
729
        switch (key($values)) {
×
730
            case 'elasticsearchOptions':
×
731
                return null;
×
732
        }
733

734
        throw new \LogicException(\sprintf('Unsupported "%s" state options.', key($values)));
×
735
    }
736

737
    private function withLinks(array $values): ?array
738
    {
739
        if (!$values) {
×
740
            return null;
×
741
        }
742

743
        return [new Link($values[0]['rel'] ?? null, $values[0]['href'] ?? null)];
×
744
    }
745

746
    private function withParameters(array $values): ?array
747
    {
748
        if (!$values) {
×
749
            return null;
×
750
        }
751

752
        $parameters = [];
×
753
        foreach ($values as $k => $value) {
×
754
            $parameters[$k] = new QueryParameter(key: $value['key'] ?? $k, required: $value['required'] ?? null, schema: $value['schema'] ?? null);
×
755
        }
756

757
        return $parameters;
×
758
    }
759

760
    private function withJsonStream(bool $value): bool
761
    {
762
        return $value;
×
763
    }
764

765
    private function withMap(bool $value): bool
766
    {
NEW
767
        return $value;
×
768
    }
769
}
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