Skip to content

Commit

Permalink
Merge 5a5cdd2 into 90f422f
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentchalamon committed Apr 8, 2019
2 parents 90f422f + 5a5cdd2 commit c4370b3
Show file tree
Hide file tree
Showing 18 changed files with 435 additions and 16 deletions.
267 changes: 267 additions & 0 deletions features/main/subresource.feature
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,273 @@ Feature: Subresource support
}
"""

@createSchema
Scenario: Create an entry on a subresource collection
Given there is a dummy object with a fourth level relation
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummies/1/related_dummies" with body:
"""
{
"name": "New related dummy"
}
"""
Then the response status code should be 201
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
Then I send a "GET" request to "/dummies/1/related_dummies"
And the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/RelatedDummy",
"@id": "/dummies/1/related_dummies",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/related_dummies/1",
"@type": "https://schema.org/Product",
"id": 1,
"name": "Hello",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": {
"@id": "/third_levels/1",
"@type": "ThirdLevel",
"fourthLevel": "/fourth_levels/1"
},
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
},
{
"@id": "/related_dummies/2",
"@type": "https://schema.org/Product",
"id": 2,
"name": null,
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": {
"@id": "/third_levels/1",
"@type": "ThirdLevel",
"fourthLevel": "/fourth_levels/1"
},
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
},
{
"@id": "/related_dummies/3",
"@type": "https://schema.org/Product",
"id": 3,
"name": "New related dummy",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": null,
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
}
],
"hydra:totalItems": 3,
"hydra:search": {
"@type": "hydra:IriTemplate",
"hydra:template": "/dummies/1/related_dummies{?relatedToDummyFriend.dummyFriend,relatedToDummyFriend.dummyFriend[],name}",
"hydra:variableRepresentation": "BasicRepresentation",
"hydra:mapping": [
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend[]",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "name",
"property": "name",
"required": false
}
]
}
}
"""

@createSchema
Scenario: Add an existing entry on a subresource collection
Given there is a dummy object with a fourth level relation
And there is a RelatedDummy with 0 friends
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummies/1/related_dummies" with body:
"""
{
"@id": "/related_dummies/3"
}
"""
Then the response status code should be 201
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
Then I send a "GET" request to "/dummies/1/related_dummies"
And the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/RelatedDummy",
"@id": "/dummies/1/related_dummies",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/related_dummies/1",
"@type": "https://schema.org/Product",
"id": 1,
"name": "Hello",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": {
"@id": "/third_levels/1",
"@type": "ThirdLevel",
"fourthLevel": "/fourth_levels/1"
},
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
},
{
"@id": "/related_dummies/2",
"@type": "https://schema.org/Product",
"id": 2,
"name": null,
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": {
"@id": "/third_levels/1",
"@type": "ThirdLevel",
"fourthLevel": "/fourth_levels/1"
},
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
},
{
"@id": "/related_dummies/3",
"@type": "https://schema.org/Product",
"id": 3,
"name": "RelatedDummy with friends",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": null,
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
}
],
"hydra:totalItems": 3,
"hydra:search": {
"@type": "hydra:IriTemplate",
"hydra:template": "/dummies/1/related_dummies{?relatedToDummyFriend.dummyFriend,relatedToDummyFriend.dummyFriend[],name}",
"hydra:variableRepresentation": "BasicRepresentation",
"hydra:mapping": [
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend[]",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "name",
"property": "name",
"required": false
}
]
}
}
"""

@createSchema
Scenario: Delete an entry on a subresource collection
Given there is a dummy object with a fourth level relation
When I send a "DELETE" request to "/dummies/1/related_dummies/2"
Then the response status code should be 204
And the response should be empty
And I send a "GET" request to "/dummies/1/related_dummies"
And the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/RelatedDummy",
"@id": "/dummies/1/related_dummies",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/related_dummies/1",
"@type": "https://schema.org/Product",
"id": 1,
"name": "Hello",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": {
"@id": "/third_levels/1",
"@type": "ThirdLevel",
"fourthLevel": "/fourth_levels/1"
},
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
}
],
"hydra:totalItems": 1,
"hydra:search": {
"@type": "hydra:IriTemplate",
"hydra:template": "/dummies/1/related_dummies{?relatedToDummyFriend.dummyFriend,relatedToDummyFriend.dummyFriend[],name}",
"hydra:variableRepresentation": "BasicRepresentation",
"hydra:mapping": [
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedToDummyFriend.dummyFriend[]",
"property": "relatedToDummyFriend.dummyFriend",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "name",
"property": "name",
"required": false
}
]
}
}
"""

Scenario: Get offers subresource from aggregate offers subresource
Given I have a product with offers
When I send a "GET" request to "/dummy_products/2/offers/1/offers"
Expand Down
15 changes: 15 additions & 0 deletions src/Annotation/ApiSubresource.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,26 @@
*
* @Annotation
* @Target({"METHOD", "PROPERTY"})
* @Attributes(
* @Attribute("maxDepth", type="int"),
* @Attribute("postEnabled", type="bool"),
* @Attribute("deleteEnabled", type="bool")
* )
*/
final class ApiSubresource
{
/**
* @var int
*/
public $maxDepth;

/**
* @var bool
*/
public $postEnabled = true;

/**
* @var bool
*/
public $deleteEnabled = true;
}
19 changes: 18 additions & 1 deletion src/Bridge/Doctrine/Common/DataPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager as DoctrineObjectManager;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;

/**
* Data persister for Doctrine.
Expand All @@ -29,10 +31,12 @@ final class DataPersister implements ContextAwareDataPersisterInterface
use ClassInfoTrait;

private $managerRegistry;
private $propertyAccessor;

public function __construct(ManagerRegistry $managerRegistry)
public function __construct(ManagerRegistry $managerRegistry, PropertyAccessorInterface $propertyAccessor = null)
{
$this->managerRegistry = $managerRegistry;
$this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
}

/**
Expand Down Expand Up @@ -75,6 +79,19 @@ public function remove($data, array $context = [])
$manager->flush();
}

/**
* {@inheritdoc}
*/
public function removeElementFromCollection($data, array $context = [])
{
if (!$manager = $this->getManager($data)) {
return;
}

$context['parentData']->removeRelatedDummy($data);
$this->persist($context['parentData'], $context);
}

/**
* Gets the Doctrine object manager associated with given data.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ public function remove($data, array $context = [])
}
}

/**
* {@inheritdoc}
*/
public function removeElementFromCollection($data, array $context = [])
{
if ($match = $this->tracePersisters($data, $context)) {
return $match->removeElementFromCollection($data, $context);
}
}

private function tracePersisters($data, array $context = [])
{
$match = null;
Expand Down
2 changes: 2 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@
<service id="api_platform.action.put_item" alias="api_platform.action.placeholder" public="true" />
<service id="api_platform.action.delete_item" alias="api_platform.action.placeholder" public="true" />
<service id="api_platform.action.get_subresource" alias="api_platform.action.placeholder" public="true" />
<service id="api_platform.action.post_subresource" alias="api_platform.action.placeholder" public="true" />
<service id="api_platform.action.delete_subresource" alias="api_platform.action.placeholder" public="true" />

<service id="api_platform.action.entrypoint" class="ApiPlatform\Core\Action\EntrypointAction" public="true">
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />
Expand Down
8 changes: 8 additions & 0 deletions src/Bridge/Symfony/Messenger/DataPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,12 @@ public function remove($data, array $context = [])
{
$this->messageBus->dispatch(new Envelope($data, new RemoveStamp()));
}

/**
* {@inheritdoc}
*/
public function removeElementFromCollection($data, array $context = [])
{
$this->messageBus->dispatch(new Envelope($data, new RemoveStamp()));
}
}
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Routing/ApiLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public function load($data, $type = null): RouteCollection
$operation['options'] ?? [],
$operation['host'] ?? '',
$operation['schemes'] ?? [],
['GET'],
[$operation['method'] ?? 'GET'],
$operation['condition'] ?? ''
));
}
Expand Down
Loading

0 comments on commit c4370b3

Please sign in to comment.