Skip to content

Commit e3d50ff

Browse files
authored
Merge pull request #1929 from soyuka/merge-2.2
Merge 2.2 in master
2 parents c304b11 + ef54e37 commit e3d50ff

File tree

17 files changed

+196
-64
lines changed

17 files changed

+196
-64
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
<argument type="service" id="api_platform.router" />
5959
<argument type="service" id="api_platform.property_accessor" />
6060
<argument type="service" id="api_platform.identifiers_extractor.cached" />
61-
<argument type="service" id="api_platform.identifier.denormalizer" />
62-
<argument type="service" id="api_platform.subresource_data_provider" />
61+
<argument type="service" id="api_platform.subresource_data_provider" on-invalid="ignore" />
62+
<argument type="service" id="api_platform.identifier.denormalizer" on-invalid="ignore" />
6363
</service>
6464
<service id="ApiPlatform\Core\Api\IriConverterInterface" alias="api_platform.iri_converter" />
6565

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<argument type="service" id="api_platform.operation_method_resolver" />
1414
<argument type="service" id="api_platform.router" />
1515
<argument type="service" id="api_platform.subresource_operation_factory" />
16+
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
1617

1718
<tag name="serializer.normalizer" priority="32" />
1819
</service>

src/Bridge/Symfony/Routing/IriConverter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ final class IriConverter implements IriConverterInterface
4949
private $router;
5050
private $identifiersExtractor;
5151

52-
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, ChainIdentifierDenormalizer $identifierDenormalizer = null, SubresourceDataProviderInterface $subresourceDataProvider = null)
52+
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, SubresourceDataProviderInterface $subresourceDataProvider = null, ChainIdentifierDenormalizer $identifierDenormalizer = null)
5353
{
5454
$this->itemDataProvider = $itemDataProvider;
5555
$this->routeNameResolver = $routeNameResolver;
5656
$this->router = $router;
5757
$this->identifiersExtractor = $identifiersExtractor;
58-
$this->identifierDenormalizer = $identifierDenormalizer;
5958
$this->subresourceDataProvider = $subresourceDataProvider;
59+
$this->identifierDenormalizer = $identifierDenormalizer;
6060

6161
if (null === $identifiersExtractor) {
6262
@trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3', IdentifiersExtractorInterface::class), E_USER_DEPRECATED);

src/GraphQl/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
namespace ApiPlatform\Core\GraphQl\Serializer;
2323

2424
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
25-
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
2625
use ApiPlatform\Core\Serializer\ItemNormalizer as BaseItemNormalizer;
2726

2827
/**
@@ -48,7 +47,7 @@ public function supportsNormalization($data, $format = null)
4847
*/
4948
public function normalize($object, $format = null, array $context = [])
5049
{
51-
$data = AbstractItemNormalizer::normalize($object, $format, $context);
50+
$data = parent::normalize($object, $format, $context);
5251
$data[self::ITEM_KEY] = serialize($object); // calling serialize prevent weird normalization process done by Webonyx's GraphQL PHP
5352

5453
return $data;

src/Hal/Serializer/ItemNormalizer.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,24 +174,29 @@ private function populateRelation(array $data, $object, string $format = null, a
174174
continue;
175175
}
176176

177+
$relationName = $relation['name'];
178+
if ($this->nameConverter) {
179+
$relationName = $this->nameConverter->normalize($relationName);
180+
}
181+
177182
if ('one' === $relation['cardinality']) {
178183
if ('links' === $type) {
179-
$data[$key][$relation['name']]['href'] = $this->getRelationIri($attributeValue);
184+
$data[$key][$relationName]['href'] = $this->getRelationIri($attributeValue);
180185
continue;
181186
}
182187

183-
$data[$key][$relation['name']] = $attributeValue;
188+
$data[$key][$relationName] = $attributeValue;
184189
continue;
185190
}
186191

187192
// many
188-
$data[$key][$relation['name']] = [];
193+
$data[$key][$relationName] = [];
189194
foreach ($attributeValue as $rel) {
190195
if ('links' === $type) {
191196
$rel = ['href' => $this->getRelationIri($rel)];
192197
}
193198

194-
$data[$key][$relation['name']][] = $rel;
199+
$data[$key][$relationName][] = $rel;
195200
}
196201
}
197202

src/Hydra/Serializer/DocumentationNormalizer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
2828
use ApiPlatform\Core\Operation\Factory\SubresourceOperationFactoryInterface;
2929
use Symfony\Component\PropertyInfo\Type;
30+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
3031
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
3132
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
3233

@@ -46,8 +47,9 @@ final class DocumentationNormalizer implements NormalizerInterface
4647
private $operationMethodResolver;
4748
private $urlGenerator;
4849
private $subresourceOperationFactory;
50+
private $nameConverter;
4951

50-
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, UrlGeneratorInterface $urlGenerator, SubresourceOperationFactoryInterface $subresourceOperationFactory = null)
52+
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, UrlGeneratorInterface $urlGenerator, SubresourceOperationFactoryInterface $subresourceOperationFactory = null, NameConverterInterface $nameConverter = null)
5153
{
5254
$this->resourceMetadataFactory = $resourceMetadataFactory;
5355
$this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
@@ -56,6 +58,7 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa
5658
$this->operationMethodResolver = $operationMethodResolver;
5759
$this->urlGenerator = $urlGenerator;
5860
$this->subresourceOperationFactory = $subresourceOperationFactory;
61+
$this->nameConverter = $nameConverter;
5962
}
6063

6164
/**
@@ -194,6 +197,10 @@ private function getHydraProperties(string $resourceClass, ResourceMetadata $res
194197
continue;
195198
}
196199

200+
if ($this->nameConverter) {
201+
$propertyName = $this->nameConverter->normalize($propertyName);
202+
}
203+
197204
$properties[] = $this->getProperty($propertyMetadata, $propertyName, $prefixedShortName, $shortName);
198205
}
199206

src/Serializer/AbstractItemNormalizer.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2323
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2424
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
25+
use ApiPlatform\Core\Util\ClassInfoTrait;
2526
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
2627
use Symfony\Component\PropertyAccess\PropertyAccess;
2728
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -37,6 +38,7 @@
3738
*/
3839
abstract class AbstractItemNormalizer extends AbstractObjectNormalizer
3940
{
41+
use ClassInfoTrait;
4042
use ContextTrait;
4143

4244
protected $propertyNameCollectionFactory;
@@ -74,13 +76,7 @@ public function supportsNormalization($data, $format = null)
7476
return false;
7577
}
7678

77-
try {
78-
$this->resourceClassResolver->getResourceClass($data);
79-
} catch (InvalidArgumentException $e) {
80-
return false;
81-
}
82-
83-
return true;
79+
return $this->resourceClassResolver->isResourceClass($this->getObjectClass($data));
8480
}
8581

8682
/**

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,8 @@ private function getPartialContainerBuilderProphecy($test = false)
498498
'api_platform.filter_locator',
499499
'api_platform.filter_collection_factory',
500500
'api_platform.filters',
501+
'api_platform.identifiers_extractor',
502+
'api_platform.identifiers_extractor.cached',
501503
'api_platform.iri_converter',
502504
'api_platform.identifier.denormalizer',
503505
'api_platform.identifier.integer',

tests/Bridge/Symfony/Routing/IriConverterTest.php

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,30 @@ public function testGetItemFromIri()
101101
$this->assertEquals($converter->getItemFromIri('/users/3', ['fetch_data' => true]), $item);
102102
}
103103

104+
public function testGetItemFromIriWithOperationName()
105+
{
106+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
107+
108+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
109+
110+
$itemDataProviderProphecy = $this->prophesize(ItemDataProviderInterface::class);
111+
$itemDataProviderProphecy->getItem('AppBundle\Entity\User', '3', 'operation_name', ['fetch_data' => true])
112+
->willReturn('foo')
113+
->shouldBeCalledTimes(1);
114+
115+
$routeNameResolverProphecy = $this->prophesize(RouteNameResolverInterface::class);
116+
117+
$routerProphecy = $this->prophesize(RouterInterface::class);
118+
$routerProphecy->match('/users/3')->willReturn([
119+
'_api_resource_class' => 'AppBundle\Entity\User',
120+
'_api_item_operation_name' => 'operation_name',
121+
'id' => 3,
122+
])->shouldBeCalledTimes(1);
123+
124+
$converter = $this->getIriConverter($routerProphecy, null, $itemDataProviderProphecy);
125+
$this->assertEquals($converter->getItemFromIri('/users/3', ['fetch_data' => true]), 'foo');
126+
}
127+
104128
public function testGetIriFromResourceClass()
105129
{
106130
$routeNameResolverProphecy = $this->prophesize(RouteNameResolverInterface::class);
@@ -199,7 +223,7 @@ public function testGetItemFromIriWithIdentifierDenormalizer()
199223
'id' => 3,
200224
])->shouldBeCalledTimes(1);
201225

202-
$converter = $this->getIriConverter($routerProphecy, null, $itemDataProviderProphecy, $identifierDenormalizerProphecy);
226+
$converter = $this->getIriConverter($routerProphecy, null, $itemDataProviderProphecy, null, $identifierDenormalizerProphecy);
203227
$this->assertEquals($converter->getItemFromIri('/users/3', ['fetch_data' => true]), $item);
204228
}
205229

@@ -216,11 +240,9 @@ public function testGetItemFromIriWithSubresourceDataProvider()
216240
'_api_subresource_operation_name' => 'get_subresource',
217241
'id' => 3,
218242
])->shouldBeCalledTimes(1);
219-
$identifierDenormalizerProphecy = $this->prophesize(ChainIdentifierDenormalizer::class);
220-
$identifierDenormalizerProphecy->denormalize('3', Dummy::class)->shouldBeCalled()->willReturn(['id' => 3]);
221243
$subresourceDataProviderProphecy = $this->prophesize(SubresourceDataProviderInterface::class);
222-
$subresourceDataProviderProphecy->getSubresource(Dummy::class, ['id' => ['id' => 3]], $subresourceContext + ['fetch_data' => true, ChainIdentifierDenormalizer::HAS_IDENTIFIER_DENORMALIZER => true], 'get_subresource')->shouldBeCalled()->willReturn($item);
223-
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, $identifierDenormalizerProphecy, $subresourceDataProviderProphecy);
244+
$subresourceDataProviderProphecy->getSubresource(Dummy::class, ['id' => 3], $subresourceContext + ['fetch_data' => true], 'get_subresource')->shouldBeCalled()->willReturn($item);
245+
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, $subresourceDataProviderProphecy);
224246
$this->assertEquals($converter->getItemFromIri('/users/3/adresses', ['fetch_data' => true]), $item);
225247
}
226248

@@ -244,7 +266,7 @@ public function testGetItemFromIriWithSubresourceDataProviderNotFound()
244266
$identifierDenormalizerProphecy->denormalize('3', Dummy::class)->shouldBeCalled()->willReturn(['id' => 3]);
245267
$subresourceDataProviderProphecy = $this->prophesize(SubresourceDataProviderInterface::class);
246268
$subresourceDataProviderProphecy->getSubresource(Dummy::class, ['id' => ['id' => 3]], $subresourceContext + ['fetch_data' => true, ChainIdentifierDenormalizer::HAS_IDENTIFIER_DENORMALIZER => true], 'get_subresource')->shouldBeCalled()->willReturn(null);
247-
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, $identifierDenormalizerProphecy, $subresourceDataProviderProphecy);
269+
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, $subresourceDataProviderProphecy, $identifierDenormalizerProphecy);
248270
$converter->getItemFromIri('/users/3/adresses', ['fetch_data' => true]);
249271
}
250272

@@ -265,7 +287,7 @@ public function testGetItemFromIriBadIdentifierException()
265287
])->shouldBeCalledTimes(1);
266288
$identifierDenormalizerProphecy = $this->prophesize(ChainIdentifierDenormalizer::class);
267289
$identifierDenormalizerProphecy->denormalize('3', Dummy::class)->shouldBeCalled()->willThrow(new InvalidIdentifierException('fail'));
268-
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, $identifierDenormalizerProphecy);
290+
$converter = $this->getIriConverter($routerProphecy, $routeNameResolverProphecy, null, null, $identifierDenormalizerProphecy);
269291
$this->assertEquals($converter->getItemFromIri('/users/3', ['fetch_data' => true]), $item);
270292
}
271293

@@ -302,7 +324,7 @@ private function getResourceClassResolver()
302324
return $resourceClassResolver->reveal();
303325
}
304326

305-
private function getIriConverter($routerProphecy = null, $routeNameResolverProphecy = null, $itemDataProviderProphecy = null, $identifierDenormalizerProphecy = null, $subresourceDataProviderProphecy = null)
327+
private function getIriConverter($routerProphecy = null, $routeNameResolverProphecy = null, $itemDataProviderProphecy = null, $subresourceDataProviderProphecy = null, $identifierDenormalizerProphecy = null)
306328
{
307329
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
308330
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
@@ -328,8 +350,8 @@ private function getIriConverter($routerProphecy = null, $routeNameResolverProph
328350
$routerProphecy->reveal(),
329351
null,
330352
new IdentifiersExtractor($propertyNameCollectionFactory, $propertyMetadataFactory, null, $this->getResourceClassResolver()),
331-
$identifierDenormalizerProphecy ? $identifierDenormalizerProphecy->reveal() : null,
332-
$subresourceDataProviderProphecy ? $subresourceDataProviderProphecy->reveal() : null
353+
$subresourceDataProviderProphecy ? $subresourceDataProviderProphecy->reveal() : null,
354+
$identifierDenormalizerProphecy ? $identifierDenormalizerProphecy->reveal() : null
333355
);
334356
}
335357
}

tests/EventListener/ReadListenerTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,32 @@ public function testRetrieveItem()
171171
$this->assertSame($data, $request->attributes->get('data'));
172172
}
173173

174+
/**
175+
* @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
176+
*/
177+
public function testRetrieveItemNoIdentifier()
178+
{
179+
$collectionDataProvider = $this->prophesize(CollectionDataProviderInterface::class);
180+
$collectionDataProvider->getCollection()->shouldNotBeCalled();
181+
182+
$itemDataProvider = $this->prophesize(ItemDataProviderInterface::class);
183+
$itemDataProvider->getItem()->shouldNotBeCalled();
184+
185+
$subresourceDataProvider = $this->prophesize(SubresourceDataProviderInterface::class);
186+
$subresourceDataProvider->getSubresource()->shouldNotBeCalled();
187+
188+
$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_item_operation_name' => 'get', '_api_format' => 'json', '_api_mime_type' => 'application/json']);
189+
$request->setMethod('GET');
190+
191+
$event = $this->prophesize(GetResponseEvent::class);
192+
$event->getRequest()->willReturn($request)->shouldBeCalled();
193+
194+
$listener = new ReadListener($collectionDataProvider->reveal(), $itemDataProvider->reveal(), $subresourceDataProvider->reveal());
195+
$listener->onKernelRequest($event->reveal());
196+
197+
$request->attributes->get('data');
198+
}
199+
174200
public function testRetrieveSubresource()
175201
{
176202
$identifierDenormalizer = $this->prophesize(ChainIdentifierDenormalizer::class);
@@ -198,6 +224,29 @@ public function testRetrieveSubresource()
198224
$this->assertSame($data, $request->attributes->get('data'));
199225
}
200226

227+
/**
228+
* @expectedException \ApiPlatform\Core\Exception\RuntimeException
229+
*/
230+
public function testRetrieveSubresourceNoDataProvider()
231+
{
232+
$collectionDataProvider = $this->prophesize(CollectionDataProviderInterface::class);
233+
$collectionDataProvider->getCollection()->shouldNotBeCalled();
234+
235+
$itemDataProvider = $this->prophesize(ItemDataProviderInterface::class);
236+
$itemDataProvider->getItem()->shouldNotBeCalled();
237+
238+
$request = new Request([], [], ['id' => 1, '_api_resource_class' => 'Foo', '_api_subresource_operation_name' => 'get', '_api_format' => 'json', '_api_mime_type' => 'application/json', '_api_subresource_context' => ['identifiers' => [['id', 'Bar', true]], 'property' => 'bar']]);
239+
$request->setMethod('GET');
240+
241+
$event = $this->prophesize(GetResponseEvent::class);
242+
$event->getRequest()->willReturn($request)->shouldBeCalled();
243+
244+
$listener = new ReadListener($collectionDataProvider->reveal(), $itemDataProvider->reveal());
245+
$listener->onKernelRequest($event->reveal());
246+
247+
$request->attributes->get('data');
248+
}
249+
201250
/**
202251
* @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
203252
*/

tests/GraphQl/Serializer/ItemNormalizerTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
use ApiPlatform\Core\Api\IriConverterInterface;
1717
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
18-
use ApiPlatform\Core\Exception\InvalidArgumentException;
1918
use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer;
2019
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2120
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
@@ -44,8 +43,6 @@ public function testSupportNormalization()
4443
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
4544

4645
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
47-
$resourceClassResolverProphecy->getResourceClass($dummy)->willReturn(Dummy::class)->shouldBeCalled();
48-
$resourceClassResolverProphecy->getResourceClass($std)->willThrow(new InvalidArgumentException())->shouldBeCalled();
4946
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true)->shouldBeCalled();
5047
$resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false)->shouldBeCalled();
5148

0 commit comments

Comments
 (0)