Skip to content

Commit 8c626bd

Browse files
committed
Use InputUnionType for updating related existing resources
1 parent 937fc6f commit 8c626bd

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

features/graphql/mutation.feature

+22
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ Feature: GraphQL mutation support
122122
And the JSON node "data.createRelatedDummy.id" should be equal to "/related_dummies/2"
123123
And the JSON node "data.createRelatedDummy.clientMutationId" should be equal to "myId"
124124

125+
Scenario: Create an item and update a nested resource through a mutation
126+
When I send the following GraphQL request:
127+
"""
128+
mutation {
129+
createRelationEmbedder(input: {paris: "paris", krondstadt: "Krondstadt", anotherRelated: {id: 2, symfony: "laravel"}, clientMutationId: "myId"}) {
130+
id
131+
anotherRelated {
132+
id
133+
symfony
134+
}
135+
clientMutationId
136+
}
137+
}
138+
"""
139+
Then the response status code should be 200
140+
And the response should be in JSON
141+
And the header "Content-Type" should be equal to "application/json"
142+
And the JSON node "data.createRelationEmbedder.id" should be equal to "/relation_embedders/1"
143+
And the JSON node "data.createRelationEmbedder.anotherRelated.id" should be equal to "/related_dummies/2"
144+
And the JSON node "data.createRelationEmbedder.anotherRelated.symfony" should be equal to "laravel"
145+
And the JSON node "data.createRelationEmbedder.clientMutationId" should be equal to "myId"
146+
125147
Scenario: Delete an item through a mutation
126148
When I send the following GraphQL request:
127149
"""

src/Bridge/Symfony/Bundle/Resources/config/graphql.xml

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@
7777
<argument type="service" id="api_platform.item_data_provider" on-invalid="ignore" />
7878
<argument>%api_platform.allow_plain_identifiers%</argument>
7979

80+
<call method="setItemNormalizer">
81+
<argument type="service" id="api_platform.serializer.normalizer.item" />
82+
</call>
83+
8084
<tag name="serializer.normalizer" priority="8" />
8185
</service>
8286
</services>

src/GraphQl/Serializer/ItemNormalizer.php

+16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
2525
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
26+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2627

2728
/**
2829
* GraphQL normalizer.
@@ -34,6 +35,8 @@ final class ItemNormalizer extends AbstractItemNormalizer
3435
const FORMAT = 'graphql';
3536
const ITEM_KEY = '#item';
3637

38+
private $itemNormalizer;
39+
3740
/**
3841
* {@inheritdoc}
3942
*/
@@ -62,11 +65,24 @@ public function supportsNormalization($data, $format = null)
6265
return self::FORMAT === $format && parent::supportsNormalization($data, $format);
6366
}
6467

68+
/**
69+
* {@inheritdoc}
70+
*/
71+
public function denormalize($data, $class, $format = null, array $context = [])
72+
{
73+
return $this->itemNormalizer->denormalize($data, $class, $format, $context);
74+
}
75+
6576
/**
6677
* {@inheritdoc}
6778
*/
6879
public function supportsDenormalization($data, $type, $format = null)
6980
{
7081
return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format);
7182
}
83+
84+
public function setItemNormalizer(DenormalizerInterface $itemNormalizer)
85+
{
86+
$this->itemNormalizer = $itemNormalizer;
87+
}
7288
}

tests/GraphQl/Serializer/ItemNormalizerTest.php

+8-14
Original file line numberDiff line numberDiff line change
@@ -101,31 +101,25 @@ public function testNormalize()
101101

102102
public function testDenormalize()
103103
{
104-
$context = ['resource_class' => Dummy::class, 'api_allow_update' => true];
105-
106-
$propertyNameCollection = new PropertyNameCollection(['name']);
107104
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
108-
$propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
109-
110-
$propertyMetadataFactory = new PropertyMetadata(null, null, true, true);
111105
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
112-
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadataFactory)->shouldBeCalled();
113-
114106
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
115-
116107
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
117108

118-
$serializerProphecy = $this->prophesize(SerializerInterface::class);
119-
$serializerProphecy->willImplement(DenormalizerInterface::class);
120-
121109
$normalizer = new ItemNormalizer(
122110
$propertyNameCollectionFactoryProphecy->reveal(),
123111
$propertyMetadataFactoryProphecy->reveal(),
124112
$iriConverterProphecy->reveal(),
125113
$resourceClassResolverProphecy->reveal()
126114
);
127-
$normalizer->setSerializer($serializerProphecy->reveal());
128115

129-
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['name' => 'hello'], Dummy::class, ItemNormalizer::FORMAT, $context));
116+
$itemNormalizer = $this->prophesize(DenormalizerInterface::class);
117+
$itemNormalizer->denormalize(['name' => 'hello'], Dummy::class, ItemNormalizer::FORMAT, [])
118+
->willReturn('denormalized')
119+
->shouldBeCalled()
120+
;
121+
$normalizer->setItemNormalizer($itemNormalizer->reveal());
122+
123+
$this->assertEquals('denormalized', $normalizer->denormalize(['name' => 'hello'], Dummy::class, ItemNormalizer::FORMAT));
130124
}
131125
}

0 commit comments

Comments
 (0)