Skip to content

Commit 8771988

Browse files
fix(metadata): enum resource identifier default to value
1 parent 995b6b6 commit 8771988

File tree

2 files changed

+288
-0
lines changed

2 files changed

+288
-0
lines changed

src/Metadata/Resource/Factory/LinkFactory.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ public function createLinksFromIdentifiers(Metadata $operation): array
5959

6060
$link = (new Link())->withFromClass($resourceClass)->withIdentifiers($identifiers);
6161
$parameterName = $identifiers[0];
62+
if ('value' === $parameterName && enum_exists($resourceClass)) {
63+
$parameterName = 'id';
64+
}
6265

6366
if (1 < \count($identifiers)) {
6467
$parameterName = 'id';
@@ -155,6 +158,10 @@ private function getIdentifiersFromResourceClass(string $resourceClass): array
155158
return ['id'];
156159
}
157160

161+
if (!$hasIdProperty && !$identifiers && enum_exists($resourceClass)) {
162+
return ['value'];
163+
}
164+
158165
return $identifiers;
159166
}
160167

tests/Functional/BackedEnumResourceTest.php

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515

1616
use ApiPlatform\Metadata\Get;
1717
use ApiPlatform\Metadata\GetCollection;
18+
use ApiPlatform\Metadata\Link;
1819
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
1920
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\BackedEnumIntegerResource;
2021
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\BackedEnumStringResource;
22+
use Symfony\Component\HttpClient\HttpOptions;
2123

2224
final class BackedEnumResourceTest extends ApiTestCase
2325
{
@@ -43,4 +45,283 @@ public function testOnlyGetOperationsAddedWhenNonSpecified(string $resourceClass
4345

4446
$this->assertInstanceOf($operationClass, $operations[$operationName]);
4547
}
48+
49+
public function testEnumsAreAssignedValuePropertyAsIdentifierByDefault(): void
50+
{
51+
$linkFactory = self::getContainer()->get('api_platform.metadata.resource.link_factory');
52+
$result = $linkFactory->completeLink(new Link(fromClass: BackedEnumIntegerResource::class));
53+
$identifiers = $result->getIdentifiers();
54+
55+
$this->assertCount(1, $identifiers);
56+
$this->assertNotContains('id', $identifiers);
57+
$this->assertContains('value', $identifiers);
58+
}
59+
60+
public static function providerCollection(): iterable
61+
{
62+
yield 'JSON' => ['application/json', [
63+
[
64+
'name' => 'Yes',
65+
'value' => 1,
66+
'description' => 'We say yes',
67+
],
68+
[
69+
'name' => 'No',
70+
'value' => 2,
71+
'description' => 'Computer says no',
72+
],
73+
[
74+
'name' => 'Maybe',
75+
'value' => 3,
76+
'description' => 'Let me think about it',
77+
],
78+
]];
79+
80+
yield 'JSON:API' => ['application/vnd.api+json', [
81+
'links' => [
82+
'self' => '/backed_enum_integer_resources',
83+
],
84+
'meta' => [
85+
'totalItems' => 3,
86+
],
87+
'data' => [
88+
[
89+
'id' => '/backed_enum_integer_resources/1',
90+
'type' => 'BackedEnumIntegerResource',
91+
'attributes' => [
92+
'name' => 'Yes',
93+
'value' => 1,
94+
'description' => 'We say yes',
95+
],
96+
],
97+
[
98+
'id' => '/backed_enum_integer_resources/2',
99+
'type' => 'BackedEnumIntegerResource',
100+
'attributes' => [
101+
'name' => 'No',
102+
'value' => 2,
103+
'description' => 'Computer says no',
104+
],
105+
],
106+
[
107+
'id' => '/backed_enum_integer_resources/3',
108+
'type' => 'BackedEnumIntegerResource',
109+
'attributes' => [
110+
'name' => 'Maybe',
111+
'value' => 3,
112+
'description' => 'Let me think about it',
113+
],
114+
],
115+
],
116+
]];
117+
118+
yield 'LD+JSON' => ['application/ld+json', [
119+
'@context' => '/contexts/BackedEnumIntegerResource',
120+
'@id' => '/backed_enum_integer_resources',
121+
'@type' => 'hydra:Collection',
122+
'hydra:totalItems' => 3,
123+
'hydra:member' => [
124+
[
125+
'@id' => '/backed_enum_integer_resources/1',
126+
'@type' => 'BackedEnumIntegerResource',
127+
'name' => 'Yes',
128+
'value' => 1,
129+
'description' => 'We say yes',
130+
],
131+
[
132+
'@id' => '/backed_enum_integer_resources/2',
133+
'@type' => 'BackedEnumIntegerResource',
134+
'name' => 'No',
135+
'value' => 2,
136+
'description' => 'Computer says no',
137+
],
138+
[
139+
'@id' => '/backed_enum_integer_resources/3',
140+
'@type' => 'BackedEnumIntegerResource',
141+
'name' => 'Maybe',
142+
'value' => 3,
143+
'description' => 'Let me think about it',
144+
],
145+
],
146+
]];
147+
148+
yield 'HAL+JSON' => ['application/hal+json', [
149+
'_links' => [
150+
'self' => [
151+
'href' => '/backed_enum_integer_resources',
152+
],
153+
'item' => [
154+
[
155+
'href' => '/backed_enum_integer_resources/1',
156+
],
157+
[
158+
'href' => '/backed_enum_integer_resources/2',
159+
],
160+
[
161+
'href' => '/backed_enum_integer_resources/3',
162+
],
163+
],
164+
],
165+
'totalItems' => 3,
166+
'_embedded' => [
167+
'item' => [
168+
[
169+
'_links' => [
170+
'self' => [
171+
'href' => '/backed_enum_integer_resources/1',
172+
],
173+
],
174+
'name' => 'Yes',
175+
'value' => 1,
176+
'description' => 'We say yes',
177+
],
178+
[
179+
'_links' => [
180+
'self' => [
181+
'href' => '/backed_enum_integer_resources/2',
182+
],
183+
],
184+
'name' => 'No',
185+
'value' => 2,
186+
'description' => 'Computer says no',
187+
],
188+
[
189+
'_links' => [
190+
'self' => [
191+
'href' => '/backed_enum_integer_resources/3',
192+
],
193+
],
194+
'name' => 'Maybe',
195+
'value' => 3,
196+
'description' => 'Let me think about it',
197+
],
198+
],
199+
],
200+
]];
201+
}
202+
203+
/** @dataProvider providerCollection */
204+
public function testCollection(string $mimeType, array $expected): void
205+
{
206+
self::createClient()->request('GET', '/backed_enum_integer_resources', ['headers' => ['Accept' => $mimeType]]);
207+
208+
$this->assertResponseIsSuccessful();
209+
$this->assertJsonEquals($expected);
210+
}
211+
212+
public static function providerItem(): iterable
213+
{
214+
yield 'JSON' => ['application/json', [
215+
'name' => 'Yes',
216+
'value' => 1,
217+
'description' => 'We say yes',
218+
]];
219+
220+
yield 'JSON:API' => ['application/vnd.api+json', [
221+
'data' => [
222+
'id' => '/backed_enum_integer_resources/1',
223+
'type' => 'BackedEnumIntegerResource',
224+
'attributes' => [
225+
'name' => 'Yes',
226+
'value' => 1,
227+
'description' => 'We say yes',
228+
],
229+
],
230+
]];
231+
232+
yield 'JSON:HAL' => ['application/hal+json', [
233+
'_links' => [
234+
'self' => [
235+
'href' => '/backed_enum_integer_resources/1',
236+
],
237+
],
238+
'name' => 'Yes',
239+
'value' => 1,
240+
'description' => 'We say yes',
241+
]];
242+
243+
yield 'LD+JSON' => ['application/ld+json', [
244+
'@context' => '/contexts/BackedEnumIntegerResource',
245+
'@id' => '/backed_enum_integer_resources/1',
246+
'@type' => 'BackedEnumIntegerResource',
247+
'name' => 'Yes',
248+
'value' => 1,
249+
'description' => 'We say yes',
250+
]];
251+
}
252+
253+
/** @dataProvider providerItem */
254+
public function testItem(string $mimeType, array $expected): void
255+
{
256+
self::createClient()->request('GET', '/backed_enum_integer_resources/1', ['headers' => ['Accept' => $mimeType]]);
257+
258+
$this->assertResponseIsSuccessful();
259+
$this->assertJsonEquals($expected);
260+
}
261+
262+
public function testCollectionGraphQl(): void
263+
{
264+
$query = <<<'GRAPHQL'
265+
query {
266+
backedEnumIntegerResources {
267+
value
268+
}
269+
}
270+
GRAPHQL;
271+
$options = (new HttpOptions())
272+
->setJson(['query' => $query, 'variables' => []])
273+
->setHeaders(['Content-Type' => 'application/json']);
274+
self::createClient()->request('POST', '/graphql', $options->toArray());
275+
276+
$this->assertResponseIsSuccessful();
277+
$this->assertJsonEquals([
278+
'data' => [
279+
'backedEnumIntegerResources' => [
280+
['value' => 1],
281+
['value' => 2],
282+
['value' => 3],
283+
],
284+
],
285+
]);
286+
}
287+
288+
public function testItemGraphQl(): void
289+
{
290+
$query = <<<'GRAPHQL'
291+
query GetBackedEnumIntegerResource($identifier: ID!) {
292+
backedEnumIntegerResource(id: $identifier) {
293+
name
294+
value
295+
description
296+
}
297+
}
298+
GRAPHQL;
299+
$options = (new HttpOptions())
300+
->setJson(['query' => $query, 'variables' => ['identifier' => '/backed_enum_integer_resources/1']])
301+
->setHeaders(['Content-Type' => 'application/json']);
302+
self::createClient()->request('POST', '/graphql', $options->toArray());
303+
304+
$this->assertResponseIsSuccessful();
305+
$this->assertJsonEquals([
306+
'data' => [
307+
'status' => [
308+
'value' => 1,
309+
],
310+
],
311+
]);
312+
}
313+
314+
public static function provider404s(): iterable
315+
{
316+
yield ['/backed_enum_integer_resources/42'];
317+
yield ['/backed_enum_integer_resources/fortytwo'];
318+
}
319+
320+
/** @dataProvider provider404s */
321+
public function testItem404(string $uri): void
322+
{
323+
self::createClient()->request('GET', $uri);
324+
325+
$this->assertResponseStatusCodeSame(404);
326+
}
46327
}

0 commit comments

Comments
 (0)