Skip to content

Commit

Permalink
Merge 305a65a into efa341c
Browse files Browse the repository at this point in the history
  • Loading branch information
antograssiot committed Jul 3, 2019
2 parents efa341c + 305a65a commit 6a2c30d
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 12 deletions.
1 change: 1 addition & 0 deletions features/bootstrap/DoctrineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ public function thereAreDummyObjectsWithRelatedDummy(int $nb)
$dummy = $this->buildDummy();
$dummy->setName('Dummy #'.$i);
$dummy->setAlias('Alias #'.($nb - $i));
$dummy->nameConverted = "Converted $i";
$dummy->setRelatedDummy($relatedDummy);

$this->manager->persist($relatedDummy);
Expand Down
20 changes: 20 additions & 0 deletions features/graphql/collection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -587,3 +587,23 @@ Feature: GraphQL collection support
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.compositeRelation.value" should be equal to "somefoobardummy"

@createSchema
Scenario: Retrieve a collection using name converter
Given there are 4 dummy objects
When I send the following GraphQL request:
"""
{
dummies {
edges {
node {
name_converted
}
}
}
}
"""
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.dummies.edges[1].node.name_converted" should be equal to "Converted 2"
21 changes: 21 additions & 0 deletions features/graphql/filters.feature
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ Feature: Collections filtering
Then the JSON node "data.dummies.edges" should have 1 element
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"

@createSchema
Scenario: Retrieve a collection filtered using the search filter and a name converter
Given there are 10 dummy objects
When I send the following GraphQL request:
"""
{
dummies(name_converted: "Converted 2") {
edges {
node {
id
name
name_converted
}
}
}
}
"""
Then the JSON node "data.dummies.edges" should have 1 element
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"
And the JSON node "data.dummies.edges[0].node.name_converted" should be equal to "Converted 2"

@createSchema
Scenario: Retrieve a collection filtered using the search filter
Given there are 3 dummy objects having each 3 relatedDummies
Expand Down
4 changes: 3 additions & 1 deletion features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Feature: GraphQL mutation support
When I send the following GraphQL request:
"""
mutation {
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", clientMutationId: "myId"}) {
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", name_converted: "Converted" clientMutationId: "myId"}) {
dummy {
id
name
Expand All @@ -99,6 +99,7 @@ Feature: GraphQL mutation support
name
__typename
}
name_converted
}
clientMutationId
}
Expand All @@ -112,6 +113,7 @@ Feature: GraphQL mutation support
And the JSON node "data.createDummy.dummy.foo" should have 0 elements
And the JSON node "data.createDummy.dummy.relatedDummy.name" should be equal to "RelatedDummy #1"
And the JSON node "data.createDummy.dummy.relatedDummy.__typename" should be equal to "RelatedDummy"
And the JSON node "data.createDummy.dummy.name_converted" should be equal to "Converted"
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"

Scenario: Create an item with an iterable field
Expand Down
2 changes: 2 additions & 0 deletions features/graphql/query.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Feature: GraphQL query support
dummy(id: "/dummies/1") {
id
name
name_converted
}
}
"""
Expand All @@ -16,6 +17,7 @@ Feature: GraphQL query support
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.dummy.id" should be equal to "/dummies/1"
And the JSON node "data.dummy.name" should be equal to "Dummy #1"
And the JSON node "data.dummy.name_converted" should be equal to "Converted 1"

Scenario: Retrieve a Relay Node
When I send the following GraphQL request:
Expand Down
4 changes: 4 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/graphql.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<argument type="service" id="serializer" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument type="service" id="api_platform.security.resource_access_checker" on-invalid="ignore" />
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
</service>

<service id="api_platform.graphql.resolver.factory.collection" class="ApiPlatform\Core\GraphQl\Resolver\Factory\CollectionResolverFactory" public="false">
Expand All @@ -26,6 +27,7 @@
<argument type="service" id="api_platform.security.resource_access_checker" on-invalid="null" />
<argument type="service" id="request_stack" />
<argument>%api_platform.collection.pagination.enabled%</argument>
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
</service>

<service id="api_platform.graphql.resolver.factory.item_mutation" class="ApiPlatform\Core\GraphQl\Resolver\Factory\ItemMutationResolverFactory" public="false">
Expand All @@ -36,6 +38,7 @@
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument type="service" id="api_platform.security.resource_access_checker" on-invalid="null" />
<argument type="service" id="api_platform.validator" on-invalid="null" />
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
</service>

<service id="api_platform.graphql.resolver.resource_field" class="ApiPlatform\Core\GraphQl\Resolver\ResourceFieldResolver" public="false">
Expand Down Expand Up @@ -89,6 +92,7 @@
<argument type="service" id="api_platform.graphql.resolver.factory.collection" />
<argument type="service" id="api_platform.graphql.resolver.factory.item_mutation" />
<argument type="service" id="api_platform.filter_locator" />
<argument type="service" id="api_platform.name_converter" on-invalid="null" />
<argument>%api_platform.collection.pagination.enabled%</argument>
</service>

Expand Down
10 changes: 9 additions & 1 deletion src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use GraphQL\Type\Definition\ResolveInfo;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
Expand All @@ -50,8 +51,9 @@ final class CollectionResolverFactory implements ResolverFactoryInterface
private $requestStack;
private $paginationEnabled;
private $resourceMetadataFactory;
private $nameConverter;

public function __construct(CollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, ContainerInterface $queryResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, RequestStack $requestStack = null, bool $paginationEnabled = false)
public function __construct(CollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, ContainerInterface $queryResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, RequestStack $requestStack = null, bool $paginationEnabled = false, NameConverterInterface $nameConverter = null)
{
$this->collectionDataProvider = $collectionDataProvider;
$this->subresourceDataProvider = $subresourceDataProvider;
Expand All @@ -61,6 +63,7 @@ public function __construct(CollectionDataProviderInterface $collectionDataProvi
$this->requestStack = $requestStack;
$this->paginationEnabled = $paginationEnabled;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->nameConverter = $nameConverter;
}

public function __invoke(string $resourceClass = null, string $rootClass = null, string $operationName = null): callable
Expand Down Expand Up @@ -204,4 +207,9 @@ private function getNormalizedFilters(array $args): array

return $filters;
}

protected function denormalizePropertyName($property)
{
return null !== $this->nameConverter ? $this->nameConverter->denormalize($property) : $property;
}
}
10 changes: 9 additions & 1 deletion src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ResolveInfo;
use Psr\Container\ContainerInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

Expand All @@ -53,8 +54,9 @@ final class ItemMutationResolverFactory implements ResolverFactoryInterface
private $resourceMetadataFactory;
private $resourceAccessChecker;
private $validator;
private $nameConverter;

public function __construct(IriConverterInterface $iriConverter, DataPersisterInterface $dataPersister, ContainerInterface $mutationResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, ValidatorInterface $validator = null)
public function __construct(IriConverterInterface $iriConverter, DataPersisterInterface $dataPersister, ContainerInterface $mutationResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, ValidatorInterface $validator = null, NameConverterInterface $nameConverter = null)
{
if (!$normalizer instanceof DenormalizerInterface) {
throw new InvalidArgumentException(sprintf('The normalizer must implements the "%s" interface', DenormalizerInterface::class));
Expand All @@ -67,6 +69,7 @@ public function __construct(IriConverterInterface $iriConverter, DataPersisterIn
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourceAccessChecker = $resourceAccessChecker;
$this->validator = $validator;
$this->nameConverter = $nameConverter;
}

public function __invoke(string $resourceClass = null, string $rootClass = null, string $operationName = null): callable
Expand Down Expand Up @@ -182,4 +185,9 @@ private function validate($item, ResolveInfo $info, ResourceMetadata $resourceMe
throw Error::createLocatedError($e->getMessage(), $info->fieldNodes, $info->path);
}
}

protected function denormalizePropertyName($property)
{
return null !== $this->nameConverter ? $this->nameConverter->denormalize($property) : $property;
}
}
10 changes: 9 additions & 1 deletion src/GraphQl/Resolver/Factory/ItemResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ResolveInfo;
use Psr\Container\ContainerInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
Expand All @@ -46,14 +47,16 @@ final class ItemResolverFactory implements ResolverFactoryInterface
private $resourceAccessChecker;
private $normalizer;
private $resourceMetadataFactory;
private $nameConverter;

public function __construct(IriConverterInterface $iriConverter, ContainerInterface $queryResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null)
public function __construct(IriConverterInterface $iriConverter, ContainerInterface $queryResolverLocator, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, NameConverterInterface $nameConverter = null)
{
$this->iriConverter = $iriConverter;
$this->queryResolverLocator = $queryResolverLocator;
$this->normalizer = $normalizer;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourceAccessChecker = $resourceAccessChecker;
$this->nameConverter = $nameConverter;
}

public function __invoke(?string $resourceClass = null, ?string $rootClass = null, ?string $operationName = null): callable
Expand Down Expand Up @@ -131,4 +134,9 @@ private function getResourceClass($item, ?string $resourceClass, ResolveInfo $in

return $resourceClass;
}

protected function denormalizePropertyName($property)
{
return null !== $this->nameConverter ? $this->nameConverter->denormalize($property) : $property;
}
}
12 changes: 7 additions & 5 deletions src/GraphQl/Resolver/FieldsToAttributesTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ private function fieldsToAttributes(ResolveInfo $info): array

private function replaceIdKeys(array $fields): array
{
$denormalizedFields = [];
foreach ($fields as $key => $value) {
if ('_id' === $key) {
$fields['id'] = $fields['_id'];
unset($fields['_id']);
} elseif (\is_array($fields[$key])) {
$fields[$key] = $this->replaceIdKeys($fields[$key]);
$denormalizedFields['id'] = $fields['_id'];
continue;
}
$denormalizedFields[$this->denormalizePropertyName($key)] = \is_array($fields[$key]) ? $this->replaceIdKeys($fields[$key]) : $value;
}

return $fields;
return $denormalizedFields;
}

abstract protected function denormalizePropertyName($property);
}
12 changes: 10 additions & 2 deletions src/GraphQl/Type/FieldsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Psr\Container\ContainerInterface;
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

/**
* Builds the GraphQL fields.
Expand All @@ -47,8 +48,9 @@ final class FieldsBuilder implements FieldsBuilderInterface
private $itemMutationResolverFactory;
private $filterLocator;
private $paginationEnabled;
private $nameConverter;

public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ContainerInterface $filterLocator, bool $paginationEnabled)
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ContainerInterface $filterLocator, ?NameConverterInterface $nameConverter, bool $paginationEnabled)
{
$this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
$this->propertyMetadataFactory = $propertyMetadataFactory;
Expand All @@ -61,6 +63,7 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->itemMutationResolverFactory = $itemMutationResolverFactory;
$this->filterLocator = $filterLocator;
$this->paginationEnabled = $paginationEnabled;
$this->nameConverter = $nameConverter;
}

/**
Expand Down Expand Up @@ -180,7 +183,7 @@ public function getResourceObjectTypeFields(?string $resourceClass, ResourceMeta
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
}
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $property, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', ''), $propertyType, $rootResource, $input, $queryName, $mutationName, $depth)) {
$fields['id' === $property ? '_id' : $property] = $fieldConfiguration;
$fields['id' === $property ? '_id' : $this->normalizePropertyName($property)] = $fieldConfiguration;
}
$resourceClass = $rootResource;
}
Expand Down Expand Up @@ -388,4 +391,9 @@ private function convertType(Type $type, bool $input, ?string $queryName, ?strin

return $type->isNullable() || (null !== $mutationName && 'update' === $mutationName) ? $graphqlType : GraphQLType::nonNull($graphqlType);
}

private function normalizePropertyName($property)
{
return null !== $this->nameConverter ? $this->nameConverter->normalize($property) : $property;
}
}
11 changes: 10 additions & 1 deletion tests/GraphQl/Type/FieldsBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use ApiPlatform\Core\Metadata\Property\SubresourceMetadata;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Serializer\NameConverter\CustomConverter;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\NonNull;
Expand Down Expand Up @@ -90,7 +91,7 @@ protected function setUp(): void
$this->collectionResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
$this->itemMutationResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
$this->filterLocatorProphecy = $this->prophesize(ContainerInterface::class);
$this->fieldsBuilder = new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->collectionResolverFactoryProphecy->reveal(), $this->itemMutationResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), true);
$this->fieldsBuilder = new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->collectionResolverFactoryProphecy->reveal(), $this->itemMutationResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), new CustomConverter(), true);
}

public function testGetNodeQueryFields(): void
Expand Down Expand Up @@ -399,6 +400,7 @@ public function resourceObjectTypeFieldsProvider(): array
'property' => new PropertyMetadata(),
'propertyBool' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_BOOL), null, true, false),
'propertyNotReadable' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_BOOL), null, false, false),
'nameConverted' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), null, true, false),
],
false, 'query', null, null,
[
Expand All @@ -412,6 +414,13 @@ public function resourceObjectTypeFieldsProvider(): array
'resolve' => null,
'deprecationReason' => '',
],
'name_converted' => [
'type' => GraphQLType::nonNull(GraphQLType::string()),
'description' => null,
'args' => [],
'resolve' => null,
'deprecationReason' => '',
],
],
],
'query input' => ['resourceClass', new ResourceMetadata(),
Expand Down

0 comments on commit 6a2c30d

Please sign in to comment.