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

api-platform / core / 8658338405

12 Apr 2024 07:07AM UTC coverage: 58.893% (-0.004%) from 58.897%
8658338405

push

github

soyuka
chore: doctrine deprecations (#6237)

0 of 1 new or added line in 1 file covered. (0.0%)

6 existing lines in 3 files now uncovered.

10900 of 18508 relevant lines covered (58.89%)

19.84 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\Elasticsearch\State\Options;
17
use ApiPlatform\Metadata\ApiResource;
18
use ApiPlatform\Metadata\Delete;
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\Put;
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 ApiPlatform\State\OptionsInterface;
45
use PHPUnit\Framework\AssertionFailedError;
46
use PHPUnit\Framework\TestCase;
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
            '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
            // TODO Remove in 4.0
145
            'openapiContext' => [
146
                'bar' => 'baz',
147
            ],
148
            'openapi' => [
149
                'extensionProperties' => [
150
                    'bar' => 'baz',
151
                ],
152
            ],
153
            'validationContext' => [
154
                'foo' => 'bar',
155
            ],
156
            'filters' => ['comment.custom_filter'],
157
            'order' => ['foo', 'bar'],
158
            'paginationViaCursor' => [
159
                'id' => 'DESC',
160
            ],
161
            'exceptionToStatus' => [
162
                'Symfony\Component\Serializer\Exception\ExceptionInterface' => 400,
163
            ],
164
            'extraProperties' => [
165
                'custom_property' => 'Lorem ipsum dolor sit amet',
166
                'another_custom_property' => [
167
                    'Lorem ipsum' => 'Dolor sit amet',
168
                ],
169
            ],
170
            'mercure' => true,
171
            'stateOptions' => [
172
                'elasticsearchOptions' => [
173
                    'index' => 'foo_index',
174
                    'type' => 'foo_type',
175
                ],
176
            ],
177
            'graphQlOperations' => [
178
                [
179
                    'args' => [
180
                        'foo' => [
181
                            'type' => 'custom',
182
                            'bar' => 'baz',
183
                        ],
184
                    ],
185
                    'shortName' => self::SHORT_NAME,
186
                    'description' => 'Creates a Comment.',
187
                    'class' => Mutation::class,
188
                    'name' => 'create',
189
                    'urlGenerationStrategy' => 0,
190
                    'deprecationReason' => 'I don\'t know',
191
                    'normalizationContext' => [
192
                        'groups' => 'comment:read_collection',
193
                    ],
194
                    'denormalizationContext' => [
195
                        'groups' => ['comment:write'],
196
                    ],
197
                    'validationContext' => [
198
                        'foo' => 'bar',
199
                    ],
200
                    'filters' => ['comment.another_custom_filter'],
201
                    'elasticsearch' => false,
202
                    'mercure' => [
203
                        'private' => true,
204
                    ],
205
                    'messenger' => 'input',
206
                    'input' => 'App\Dto\CreateCommentInput',
207
                    'output' => 'App\Dto\CommentCollectionOutut',
208
                    'order' => ['userId'],
209
                    'fetchPartial' => false,
210
                    'forceEager' => false,
211
                    'paginationClientEnabled' => false,
212
                    'paginationClientItemsPerPage' => false,
213
                    'paginationClientPartial' => false,
214
                    'paginationEnabled' => false,
215
                    'paginationFetchJoinCollection' => false,
216
                    'paginationUseOutputWalkers' => false,
217
                    'paginationItemsPerPage' => 54,
218
                    'paginationMaximumItemsPerPage' => 200,
219
                    'paginationPartial' => false,
220
                    'paginationType' => 'page',
221
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
222
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
223
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
224
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
225
                    'read' => true,
226
                    'deserialize' => false,
227
                    'validate' => false,
228
                    'write' => false,
229
                    'serialize' => true,
230
                    'priority' => 200,
231
                    'extraProperties' => [
232
                        'custom_property' => 'Lorem ipsum dolor sit amet',
233
                        'another_custom_property' => [
234
                            'Lorem ipsum' => 'Dolor sit amet',
235
                        ],
236
                        'foo' => 'bar',
237
                        'route_prefix' => '/v1', // from defaults
238
                    ],
239
                    'stateOptions' => [
240
                        'elasticsearchOptions' => [
241
                            'index' => 'foo_index',
242
                            'type' => 'foo_type',
243
                        ],
244
                    ],
245
                ],
246
                [
247
                    'class' => Query::class,
248
                    'extraProperties' => [
249
                        'route_prefix' => '/v1',
250
                        'custom_property' => 'Lorem ipsum dolor sit amet',
251
                        'another_custom_property' => [
252
                            'Lorem ipsum' => 'Dolor sit amet',
253
                        ],
254
                    ],
255
                    'stateOptions' => [
256
                        'elasticsearchOptions' => [
257
                            'index' => 'foo_index',
258
                            'type' => 'foo_type',
259
                        ],
260
                    ],
261
                ],
262
                [
263
                    'class' => QueryCollection::class,
264
                    'extraProperties' => [
265
                        'route_prefix' => '/v1',
266
                        'custom_property' => 'Lorem ipsum dolor sit amet',
267
                        'another_custom_property' => [
268
                            'Lorem ipsum' => 'Dolor sit amet',
269
                        ],
270
                    ],
271
                    'stateOptions' => [
272
                        'elasticsearchOptions' => [
273
                            'index' => 'foo_index',
274
                            'type' => 'foo_type',
275
                        ],
276
                    ],
277
                ],
278
                [
279
                    'class' => Subscription::class,
280
                    'extraProperties' => [
281
                        'route_prefix' => '/v1',
282
                        'custom_property' => 'Lorem ipsum dolor sit amet',
283
                        'another_custom_property' => [
284
                            'Lorem ipsum' => 'Dolor sit amet',
285
                        ],
286
                    ],
287
                    'stateOptions' => [
288
                        'elasticsearchOptions' => [
289
                            'index' => 'foo_index',
290
                            'type' => 'foo_type',
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
                    'condition' => 'request.headers.has(\'Accept\')',
338
                    'controller' => 'App\Controller\CustomController',
339
                    'class' => GetCollection::class,
340
                    'urlGenerationStrategy' => 0,
341
                    'deprecationReason' => 'I don\'t know',
342
                    'cacheHeaders' => [
343
                        'max_age' => 60,
344
                        'shared_max_age' => 120,
345
                        'vary' => ['Authorization', 'Accept-Language', 'Accept'],
346
                    ],
347
                    'normalizationContext' => [
348
                        'groups' => 'comment:read_collection',
349
                    ],
350
                    'denormalizationContext' => [
351
                        'groups' => ['comment:write'],
352
                    ],
353
                    'hydraContext' => [
354
                        'foo' => ['bar' => 'baz'],
355
                    ],
356
                    // TODO Remove in 4.0
357
                    'openapiContext' => [
358
                        'bar' => 'baz',
359
                    ],
360
                    'openapi' => [
361
                        'extensionProperties' => [
362
                            'bar' => 'baz',
363
                        ],
364
                    ],
365
                    'validationContext' => [
366
                        'foo' => 'bar',
367
                    ],
368
                    'filters' => ['comment.another_custom_filter'],
369
                    'elasticsearch' => false,
370
                    'mercure' => [
371
                        'private' => true,
372
                    ],
373
                    'messenger' => 'input',
374
                    'input' => 'App\Dto\CreateCommentInput',
375
                    'output' => 'App\Dto\CommentCollectionOutut',
376
                    'order' => ['userId'],
377
                    'fetchPartial' => false,
378
                    'forceEager' => false,
379
                    'paginationClientEnabled' => false,
380
                    'paginationClientItemsPerPage' => false,
381
                    'paginationClientPartial' => false,
382
                    'paginationViaCursor' => [
383
                        'userId' => 'DESC',
384
                    ],
385
                    'paginationEnabled' => false,
386
                    'paginationFetchJoinCollection' => false,
387
                    'paginationUseOutputWalkers' => false,
388
                    'paginationItemsPerPage' => 54,
389
                    'paginationMaximumItemsPerPage' => 200,
390
                    'paginationPartial' => false,
391
                    'paginationType' => 'page',
392
                    'security' => 'is_granted(\'IS_AUTHENTICATED_ANONYMOUSLY\')',
393
                    'securityMessage' => 'Sorry, you can\'t access this collection.',
394
                    'securityPostDenormalize' => 'is_granted(\'ROLE_CUSTOM_ADMIN\')',
395
                    'securityPostDenormalizeMessage' => 'Sorry, you must an admin to access this collection.',
396
                    'exceptionToStatus' => [
397
                        'Symfony\Component\Serializer\Exception\ExceptionInterface' => 404,
398
                    ],
399
                    'queryParameterValidationEnabled' => false,
400
                    'read' => true,
401
                    'deserialize' => false,
402
                    'validate' => false,
403
                    'write' => false,
404
                    'serialize' => true,
405
                    'priority' => 200,
406
                    'extraProperties' => [
407
                        'custom_property' => 'Lorem ipsum dolor sit amet',
408
                        'another_custom_property' => [
409
                            'Lorem ipsum' => 'Dolor sit amet',
410
                        ],
411
                        'foo' => 'bar',
412
                    ],
413
                ],
414
                [
415
                    'uriTemplate' => '/users/{userId}/comments/{commentId}{._format}',
416
                    'class' => Get::class,
417
                    'uriVariables' => [
418
                        'userId' => [
419
                            'fromClass' => Comment::class,
420
                            'fromProperty' => 'author',
421
                            'compositeIdentifier' => true,
422
                        ],
423
                        'commentId' => [Comment::class, 'id'],
424
                    ],
425
                ],
426
            ],
427
        ],
428
    ];
429
    private const BASE = [
430
        'shortName',
431
        'description',
432
        'urlGenerationStrategy',
433
        'deprecationReason',
434
        'elasticsearch',
435
        'messenger',
436
        'mercure',
437
        'input',
438
        'output',
439
        'fetchPartial',
440
        'forceEager',
441
        'paginationClientEnabled',
442
        'paginationClientItemsPerPage',
443
        'paginationClientPartial',
444
        'paginationEnabled',
445
        'paginationFetchJoinCollection',
446
        'paginationUseOutputWalkers',
447
        'paginationItemsPerPage',
448
        'paginationMaximumItemsPerPage',
449
        'paginationPartial',
450
        'paginationType',
451
        'processor',
452
        'provider',
453
        'security',
454
        'securityMessage',
455
        'securityPostDenormalize',
456
        'securityPostDenormalizeMessage',
457
        'securityPostValidation',
458
        'securityPostValidationMessage',
459
        'normalizationContext',
460
        'denormalizationContext',
461
        'collectDenormalizationErrors',
462
        'validationContext',
463
        'filters',
464
        'order',
465
        'extraProperties',
466
    ];
467
    private const EXTENDED_BASE = [
468
        'uriTemplate',
469
        'routePrefix',
470
        'stateless',
471
        'sunset',
472
        'acceptPatch',
473
        'status',
474
        'host',
475
        'condition',
476
        'controller',
477
        'queryParameterValidationEnabled',
478
        'exceptionToStatus',
479
        'types',
480
        'formats',
481
        'inputFormats',
482
        'outputFormats',
483
        'uriVariables',
484
        'defaults',
485
        'requirements',
486
        'options',
487
        'schemes',
488
        'cacheHeaders',
489
        'hydraContext',
490
        // TODO Remove in 4.0
491
        'openapiContext',
492
        'openapi',
493
        'paginationViaCursor',
494
        'stateOptions',
495
    ];
496

497
    /**
498
     * @dataProvider getExtractors
499
     */
500
    public function testValidMetadata(string $extractorClass, ResourceAdapterInterface $adapter): void
501
    {
502
        $reflClass = new \ReflectionClass(ApiResource::class);
×
503
        $parameters = $reflClass->getConstructor()->getParameters();
×
504
        $this->defaults = self::DEFAULTS;
×
505
        $this->camelCaseToSnakeCaseNameConverter = new CamelCaseToSnakeCaseNameConverter();
×
506

507
        try {
508
            $extractor = new $extractorClass($adapter(self::RESOURCE_CLASS, $parameters, self::FIXTURES));
×
509
            $factory = new ExtractorResourceMetadataCollectionFactory($extractor, null, self::DEFAULTS, null, true);
×
510
            $collection = $factory->create(self::RESOURCE_CLASS);
×
511
        } catch (\Exception $exception) {
×
512
            throw new AssertionFailedError('Failed asserting that the schema is valid according to '.ApiResource::class, 0, $exception);
×
513
        }
514

515
        $this->assertEquals(new ResourceMetadataCollection(self::RESOURCE_CLASS, $this->buildApiResources()), $collection);
×
516
    }
517

518
    public static function getExtractors(): array
519
    {
520
        return [
×
521
            [XmlResourceExtractor::class, new XmlResourceAdapter()],
×
522
            [YamlResourceExtractor::class, new YamlResourceAdapter()],
×
523
        ];
×
524
    }
525

526
    /**
527
     * @return ApiResource[]
528
     */
529
    private function buildApiResources(): array
530
    {
531
        $resources = [];
×
532

533
        foreach (self::FIXTURES as $fixtures) {
×
534
            $resource = (new ApiResource())->withClass(self::RESOURCE_CLASS)->withShortName(self::SHORT_NAME);
×
535

536
            if (null === $fixtures) {
×
537
                // Build default operations
538
                $operations = [];
×
539
                foreach ([new Get(), new GetCollection(), new Post(), new Put(), new Patch(), new Delete()] as $operation) {
×
540
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $operation);
×
541
                    $operations[$name] = $operation;
×
542
                }
543

544
                $resource = $resource->withOperations(new Operations($operations));
×
545

546
                // Build default GraphQL operations
547
                $graphQlOperations = [];
×
548
                foreach ([new QueryCollection(), new Query(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')] as $graphQlOperation) {
×
549
                    $description = $graphQlOperation instanceof Mutation ? ucfirst("{$graphQlOperation->getName()}s a {$resource->getShortName()}.") : null;
×
550
                    [$name, $operation] = $this->getOperationWithDefaults($resource, $graphQlOperation);
×
551
                    $graphQlOperations[$name] = $operation->withDescription($description);
×
552
                }
553

554
                $resources[] = $resource->withGraphQlOperations($graphQlOperations);
×
555

556
                continue;
×
557
            }
558

559
            foreach ($fixtures as $parameter => $value) {
×
560
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
561
                    $value = $this->{'with'.ucfirst($parameter)}($value, $fixtures);
×
562
                }
563

564
                if (method_exists($resource, 'with'.ucfirst($parameter))) {
×
565
                    $resource = $resource->{'with'.ucfirst($parameter)}($value, $fixtures);
×
566
                    continue;
×
567
                }
568

569
                throw new \RuntimeException(sprintf('Unknown ApiResource parameter "%s".', $parameter));
×
570
            }
571

572
            $resources[] = $resource;
×
573
        }
574

575
        return $resources;
×
576
    }
577

578
    private function withOpenapi(array|bool $values): bool|OpenApiOperation
579
    {
580
        if (\is_bool($values)) {
×
581
            return $values;
×
582
        }
583

584
        $allowedProperties = array_map(fn (\ReflectionProperty $reflProperty): string => $reflProperty->getName(), (new \ReflectionClass(OpenApiOperation::class))->getProperties());
×
585
        foreach ($values as $key => $value) {
×
586
            $values[$key] = match ($key) {
×
587
                'externalDocs' => new ExternalDocumentation(description: $value['description'] ?? '', url: $value['url'] ?? ''),
×
588
                'requestBody' => new RequestBody(description: $value['description'] ?? '', content: isset($value['content']) ? new \ArrayObject($value['content']) : null, required: $value['required'] ?? false),
×
589
                'callbacks' => new \ArrayObject($value),
×
590
                default => $value,
×
591
            };
×
592

593
            if (\in_array($key, $allowedProperties, true)) {
×
594
                continue;
×
595
            }
596

597
            $values['extensionProperties'][$key] = $value;
×
598
            unset($values[$key]);
×
599
        }
600

601
        return new OpenApiOperation(...$values);
×
602
    }
603

604
    private function withUriVariables(array $values): array
605
    {
606
        $uriVariables = [];
×
607
        foreach ($values as $parameterName => $value) {
×
608
            if (\is_string($value)) {
×
609
                $uriVariables[$value] = $value;
×
610
                continue;
×
611
            }
612

613
            if (isset($value['fromClass']) || isset($value[0])) {
×
614
                $uriVariables[$parameterName]['from_class'] = $value['fromClass'] ?? $value[0];
×
615
            }
616
            if (isset($value['fromProperty']) || isset($value[1])) {
×
617
                $uriVariables[$parameterName]['from_property'] = $value['fromProperty'] ?? $value[1];
×
618
            }
619
            if (isset($value['toClass'])) {
×
620
                $uriVariables[$parameterName]['to_class'] = $value['toClass'];
×
621
            }
622
            if (isset($value['toProperty'])) {
×
623
                $uriVariables[$parameterName]['to_property'] = $value['toProperty'];
×
624
            }
625
            if (isset($value['identifiers'])) {
×
626
                $uriVariables[$parameterName]['identifiers'] = $value['identifiers'];
×
627
            }
628
            if (isset($value['compositeIdentifier'])) {
×
629
                $uriVariables[$parameterName]['composite_identifier'] = $value['compositeIdentifier'];
×
630
            }
631
        }
632

633
        return $uriVariables;
×
634
    }
635

636
    private function withOperations(array $values, ?array $fixtures): Operations
637
    {
638
        $operations = [];
×
639
        foreach ($values as $value) {
×
640
            $class = $value['class'] ?? HttpOperation::class;
×
641
            unset($value['class']);
×
642
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
643

644
            foreach (array_merge(self::BASE, self::EXTENDED_BASE) as $parameter) {
×
645
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
646
                    $value[$parameter] = $fixtures[$parameter];
×
647
                }
648
            }
649

650
            foreach ($value as $parameter => $parameterValue) {
×
651
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
652
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
653
                }
654

655
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
656
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
657
                    continue;
×
658
                }
659

660
                throw new \RuntimeException(sprintf('Unknown Operation parameter "%s".', $parameter));
×
661
            }
662

663
            $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, self::RESOURCE_CLASS);
×
664
            $operations[$operationName] = $operation;
×
665
        }
666

667
        return new Operations($operations);
×
668
    }
669

670
    private function withGraphQlOperations(array $values, ?array $fixtures): array
671
    {
672
        $operations = [];
×
673
        foreach ($values as $value) {
×
674
            $class = $value['class'];
×
675
            unset($value['class']);
×
676
            $operation = (new $class())->withClass(self::RESOURCE_CLASS);
×
677

678
            foreach (self::BASE as $parameter) {
×
679
                if ((!\array_key_exists($parameter, $value) || null === $value[$parameter]) && isset($fixtures[$parameter])) {
×
680
                    $value[$parameter] = $fixtures[$parameter];
×
681
                }
682
            }
683

684
            foreach ($value as $parameter => $parameterValue) {
×
685
                if (method_exists($this, 'with'.ucfirst($parameter))) {
×
686
                    $parameterValue = $this->{'with'.ucfirst($parameter)}($parameterValue);
×
687
                }
688

689
                if (method_exists($operation, 'with'.ucfirst($parameter))) {
×
690
                    $operation = $operation->{'with'.ucfirst($parameter)}($parameterValue);
×
691
                    continue;
×
692
                }
693

694
                throw new \RuntimeException(sprintf('Unknown GraphQlOperation parameter "%s".', $parameter));
×
695
            }
696

697
            $operationName = $operation->getName();
×
698
            $operations[$operationName] = $operation;
×
699
        }
700

701
        return $operations;
×
702
    }
703

704
    private function withStateOptions(array $values): ?OptionsInterface
705
    {
706
        if (!$values) {
×
707
            return null;
×
708
        }
709

710
        if (1 !== \count($values)) {
×
711
            throw new \InvalidArgumentException('Only one options can be configured at a time.');
×
712
        }
713

714
        $configuration = reset($values);
×
715
        switch (key($values)) {
×
716
            case 'elasticsearchOptions':
×
717
                if (class_exists(Options::class)) {
×
UNCOV
718
                    return new Options(isset($elasticsearchOptions['index']) ? (string) $elasticsearchOptions['index'] : null, isset($elasticsearchOptions['type']) ? (string) $elasticsearchOptions['type'] : null);
×
719
                }
720

UNCOV
721
                return null;
×
722
        }
723

UNCOV
724
        throw new \LogicException(sprintf('Unsupported "%s" state options.', key($values)));
×
725
    }
726
}
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