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

api-platform / core / 15394724286

02 Jun 2025 02:19PM UTC coverage: 22.201% (+0.1%) from 22.062%
15394724286

push

github

soyuka
feat(elasticsearch): add support for v9 (#7180)

Co-authored-by: darthf1 <17253332+darthf1@users.noreply.github.com>

10888 of 49042 relevant lines covered (22.2%)

10.59 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
            'mercure' => true,
167
            'stateOptions' => [
168
                'elasticsearchOptions' => [
169
                    'index' => 'foo_index',
170
                ],
171
            ],
172
            'graphQlOperations' => [
173
                [
174
                    'args' => [
175
                        'foo' => [
176
                            'type' => 'custom',
177
                            'bar' => 'baz',
178
                        ],
179
                    ],
180
                    'extraArgs' => [
181
                        'bar' => [
182
                            'type' => 'custom',
183
                            'baz' => 'qux',
184
                        ],
185
                    ],
186
                    'queryParameterValidationEnabled' => true,
187
                    'shortName' => self::SHORT_NAME,
188
                    'description' => 'Creates a Comment.',
189
                    'class' => Mutation::class,
190
                    'name' => 'create',
191
                    'urlGenerationStrategy' => 0,
192
                    'deprecationReason' => 'I don\'t know',
193
                    'normalizationContext' => [
194
                        'groups' => 'comment:read_collection',
195
                    ],
196
                    'denormalizationContext' => [
197
                        'groups' => ['comment:write'],
198
                    ],
199
                    'validationContext' => [
200
                        'foo' => 'bar',
201
                    ],
202
                    'filters' => ['comment.another_custom_filter'],
203
                    'mercure' => [
204
                        'private' => true,
205
                    ],
206
                    'messenger' => 'input',
207
                    'input' => 'App\Dto\CreateCommentInput',
208
                    'output' => 'App\Dto\CommentCollectionOutut',
209
                    'order' => ['userId'],
210
                    'fetchPartial' => false,
211
                    'forceEager' => false,
212
                    'paginationClientEnabled' => false,
213
                    'paginationClientItemsPerPage' => false,
214
                    'paginationClientPartial' => false,
215
                    'paginationEnabled' => false,
216
                    'paginationFetchJoinCollection' => false,
217
                    'paginationUseOutputWalkers' => false,
218
                    'paginationItemsPerPage' => 54,
219
                    'paginationMaximumItemsPerPage' => 200,
220
                    'paginationPartial' => false,
221
                    'paginationType' => 'page',
222
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
223
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
224
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
225
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
226
                    'read' => true,
227
                    'deserialize' => false,
228
                    'validate' => false,
229
                    'write' => false,
230
                    'serialize' => true,
231
                    'priority' => 200,
232
                    'extraProperties' => [
233
                        'custom_property' => 'Lorem ipsum dolor sit amet',
234
                        'another_custom_property' => [
235
                            'Lorem ipsum' => 'Dolor sit amet',
236
                        ],
237
                        'foo' => 'bar',
238
                        'route_prefix' => '/v1', // from defaults
239
                    ],
240
                    'stateOptions' => [
241
                        'elasticsearchOptions' => [
242
                            'index' => 'foo_index',
243
                        ],
244
                    ],
245
                ],
246
                [
247
                    'class' => Query::class,
248
                    'queryParameterValidationEnabled' => true,
249
                    'extraProperties' => [
250
                        'route_prefix' => '/v1',
251
                        'custom_property' => 'Lorem ipsum dolor sit amet',
252
                        'another_custom_property' => [
253
                            'Lorem ipsum' => 'Dolor sit amet',
254
                        ],
255
                    ],
256
                    'stateOptions' => [
257
                        'elasticsearchOptions' => [
258
                            'index' => 'foo_index',
259
                        ],
260
                    ],
261
                ],
262
                [
263
                    'class' => QueryCollection::class,
264
                    'queryParameterValidationEnabled' => true,
265
                    'extraProperties' => [
266
                        'route_prefix' => '/v1',
267
                        'custom_property' => 'Lorem ipsum dolor sit amet',
268
                        'another_custom_property' => [
269
                            'Lorem ipsum' => 'Dolor sit amet',
270
                        ],
271
                    ],
272
                    'stateOptions' => [
273
                        'elasticsearchOptions' => [
274
                            'index' => 'foo_index',
275
                        ],
276
                    ],
277
                ],
278
                [
279
                    'class' => Subscription::class,
280
                    'queryParameterValidationEnabled' => true,
281
                    'extraProperties' => [
282
                        'route_prefix' => '/v1',
283
                        'custom_property' => 'Lorem ipsum dolor sit amet',
284
                        'another_custom_property' => [
285
                            'Lorem ipsum' => 'Dolor sit amet',
286
                        ],
287
                    ],
288
                    'stateOptions' => [
289
                        'elasticsearchOptions' => [
290
                            'index' => 'foo_index',
291
                        ],
292
                    ],
293
                ],
294
            ],
295
            'operations' => [
296
                [
297
                    'name' => 'custom_operation_name',
298
                    'method' => 'GET',
299
                    'uriTemplate' => '/users/{userId}/comments{._format}',
300
                    'shortName' => self::SHORT_NAME,
301
                    'description' => 'A list of Comments',
302
                    'types' => ['Comment'],
303
                    'formats' => [
304
                        'json' => null,
305
                        'jsonld' => null,
306
                        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
307
                    ],
308
                    'inputFormats' => [
309
                        'jsonld' => 'application/merge-patch+json+ld',
310
                    ],
311
                    'outputFormats' => [
312
                        'jsonld' => 'application/merge-patch+json+ld',
313
                    ],
314
                    'uriVariables' => [
315
                        'userId' => [
316
                            'fromClass' => Comment::class,
317
                            'fromProperty' => 'author',
318
                            'compositeIdentifier' => true,
319
                        ],
320
                    ],
321
                    'routePrefix' => '/foo/api',
322
                    'defaults' => [
323
                        '_bar' => '_foo',
324
                    ],
325
                    'requirements' => [
326
                        'userId' => '\d+',
327
                    ],
328
                    'options' => [
329
                        'bar' => 'baz',
330
                    ],
331
                    'stateless' => false,
332
                    'sunset' => '2021-12-01',
333
                    'acceptPatch' => 'text/example;charset=utf-8',
334
                    'status' => 204,
335
                    'host' => 'api-platform.com',
336
                    'schemes' => ['https'],
337
                    'headers' => ['key' => 'value'],
338
                    'condition' => 'request.headers.has(\'Accept\')',
339
                    'controller' => 'App\Controller\CustomController',
340
                    'class' => GetCollection::class,
341
                    'urlGenerationStrategy' => 0,
342
                    'deprecationReason' => 'I don\'t know',
343
                    'cacheHeaders' => [
344
                        'max_age' => 60,
345
                        'shared_max_age' => 120,
346
                        'vary' => ['Authorization', 'Accept-Language', 'Accept'],
347
                    ],
348
                    'normalizationContext' => [
349
                        'groups' => 'comment:read_collection',
350
                    ],
351
                    'denormalizationContext' => [
352
                        'groups' => ['comment:write'],
353
                    ],
354
                    'hydraContext' => [
355
                        'foo' => ['bar' => 'baz'],
356
                    ],
357
                    'openapi' => [
358
                        'extensionProperties' => [
359
                            'bar' => 'baz',
360
                        ],
361
                    ],
362
                    'validationContext' => [
363
                        'foo' => 'bar',
364
                    ],
365
                    'filters' => ['comment.another_custom_filter'],
366
                    'mercure' => [
367
                        'private' => true,
368
                    ],
369
                    'messenger' => 'input',
370
                    'input' => 'App\Dto\CreateCommentInput',
371
                    'output' => 'App\Dto\CommentCollectionOutut',
372
                    'order' => ['userId'],
373
                    'fetchPartial' => false,
374
                    'forceEager' => false,
375
                    'paginationClientEnabled' => false,
376
                    'paginationClientItemsPerPage' => false,
377
                    'paginationClientPartial' => false,
378
                    'paginationViaCursor' => [
379
                        'userId' => 'DESC',
380
                    ],
381
                    'paginationEnabled' => false,
382
                    'paginationFetchJoinCollection' => false,
383
                    'paginationUseOutputWalkers' => false,
384
                    'paginationItemsPerPage' => 54,
385
                    'paginationMaximumItemsPerPage' => 200,
386
                    'paginationPartial' => false,
387
                    'paginationType' => 'page',
388
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
389
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
390
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
391
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
392
                    'exceptionToStatus' => [
393
                        'Symfony\Component\Serializer\Exception\ExceptionInterface' => 404,
394
                    ],
395
                    'queryParameterValidationEnabled' => false,
396
                    'strictQueryParameterValidation' => false,
397
                    'hideHydraOperation' => false,
398
                    'read' => true,
399
                    'deserialize' => false,
400
                    'validate' => false,
401
                    'write' => false,
402
                    'serialize' => true,
403
                    'priority' => 200,
404
                    'extraProperties' => [
405
                        'custom_property' => 'Lorem ipsum dolor sit amet',
406
                        'another_custom_property' => [
407
                            'Lorem ipsum' => 'Dolor sit amet',
408
                        ],
409
                        'foo' => 'bar',
410
                    ],
411
                    'links' => [
412
                        ['rel' => 'http://www.w3.org/ns/json-ld#error', 'href' => 'http://www.w3.org/ns/hydra/error'],
413
                    ],
414
                    'parameters' => [
415
                        'author' => ['key' => 'author', 'required' => true, 'schema' => ['type' => 'string']],
416
                    ],
417
                ],
418
                [
419
                    'uriTemplate' => '/users/{userId}/comments/{commentId}{._format}',
420
                    'class' => Get::class,
421
                    'uriVariables' => [
422
                        'userId' => [
423
                            'fromClass' => Comment::class,
424
                            'fromProperty' => 'author',
425
                            'compositeIdentifier' => true,
426
                        ],
427
                        'commentId' => [Comment::class, 'id'],
428
                    ],
429
                    'links' => [
430
                        ['rel' => 'http://www.w3.org/ns/json-ld#error', 'href' => 'http://www.w3.org/ns/hydra/error'],
431
                    ],
432
                    'parameters' => [
433
                        'date' => ['key' => 'date'],
434
                    ],
435
                ],
436
            ],
437
        ],
438
    ];
439
    private const BASE = [
440
        'shortName',
441
        'description',
442
        'urlGenerationStrategy',
443
        'deprecationReason',
444
        'messenger',
445
        'mercure',
446
        'input',
447
        'output',
448
        'fetchPartial',
449
        'forceEager',
450
        'paginationClientEnabled',
451
        'paginationClientItemsPerPage',
452
        'paginationClientPartial',
453
        'paginationEnabled',
454
        'paginationFetchJoinCollection',
455
        'paginationUseOutputWalkers',
456
        'paginationItemsPerPage',
457
        'paginationMaximumItemsPerPage',
458
        'paginationPartial',
459
        'paginationType',
460
        'processor',
461
        'provider',
462
        'security',
463
        'securityMessage',
464
        'securityPostDenormalize',
465
        'securityPostDenormalizeMessage',
466
        'securityPostValidation',
467
        'securityPostValidationMessage',
468
        'normalizationContext',
469
        'denormalizationContext',
470
        'collectDenormalizationErrors',
471
        'validationContext',
472
        'filters',
473
        'order',
474
        'extraProperties',
475
    ];
476
    private const EXTENDED_BASE = [
477
        'uriTemplate',
478
        'routePrefix',
479
        'stateless',
480
        'sunset',
481
        'acceptPatch',
482
        'status',
483
        'host',
484
        'condition',
485
        'controller',
486
        'queryParameterValidationEnabled',
487
        'strictQueryParameterValidation',
488
        'hideHydraOperation',
489
        'exceptionToStatus',
490
        'types',
491
        'formats',
492
        'inputFormats',
493
        'outputFormats',
494
        'uriVariables',
495
        'defaults',
496
        'requirements',
497
        'options',
498
        'schemes',
499
        'cacheHeaders',
500
        'hydraContext',
501
        'openapi',
502
        'paginationViaCursor',
503
        'stateOptions',
504
        'links',
505
        'rules',
506
        'headers',
507
        'parameters',
508
    ];
509

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

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

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

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

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

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

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

556
                $resource = $resource->withOperations(new Operations($operations));
×
557

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

566
                $resources[] = $resource->withGraphQlOperations($graphQlOperations);
×
567

568
                continue;
×
569
            }
570

571
            foreach ($fixtures as $parameter => $value) {
×
572
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
573
                    $value = $this->{'with'.ucfirst($parameter)}($value, $fixtures);
×
574
                }
575

576
                if (method_exists($resource, 'with'.ucfirst($parameter))) {
×
577
                    $resource = $resource->{'with'.ucfirst($parameter)}($value, $fixtures);
×
578
                    continue;
×
579
                }
580

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

584
            $resources[] = $resource;
×
585
        }
586

587
        return $resources;
×
588
    }
589

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

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

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

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

613
        return new OpenApiOperation(...$values);
×
614
    }
615

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

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

645
        return $uriVariables;
×
646
    }
647

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

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

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

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

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

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

679
        return new Operations($operations);
×
680
    }
681

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

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

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

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

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

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

713
        return $operations;
×
714
    }
715

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

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

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

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

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

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

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

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

755
        return $parameters;
×
756
    }
757
}
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