Skip to content

Commit 9223ebb

Browse files
committed
Allow custom operations to return a different class than the expected resource class
1 parent 020bbc7 commit 9223ebb

File tree

19 files changed

+402
-41
lines changed

19 files changed

+402
-41
lines changed

features/main/custom_operation.feature

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ Feature: Custom operation
22
As a client software developer
33
I need to be able to create custom operations
44

5+
Background:
6+
Given I add "Accept" header equal to "application/ld+json"
7+
And I add "Content-Type" header equal to "application/ld+json"
8+
59
@createSchema
610
Scenario: Custom normalization operation
711
When I send a "POST" request to "/custom/denormalization"
8-
And I add "Content-Type" header equal to "application/ld+json"
912
Then the JSON should be equal to:
1013
"""
1114
{
@@ -29,7 +32,6 @@ Feature: Custom operation
2932

3033
Scenario: Custom normalization operation with shorthand configuration
3134
When I send a "POST" request to "/short_custom/denormalization"
32-
And I add "Content-Type" header equal to "application/ld+json"
3335
Then the JSON should be equal to:
3436
"""
3537
{
@@ -68,3 +70,57 @@ Feature: Custom operation
6870
"foo": "custom!"
6971
}
7072
"""
73+
74+
Scenario: Create a payment
75+
When I send a "POST" request to "/payments" with body:
76+
"""
77+
{
78+
"amount": "123.45"
79+
}
80+
"""
81+
Then the response status code should be 201
82+
And the response should be in JSON
83+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
84+
And the JSON should be equal to:
85+
"""
86+
{
87+
"@context": "/contexts/Payment",
88+
"@id": "/payments/1",
89+
"@type": "Payment",
90+
"id": 1,
91+
"amount": "123.45",
92+
"voidPayment": null
93+
}
94+
"""
95+
96+
Scenario: Void a payment
97+
When I send a "POST" request to "/payments/1/void"
98+
Then the response status code should be 201
99+
And the response should be in JSON
100+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
101+
And the JSON should be equal to:
102+
"""
103+
{
104+
"@context": "/contexts/VoidPayment",
105+
"@id": "/void_payments/1",
106+
"@type": "VoidPayment",
107+
"id": 1,
108+
"payment": "/payments/1"
109+
}
110+
"""
111+
112+
Scenario: Get a void payment
113+
When I send a "GET" request to "/void_payments/1"
114+
Then the response status code should be 200
115+
And the response should be in JSON
116+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
117+
And the JSON should be equal to:
118+
"""
119+
{
120+
"@context": "/contexts/VoidPayment",
121+
"@id": "/void_payments/1",
122+
"@type": "VoidPayment",
123+
"id": 1,
124+
"payment": "/payments/1"
125+
}
126+
"""

src/Hal/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function normalize($object, $format = null, array $context = [])
5656
$context['cache_key'] = $this->getHalCacheKey($format, $context);
5757
}
5858

59-
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
59+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null);
6060
$context = $this->initContext($resourceClass, $context);
6161
$iri = $this->iriConverter->getIriFromItem($object);
6262
$context['iri'] = $iri;

src/JsonApi/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function normalize($object, $format = null, array $context = [])
7474
$context['cache_key'] = $this->getJsonApiCacheKey($format, $context);
7575
}
7676

77-
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
77+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null);
7878
$context = $this->initContext($resourceClass, $context);
7979
$iri = $this->iriConverter->getIriFromItem($object);
8080
$context['iri'] = $iri;

src/JsonLd/Serializer/ItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function normalize($object, $format = null, array $context = [])
6969
return parent::normalize($object, $format, $context);
7070
}
7171

72-
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
72+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null);
7373
$context = $this->initContext($resourceClass, $context);
7474
$iri = $this->iriConverter->getIriFromItem($object);
7575
$context['iri'] = $iri;

src/Serializer/AbstractItemNormalizer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public function normalize($object, $format = null, array $context = [])
125125
return $this->serializer->normalize($transformed, $format, $context);
126126
}
127127

128-
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, isset($context['resource_class']));
128+
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null);
129129
$context = $this->initContext($resourceClass, $context);
130130
$iri = $context['iri'] ?? $this->iriConverter->getIriFromItem($object);
131131
$context['iri'] = $iri;
@@ -542,7 +542,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array
542542
($className = $type->getClassName()) &&
543543
$this->resourceClassResolver->isResourceClass($className)
544544
) {
545-
$resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className, true);
545+
$resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className);
546546
$childContext = $this->createChildContext($context, $attribute);
547547
$childContext['resource_class'] = $resourceClass;
548548

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Controller\Payment;
15+
16+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\Payment as PaymentDocument;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Payment;
18+
19+
final class VoidPaymentAction
20+
{
21+
public function __invoke($data)
22+
{
23+
if (!$data instanceof Payment && !$data instanceof PaymentDocument) {
24+
throw new \InvalidArgumentException();
25+
}
26+
$payment = $data;
27+
28+
$payment->void();
29+
30+
return $payment->getVoidPayment();
31+
}
32+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Document;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Controller\Payment\VoidPaymentAction;
18+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
19+
20+
/**
21+
* @ODM\Document
22+
*
23+
* @ApiResource(
24+
* itemOperations={
25+
* "get",
26+
* "post_void"={
27+
* "method"="POST",
28+
* "path"="/payments/{id}/void",
29+
* "controller"=VoidPaymentAction::class,
30+
* "deserialize"=false,
31+
* },
32+
* },
33+
* )
34+
*/
35+
class Payment
36+
{
37+
/**
38+
* @var int|null
39+
*
40+
* @ODM\Id(strategy="INCREMENT", type="integer")
41+
*/
42+
private $id;
43+
44+
/**
45+
* @var string
46+
*/
47+
private $amount;
48+
49+
/**
50+
* @ODM\ReferenceOne(targetDocument=VoidPayment::class, mappedBy="payment")
51+
*/
52+
private $voidPayment;
53+
54+
public function __construct(string $amount)
55+
{
56+
$this->amount = $amount;
57+
}
58+
59+
public function getId(): ?int
60+
{
61+
return $this->id;
62+
}
63+
64+
public function getAmount(): string
65+
{
66+
return $this->amount;
67+
}
68+
69+
public function void(): void
70+
{
71+
if (null !== $this->voidPayment) {
72+
return;
73+
}
74+
75+
$this->voidPayment = new VoidPayment($this);
76+
}
77+
78+
public function getVoidPayment(): ?VoidPayment
79+
{
80+
return $this->voidPayment;
81+
}
82+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Document;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
18+
19+
/**
20+
* @ODM\Document
21+
*
22+
* @ApiResource
23+
*/
24+
class VoidPayment
25+
{
26+
/**
27+
* @var int|null
28+
*
29+
* @ODM\Id(strategy="INCREMENT", type="integer")
30+
*/
31+
private $id;
32+
33+
/**
34+
* @ODM\ReferenceOne(targetDocument=Payment::class, inversedBy="voidPayment")
35+
*/
36+
private $payment;
37+
38+
public function __construct(Payment $payment)
39+
{
40+
$this->payment = $payment;
41+
}
42+
43+
public function getId(): ?int
44+
{
45+
return $this->id;
46+
}
47+
48+
public function getPayment(): Payment
49+
{
50+
return $this->payment;
51+
}
52+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Controller\Payment\VoidPaymentAction;
18+
use Doctrine\ORM\Mapping as ORM;
19+
20+
/**
21+
* @ORM\Entity
22+
*
23+
* @ApiResource(
24+
* itemOperations={
25+
* "get",
26+
* "post_void"={
27+
* "method"="POST",
28+
* "path"="/payments/{id}/void",
29+
* "controller"=VoidPaymentAction::class,
30+
* "deserialize"=false,
31+
* },
32+
* },
33+
* )
34+
*/
35+
class Payment
36+
{
37+
/**
38+
* @var int|null
39+
*
40+
* @ORM\Column(type="integer")
41+
* @ORM\Id
42+
* @ORM\GeneratedValue(strategy="AUTO")
43+
*/
44+
private $id;
45+
46+
/**
47+
* @var string
48+
*
49+
* @ORM\Column(type="decimal", precision=6, scale=2)
50+
*/
51+
private $amount;
52+
53+
/**
54+
* @ORM\OneToOne(targetEntity=VoidPayment::class, mappedBy="payment")
55+
*/
56+
private $voidPayment;
57+
58+
public function __construct(string $amount)
59+
{
60+
$this->amount = $amount;
61+
}
62+
63+
public function getId(): ?int
64+
{
65+
return $this->id;
66+
}
67+
68+
public function getAmount(): string
69+
{
70+
return $this->amount;
71+
}
72+
73+
public function void(): void
74+
{
75+
if (null !== $this->voidPayment) {
76+
return;
77+
}
78+
79+
$this->voidPayment = new VoidPayment($this);
80+
}
81+
82+
public function getVoidPayment(): ?VoidPayment
83+
{
84+
return $this->voidPayment;
85+
}
86+
}

0 commit comments

Comments
 (0)