Skip to content

Commit

Permalink
Merge pull request #877 from Simperfit/fix/857-embedded
Browse files Browse the repository at this point in the history
fix #857 - fix on put when both @id and id are present
  • Loading branch information
dunglas authored Jan 25, 2017
2 parents f9707ec + 137a36e commit 01303fa
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 6 deletions.
60 changes: 59 additions & 1 deletion features/main/custom_normalized.feature
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,67 @@ Feature: Using custom normalized entity
"@context": "/contexts/CustomNormalizedDummy",
"@id": "/custom_normalized_dummies/1",
"@type": "CustomNormalizedDummy",
"id": 1,
"name": "My Dummy",
"alias": "My alias"
}
"""

Scenario: Get a resource
Scenario: Create a resource with a custom normalized dummy
When I add "Content-Type" header equal to "application/json"
When I add "Accept" header equal to "application/json"
And I send a "POST" request to "/related_normalized_dummies" with body:
"""
{
"name": "My 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/json; charset=utf-8"
And the JSON should be equal to:
"""
{
"id": 1,
"name": "My Dummy",
"customNormalizedDummy": []
}
"""

Scenario: Create a resource with a custom normalized dummy and an id
When I add "Content-Type" header equal to "application/json"
When I add "Accept" header equal to "application/json"
And I send a "PUT" request to "/related_normalized_dummies/1" with body:
"""
{
"name": "My Dummy",
"customNormalizedDummy":[{
"@context": "/contexts/CustomNormalizedDummy",
"@id": "/custom_normalized_dummies/1",
"@type": "CustomNormalizedDummy",
"id": 1,
"name": "My Dummy"
}]
}
"""
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/json; charset=utf-8"
And the JSON should be equal to:
"""
{
"id": 1,
"name": "My Dummy",
"customNormalizedDummy":[{
"id": 1,
"name": "My Dummy",
"alias": "My alias"
}]
}
"""


Scenario: Get a custom normalized dummy resource
When I send a "GET" request to "/custom_normalized_dummies/1"
Then the response status code should be 200
And the response should be in JSON
Expand All @@ -38,6 +93,7 @@ Feature: Using custom normalized entity
"@context": "/contexts/CustomNormalizedDummy",
"@id": "/custom_normalized_dummies/1",
"@type": "CustomNormalizedDummy",
"id": 1,
"name": "My Dummy",
"alias": "My alias"
}
Expand All @@ -58,6 +114,7 @@ Feature: Using custom normalized entity
{
"@id": "/custom_normalized_dummies/1",
"@type": "CustomNormalizedDummy",
"id": 1,
"name": "My Dummy",
"alias": "My alias"
}
Expand All @@ -83,6 +140,7 @@ Feature: Using custom normalized entity
"@context": "/contexts/CustomNormalizedDummy",
"@id": "/custom_normalized_dummies/1",
"@type": "CustomNormalizedDummy",
"id": 1,
"name": "My Dummy modified",
"alias": "My alias"
}
Expand Down
23 changes: 22 additions & 1 deletion src/Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,30 @@ public function denormalize($data, $class, $format = null, array $context = [])
throw new InvalidArgumentException('Update is not allowed for this operation.');
}

$context['object_to_populate'] = $this->iriConverter->getItemFromIri($data['id'], $context + ['fetch_data' => false]);
$this->updateObjectToPopulate($data, $context);
}

return parent::denormalize($data, $class, $format, $context);
}

private function updateObjectToPopulate(array $data, array &$context)
{
try {
$context['object_to_populate'] = $this->iriConverter->getItemFromIri($data['id'], $context + ['fetch_data' => false]);
} catch (InvalidArgumentException $e) {
$identifier = null;
foreach ($this->propertyNameCollectionFactory->create($context['resource_class'], $context) as $propertyName) {
if (true === $this->propertyMetadataFactory->create($context['resource_class'], $propertyName)->isIdentifier()) {
$identifier = $propertyName;
break;
}
}

if (null === $identifier) {
throw $e;
}

$context['object_to_populate'] = $this->iriConverter->getItemFromIri(sprintf('%s/%s', $this->iriConverter->getIriFromResourceClass($context['resource_class']), $data[$identifier]), $context + ['fetch_data' => false]);
}
}
}
1 change: 1 addition & 0 deletions tests/Fixtures/TestBundle/Entity/CustomNormalizedDummy.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CustomNormalizedDummy
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"input", "output"})
*/
private $id;

Expand Down
6 changes: 3 additions & 3 deletions tests/Fixtures/TestBundle/Entity/DummyFriend.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class DummyFriend
/**
* Get id.
*
* @return id
* @return int
*/
public function getId()
{
Expand All @@ -69,7 +69,7 @@ public function setId($id)
/**
* Get name.
*
* @return name
* @return string
*/
public function getName()
{
Expand All @@ -79,7 +79,7 @@ public function getName()
/**
* Set name.
*
* @param name the value to set
* @param string the value to set
*/
public function setName($name)
{
Expand Down
100 changes: 100 additions & 0 deletions tests/Fixtures/TestBundle/Entity/RelatedNormalizedDummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?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\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
* Related to Normalized Dummy.
*
* @author Amrouche Hamza <hamza.simperfit@gmail.com>
*
* @ApiResource(attributes={
* "normalization_context"={"groups"={"related_output", "output"}},
* "denormalization_context"={"groups"={"related_input", "input"}}
* })
* @ORM\Entity
*/
class RelatedNormalizedDummy
{
/**
* @var int The id
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"related_output", "related_input"})
*/
private $id;

/**
* @var string The dummy name
*
* @ORM\Column
* @Assert\NotBlank
* @ApiProperty(iri="http://schema.org/name")
* @Groups({"related_output", "related_input"})
*/
private $name;

/**
* @var ArrayCollection Several Normalized dummies
*
* @ORM\ManyToMany(targetEntity="CustomNormalizedDummy")
* @Groups({"related_output", "related_input"})
*/
public $customNormalizedDummy;

public function __construct()
{
$this->customNormalizedDummy = new ArrayCollection();
}

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

/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}

public function getName(): string
{
return $this->name;
}

/**
* @return ArrayCollection
*/
public function getCustomNormalizedDummy()
{
return $this->customNormalizedDummy;
}

/**
* @param ArrayCollection $customNormalizedDummy
*/
public function setCustomNormalizedDummy($customNormalizedDummy)
{
$this->customNormalizedDummy = $customNormalizedDummy;
}
}
33 changes: 32 additions & 1 deletion tests/Serializer/ItemNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,37 @@ public function testDenormalize()
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['name' => 'hello'], Dummy::class, null, $context));
}

public function testDenormalizeWithIri()
{
$context = ['resource_class' => Dummy::class, 'api_allow_update' => true];

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

$propertyMetadataFactory = new PropertyMetadata(null, null, true);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadataFactory)->shouldBeCalled();

$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
$iriConverterProphecy->getItemFromIri('/dummies/12', ['resource_class' => Dummy::class, 'api_allow_update' => true, 'fetch_data' => false])->shouldBeCalled();

$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);

$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy->willImplement(DenormalizerInterface::class);

$normalizer = new ItemNormalizer(
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
$iriConverterProphecy->reveal(),
$resourceClassResolverProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());

$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['id' => '/dummies/12', 'name' => 'hello'], Dummy::class, null, $context));
}

/**
* @expectedException \ApiPlatform\Core\Exception\InvalidArgumentException
* @expectedExceptionMessage Update is not allowed for this operation.
Expand All @@ -151,6 +182,6 @@ public function testDenormalizeWithIdAndUpdateNotAllowed()
$resourceClassResolverProphecy->reveal()
);
$normalizer->setSerializer($serializerProphecy->reveal());
$normalizer->denormalize(['id' => '/dummies/12', 'name' => 'hello'], Dummy::class, null, $context);
$normalizer->denormalize(['id' => '12', 'name' => 'hello'], Dummy::class, null, $context);
}
}

0 comments on commit 01303fa

Please sign in to comment.