Skip to content

Commit bd114f5

Browse files
authored
Merge pull request #1962 from dunglas/deprecation
[GraphQL] Deprecation support
2 parents 4441501 + fde0936 commit bd114f5

File tree

6 files changed

+137
-7
lines changed

6 files changed

+137
-7
lines changed

features/bootstrap/GraphqlContext.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Behatch\Context\RestContext;
1919
use Behatch\HttpCall\Request;
2020
use GraphQL\Type\Introspection;
21+
use PHPUnit\Framework\ExpectationFailedException;
2122

2223
/**
2324
* Context for GraphQL.
@@ -105,6 +106,20 @@ public function ISendTheQueryToIntrospectTheSchema()
105106
$this->sendGraphqlRequest();
106107
}
107108

109+
/**
110+
* @Then the GraphQL field :fieldName is deprecated for the reason :reason
111+
*/
112+
public function theGraphQLFieldIsDeprecatedForTheReason(string $fieldName, string $reason)
113+
{
114+
foreach (json_decode($this->request->getContent(), true)['data']['__type']['fields'] as $field) {
115+
if ($fieldName === $field['name'] && $field['isDeprecated'] && $reason === $field['deprecationReason']) {
116+
return;
117+
}
118+
}
119+
120+
throw new ExpectationFailedException(sprintf('The field "%s" is not deprecated.', $fieldName));
121+
}
122+
108123
private function sendGraphqlRequest()
109124
{
110125
$this->request->setHttpHeader('Accept', null);

features/graphql/introspection.feature

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,65 @@ Feature: GraphQL introspection support
7676
And the JSON node "data.type3.fields[1].name" should be equal to "cursor"
7777
And the JSON node "data.type3.fields[0].type.name" should be equal to "DummyAggregateOffer"
7878

79+
Scenario: Introspect deprecated queries
80+
When I send the following GraphQL request:
81+
"""
82+
{
83+
__type (name: "Query") {
84+
name
85+
fields(includeDeprecated: true) {
86+
name
87+
isDeprecated
88+
deprecationReason
89+
}
90+
}
91+
}
92+
"""
93+
Then the response status code should be 200
94+
And the response should be in JSON
95+
And the header "Content-Type" should be equal to "application/json"
96+
And the GraphQL field "deprecatedResource" is deprecated for the reason "This resource is deprecated"
97+
And the GraphQL field "deprecatedResources" is deprecated for the reason "This resource is deprecated"
98+
99+
Scenario: Introspect deprecated mutations
100+
When I send the following GraphQL request:
101+
"""
102+
{
103+
__type (name: "Mutation") {
104+
name
105+
fields(includeDeprecated: true) {
106+
name
107+
isDeprecated
108+
deprecationReason
109+
}
110+
}
111+
}
112+
"""
113+
Then the response status code should be 200
114+
And the response should be in JSON
115+
And the header "Content-Type" should be equal to "application/json"
116+
And the GraphQL field "deleteDeprecatedResource" is deprecated for the reason "This resource is deprecated"
117+
And the GraphQL field "updateDeprecatedResource" is deprecated for the reason "This resource is deprecated"
118+
And the GraphQL field "createDeprecatedResource" is deprecated for the reason "This resource is deprecated"
119+
120+
Scenario: Introspect a deprecated field
121+
When I send the following GraphQL request:
122+
"""
123+
{
124+
__type(name: "DeprecatedResource") {
125+
fields(includeDeprecated: true) {
126+
name
127+
isDeprecated
128+
deprecationReason
129+
}
130+
}
131+
}
132+
"""
133+
Then the response status code should be 200
134+
And the response should be in JSON
135+
And the header "Content-Type" should be equal to "application/json"
136+
And the GraphQL field "deprecatedField" is deprecated for the reason "This field is deprecated"
137+
79138
Scenario: Retrieve the Relay's node interface
80139
When I send the following GraphQL request:
81140
"""

src/Annotation/ApiProperty.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
namespace ApiPlatform\Core\Annotation;
1515

16+
use ApiPlatform\Core\Exception\InvalidArgumentException;
17+
use Doctrine\Common\Annotations\Annotation\Attribute;
18+
1619
/**
17-
* Property annotation.
20+
* ApiProperty annotation.
1821
*
1922
* @author Kévin Dunglas <[email protected]>
2023
*

src/Annotation/ApiResource.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Annotation;
1515

16+
use ApiPlatform\Core\Exception\InvalidArgumentException;
1617
use Doctrine\Common\Annotations\Annotation\Attribute;
1718

1819
/**
@@ -28,6 +29,7 @@
2829
* @Attribute("attributes", type="array"),
2930
* @Attribute("collectionOperations", type="array"),
3031
* @Attribute("denormalizationContext", type="array"),
32+
* @Attribute("deprecationReason", type="string"),
3133
* @Attribute("description", type="string"),
3234
* @Attribute("fetchPartial", type="bool"),
3335
* @Attribute("forceEager", type="bool"),
@@ -113,6 +115,13 @@ final class ApiResource
113115
*/
114116
private $denormalizationContext;
115117

118+
/**
119+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
120+
*
121+
* @var string
122+
*/
123+
private $deprecationReason;
124+
116125
/**
117126
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
118127
*

src/GraphQl/Type/SchemaBuilder.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,14 @@ private function getQueryFields(string $resourceClass, ResourceMetadata $resourc
149149
{
150150
$queryFields = [];
151151
$shortName = $resourceMetadata->getShortName();
152+
$deprecationReason = $resourceMetadata->getGraphqlAttribute('query', 'deprecation_reason', '', true);
152153

153-
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass), $resourceClass)) {
154+
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass), $resourceClass)) {
154155
$fieldConfiguration['args'] += ['id' => ['type' => GraphQLType::id()]];
155156
$queryFields[lcfirst($shortName)] = $fieldConfiguration;
156157
}
157158

158-
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, $resourceClass)), $resourceClass)) {
159+
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, $resourceClass)), $resourceClass)) {
159160
$queryFields[lcfirst(Inflector::pluralize($shortName))] = $fieldConfiguration;
160161
}
161162

@@ -169,9 +170,10 @@ private function getMutationFields(string $resourceClass, ResourceMetadata $reso
169170
{
170171
$shortName = $resourceMetadata->getShortName();
171172
$resourceType = new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass);
173+
$deprecationReason = $resourceMetadata->getGraphqlAttribute($mutationName, 'deprecation_reason', '', true);
172174

173-
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, ucfirst("{$mutationName}s a $shortName."), $resourceType, $resourceClass, false, $mutationName)) {
174-
$fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, $resourceType, $resourceClass, true, $mutationName)];
175+
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, ucfirst("{$mutationName}s a $shortName."), $deprecationReason, $resourceType, $resourceClass, false, $mutationName)) {
176+
$fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, null, $deprecationReason, $resourceType, $resourceClass, true, $mutationName)];
175177

176178
if (!$this->isCollection($resourceType)) {
177179
$itemMutationResolverFactory = $this->itemMutationResolverFactory;
@@ -189,7 +191,7 @@ private function getMutationFields(string $resourceClass, ResourceMetadata $reso
189191
*
190192
* @return array|null
191193
*/
192-
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, Type $type, string $rootResource, bool $input = false, string $mutationName = null, int $depth = 0)
194+
private function getResourceFieldConfiguration(string $resourceClass, ResourceMetadata $resourceMetadata, string $fieldDescription = null, string $deprecationReason = '', Type $type, string $rootResource, bool $input = false, string $mutationName = null, int $depth = 0)
193195
{
194196
try {
195197
if (null === $graphqlType = $this->convertType($type, $input, $mutationName, $depth)) {
@@ -258,6 +260,7 @@ private function getResourceFieldConfiguration(string $resourceClass, ResourceMe
258260
'description' => $fieldDescription,
259261
'args' => $args,
260262
'resolve' => $resolve,
263+
'deprecationReason' => $deprecationReason,
261264
];
262265
} catch (InvalidTypeException $e) {
263266
// just ignore invalid types
@@ -441,7 +444,7 @@ private function getResourceObjectTypeFields(string $resourceClass, ResourceMeta
441444
continue;
442445
}
443446

444-
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyType, $resourceClass, $input, $mutationName, ++$depth)) {
447+
if ($fieldConfiguration = $this->getResourceFieldConfiguration($resourceClass, $resourceMetadata, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', ''), $propertyType, $resourceClass, $input, $mutationName, ++$depth)) {
445448
$fields['id' === $property ? '_id' : $property] = $fieldConfiguration;
446449
}
447450
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiProperty;
17+
use ApiPlatform\Core\Annotation\ApiResource;
18+
use Doctrine\ORM\Mapping as ORM;
19+
20+
/**
21+
* @ApiResource(deprecationReason="This resource is deprecated")
22+
* @ORM\Entity
23+
*
24+
* @author Kévin Dunglas <[email protected]>
25+
*/
26+
class DeprecatedResource
27+
{
28+
/**
29+
* @ORM\Id
30+
* @ORM\Column
31+
*/
32+
public $id;
33+
34+
/**
35+
* @var string
36+
*
37+
* @ApiProperty(attributes={"deprecation_reason"="This field is deprecated"})
38+
* @ORM\Column
39+
*/
40+
public $deprecatedField;
41+
}

0 commit comments

Comments
 (0)