Skip to content

[GraphQL] Fix type for subresource #1702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 12, 2018
Merged
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
29 changes: 27 additions & 2 deletions features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,36 @@ Feature: GraphQL mutation support
And the JSON node "data.createFoo.bar" should be equal to "new"
And the JSON node "data.createFoo.clientMutationId" should be equal to "myId"

Scenario: Create an item with a subresource
Given there are 1 dummy objects with relatedDummy
When I send the following GraphQL request:
"""
mutation {
createDummy(input: {_id: 1, name: "A dummy", foo: [], relatedDummy: "/related_dummies/1", clientMutationId: "myId"}) {
id
name
foo
relatedDummy {
name
}
clientMutationId
}
}
"""
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.createDummy.id" should be equal to "/dummies/2"
And the JSON node "data.createDummy.name" should be equal to "A dummy"
And the JSON node "data.createDummy.foo" should have 0 elements
And the JSON node "data.createDummy.relatedDummy.name" should be equal to "RelatedDummy #1"
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"

Scenario: Create an item with an iterable field
When I send the following GraphQL request:
"""
mutation {
createDummy(input: {_id: 1, name: "A dummy", foo: [], jsonData: {bar:{baz:3,qux:[7.6,false,null]}}, clientMutationId: "myId"}) {
createDummy(input: {_id: 2, name: "A dummy", foo: [], jsonData: {bar:{baz:3,qux:[7.6,false,null]}}, clientMutationId: "myId"}) {
id
name
foo
Expand All @@ -70,7 +95,7 @@ Feature: GraphQL mutation support
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.createDummy.id" should be equal to "/dummies/1"
And the JSON node "data.createDummy.id" should be equal to "/dummies/3"
And the JSON node "data.createDummy.name" should be equal to "A dummy"
And the JSON node "data.createDummy.foo" should have 0 elements
And the JSON node "data.createDummy.jsonData.bar.baz" should be equal to the number 3
Expand Down
43 changes: 20 additions & 23 deletions src/GraphQl/Type/SchemaBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ private function getMutationFields(string $resourceClass, ResourceMetadata $reso
*
* @return array|null
*/
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, Type $type, string $rootResource, bool $input = false, string $mutationName = null)
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, Type $type, string $rootResource, bool $input = false, string $mutationName = null, int $depth = 0)
{
try {
if (null === $graphqlType = $this->convertType($type, $input, $mutationName)) {
if (null === $graphqlType = $this->convertType($type, $input, $mutationName, $depth)) {
return null;
}

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

if ('[]' === $newKey = substr($key, -2)) {
$key = $newKey;
Expand Down Expand Up @@ -317,7 +317,7 @@ private function convertFilterArgsToTypes(array $args): array
*
* @throws InvalidTypeException
*/
private function convertType(Type $type, bool $input = false, string $mutationName = null)
private function convertType(Type $type, bool $input = false, string $mutationName = null, int $depth = 0)
{
$resourceClass = null;
switch ($builtinType = $type->getBuiltinType()) {
Expand All @@ -341,7 +341,7 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
$graphqlType = $this->graphqlTypes['#iterable'];
break;
case Type::BUILTIN_TYPE_OBJECT:
if (is_a($type->getClassName(), \DateTimeInterface::class, true)) {
if (($input && $depth > 0) || is_a($type->getClassName(), \DateTimeInterface::class, true)) {
$graphqlType = GraphQLType::string();
break;
}
Expand All @@ -357,14 +357,14 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
return null;
}

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

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

return $type->isNullable() || (null !== $mutationName && 'update' === $mutationName) ? $graphqlType : GraphQLType::nonNull($graphqlType);
Expand All @@ -375,7 +375,7 @@ private function convertType(Type $type, bool $input = false, string $mutationNa
*
* @return ObjectType|InputObjectType
*/
private function getResourceObjectType(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null): GraphQLType
private function getResourceObjectType(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null, int $depth = 0): GraphQLType
{
if (isset($this->graphqlTypes[$resourceClass][$mutationName][$input])) {
return $this->graphqlTypes[$resourceClass][$mutationName][$input];
Expand All @@ -395,8 +395,8 @@ private function getResourceObjectType(string $resourceClass, ResourceMetadata $
'name' => $shortName,
'description' => $resourceMetadata->getDescription(),
'resolveField' => $this->defaultFieldResolver,
'fields' => function () use ($resourceClass, $resourceMetadata, $input, $mutationName) {
return $this->getResourceObjectTypeFields($resourceClass, $resourceMetadata, $input, $mutationName);
'fields' => function () use ($resourceClass, $resourceMetadata, $input, $mutationName, $depth) {
return $this->getResourceObjectTypeFields($resourceClass, $resourceMetadata, $input, $mutationName, $depth);
},
'interfaces' => [$this->getNodeInterface()],
];
Expand All @@ -407,7 +407,7 @@ private function getResourceObjectType(string $resourceClass, ResourceMetadata $
/**
* Gets the fields of the type of the given resource.
*/
private function getResourceObjectTypeFields(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null): array
private function getResourceObjectTypeFields(string $resourceClass, ResourceMetadata $resourceMetadata, bool $input = false, string $mutationName = null, int $depth = 0): array
{
$fields = [];
$idField = ['type' => GraphQLType::nonNull(GraphQLType::id())];
Expand All @@ -434,7 +434,7 @@ private function getResourceObjectTypeFields(string $resourceClass, ResourceMeta
continue;
}

if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyType, $resourceClass, $input, $mutationName)) {
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyType, $resourceClass, $input, $mutationName, ++$depth)) {
$fields['id' === $property ? '_id' : $property] = $fieldConfiguration;
}
}
Expand All @@ -449,19 +449,16 @@ private function getResourceObjectTypeFields(string $resourceClass, ResourceMeta
/**
* Gets the type of a paginated collection of the given resource type.
*
* @param ObjectType|InputObjectType $resourceType
* @param ObjectType $resourceType
*
* @return ObjectType|InputObjectType
* @return ObjectType
*/
private function getResourcePaginatedCollectionType(string $resourceClass, GraphQLType $resourceType, bool $input = false): GraphQLType
private function getResourcePaginatedCollectionType(string $resourceClass, GraphQLType $resourceType): GraphQLType
{
$shortName = $resourceType->name;
if ($input) {
$shortName .= 'Input';
}

if (isset($this->graphqlTypes[$resourceClass]['connection'][$input])) {
return $this->graphqlTypes[$resourceClass]['connection'][$input];
if (isset($this->graphqlTypes[$resourceClass]['connection'])) {
return $this->graphqlTypes[$resourceClass]['connection'];
}

$edgeObjectTypeConfiguration = [
Expand All @@ -472,7 +469,7 @@ private function getResourcePaginatedCollectionType(string $resourceClass, Graph
'cursor' => GraphQLType::nonNull(GraphQLType::string()),
],
];
$edgeObjectType = $input ? new InputObjectType($edgeObjectTypeConfiguration) : new ObjectType($edgeObjectTypeConfiguration);
$edgeObjectType = new ObjectType($edgeObjectTypeConfiguration);
$pageInfoObjectTypeConfiguration = [
'name' => "{$shortName}PageInfo",
'description' => 'Information about the current page.',
Expand All @@ -481,7 +478,7 @@ private function getResourcePaginatedCollectionType(string $resourceClass, Graph
'hasNextPage' => GraphQLType::nonNull(GraphQLType::boolean()),
],
];
$pageInfoObjectType = $input ? new InputObjectType($pageInfoObjectTypeConfiguration) : new ObjectType($pageInfoObjectTypeConfiguration);
$pageInfoObjectType = new ObjectType($pageInfoObjectTypeConfiguration);

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

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

private function isCollection(Type $type): bool
Expand Down