Skip to content

Commit 67aa2a0

Browse files
authored
GraphQL: add "id" to attributes if "_id" is requested (fixes #2194) (#2195)
* GraphQL: add "id" to attributes if "_id" is requested Fixes #2194. * Fix all resolvers. Review. * Wording * Typo
1 parent b64da31 commit 67aa2a0

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

features/graphql/query.feature

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ Feature: GraphQL query support
124124
And the header "Content-Type" should be equal to "application/json"
125125
And the JSON node "data.dummyGroup.foo" should be equal to "Foo #1"
126126

127+
Scenario: Fetch only the internal id
128+
When I send the following GraphQL request:
129+
"""
130+
{
131+
dummy(id: "/dummies/1") {
132+
_id
133+
}
134+
}
135+
"""
136+
Then the response status code should be 200
137+
And the response should be in JSON
138+
And the header "Content-Type" should be equal to "application/json"
139+
And the JSON node "data.dummy._id" should be equal to "1"
140+
127141
@dropSchema
128142
Scenario: Retrieve an nonexistent item through a GraphQL query
129143
When I send the following GraphQL request:

src/GraphQl/Resolver/Factory/CollectionResolverFactory.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use ApiPlatform\Core\DataProvider\PaginatorInterface;
1919
use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface;
2020
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
21+
use ApiPlatform\Core\GraphQl\Resolver\FieldsToAttributesTrait;
2122
use ApiPlatform\Core\GraphQl\Resolver\ResourceAccessCheckerTrait;
2223
use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer;
2324
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
@@ -37,6 +38,7 @@
3738
*/
3839
final class CollectionResolverFactory implements ResolverFactoryInterface
3940
{
41+
use FieldsToAttributesTrait;
4042
use ResourceAccessCheckerTrait;
4143

4244
private $collectionDataProvider;
@@ -125,16 +127,6 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
125127
};
126128
}
127129

128-
private function fieldsToAttributes(ResolveInfo $info): array
129-
{
130-
$fields = $info->getFieldSelection(PHP_INT_MAX);
131-
if (isset($fields['edges']['node'])) {
132-
$fields = $fields['edges']['node'];
133-
}
134-
135-
return $fields;
136-
}
137-
138130
/**
139131
* @throws ResourceClassNotSupportedException
140132
*

src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
1818
use ApiPlatform\Core\Exception\InvalidArgumentException;
1919
use ApiPlatform\Core\Exception\ItemNotFoundException;
20+
use ApiPlatform\Core\GraphQl\Resolver\FieldsToAttributesTrait;
2021
use ApiPlatform\Core\GraphQl\Resolver\ResourceAccessCheckerTrait;
2122
use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer;
2223
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
@@ -38,6 +39,7 @@
3839
*/
3940
final class ItemMutationResolverFactory implements ResolverFactoryInterface
4041
{
42+
use FieldsToAttributesTrait;
4143
use ResourceAccessCheckerTrait;
4244

4345
private $iriConverter;
@@ -73,7 +75,7 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
7375

7476
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
7577
$normalizationContext = $resourceMetadata->getGraphqlAttribute($operationName ?? '', 'normalization_context', [], true);
76-
$normalizationContext['attributes'] = $info->getFieldSelection(PHP_INT_MAX);
78+
$normalizationContext['attributes'] = $this->fieldsToAttributes($info);
7779

7880
if (isset($args['input']['id'])) {
7981
try {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\GraphQl\Resolver;
15+
16+
use GraphQL\Type\Definition\ResolveInfo;
17+
18+
/**
19+
* Transforms the passed GraphQL fields to the list of attributes to serialize.
20+
*
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
trait FieldsToAttributesTrait
24+
{
25+
/**
26+
* Retrieves fields, recursively replaces the "_id" key (the raw id) by "id" (the name of the property expected by the Serializer) and flattens edge and node structures (pagination).
27+
*/
28+
private function fieldsToAttributes(ResolveInfo $info): array
29+
{
30+
$fields = $info->getFieldSelection(PHP_INT_MAX);
31+
if (isset($fields['edges']['node'])) {
32+
$fields = $fields['edges']['node'];
33+
}
34+
35+
return $this->replaceIdKeys($fields);
36+
}
37+
38+
private function replaceIdKeys(array $fields): array
39+
{
40+
foreach ($fields as $key => $value) {
41+
if ('_id' === $key) {
42+
$fields['id'] = $fields['_id'];
43+
unset($fields['_id']);
44+
} elseif (\is_array($fields[$key])) {
45+
$fields[$key] = $this->replaceIdKeys($fields[$key]);
46+
}
47+
}
48+
49+
return $fields;
50+
}
51+
}

src/GraphQl/Resolver/ItemResolver.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
final class ItemResolver
3434
{
3535
use ClassInfoTrait;
36+
use FieldsToAttributesTrait;
3637
use ResourceAccessCheckerTrait;
3738

3839
private $iriConverter;
@@ -59,7 +60,7 @@ public function __invoke($source, $args, $context, ResolveInfo $info)
5960
return null;
6061
}
6162

62-
$baseNormalizationContext = ['attributes' => $info->getFieldSelection(PHP_INT_MAX)];
63+
$baseNormalizationContext = ['attributes' => $this->fieldsToAttributes($info)];
6364
try {
6465
$item = $this->iriConverter->getItemFromIri($args['id'], $baseNormalizationContext);
6566
} catch (ItemNotFoundException $e) {

0 commit comments

Comments
 (0)