Skip to content

Commit b0939ad

Browse files
antograssiotalanpoulain
authored andcommitted
Add support for name converter in GraphQL (#2765)
1 parent 6873693 commit b0939ad

File tree

19 files changed

+145
-24
lines changed

19 files changed

+145
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 2.5.0 beta 2
44

55
* GraphQL: Add support for multipart request so user can create custom file upload mutations (#3041)
6+
* GraphQL: Add support for name converter (#2765)
67

78
## 2.5.0 beta 1
89

features/bootstrap/DoctrineContext.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ public function thereAreDummyObjectsWithRelatedDummy(int $nb)
420420
$dummy = $this->buildDummy();
421421
$dummy->setName('Dummy #'.$i);
422422
$dummy->setAlias('Alias #'.($nb - $i));
423+
$dummy->nameConverted = "Converted $i";
423424
$dummy->setRelatedDummy($relatedDummy);
424425

425426
$this->manager->persist($relatedDummy);

features/graphql/collection.feature

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,23 @@ Feature: GraphQL collection support
636636
And the response should be in JSON
637637
And the header "Content-Type" should be equal to "application/json"
638638
And the JSON node "data.compositeRelation.value" should be equal to "somefoobardummy"
639+
640+
@createSchema
641+
Scenario: Retrieve a collection using name converter
642+
Given there are 4 dummy objects
643+
When I send the following GraphQL request:
644+
"""
645+
{
646+
dummies {
647+
edges {
648+
node {
649+
name_converted
650+
}
651+
}
652+
}
653+
}
654+
"""
655+
Then the response status code should be 200
656+
And the response should be in JSON
657+
And the header "Content-Type" should be equal to "application/json"
658+
And the JSON node "data.dummies.edges[1].node.name_converted" should be equal to "Converted 2"

features/graphql/filters.feature

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,51 @@ Feature: Collections filtering
8484
Then the JSON node "data.dummies.edges" should have 1 element
8585
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"
8686

87+
@createSchema
88+
Scenario: Retrieve a collection filtered using the search filter and a name converter
89+
Given there are 10 dummy objects
90+
When I send the following GraphQL request:
91+
"""
92+
{
93+
dummies(name_converted: "Converted 2") {
94+
edges {
95+
node {
96+
id
97+
name
98+
name_converted
99+
}
100+
}
101+
}
102+
}
103+
"""
104+
Then the JSON node "data.dummies.edges" should have 1 element
105+
And the JSON node "data.dummies.edges[0].node.id" should be equal to "/dummies/2"
106+
And the JSON node "data.dummies.edges[0].node.name_converted" should be equal to "Converted 2"
107+
108+
@createSchema
109+
Scenario: Retrieve a collection filtered using the search filter and a name converter
110+
Given there are 20 convertedOwner objects with convertedRelated
111+
When I send the following GraphQL request:
112+
"""
113+
{
114+
convertedOwners(name_converted__name_converted: "Converted 2") {
115+
edges {
116+
node {
117+
id
118+
name_converted {
119+
name_converted
120+
}
121+
}
122+
}
123+
}
124+
}
125+
"""
126+
Then the JSON node "data.convertedOwners.edges" should have 2 element
127+
And the JSON node "data.convertedOwners.edges[0].node.id" should be equal to "/converted_owners/2"
128+
And the JSON node "data.convertedOwners.edges[0].node.name_converted.name_converted" should be equal to "Converted 2"
129+
And the JSON node "data.convertedOwners.edges[1].node.id" should be equal to "/converted_owners/20"
130+
And the JSON node "data.convertedOwners.edges[1].node.name_converted.name_converted" should be equal to "Converted 20"
131+
87132
@createSchema
88133
Scenario: Retrieve a collection filtered using the search filter
89134
Given there are 3 dummy objects having each 3 relatedDummies
@@ -141,7 +186,7 @@ Feature: Collections filtering
141186
When I send the following GraphQL request:
142187
"""
143188
{
144-
dummies(relatedDummies_name: "RelatedDummy31") {
189+
dummies(relatedDummies__name: "RelatedDummy31") {
145190
edges {
146191
node {
147192
id
@@ -159,7 +204,7 @@ Feature: Collections filtering
159204
When I send the following GraphQL request:
160205
"""
161206
{
162-
dummies(order: {relatedDummy_name: "DESC"}) {
207+
dummies(order: {relatedDummy__name: "DESC"}) {
163208
edges {
164209
node {
165210
name
@@ -183,7 +228,7 @@ Feature: Collections filtering
183228
When I send the following GraphQL request:
184229
"""
185230
{
186-
dummies(relatedDummy_name_list: ["RelatedDummy #1", "RelatedDummy #2"]) {
231+
dummies(relatedDummy__name_list: ["RelatedDummy #1", "RelatedDummy #2"]) {
187232
edges {
188233
node {
189234
id

features/graphql/mutation.feature

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Feature: GraphQL mutation support
9191
When I send the following GraphQL request:
9292
"""
9393
mutation {
94-
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", clientMutationId: "myId"}) {
94+
createDummy(input: {name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", name_converted: "Converted" clientMutationId: "myId"}) {
9595
dummy {
9696
id
9797
name
@@ -100,6 +100,7 @@ Feature: GraphQL mutation support
100100
name
101101
__typename
102102
}
103+
name_converted
103104
}
104105
clientMutationId
105106
}
@@ -113,6 +114,7 @@ Feature: GraphQL mutation support
113114
And the JSON node "data.createDummy.dummy.foo" should have 0 elements
114115
And the JSON node "data.createDummy.dummy.relatedDummy.name" should be equal to "RelatedDummy #1"
115116
And the JSON node "data.createDummy.dummy.relatedDummy.__typename" should be equal to "RelatedDummyItem"
117+
And the JSON node "data.createDummy.dummy.name_converted" should be equal to "Converted"
116118
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"
117119

118120
Scenario: Create an item with an iterable field

features/graphql/query.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Feature: GraphQL query support
88
dummy(id: "/dummies/1") {
99
id
1010
name
11+
name_converted
1112
}
1213
}
1314
"""
@@ -16,6 +17,7 @@ Feature: GraphQL query support
1617
And the header "Content-Type" should be equal to "application/json"
1718
And the JSON node "data.dummy.id" should be equal to "/dummies/1"
1819
And the JSON node "data.dummy.name" should be equal to "Dummy #1"
20+
And the JSON node "data.dummy.name_converted" should be equal to "Converted 1"
1921

2022
Scenario: Retrieve a Relay Node
2123
When I send the following GraphQL request:

src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ private function registerGraphQlConfiguration(ContainerBuilder $container, array
384384
}
385385

386386
$container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
387+
$container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
387388

388389
$loader->load('graphql.xml');
389390

src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
239239
->arrayNode('graphql_playground')
240240
->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
241241
->end()
242+
->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
242243
->arrayNode('collection')
243244
->addDefaultsIfNotSet()
244245
->children()

src/Bridge/Symfony/Bundle/Resources/config/graphql.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<argument type="service" id="api_platform.collection_data_provider" />
4949
<argument type="service" id="api_platform.subresource_data_provider" />
5050
<argument type="service" id="api_platform.graphql.serializer.context_builder" />
51+
<argument>%api_platform.graphql.nesting_separator%</argument>
5152
</service>
5253

5354
<service id="api_platform.graphql.resolver.stage.security" class="ApiPlatform\Core\GraphQl\Resolver\Stage\SecurityStage" public="false">
@@ -140,6 +141,8 @@
140141
<argument type="service" id="api_platform.graphql.resolver.factory.item_mutation" />
141142
<argument type="service" id="api_platform.filter_locator" />
142143
<argument type="service" id="api_platform.pagination" />
144+
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
145+
<argument>%api_platform.graphql.nesting_separator%</argument>
143146
</service>
144147

145148
<service id="api_platform.graphql.fields_builder_locator" class="Symfony\Component\DependencyInjection\ServiceLocator" public="false">
@@ -216,6 +219,7 @@
216219

217220
<service id="api_platform.graphql.serializer.context_builder" class="ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilder" public="false">
218221
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
222+
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
219223
</service>
220224
<service id="ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilderInterface" alias="api_platform.graphql.serializer.context_builder" />
221225

src/GraphQl/Resolver/Stage/ReadStage.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ final class ReadStage implements ReadStageInterface
4040
private $collectionDataProvider;
4141
private $subresourceDataProvider;
4242
private $serializerContextBuilder;
43+
private $nestingSeparator;
4344

44-
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, IriConverterInterface $iriConverter, ContextAwareCollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, SerializerContextBuilderInterface $serializerContextBuilder)
45+
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, IriConverterInterface $iriConverter, ContextAwareCollectionDataProviderInterface $collectionDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, SerializerContextBuilderInterface $serializerContextBuilder, string $nestingSeparator)
4546
{
4647
$this->resourceMetadataFactory = $resourceMetadataFactory;
4748
$this->iriConverter = $iriConverter;
4849
$this->collectionDataProvider = $collectionDataProvider;
4950
$this->subresourceDataProvider = $subresourceDataProvider;
5051
$this->serializerContextBuilder = $serializerContextBuilder;
52+
$this->nestingSeparator = $nestingSeparator;
5153
}
5254

5355
/**
@@ -144,9 +146,9 @@ private function getNormalizedFilters(array $args): array
144146
$filters[$name] = $this->getNormalizedFilters($value);
145147
}
146148

147-
if (\is_string($name) && strpos($name, '_')) {
149+
if (\is_string($name) && strpos($name, $this->nestingSeparator)) {
148150
// Gives a chance to relations/nested fields.
149-
$filters[str_replace('_', '.', $name)] = $value;
151+
$filters[str_replace($this->nestingSeparator, '.', $name)] = $value;
150152
}
151153
}
152154

0 commit comments

Comments
 (0)