Skip to content

Commit

Permalink
Add and fix tests about relation inheritance
Browse files Browse the repository at this point in the history
Previous commits fix the behavior with non api resources inherited from
resource but relation management was still not ok since the update was
not done on the abstract item normalizer.

The resource class resolver is now used as it should in every
normalizers.

Most of this commit (tests) comes from @vincentchalamon and its PR #2760
  • Loading branch information
Nek- committed Apr 29, 2019
1 parent f1bcbe4 commit af70ddf
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 21 deletions.
40 changes: 40 additions & 0 deletions features/bootstrap/DoctrineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -1561,4 +1561,44 @@ public function testEagerLoadingNotDuplicateRelation()
$this->manager->flush();
$this->manager->clear();
}

/**
* @Given there are :nb sites with internal owner
*/
public function thereAreSitesWithInternalOwner(int $nb)
{
for ($i = 1; $i <= $nb; ++$i) {
$internalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InternalUser();
$internalUser->setFirstname('Internal');
$internalUser->setLastname('User');
$internalUser->setEmail('john.doe@example.com');
$internalUser->setInternalId('INT');
$site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
$site->setTitle('title');
$site->setDescription('description');
$site->setOwner($internalUser);
$this->manager->persist($site);
}
$this->manager->flush();
}

/**
* @Given there are :nb sites with external owner
*/
public function thereAreSitesWithExternalOwner(int $nb)
{
for ($i = 1; $i <= $nb; ++$i) {
$externalUser = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ExternalUser();
$externalUser->setFirstname('External');
$externalUser->setLastname('User');
$externalUser->setEmail('john.doe@example.com');
$externalUser->setExternalId('EXT');
$site = new \ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Site();
$site->setTitle('title');
$site->setDescription('description');
$site->setOwner($externalUser);
$this->manager->persist($site);
}
$this->manager->flush();
}
}
115 changes: 115 additions & 0 deletions features/main/table_inheritance.feature
Original file line number Diff line number Diff line change
Expand Up @@ -446,3 +446,118 @@ Feature: Table inheritance
}
}
"""

@!mongodb
@createSchema
@dropSchema
Scenario: Generate iri from parent resource
Given there are 3 sites with internal owner
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/sites"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@type": {
"type": "string",
"pattern": "^Site$",
"required": "true"
},
"@id": {
"type": "string",
"pattern": "^/sites/\\d+$",
"required": "true"
},
"id": {
"type": "integer",
"required": "true"
},
"title": {
"type": "string",
"required": "true"
},
"description": {
"type": "string",
"required": "true"
},
"owner": {
"type": "string",
"pattern": "^/custom_users/\\d+$",
"required": "true"
}
}
},
"minItems": 3,
"maxItems": 3,
"required": "true"
}
}
}
"""

@!mongodb
@createSchema
Scenario: Generate iri from current resource even if parent class is a resource
Given there are 3 sites with external owner
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/sites"
And print last JSON response
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 print last JSON response
And the JSON should be valid according to this schema:
"""
{
"type": "object",
"properties": {
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@type": {
"type": "string",
"pattern": "^Site$",
"required": "true"
},
"@id": {
"type": "string",
"pattern": "^/sites/\\d+$",
"required": "true"
},
"id": {
"type": "integer",
"required": "true"
},
"title": {
"type": "string",
"required": "true"
},
"description": {
"type": "string",
"required": "true"
},
"owner": {
"type": "string",
"pattern": "^/external_users/\\d+$",
"required": "true"
}
}
},
"minItems": 3,
"maxItems": 3,
"required": "true"
}
}
}
"""
11 changes: 7 additions & 4 deletions src/Serializer/AbstractItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public function normalize($object, $format = null, array $context = [])
// Use resolved resource class instead of given resource class to support multiple inheritance child types
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true);
$context = $this->initContext($resourceClass, $context);
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItem($object);
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItemWithResource($object, $resourceClass);
$context['iri'] = $iri;
$context['api_normalize'] = true;

Expand Down Expand Up @@ -546,15 +546,17 @@ protected function getAttributeValue($object, $attribute, $format = null, array
$type->isCollection() &&
($collectionValueType = $type->getCollectionValueType()) &&
($className = $collectionValueType->getClassName()) &&
$this->resourceClassResolver->isResourceClass($className)
$this->resourceClassResolver->isResourceClass($className) &&
($className = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true))
) {
return $this->normalizeCollectionOfRelations($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
}

if (
$type &&
($className = $type->getClassName()) &&
$this->resourceClassResolver->isResourceClass($className)
$this->resourceClassResolver->isResourceClass($className) &&
($className = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true))
) {
return $this->normalizeRelation($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute));
}
Expand Down Expand Up @@ -606,7 +608,8 @@ protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relate
return $this->serializer->normalize($relatedObject, $format, $context);
}

$iri = $this->iriConverter->getIriFromItem($relatedObject);
$iri = $this->iriConverter->getIriFromItemWithResource($relatedObject, $resourceClass);

if (isset($context['resources'])) {
$context['resources'][$iri] = $iri;
}
Expand Down
92 changes: 92 additions & 0 deletions tests/Fixtures/TestBundle/Entity/AbstractUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?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.
*/

declare(strict_types=1);

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

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

/**
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ApiResource(
* collectionOperations={
* "get"={"path"="/custom_users"}
* },
* itemOperations={
* "get"={"path"="/custom_users/{id}"}
* }
* )
*/
abstract class AbstractUser
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column
*/
private $firstname;
/**
* @ORM\Column
*/
private $lastname;
/**
* @ORM\Column
*/
private $email;

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

public function getFirstname(): ?string
{
return $this->firstname;
}

public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;

return $this;
}

public function getLastname(): ?string
{
return $this->lastname;
}

public function setLastname(string $lastname): self
{
$this->lastname = $lastname;

return $this;
}

public function getEmail(): ?string
{
return $this->email;
}

public function setEmail(string $email): self
{
$this->email = $email;

return $this;
}
}
41 changes: 41 additions & 0 deletions tests/Fixtures/TestBundle/Entity/ExternalUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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.
*/

declare(strict_types=1);

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

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

/**
* @ORM\Entity
* @ApiResource
*/
class ExternalUser extends AbstractUser
{
/**
* @ORM\Column
*/
private $externalId;

public function getExternalId(): ?string
{
return $this->externalId;
}

public function setExternalId(string $externalId): self
{
$this->externalId = $externalId;

return $this;
}
}
39 changes: 39 additions & 0 deletions tests/Fixtures/TestBundle/Entity/InternalUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?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.
*/

declare(strict_types=1);

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

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
*/
class InternalUser extends AbstractUser
{
/**
* @ORM\Column
*/
private $internalId;

public function getInternalId(): ?string
{
return $this->internalId;
}

public function setInternalId(string $internalId): self
{
$this->internalId = $internalId;

return $this;
}
}
Loading

0 comments on commit af70ddf

Please sign in to comment.