Skip to content

Commit 8570146

Browse files
alanpoulaindunglas
authored andcommitted
[GraphQL] Fix type for subresource (#1702)
* Add a depth variable in SchemaBuilder * Pagination should not be used for input * String type (IRI) if depth > 0 (= subresource) for input * Add a test for a mutation with a subresource
1 parent 27f331c commit 8570146

File tree

2 files changed

+47
-25
lines changed

2 files changed

+47
-25
lines changed

features/graphql/mutation.feature

+27-2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,36 @@ Feature: GraphQL mutation support
5454
And the JSON node "data.createFoo.bar" should be equal to "new"
5555
And the JSON node "data.createFoo.clientMutationId" should be equal to "myId"
5656

57+
Scenario: Create an item with a subresource
58+
Given there are 1 dummy objects with relatedDummy
59+
When I send the following GraphQL request:
60+
"""
61+
mutation {
62+
createDummy(input: {_id: 1, name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", clientMutationId: "myId"}) {
63+
id
64+
name
65+
foo
66+
relatedDummy {
67+
name
68+
}
69+
clientMutationId
70+
}
71+
}
72+
"""
73+
Then the response status code should be 200
74+
And the response should be in JSON
75+
And the header "Content-Type" should be equal to "application/json"
76+
And the JSON node "data.createDummy.id" should be equal to "/dummies/2"
77+
And the JSON node "data.createDummy.name" should be equal to "A dummy"
78+
And the JSON node "data.createDummy.foo" should have 0 elements
79+
And the JSON node "data.createDummy.relatedDummy.name" should be equal to "RelatedDummy #1"
80+
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"
81+
5782
Scenario: Create an item with an iterable field
5883
When I send the following GraphQL request:
5984
"""
6085
mutation {
61-
createDummy(input: {_id: 1, name: "A dummy", foo: [], jsonData: {bar:{baz:3,qux:[7.6,false,null]}}, clientMutationId: "myId"}) {
86+
createDummy(input: {_id: 2, name: "A dummy", foo: [], jsonData: {bar:{baz:3,qux:[7.6,false,null]}}, clientMutationId: "myId"}) {
6287
id
6388
name
6489
foo
@@ -70,7 +95,7 @@ Feature: GraphQL mutation support
7095
Then the response status code should be 200
7196
And the response should be in JSON
7297
And the header "Content-Type" should be equal to "application/json"
73-
And the JSON node "data.createDummy.id" should be equal to "/dummies/1"
98+
And the JSON node "data.createDummy.id" should be equal to "/dummies/3"
7499
And the JSON node "data.createDummy.name" should be equal to "A dummy"
75100
And the JSON node "data.createDummy.foo" should have 0 elements
76101
And the JSON node "data.createDummy.jsonData.bar.baz" should be equal to the number 3

src/GraphQl/Type/SchemaBuilder.php

+20-23
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,10 @@ private function getMutationFields(string $resourceClass, ResourceMetadata $reso
189189
*
190190
* @return array|null
191191
*/
192-
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, Type $type, string $rootResource, bool $input = false, string $mutationName = null)
192+
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, Type $type, string $rootResource, bool $input = false, string $mutationName = null, int $depth = 0)
193193
{
194194
try {
195-
if (null === $graphqlType = $this->convertType($type, $input, $mutationName)) {
195+
if (null === $graphqlType = $this->convertType($type, $input, $mutationName, $depth)) {
196196
return null;
197197
}
198198

@@ -227,7 +227,7 @@ private function getResourceFieldConfiguration(string $resourceClass, ResourceMe
227227
foreach ($this->filterLocator->get($filterId)->getDescription($resourceClass) as $key => $value) {
228228
$nullable = isset($value['required']) ? !$value['required'] : true;
229229
$filterType = \in_array($value['type'], Type::$builtinTypes, true) ? new Type($value['type'], $nullable) : new Type('object', $nullable, $value['type']);
230-
$graphqlFilterType = $this->convertType($filterType);
230+
$graphqlFilterType = $this->convertType($filterType, false, null, $depth);
231231

232232
if ('[]' === $newKey = substr($key, -2)) {
233233
$key = $newKey;
@@ -317,7 +317,7 @@ private function convertFilterArgsToTypes(array $args): array
317317
*
318318
* @throws InvalidTypeException
319319
*/
320-
private function convertType(Type $type, bool $input = false, string $mutationName = null)
320+
private function convertType(Type $type, bool $input = false, string $mutationName = null, int $depth = 0)
321321
{
322322
$resourceClass = null;
323323
switch ($builtinType = $type->getBuiltinType()) {
@@ -341,7 +341,7 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
341341
$graphqlType = $this->graphqlTypes['#iterable'];
342342
break;
343343
case Type::BUILTIN_TYPE_OBJECT:
344-
if (is_a($type->getClassName(), \DateTimeInterface::class, true)) {
344+
if (($input && $depth > 0) || is_a($type->getClassName(), \DateTimeInterface::class, true)) {
345345
$graphqlType = GraphQLType::string();
346346
break;
347347
}
@@ -357,14 +357,14 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
357357
return null;
358358
}
359359

360-
$graphqlType = $this->getResourceObjectType($resourceClass, $resourceMetadata, $input, $mutationName);
360+
$graphqlType = $this->getResourceObjectType($resourceClass, $resourceMetadata, $input, $mutationName, $depth);
361361
break;
362362
default:
363363
throw new InvalidTypeException(sprintf('The type "%s" is not supported.', $builtinType));
364364
}
365365

366366
if ($this->isCollection($type)) {
367-
return $this->paginationEnabled ? $this->getResourcePaginatedCollectionType($resourceClass, $graphqlType, $input) : GraphQLType::listOf($graphqlType);
367+
return $this->paginationEnabled && !$input ? $this->getResourcePaginatedCollectionType($resourceClass, $graphqlType) : GraphQLType::listOf($graphqlType);
368368
}
369369

370370
return $type->isNullable() || (null !== $mutationName && 'update' === $mutationName) ? $graphqlType : GraphQLType::nonNull($graphqlType);
@@ -375,7 +375,7 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
375375
*
376376
* @return ObjectType|InputObjectType
377377
*/
378-
private function getResourceObjectType(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null): GraphQLType
378+
private function getResourceObjectType(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null, int $depth = 0): GraphQLType
379379
{
380380
if (isset($this->graphqlTypes[$resourceClass][$mutationName][$input])) {
381381
return $this->graphqlTypes[$resourceClass][$mutationName][$input];
@@ -395,8 +395,8 @@ private function getResourceObjectType(string $resourceClass, ResourceMetadata $
395395
'name' => $shortName,
396396
'description' => $resourceMetadata->getDescription(),
397397
'resolveField' => $this->defaultFieldResolver,
398-
'fields' => function () use ($resourceClass, $resourceMetadata, $input, $mutationName) {
399-
return $this->getResourceObjectTypeFields($resourceClass, $resourceMetadata, $input, $mutationName);
398+
'fields' => function () use ($resourceClass, $resourceMetadata, $input, $mutationName, $depth) {
399+
return $this->getResourceObjectTypeFields($resourceClass, $resourceMetadata, $input, $mutationName, $depth);
400400
},
401401
'interfaces' => [$this->getNodeInterface()],
402402
];
@@ -407,7 +407,7 @@ private function getResourceObjectType(string $resourceClass, ResourceMetadata $
407407
/**
408408
* Gets the fields of the type of the given resource.
409409
*/
410-
private function getResourceObjectTypeFields(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null): array
410+
private function getResourceObjectTypeFields(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null, int $depth = 0): array
411411
{
412412
$fields = [];
413413
$idField = ['type' => GraphQLType::nonNull(GraphQLType::id())];
@@ -434,7 +434,7 @@ private function getResourceObjectTypeFields(string $resourceClass, ResourceMeta
434434
continue;
435435
}
436436

437-
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyType, $resourceClass, $input, $mutationName)) {
437+
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyType, $resourceClass, $input, $mutationName, ++$depth)) {
438438
$fields['id' === $property ? '_id' : $property] = $fieldConfiguration;
439439
}
440440
}
@@ -449,19 +449,16 @@ private function getResourceObjectTypeFields(string $resourceClass, ResourceMeta
449449
/**
450450
* Gets the type of a paginated collection of the given resource type.
451451
*
452-
* @param ObjectType|InputObjectType $resourceType
452+
* @param ObjectType $resourceType
453453
*
454-
* @return ObjectType|InputObjectType
454+
* @return ObjectType
455455
*/
456-
private function getResourcePaginatedCollectionType(string $resourceClass, GraphQLType $resourceType, bool $input = false): GraphQLType
456+
private function getResourcePaginatedCollectionType(string $resourceClass, GraphQLType $resourceType): GraphQLType
457457
{
458458
$shortName = $resourceType->name;
459-
if ($input) {
460-
$shortName .= 'Input';
461-
}
462459

463-
if (isset($this->graphqlTypes[$resourceClass]['connection'][$input])) {
464-
return $this->graphqlTypes[$resourceClass]['connection'][$input];
460+
if (isset($this->graphqlTypes[$resourceClass]['connection'])) {
461+
return $this->graphqlTypes[$resourceClass]['connection'];
465462
}
466463

467464
$edgeObjectTypeConfiguration = [
@@ -472,7 +469,7 @@ private function getResourcePaginatedCollectionType(string $resourceClass, Graph
472469
'cursor' => GraphQLType::nonNull(GraphQLType::string()),
473470
],
474471
];
475-
$edgeObjectType = $input ? new InputObjectType($edgeObjectTypeConfiguration) : new ObjectType($edgeObjectTypeConfiguration);
472+
$edgeObjectType = new ObjectType($edgeObjectTypeConfiguration);
476473
$pageInfoObjectTypeConfiguration = [
477474
'name' => "{$shortName}PageInfo",
478475
'description' => 'Information about the current page.',
@@ -481,7 +478,7 @@ private function getResourcePaginatedCollectionType(string $resourceClass, Graph
481478
'hasNextPage' => GraphQLType::nonNull(GraphQLType::boolean()),
482479
],
483480
];
484-
$pageInfoObjectType = $input ? new InputObjectType($pageInfoObjectTypeConfiguration) : new ObjectType($pageInfoObjectTypeConfiguration);
481+
$pageInfoObjectType = new ObjectType($pageInfoObjectTypeConfiguration);
485482

486483
$configuration = [
487484
'name' => "{$shortName}Connection",
@@ -492,7 +489,7 @@ private function getResourcePaginatedCollectionType(string $resourceClass, Graph
492489
],
493490
];
494491

495-
return $this->graphqlTypes[$resourceClass]['connection'][$input] = $input ? new InputObjectType($configuration) : new ObjectType($configuration);
492+
return $this->graphqlTypes[$resourceClass]['connection'] = new ObjectType($configuration);
496493
}
497494

498495
private function isCollection(Type $type): bool

0 commit comments

Comments
 (0)