Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds jsonld context, Adds support for jsonld typed values #752

Merged
merged 1 commit into from
Sep 30, 2016
Merged
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
20 changes: 20 additions & 0 deletions features/jsonld/context.feature
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,23 @@ Feature: JSON-LD contexts generation
}
}
"""

Scenario: Retrieve Dummy with extended jsonld context
When I send a "GET" request to "/contexts/JsonldContextDummy"
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; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": {
"@vocab": "http:\/\/example.com\/doc.jsonld#",
"hydra": "http:\/\/www.w3.org\/ns\/hydra\/core#",
"person": {
"@id": "http:\/\/example.com\/id",
"@type": "@id",
"foo": "bar"
}
}
}
"""
6 changes: 6 additions & 0 deletions src/Hydra/Serializer/DocumentationNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ private function getHydraOperation(string $resourceClass, ResourceMetadata $reso
*/
private function getRange(PropertyMetadata $propertyMetadata)
{
$jsonldContext = $propertyMetadata->getAttributes()['jsonld_context'] ?? [];

if (isset($jsonldContext['@type'])) {
return $jsonldContext['@type'];
}

if (null === $type = $propertyMetadata->getType()) {
return;
}
Expand Down
11 changes: 9 additions & 2 deletions src/JsonLd/ContextBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,25 @@ public function getResourceContext(string $resourceClass, int $referenceType = U
}

$convertedName = $this->nameConverter ? $this->nameConverter->normalize($propertyName) : $propertyName;
$jsonldContext = $propertyMetadata->getAttributes()['jsonld_context'] ?? [];
Copy link
Contributor

Choose a reason for hiding this comment

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

What's set in the jsonld_context attribute should be used as the base. We don't want to limit it to just @type.

Copy link
Contributor

Choose a reason for hiding this comment

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

See for example how hydra_context is handled in

/**
* Gets and populates if applicable a Hydra operation.
*
* @param string $resourceClass
* @param ResourceMetadata $resourceMetadata
* @param string $operationName
* @param array $operation
* @param string $prefixedShortName
* @param bool $collection
*
* @return array
*/
private function getHydraOperation(string $resourceClass, ResourceMetadata $resourceMetadata, string $operationName, array $operation, string $prefixedShortName, bool $collection) : array
{
if ($collection) {
$method = $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $operationName);
} else {
$method = $this->operationMethodResolver->getItemOperationMethod($resourceClass, $operationName);
}
$hydraOperation = $operation['hydra_context'] ?? [];
$shortName = $resourceMetadata->getShortName();
if ('GET' === $method && $collection) {
$hydraOperation = [
'hydra:title' => sprintf('Retrieves the collection of %s resources.', $shortName),
'returns' => 'hydra:PagedCollection',
] + $hydraOperation;
} elseif ('GET' === $method) {
$hydraOperation = [
'hydra:title' => sprintf('Retrieves %s resource.', $shortName),
'returns' => $prefixedShortName,
] + $hydraOperation;
} elseif ('POST' === $method) {
$hydraOperation = [
'@type' => 'hydra:CreateResourceOperation',
'hydra:title' => sprintf('Creates a %s resource.', $shortName),
'returns' => $prefixedShortName,
'expects' => $prefixedShortName,
] + $hydraOperation;
} elseif ('PUT' === $method) {
$hydraOperation = [
'@type' => 'hydra:ReplaceResourceOperation',
'hydra:title' => sprintf('Replaces the %s resource.', $shortName),
'returns' => $prefixedShortName,
'expects' => $prefixedShortName,
] + $hydraOperation;
} elseif ('DELETE' === $method) {
$hydraOperation = [
'hydra:title' => sprintf('Deletes the %s resource.', $shortName),
'returns' => 'owl:Nothing',
] + $hydraOperation;
}
$hydraOperation['@type'] ?? $hydraOperation['@type'] = 'hydra:Operation';
$hydraOperation['hydra:method'] ?? $hydraOperation['hydra:method'] = $method;
if (!isset($hydraOperation['rdfs:label']) && isset($hydraOperation['hydra:title'])) {
$hydraOperation['rdfs:label'] = $hydraOperation['hydra:title'];
}
ksort($hydraOperation);
return $hydraOperation;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great input! I did not think of that, I updated the pull request.


if (!$id = $propertyMetadata->getIri()) {
$id = sprintf('%s/%s', $prefixedShortName, $convertedName);
}

if (true !== $propertyMetadata->isReadableLink()) {
$context[$convertedName] = [
$jsonldContext = $jsonldContext + [
'@id' => $id,
'@type' => '@id',
];
} else {
}

if (empty($jsonldContext)) {
$context[$convertedName] = $id;
} else {
$context[$convertedName] = $jsonldContext + [
'@id' => $id,
];
}
}

Expand Down
66 changes: 66 additions & 0 deletions tests/Fixtures/TestBundle/Entity/JsonldContextDummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;

/**
* Jsonld Context Dummy.
*
*
* @ApiResource
* @ORM\Entity
*/
class JsonldContextDummy
{
/**
* @var int The id.
*
* @ApiProperty(identifier=true)
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
* @var string The dummy person.
*
* @ApiProperty(
* attributes={
* "jsonld_context"= {
* "@id"="http://example.com/id",
* "@type"="@id",
* "foo"="bar"
* }
* },
* )
*/
private $person;

public function getId()
{
return $this->id;
}

public function setPerson($person)
{
$this->person = $person;
}

public function getPerson()
{
return $this->person;
}
}
18 changes: 17 additions & 1 deletion tests/Hydra/Serializer/DocumentationNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ public function testNormalize()
$documentation = new Documentation(new ResourceNameCollection(['dummy' => 'dummy']), $title, $desc, $version, []);

$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$propertyNameCollectionFactoryProphecy->create('dummy', [])->shouldBeCalled()->willReturn(new PropertyNameCollection(['name']));
$propertyNameCollectionFactoryProphecy->create('dummy', [])->shouldBeCalled()->willReturn(new PropertyNameCollection(['name', 'description']));

$dummyMetadata = new ResourceMetadata('dummy', 'dummy', '#dummy', ['get' => ['method' => 'GET'], 'put' => ['method' => 'PUT']], ['get' => ['method' => 'GET'], 'post' => ['method' => 'POST']], []);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$resourceMetadataFactoryProphecy->create('dummy')->shouldBeCalled()->willReturn($dummyMetadata);

$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create('dummy', 'name')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'name', true, true, true, true, false, false, null, null, []));
$propertyMetadataFactoryProphecy->create('dummy', 'description')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'description', true, true, true, true, false, false, null, null, ['jsonld_context' => ['@type' => '@id']]));

$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true);
Expand Down Expand Up @@ -126,6 +127,21 @@ public function testNormalize()
'hydra:writable' => true,
'hydra:description' => 'name',
],
1 => [
'@type' => 'hydra:SupportedProperty',
'hydra:property' => [
'@id' => '#dummy/description',
'@type' => 'rdf:Property',
'rdfs:label' => 'description',
'domain' => '#dummy',
'range' => '@id',
],
'hydra:title' => 'description',
'hydra:required' => false,
'hydra:readable' => true,
'hydra:writable' => true,
'hydra:description' => 'description',
],
],
'hydra:supportedOperation' => [
0 => [
Expand Down
104 changes: 104 additions & 0 deletions tests/JsonLd/ContextBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Tests\JsonLd;

use ApiPlatform\Core\Api\UrlGeneratorInterface;
use ApiPlatform\Core\JsonLd\ContextBuilder;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use ApiPlatform\Core\Metadata\Property\PropertyNameCollection;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use Symfony\Component\PropertyInfo\Type;

/**
* @author Markus Mächler <markus.maechler@bithost.ch>
*/
class ContextBuilderTest extends \PHPUnit_Framework_TestCase
{
private $entityClass;
private $resourceNameCollectionFactoryProphecy;
private $resourceMetadataFactoryProphecy;
private $propertyNameCollectionFactoryProphecy;
private $propertyMetadataFactoryProphecy;
private $urlGeneratorProphecy;

public function setUp()
{
$this->entityClass = '\Dummy\DummyEntity';
$this->resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
$this->resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$this->propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$this->propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$this->urlGeneratorProphecy = $this->prophesize(UrlGeneratorInterface::class);
}

public function testResourceContext()
{
$this->resourceMetadataFactoryProphecy->create($this->entityClass)->willReturn(new ResourceMetadata('DummyEntity'));
$this->propertyNameCollectionFactoryProphecy->create($this->entityClass)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
$this->propertyMetadataFactoryProphecy->create($this->entityClass, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, []));

$contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal());

$expected = [
'@vocab' => '#',
'hydra' => 'http://www.w3.org/ns/hydra/core#',
'dummyPropertyA' => '#DummyEntity/dummyPropertyA',
];

$this->assertEquals($expected, $contextBuilder->getResourceContext($this->entityClass));
}

public function testResourceContextWithJsonldContext()
{
$this->resourceMetadataFactoryProphecy->create($this->entityClass)->willReturn(new ResourceMetadata('DummyEntity'));
$this->propertyNameCollectionFactoryProphecy->create($this->entityClass)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
$this->propertyMetadataFactoryProphecy->create($this->entityClass, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, ['jsonld_context' => ['@type' => '@id', '@id' => 'customId', 'foo' => 'bar']]));

$contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal());

$expected = [
'@vocab' => '#',
'hydra' => 'http://www.w3.org/ns/hydra/core#',
'dummyPropertyA' => [
'@type' => '@id',
'@id' => 'customId',
'foo' => 'bar',
],
];

$this->assertEquals($expected, $contextBuilder->getResourceContext($this->entityClass));
}

public function testResourceContextWithReverse()
{
$this->resourceMetadataFactoryProphecy->create($this->entityClass)->willReturn(new ResourceMetadata('DummyEntity'));
$this->propertyNameCollectionFactoryProphecy->create($this->entityClass)->willReturn(new PropertyNameCollection(['dummyPropertyA']));
$this->propertyMetadataFactoryProphecy->create($this->entityClass, 'dummyPropertyA')->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'Dummy property A', true, true, true, true, false, false, null, null, ['jsonld_context' => ['@reverse' => 'parent']]));

$contextBuilder = new ContextBuilder($this->resourceNameCollectionFactoryProphecy->reveal(), $this->resourceMetadataFactoryProphecy->reveal(), $this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->urlGeneratorProphecy->reveal());

$expected = [
'@vocab' => '#',
'hydra' => 'http://www.w3.org/ns/hydra/core#',
'dummyPropertyA' => [
'@id' => '#DummyEntity/dummyPropertyA',
'@reverse' => 'parent'
],
];

$this->assertEquals($expected, $contextBuilder->getResourceContext($this->entityClass));
}
}