Skip to content

Commit 59fa1e2

Browse files
committed
Merge branch '2.5'
2 parents 81d276c + 78478a7 commit 59fa1e2

File tree

14 files changed

+338
-29
lines changed

14 files changed

+338
-29
lines changed

features/main/patch.feature

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Feature: Sending PATCH requets
2+
As a client software developer
3+
I need to be able to send partial updates
4+
5+
@createSchema
6+
Scenario: Detect accepted patch formats
7+
Given I add "Content-Type" header equal to "application/ld+json"
8+
And I send a "POST" request to "/patch_dummies" with body:
9+
"""
10+
{"name": "Hello"}
11+
"""
12+
When I add "Content-Type" header equal to "application/ld+json"
13+
And I send a "GET" request to "/patch_dummies/1"
14+
Then the header "Accept-Patch" should be equal to "application/merge-patch+json, application/vnd.api+json"
15+
16+
Scenario: Patch an item
17+
When I add "Content-Type" header equal to "application/merge-patch+json"
18+
And I send a "PATCH" request to "/patch_dummies/1" with body:
19+
"""
20+
{"name": "Patched"}
21+
"""
22+
Then the JSON node "name" should contain "Patched"
23+
24+
Scenario: Remove a property according to RFC 7386
25+
When I add "Content-Type" header equal to "application/merge-patch+json"
26+
And I send a "PATCH" request to "/patch_dummies/1" with body:
27+
"""
28+
{"name": null}
29+
"""
30+
Then the JSON node "name" should not exist

src/Annotation/ApiResource.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
* @Attribute("input", type="mixed"),
4646
* @Attribute("iri", type="string"),
4747
* @Attribute("itemOperations", type="array"),
48-
* @Attribute("maximumItemsPerPage", type="int"),
4948
* @Attribute("mercure", type="mixed"),
5049
* @Attribute("messenger", type="mixed"),
5150
* @Attribute("normalizationContext", type="array"),
@@ -58,6 +57,8 @@
5857
* @Attribute("paginationEnabled", type="bool"),
5958
* @Attribute("paginationFetchJoinCollection", type="bool"),
6059
* @Attribute("paginationItemsPerPage", type="int"),
60+
* @Attribute("maximumItemsPerPage", type="int"),
61+
* @Attribute("paginationMaximumItemsPerPage", type="int"),
6162
* @Attribute("paginationPartial", type="bool"),
6263
* @Attribute("paginationViaCursor", type="array"),
6364
* @Attribute("routePrefix", type="string"),
@@ -198,13 +199,6 @@ final class ApiResource
198199
*/
199200
private $hydraContext;
200201

201-
/**
202-
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
203-
*
204-
* @var int
205-
*/
206-
private $maximumItemsPerPage;
207-
208202
/**
209203
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
210204
*
@@ -275,6 +269,22 @@ final class ApiResource
275269
*/
276270
private $paginationItemsPerPage;
277271

272+
/**
273+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
274+
*
275+
* @var int
276+
*
277+
* @deprecated - Use $paginationMaximumItemsPerPage instead
278+
*/
279+
private $maximumItemsPerPage;
280+
281+
/**
282+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
283+
*
284+
* @var int
285+
*/
286+
private $paginationMaximumItemsPerPage;
287+
278288
/**
279289
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
280290
*

src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,14 @@ private function getPagination(QueryBuilder $queryBuilder, string $resourceClass
218218
}
219219

220220
if ($resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_items_per_page', $this->clientItemsPerPage, true)) {
221-
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', $this->maximumItemPerPage, true);
221+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', null, true);
222+
223+
if (null !== $maxItemsPerPage) {
224+
@trigger_error('The "maximum_items_per_page" option has been deprecated since API Platform 2.5 in favor of "pagination_maximum_items_per_page" and will be removed in API Platform 3.', E_USER_DEPRECATED);
225+
}
226+
227+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_maximum_items_per_page', $maxItemsPerPage ?? $this->maximumItemPerPage, true);
228+
222229
$itemsPerPage = (int) $this->getPaginationParameter($request, $this->itemsPerPageParameterName, $itemsPerPage);
223230
$itemsPerPage = (null !== $maxItemsPerPage && $itemsPerPage >= $maxItemsPerPage ? $maxItemsPerPage : $itemsPerPage);
224231
}

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282

8383
<service id="api_platform.serializer.context_builder" class="ApiPlatform\Core\Serializer\SerializerContextBuilder" public="false">
8484
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
85-
<argument>%api_platform.patch_formats%</argument>
8685
</service>
8786
<service id="ApiPlatform\Core\Serializer\SerializerContextBuilderInterface" alias="api_platform.serializer.context_builder" />
8887

src/DataProvider/Pagination.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,15 @@ public function getLimit(string $resourceClass = null, string $operationName = n
127127

128128
if ($clientLimit) {
129129
$limit = (int) $this->getParameterFromContext($context, $this->options['items_per_page_parameter_name'], $limit);
130-
$maxItemsPerPage = $this->options['maximum_items_per_page'];
130+
$maxItemsPerPage = null;
131131

132132
if (null !== $resourceClass) {
133133
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
134-
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', $maxItemsPerPage, true);
134+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', null, true);
135+
if (null !== $maxItemsPerPage) {
136+
@trigger_error('The "maximum_items_per_page" option has been deprecated since API Platform 2.5 in favor of "pagination_maximum_items_per_page" and will be removed in API Platform 3.', E_USER_DEPRECATED);
137+
}
138+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_maximum_items_per_page', $maxItemsPerPage ?? $this->options['maximum_items_per_page'], true);
135139
}
136140

137141
if (null !== $maxItemsPerPage && $limit > $maxItemsPerPage) {

src/EventListener/RespondListener.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Core\EventListener;
1515

1616
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
17+
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
1718
use ApiPlatform\Core\Util\RequestAttributesExtractor;
1819
use Symfony\Component\HttpFoundation\Response;
1920
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -51,7 +52,7 @@ public function onKernelView(GetResponseForControllerResultEvent $event): void
5152

5253
return;
5354
}
54-
if ($controllerResult instanceof Response || !($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond', false))) {
55+
if ($controllerResult instanceof Response || !($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond'))) {
5556
return;
5657
}
5758

@@ -78,6 +79,7 @@ public function onKernelView(GetResponseForControllerResultEvent $event): void
7879
$headers['Sunset'] = (new \DateTimeImmutable($sunset))->format(\DateTime::RFC1123);
7980
}
8081

82+
$headers = $this->addAcceptPatchHeader($headers, $attributes, $resourceMetadata);
8183
$status = $resourceMetadata->getOperationAttribute($attributes, 'status');
8284
}
8385

@@ -87,4 +89,29 @@ public function onKernelView(GetResponseForControllerResultEvent $event): void
8789
$headers
8890
));
8991
}
92+
93+
private function addAcceptPatchHeader(array $headers, array $attributes, ResourceMetadata $resourceMetadata): array
94+
{
95+
if (!isset($attributes['item_operation_name'])) {
96+
return $headers;
97+
}
98+
99+
$patchMimeTypes = [];
100+
foreach ($resourceMetadata->getItemOperations() as $operation) {
101+
if ('PATCH' !== ($operation['method'] ?? '') || !isset($operation['input_formats'])) {
102+
continue;
103+
}
104+
105+
foreach ($operation['input_formats'] as $mimeTypes) {
106+
foreach ($mimeTypes as $mimeType) {
107+
$patchMimeTypes[] = $mimeType;
108+
}
109+
}
110+
$headers['Accept-Patch'] = implode(', ', $patchMimeTypes);
111+
112+
return $headers;
113+
}
114+
115+
return $headers;
116+
}
90117
}

src/Serializer/SerializerContextBuilder.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,10 @@
2828
final class SerializerContextBuilder implements SerializerContextBuilderInterface
2929
{
3030
private $resourceMetadataFactory;
31-
private $patchFormats;
3231

33-
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, array $patchFormats = [])
32+
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory)
3433
{
3534
$this->resourceMetadataFactory = $resourceMetadataFactory;
36-
$this->patchFormats = $patchFormats;
3735
}
3836

3937
/**
@@ -91,12 +89,16 @@ public function createFromRequest(Request $request, bool $normalization, array $
9189

9290
unset($context[DocumentationNormalizer::SWAGGER_DEFINITION_NAME]);
9391

94-
if (
95-
isset($this->patchFormats['json'])
96-
&& !isset($context['skip_null_values'])
97-
&& \in_array('application/merge-patch+json', $this->patchFormats['json'], true)
98-
) {
99-
$context['skip_null_values'] = true;
92+
if (isset($context['skip_null_values'])) {
93+
return $context;
94+
}
95+
96+
foreach ($resourceMetadata->getItemOperations() as $operation) {
97+
if ('PATCH' === $operation['method'] && \in_array('application/merge-patch+json', $operation['input_formats']['json'] ?? [], true)) {
98+
$context['skip_null_values'] = true;
99+
100+
break;
101+
}
100102
}
101103

102104
return $context;

src/Swagger/Serializer/DocumentationNormalizer.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,14 @@ private function addPaginationParameters(bool $v3, ResourceMetadata $resourceMet
379379
'minimum' => 0,
380380
];
381381

382-
if ($maximumItemPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', false, true)) {
383-
$itemPerPageParameter['schema']['maximum'] = $maximumItemPerPage;
382+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', null, true);
383+
if (null !== $maxItemsPerPage) {
384+
@trigger_error('The "maximum_items_per_page" option has been deprecated since API Platform 2.5 in favor of "pagination_maximum_items_per_page" and will be removed in API Platform 3.', E_USER_DEPRECATED);
385+
}
386+
$maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_maximum_items_per_page', $maxItemsPerPage, true);
387+
388+
if (null !== $maxItemsPerPage) {
389+
$itemPerPageParameter['schema']['maximum'] = $maxItemsPerPage;
384390
}
385391
} else {
386392
$itemPerPageParameter['type'] = 'integer';

tests/Annotation/ApiResourceTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public function testConstruct()
4343
'input' => 'Foo',
4444
'iri' => 'http://example.com/res',
4545
'itemOperations' => ['foo' => ['bar']],
46-
'maximumItemsPerPage' => 42,
4746
'mercure' => '[\'foo\', object.owner]',
4847
'messenger' => true,
4948
'normalizationContext' => ['groups' => ['bar']],
@@ -56,6 +55,8 @@ public function testConstruct()
5655
'paginationEnabled' => true,
5756
'paginationFetchJoinCollection' => true,
5857
'paginationItemsPerPage' => 42,
58+
'maximumItemsPerPage' => 42, // deprecated, see paginationMaximumItemsPerPage
59+
'paginationMaximumItemsPerPage' => 50,
5960
'paginationPartial' => true,
6061
'routePrefix' => '/foo',
6162
'shortName' => 'shortName',
@@ -84,7 +85,6 @@ public function testConstruct()
8485
'formats' => ['foo', 'bar' => ['application/bar']],
8586
'filters' => ['foo', 'bar'],
8687
'input' => 'Foo',
87-
'maximum_items_per_page' => 42,
8888
'mercure' => '[\'foo\', object.owner]',
8989
'messenger' => true,
9090
'normalization_context' => ['groups' => ['bar']],
@@ -97,6 +97,8 @@ public function testConstruct()
9797
'pagination_enabled' => true,
9898
'pagination_fetch_join_collection' => true,
9999
'pagination_items_per_page' => 42,
100+
'maximum_items_per_page' => 42,
101+
'pagination_maximum_items_per_page' => 50,
100102
'pagination_partial' => true,
101103
'route_prefix' => '/foo',
102104
'swagger_context' => ['description' => 'bar'],

tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,15 +318,15 @@ public function testApplyToCollectionWithMaximumItemsPerPage()
318318
$attributes = [
319319
'pagination_enabled' => true,
320320
'pagination_client_enabled' => true,
321-
'maximum_items_per_page' => 80,
321+
'pagination_maximum_items_per_page' => 80,
322322
];
323323
$resourceMetadataFactoryProphecy->create('Foo')->willReturn(new ResourceMetadata(null, null, null, [], [], $attributes));
324324
$resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal();
325325

326326
$pagination = new Pagination($resourceMetadataFactory, [
327327
'client_enabled' => true,
328328
'client_items_per_page' => true,
329-
'maximum_items_per_page' => 50,
329+
'pagination_maximum_items_per_page' => 50,
330330
]);
331331

332332
$aggregationBuilderProphecy = $this->mockAggregationBuilder(0, 80);

0 commit comments

Comments
 (0)