Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ private function addGraphQlOperations(?array $data, ApiResource $resource): ApiR
}

[$key, $operation] = $this->getOperationWithDefaults($resource, $operation);
if (\array_key_exists($key, $operations)) {
throw new RuntimeException(\sprintf('GraphQL operation name "%s" is declared twice on resource "%s". GraphQL operation names must be unique, set distinct names.', $key, $resource->getClass() ?? ''));
}
$operations[$key] = $operation;
}

Expand Down
16 changes: 16 additions & 0 deletions src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ private function buildResourceOperations(array $metadataCollection, string $reso
}
[$key, $operation] = $this->getOperationWithDefaults($resources[$index], $metadata);
$graphQlOperations = $resources[$index]->getGraphQlOperations();
$this->assertGraphQlOperationNameIsUnique($graphQlOperations ?? [], $key, $resourceClass);
$graphQlOperations[$key] = $operation;
$resources[$index] = $resources[$index]->withGraphQlOperations($graphQlOperations);
continue;
Expand Down Expand Up @@ -205,6 +206,7 @@ private function buildResourceOperations(array $metadataCollection, string $reso
$operation = $this->mergeOperationParameters($operation, $parameters);
}

$this->assertGraphQlOperationNameIsUnique($graphQlOperationsWithDefaults, $key, $resourceClass);
$graphQlOperationsWithDefaults[$key] = $operation;
}

Expand Down Expand Up @@ -270,6 +272,20 @@ private function deduplicateShortNames(array $resources): array
return $resources;
}

/**
* GraphQL operations are indexed by name, a duplicate would silently replace the first one.
*
* @param array<string, mixed> $operations
*/
private function assertGraphQlOperationNameIsUnique(array $operations, string $key, string $resourceClass): void
{
if (!\array_key_exists($key, $operations)) {
return;
}

throw new RuntimeException(\sprintf('GraphQL operation name "%s" is declared twice on resource "%s". GraphQL operation names must be unique, set distinct names.', $key, $resourceClass));
}

/**
* Two operations sharing an explicit name silently overwrite each other because
* the operation name is the unique key (and the Symfony route name).
Expand Down
11 changes: 11 additions & 0 deletions src/Metadata/Tests/Extractor/XmlExtractorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,17 @@ public function testDuplicateOperationNameFromXmlThrows(): void
$factory->create(Comment::class);
}

public function testDuplicateGraphQlOperationNameFromXmlThrows(): void
{
$extractor = new XmlResourceExtractor([__DIR__.'/xml/duplicate_graphql_operation_name.xml']);
$factory = new ExtractorResourceMetadataCollectionFactory($extractor, graphQlEnabled: true);

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage(\sprintf('GraphQL operation name "item_query" is declared twice on resource "%s".', Comment::class));

$factory->create(Comment::class);
}

public function testOpenApiParametersAreAList(): void
{
$extractor = new XmlResourceExtractor([__DIR__.'/xml/openapi_parameters.xml']);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
https://api-platform.com/schema/metadata/resources-3.0.xsd">
<resource class="ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Comment">
<graphQlOperations>
<graphQlOperation class="ApiPlatform\Metadata\GraphQl\Query"
name="item_query"
security="is_granted('ROLE_ADMIN')" />
<graphQlOperation class="ApiPlatform\Metadata\GraphQl\Query"
name="item_query" />
</graphQlOperations>
</resource>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

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

declare(strict_types=1);

namespace ApiPlatform\Metadata\Tests\Fixtures\ApiResource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\GraphQl\Query;

#[Query(name: 'item_query', security: 'is_granted("ROLE_ADMIN")')]
#[Query(name: 'item_query')]
class SameNameGraphQlOperationAttributes
{
#[ApiProperty(identifier: true)]
public ?string $id = null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

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

declare(strict_types=1);

namespace ApiPlatform\Metadata\Tests\Fixtures\ApiResource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GraphQl\Query;

#[ApiResource(
graphQlOperations: [
new Query(name: 'item_query', security: 'is_granted("ROLE_ADMIN")'),
new Query(name: 'item_query'),
],
)]
class SameNameGraphQlOperations
{
#[ApiProperty(identifier: true)]
public ?string $id = null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\ResourceClassPropagation;
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\ResourceClassPropagationTarget;
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\SameNameDifferentMethodOperations;
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\SameNameGraphQlOperationAttributes;
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\SameNameGraphQlOperations;
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\WithParameter;
use ApiPlatform\Metadata\Tests\Fixtures\State\AttributeResourceProcessor;
use ApiPlatform\Metadata\Tests\Fixtures\State\AttributeResourceProvider;
Expand Down Expand Up @@ -440,4 +442,24 @@ public function testDuplicateOperationNameThrows(): void

$factory->create(SameNameDifferentMethodOperations::class);
}

public function testDuplicateGraphQlOperationNameThrows(): void
{
$factory = new AttributesResourceMetadataCollectionFactory(graphQlEnabled: true);

$this->expectException(\ApiPlatform\Metadata\Exception\RuntimeException::class);
$this->expectExceptionMessage(\sprintf('GraphQL operation name "item_query" is declared twice on resource "%s".', SameNameGraphQlOperations::class));

$factory->create(SameNameGraphQlOperations::class);
}

public function testDuplicateGraphQlOperationNameOnAttributesThrows(): void
{
$factory = new AttributesResourceMetadataCollectionFactory(graphQlEnabled: true);

$this->expectException(\ApiPlatform\Metadata\Exception\RuntimeException::class);
$this->expectExceptionMessage(\sprintf('GraphQL operation name "item_query" is declared twice on resource "%s".', SameNameGraphQlOperationAttributes::class));

$factory->create(SameNameGraphQlOperationAttributes::class);
}
}
Loading