Skip to content

Commit

Permalink
Changed depth and maxDepth handling for selfreferencing subresourceOp…
Browse files Browse the repository at this point in the history
…erations.

Addresses: #2533.
  • Loading branch information
Clemens Pflaum authored and soyuka committed Jun 22, 2020
1 parent 09389c2 commit 4af5752
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 9 deletions.
15 changes: 7 additions & 8 deletions src/Operation/Factory/SubresourceOperationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
{
if (null === $rootResourceClass) {
$rootResourceClass = $resourceClass;
$depth = 0;
}

foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
Expand All @@ -85,12 +86,6 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
$visiting = "$resourceClass $property $subresourceClass";

// Handle maxDepth
if ($rootResourceClass === $resourceClass) {
$maxDepth = $subresource->getMaxDepth();
// reset depth when we return to rootResourceClass
$depth = 0;
}

if (null !== $maxDepth && $depth >= $maxDepth) {
break;
}
Expand Down Expand Up @@ -134,7 +129,7 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
$operation['path'] = $subresourceOperation['path'] ?? sprintf(
'/%s%s/{id}/%s%s',
$prefix,
$this->pathSegmentNameGenerator->getSegmentName($rootShortname),
$this->pathSegmentNameGenerator->getSegmentName($rootShortname, true),
$this->pathSegmentNameGenerator->getSegmentName($operation['property'], $operation['collection']),
self::FORMAT_SUFFIX
);
Expand Down Expand Up @@ -183,7 +178,11 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre

$tree[$operation['route_name']] = $operation;

$this->computeSubresourceOperations($subresourceClass, $tree, $rootResourceClass, $operation, $visited + [$visiting => true], ++$depth, $maxDepth);
// get the minimum maxDepth from the rootMaxDepth and the maxDepth of the to be visited Subresource
$currentMaxDepth = array_diff([$maxDepth, $subresource->getMaxDepth()], [null]);
$currentMaxDepth = empty($currentMaxDepth) ? null : min($currentMaxDepth);

$this->computeSubresourceOperations($subresourceClass, $tree, $rootResourceClass, $operation, $visited + [$visiting => true], $depth + 1, $currentMaxDepth);
}
}
}
79 changes: 78 additions & 1 deletion tests/Operation/Factory/SubresourceOperationFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public function testCreateWithMaxDepthMultipleSubresourcesSameMaxDepth()
public function testCreateSelfReferencingSubresources()
{
/**
* DummyEntity -subresource-> DummyEntity -subresource-> DummyEntity ...
* DummyEntity -subresource-> DummyEntity --> DummyEntity ...
*/
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn(new ResourceMetadata('dummyEntity'));
Expand Down Expand Up @@ -505,6 +505,83 @@ public function testCreateSelfReferencingSubresources()
], $subresourceOperationFactory->create(DummyEntity::class));
}

/**
* Test for issue: https://github.com/api-platform/core/issues/2533.
*/
public function testCreateWithDifferentMaxDepthSelfReferencingSubresources()
{
/**
* subresource: maxDepth = 2
* secondSubresource: maxDepth = 1
* DummyEntity -subresource-> DummyEntity -secondSubresource-> DummyEntity ...
* DummyEntity -secondSubresource-> DummyEntity !!!-subresource-> DummyEntity ...
*/
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn(new ResourceMetadata('dummyEntity'));

$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyNameCollectionFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn(new PropertyNameCollection(['subresource', 'secondSubresource']));

$subresourceWithMaxDepthMetadata = (new PropertyMetadata())->withSubresource(new SubresourceMetadata(DummyEntity::class, false, 2));
$secondSubresourceWithMaxDepthMetadata = (new PropertyMetadata())->withSubresource(new SubresourceMetadata(DummyEntity::class, false, 1));

$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(DummyEntity::class, 'subresource')->shouldBeCalled()->willReturn($subresourceWithMaxDepthMetadata);
$propertyMetadataFactoryProphecy->create(DummyEntity::class, 'secondSubresource')->shouldBeCalled()->willReturn($secondSubresourceWithMaxDepthMetadata);

$pathSegmentNameGeneratorProphecy = $this->prophesize(PathSegmentNameGeneratorInterface::class);
$pathSegmentNameGeneratorProphecy->getSegmentName('dummyEntity', true)->shouldBeCalled()->willReturn('dummy_entities');
$pathSegmentNameGeneratorProphecy->getSegmentName('subresource', false)->shouldBeCalled()->willReturn('subresources');
$pathSegmentNameGeneratorProphecy->getSegmentName('secondSubresource', false)->shouldBeCalled()->willReturn('second_subresources');

$subresourceOperationFactory = new SubresourceOperationFactory(
$resourceMetadataFactoryProphecy->reveal(),
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$pathSegmentNameGeneratorProphecy->reveal()
);

$this->assertEquals([
'api_dummy_entities_subresource_get_subresource' => [
'property' => 'subresource',
'collection' => false,
'resource_class' => DummyEntity::class,
'shortNames' => ['dummyEntity'],
'identifiers' => [
['id', DummyEntity::class, true],
],
'route_name' => 'api_dummy_entities_subresource_get_subresource',
'path' => '/dummy_entities/{id}/subresources.{_format}',
'operation_name' => 'subresource_get_subresource',
] + SubresourceOperationFactory::ROUTE_OPTIONS,
'api_dummy_entities_subresource_second_subresource_get_subresource' => [
'property' => 'secondSubresource',
'collection' => false,
'resource_class' => DummyEntity::class,
'shortNames' => ['dummyEntity'],
'identifiers' => [
['id', DummyEntity::class, true],
['subresource', DummyEntity::class, false],
],
'route_name' => 'api_dummy_entities_subresource_second_subresource_get_subresource',
'path' => '/dummy_entities/{id}/subresources/second_subresources.{_format}',
'operation_name' => 'subresource_second_subresource_get_subresource',
] + SubresourceOperationFactory::ROUTE_OPTIONS,
'api_dummy_entities_second_subresource_get_subresource' => [
'property' => 'secondSubresource',
'collection' => false,
'resource_class' => DummyEntity::class,
'shortNames' => ['dummyEntity'],
'identifiers' => [
['id', DummyEntity::class, true],
],
'route_name' => 'api_dummy_entities_second_subresource_get_subresource',
'path' => '/dummy_entities/{id}/second_subresources.{_format}',
'operation_name' => 'second_subresource_get_subresource',
] + SubresourceOperationFactory::ROUTE_OPTIONS,
], $subresourceOperationFactory->create(DummyEntity::class));
}

public function testCreateWithEnd()
{
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
Expand Down

0 comments on commit 4af5752

Please sign in to comment.