Skip to content

Commit

Permalink
Merge 4013795 into 0a0c7c5
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Jul 27, 2020
2 parents 0a0c7c5 + 4013795 commit 5108f76
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 8 deletions.
12 changes: 5 additions & 7 deletions src/Operation/Factory/SubresourceOperationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,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 @@ -183,7 +177,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 between the rootMaxDepth and the maxDepth of the to be visited Subresource
$currentMaxDepth = array_filter([$maxDepth, $subresource->getMaxDepth()], 'is_int');
$currentMaxDepth = empty($currentMaxDepth) ? null : min($currentMaxDepth);

$this->computeSubresourceOperations($subresourceClass, $tree, $rootResourceClass, $operation, $visited + [$visiting => true], $depth + 1, $currentMaxDepth);
}
}
}
140 changes: 139 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')->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 Expand Up @@ -652,4 +729,65 @@ public function testCreateWithRootResourcePrefix()
] + SubresourceOperationFactory::ROUTE_OPTIONS,
], $subresourceOperationFactory->create(DummyEntity::class));
}

public function testCreateSelfReferencingSubresourcesWithSubresources()
{
/**
* DummyEntity -otherSubresource-> RelatedDummyEntity
* DummyEntity -subresource (maxDepth=1) -> DummyEntity -otherSubresource-> RelatedDummyEntity.
*/
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn(new ResourceMetadata('dummyEntity'));
$resourceMetadataFactoryProphecy->create(RelatedDummyEntity::class)->shouldBeCalled()->willReturn(new ResourceMetadata('relatedDummyEntity'));

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

$subresource = (new PropertyMetadata())->withSubresource(new SubresourceMetadata(DummyEntity::class, false, 1));
$otherSubresourceSubresource = (new PropertyMetadata())->withSubresource(new SubresourceMetadata(RelatedDummyEntity::class, false));

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

$pathSegmentNameGeneratorProphecy = $this->prophesize(PathSegmentNameGeneratorInterface::class);
$pathSegmentNameGeneratorProphecy->getSegmentName('dummyEntity')->shouldBeCalled()->willReturn('dummy_entities');
$pathSegmentNameGeneratorProphecy->getSegmentName('subresource', false)->shouldBeCalled()->willReturn('subresources');
$pathSegmentNameGeneratorProphecy->getSegmentName('otherSubresource', false)->shouldBeCalled()->willReturn('other_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_other_subresource_get_subresource' => [
'property' => 'otherSubresource',
'collection' => false,
'resource_class' => RelatedDummyEntity::class,
'shortNames' => ['relatedDummyEntity', 'dummyEntity'],
'identifiers' => [
['id', DummyEntity::class, true],
],
'route_name' => 'api_dummy_entities_other_subresource_get_subresource',
'path' => '/dummy_entities/{id}/other_subresources.{_format}',
'operation_name' => 'other_subresource_get_subresource',
] + SubresourceOperationFactory::ROUTE_OPTIONS,
], $subresourceOperationFactory->create(DummyEntity::class));
}
}

0 comments on commit 5108f76

Please sign in to comment.