Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 326 additions & 0 deletions features/hal/crud.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
Feature: Create-Retrieve-Update-Delete
In order to use an hypermedia API
As a client software developer
I need to be able to retrieve, create, update and delete JSON-LD encoded resources.

@createSchema
Scenario: Create a resource
When I add "Accept" header equal to "application/hal+json"
And I send a "POST" request to "/dummies" with body:
"""
{
"name": "My Dummy",
"dummyDate": "2015-03-01T10:00:00+00:00",
"jsonData": {
"key": [
"value1",
"value2"
]
}
}
"""
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/hal+json"
And the JSON should be equal to:
"""
{
"_links": {
"self": {
"href": "\/dummies\/1"
}
},
"description": null,
"dummy": null,
"dummyBoolean": null,
"dummyDate": "2015-03-01T10:00:00+00:00",
"dummyPrice": null,
"relatedDummy": null,
"relatedDummies": [],
"jsonData": {
"key": [
"value1",
"value2"
]
},
"name_converted": null,
"name": "My Dummy",
"alias": null
}
"""

Scenario: Get a resource
When I add "Accept" header equal to "application/hal+json"
And I send a "GET" request to "/dummies/1"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/hal+json"
And the JSON should be equal to:
"""
{
"_links": {
"self": {
"href": "\/dummies\/1"
}
},
"description": null,
"dummy": null,
"dummyBoolean": null,
"dummyDate": "2015-03-01T10:00:00+00:00",
"dummyPrice": null,
"relatedDummy": null,
"relatedDummies": [],
"jsonData": {
"key": [
"value1",
"value2"
]
},
"name_converted": null,
"name": "My Dummy",
"alias": null
}
"""

Scenario: Get a not found exception
When I send a "GET" request to "/dummies/42"
Then the response status code should be 404

Scenario: Get a collection
When I send a "GET" request to "/dummies"
Then 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"
And the JSON should be equal to:
"""
{
"@context": "/contexts/Dummy",
"@id": "/dummies",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/dummies/1",
"@type": "Dummy",
"description": null,
"dummy": null,
"dummyBoolean": null,
"dummyDate": "2015-03-01T10:00:00+00:00",
"dummyPrice": null,
"relatedDummy": null,
"relatedDummies": [],
"jsonData": {
"key": [
"value1",
"value2"
]
},
"name_converted": null,
"name": "My Dummy",
"alias": null
}
],
"hydra:totalItems": 1,
"hydra:search": {
"@type": "hydra:IriTemplate",
"hydra:template": "/dummies{?id,id[],name,alias,description,relatedDummy.name,relatedDummy.name[],relatedDummies,relatedDummies[],dummy,order[id],order[name],order[relatedDummy.symfony],dummyDate[before],dummyDate[after],relatedDummy.dummyDate[before],relatedDummy.dummyDate[after],dummyPrice[between],dummyPrice[gt],dummyPrice[gte],dummyPrice[lt],dummyPrice[lte],dummyBoolean,dummyPrice}",
"hydra:variableRepresentation": "BasicRepresentation",
"hydra:mapping": [
{
"@type": "IriTemplateMapping",
"variable": "id",
"property": "id",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "id[]",
"property": "id",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "name",
"property": "name",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "alias",
"property": "alias",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "description",
"property": "description",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummy.name",
"property": "relatedDummy.name",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummy.name[]",
"property": "relatedDummy.name",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummies",
"property": "relatedDummies",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummies[]",
"property": "relatedDummies",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummy",
"property": "dummy",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "order[id]",
"property": "id",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "order[name]",
"property": "name",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "order[relatedDummy.symfony]",
"property": "relatedDummy.symfony",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyDate[before]",
"property": "dummyDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyDate[after]",
"property": "dummyDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummy.dummyDate[before]",
"property": "relatedDummy.dummyDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "relatedDummy.dummyDate[after]",
"property": "relatedDummy.dummyDate",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice[between]",
"property": "dummyPrice",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice[gt]",
"property": "dummyPrice",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice[gte]",
"property": "dummyPrice",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice[lt]",
"property": "dummyPrice",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice[lte]",
"property": "dummyPrice",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyBoolean",
"property": "dummyBoolean",
"required": false
},
{
"@type": "IriTemplateMapping",
"variable": "dummyPrice",
"property": "dummyPrice",
"required": false
}
]
}
}
"""

Scenario: Update a resource
When I send a "PUT" request to "/dummies/1" with body:
"""
{
"@id": "/dummies/1",
"name": "A nice dummy",
"jsonData": [{
"key": "value1"
},
{
"key": "value2"
}
]
}
"""
Then 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"
And the JSON should be equal to:
"""
{
"@context": "/contexts/Dummy",
"@id": "/dummies/1",
"@type": "Dummy",
"description": null,
"dummy": null,
"dummyBoolean": null,
"dummyDate": "2015-03-01T10:00:00+00:00",
"dummyPrice": null,
"relatedDummy": null,
"relatedDummies": [],
"jsonData": [
{
"key": "value1"
},
{
"key": "value2"
}
],
"name_converted": null,
"name": "A nice dummy",
"alias": null
}
"""

@dropSchema
Scenario: Delete a resource
When I send a "DELETE" request to "/dummies/1"
Then the response status code should be 204
And the response should be empty
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Hydra\Action;
namespace ApiPlatform\Core\Action;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps ApiPlatform\Core\Hypermedia\Action?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we can get rid of the Hypermedia namespace. The entrypoint can be useful even in a non-hypermedia context (i.e raw JSON or XML).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what an "entrypoint" is supposed to be (outside of the Hydra context), actually. It's a Hydra thing. Perhaps we shouldn't try to shoehorn things...

Copy link
Member

@dunglas dunglas Jul 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HAL has a similar concept. And it's a common use case to list all resources collection exposed by the API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema.org itself contains this concept: https://schema.org/EntryPoint

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema.org itself contains this concept: https://schema.org/EntryPoint

No. That's a different thing, if you look carefully. Their naming is just confusing sometimes... 😆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand as is, an entrypoint is the /, the base of the app. The first thing you will see (even if there is authentification, you will be redirect or not allowed).


use ApiPlatform\Core\JsonLd\EntrypointBuilderInterface;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the namespace here be changed to ApiPlatform\Core\Hypermedia too?


/**
* Generates the JSON-LD API entrypoint.
* Generates the API entrypoint.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,15 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('api_platform.enable_swagger', (string) $config['enable_swagger']);
}

$this->enableJsonLd($loader);
if (isset($formats['jsonld'])) {
$loader->load('jsonld.xml');
$loader->load('hydra.xml');
}

if (isset($formats['jsonhal'])) {
$loader->load('hal.xml');
}

$this->registerAnnotationLoaders($container);
$this->registerFileLoaders($container);

Expand All @@ -116,17 +124,6 @@ public function load(array $configs, ContainerBuilder $container)
}
}

/**
* Enables JSON-LD and Hydra support.
*
* @param XmlFileLoader $loader
*/
private function enableJsonLd(XmlFileLoader $loader)
{
$loader->load('jsonld.xml');
$loader->load('hydra.xml');
}

/**
* Registers annotations loaders.
*
Expand Down
8 changes: 8 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
<argument type="service" id="api_platform.property_accessor" />
</service>

<service id="api_platform.context_builder" class="ApiPlatform\Core\Hypermedia\ContextBuilder" public="false">
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument type="service" id="api_platform.metadata.property.name_collection_factory" />
<argument type="service" id="api_platform.metadata.property.metadata_factory" />
<argument type="service" id="api_platform.router" />
</service>

<!-- Serializer -->

<service id="api_platform.serializer.context_builder" class="ApiPlatform\Core\Serializer\SerializerContextBuilder" public="false">
Expand Down
Loading