Skip to content

Commit 40fd600

Browse files
committed
Merge 3.3
2 parents 9a8adff + 2f4ecc8 commit 40fd600

38 files changed

+704
-48
lines changed

.commitlintrc

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"serializer",
2121
"jsonschema",
2222
"validation",
23-
"state"
23+
"state",
24+
"test"
2425
]
2526
],
2627
"scope-empty": [
@@ -55,8 +56,7 @@
5556
[
5657
"sentence-case",
5758
"start-case",
58-
"pascal-case",
59-
"upper-case"
59+
"pascal-case"
6060
]
6161
],
6262
"subject-empty": [

CHANGELOG.md

+96-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,78 @@
11
# Changelog
22

3+
## v3.3.4
4+
5+
### Bug fixes
6+
7+
* [002d8e514](https://github.com/api-platform/core/commit/002d8e51490dbe9f5d8e5551226d70db8a33c706) fix(validation): ValidationException causes TypeError exception when called with $code=null (#6375)
8+
* [77a917f2a](https://github.com/api-platform/core/commit/77a917f2a51b50af84bdc96c1a32ff671b3951db) fix(metadata): resource class php doc (#6381)
9+
* [d809315fb](https://github.com/api-platform/core/commit/d809315fbb3822dbc6fe50d5c908183f4428f0f2) fix(symfony): store original data without clone (#6367)
10+
* [fb7c4658c](https://github.com/api-platform/core/commit/fb7c4658c327c9628bcc86d42e85c3546a74d993) fix(test): canonicalizing json arrays (#6386)
11+
12+
## v3.3.3
13+
14+
### Bug fixes
15+
16+
* [10f24f7a1](https://github.com/api-platform/core/commit/10f24f7a18649f5d463ce0a99759e9b514707e92) fix(state): no location header without output (#6356)
17+
* [20c9165f2](https://github.com/api-platform/core/commit/20c9165f240e0457d1f5c9e2760b980e7d61f777) fix(symfony): no read should not throw on wrong uri variables (#6359)
18+
* [4cd359d40](https://github.com/api-platform/core/commit/4cd359d400608340b09597d24e03f9ff1dc2f9ec) fix(graphql): resolver before validation (#6363)
19+
* [9d159f4fa](https://github.com/api-platform/core/commit/9d159f4fa2d411f00911cfc5679b81585907d053) fix(symfony): no read shouldn't throw InvalidIdentifiers (#6357)
20+
21+
## v3.3.2
22+
23+
### Bug fixes
24+
25+
* [6f806f4ee](https://github.com/api-platform/core/commit/6f806f4eeec3d120da7a4c145f9dbda9bd4be2ed) fix(state): read without output (#6347)
26+
* [735e1509e](https://github.com/api-platform/core/commit/735e1509ef67deb1c4c837ff86b445f40e2f7c8b) fix(symfony): set normalization context in request attributes (#6345)
27+
* [b4984126a](https://github.com/api-platform/core/commit/b4984126a109ec7951012614616035035978b255) fix(symfony): use_symfony_listeners before registering services (#6350)
28+
* [f63fd8101](https://github.com/api-platform/core/commit/f63fd8101f8211707806e013668f50dafab2865d) fix(symfony): define use_symfony_listeners (#6344)
29+
30+
### Notes
31+
32+
You can remove the `event_listeners_backward_compatibility_layer` flag and set `use_symfony_listeners` instead. The `use_symfony_listeners` should be `true` if you use controllers or if you rely on Symfony event listeners. Note that now flags like `read` can be forced to `true` if you want to call a Provider even on `POST` operations. These are the rules we set up on runtime if no value has been set:
33+
34+
```php
35+
if (null === $operation->canValidate()) {
36+
$operation = $operation->withValidate(!$request->isMethodSafe() && !$request->isMethod('DELETE'));
37+
}
38+
39+
if (null === $operation->canRead()) {
40+
$operation = $operation->withRead($operation->getUriVariables() || $request->isMethodSafe());
41+
}
42+
43+
if (null === $operation->canDeserialize()) {
44+
$operation = $operation->withDeserialize(\in_array($operation->getMethod(), ['POST', 'PUT', 'PATCH'], true));
45+
}
46+
```
47+
48+
Previously listeners did the checks before reading our flags and you could not force the values.
49+
50+
When using GraphQl, with `event_listeners_backward_compatibility_layer: true`, mutation resolver gets called before validation, when using `false` (the future default) validation occurs on the user's input.
51+
52+
## v3.3.1 (pre-release)
53+
54+
### Bug fixes
55+
56+
* [6f806f4ee](https://github.com/api-platform/core/commit/6f806f4eeec3d120da7a4c145f9dbda9bd4be2ed) fix(state): read without output (#6347)
57+
* [735e1509e](https://github.com/api-platform/core/commit/735e1509ef67deb1c4c837ff86b445f40e2f7c8b) fix(symfony): set normalization context in request attributes (#6345)
58+
* [f63fd8101](https://github.com/api-platform/core/commit/f63fd8101f8211707806e013668f50dafab2865d) fix(symfony): define use_symfony_listeners (#6344)
59+
60+
## v3.3.0
61+
62+
### Bug fixes
63+
64+
* [629da787b](https://github.com/api-platform/core/commit/629da787bc49fe06db02933d41dc550aee87b429) fix(symfony): use non deprecated validator exception (#6297)
65+
* [8a232a474](https://github.com/api-platform/core/commit/8a232a474466f4fbcf2001d6894fa0fec272ae6e) fix: add legacy FilterInterface as return type of getFilter function (#6311)
66+
* [97c8ae26e](https://github.com/api-platform/core/commit/97c8ae26eb5eb4cfaafadcc36b7d497b9fa2cb9e) fix(jsonapi): handle multiple relation classes, unrelated unions (#6320)
67+
* [af61482c2](https://github.com/api-platform/core/commit/af61482c21618e8abcbe7486df05e30a961bc5b6) fix(symfony)!: context stamp not serializable because of request object (#6323)
68+
* [fce42e0e7](https://github.com/api-platform/core/commit/fce42e0e783f3eb0331afad702d553f3bd63a2b3) fix(jsonapi): re-add continue once relation is determined (#6325)
69+
70+
71+
### Features
72+
73+
* [57fe13615](https://github.com/api-platform/core/commit/57fe13615dfda724fd17d18658a1cdb062d261e5) feat(serializer): update MissingConstructorArgumentsException message (#5902)
74+
* [e867d07f5](https://github.com/api-platform/core/commit/e867d07f59b82d5f1bdca69e096ddf452dd7efc8) feat(parametervalidator): parameter validation (#6296)
75+
376
## v3.3.0-beta.2
477

578
### Bug fixes
@@ -51,7 +124,7 @@ The v3.3.0-beta.1 introduces a new `QueryParameter` attribute to improve [the fi
51124
### Features
52125

53126
* [24a1a18cb](https://github.com/api-platform/core/commit/24a1a18cbe706c5a3bb4d5602b70c0a68ff8a757) feat: improve ApiProperty::security using property name (#5853)
54-
* [3d1428e4d](https://github.com/api-platform/core/commit/3d1428e4d2e4342918becf098da8832ac50fef1b) feat(symfony): add getOperation Expression Language function on Mercure topics (#5854)
127+
* [3d1428e4d](https://github.com/api-platform/core/commit/3d1428e4d2e4342918becf098da8832ac50fef1b) feat(symfony): add get_operation Expression Language function on Mercure topics (#5854)
55128
* [6b00cea91](https://github.com/api-platform/core/commit/6b00cea914dc5f9c42ca237a3ff498d629a0efb8) feat(graphql): partial pagination for page based pagination (#6120)
56129
* [79fe01b97](https://github.com/api-platform/core/commit/79fe01b970d90e3c80880f54fc0446b5294173f0) feat(doctrine): paginators for Doctrine Collection & Selectable (#6153)
57130
* [89c9229f4](https://github.com/api-platform/core/commit/89c9229f484cb409ef3eb2bd88cccc6ddc856378) feat(graphql): support nullable embedded relations in GraphQL types (#6100)
@@ -121,6 +194,21 @@ api_platform:
121194
form: ['multipart/form-data']
122195
```
123196

197+
## v3.2.23
198+
199+
### Bug fixes
200+
201+
* [fb7c4658c](https://github.com/api-platform/core/commit/fb7c4658c327c9628bcc86d42e85c3546a74d993) fix(test): canonicalizing json arrays (#6386)
202+
203+
## v3.2.22
204+
205+
### Bug fixes
206+
207+
* [50c738cf6](https://github.com/api-platform/core/commit/50c738cf6b31bb7f2ddcd74037c12315c8bcac6d) fix(graphql): check inheritance in ResolverProvider (#6314)
208+
* [9cd597f80](https://github.com/api-platform/core/commit/9cd597f80d9eecafb28b4229a313ba9b9618bf8c) fix(doctrine): remove usage of deprecated ClassUtils in PurgeHttpCacheListener for Doctrine ORM 3 (#6331)
209+
* [a59fbee97](https://github.com/api-platform/core/commit/a59fbee97dc09b7cb7e12ee3bef9451f0fbea957) fix(serializer): uriTemplate wrong cache usage in hal format (#6313)
210+
* [c083af346](https://github.com/api-platform/core/commit/c083af3461a926ea68837f216eacc67ca43a6fc3) fix(metadata): allow extracting routeName from XML config (#6329)
211+
124212
## v3.2.21
125213

126214
### Bug fixes
@@ -395,6 +483,13 @@ Listeners will not get removed in API Platform 4 but will rather use our new Pro
395483
#[Post(read: true)] // to force reading even though it's a POST
396484
```
397485

486+
- `ApiPlatform\Api` got moved to `ApiPlatform\Metadata`
487+
488+
- Adds `assertMercureUpdateMatchesJsonSchema(Update $update, array $topics, array|object|string $jsonSchema = '', bool $private = false, string $id = null, string $type = null, int $retry = null, string $message = '')`
489+
- The handle links feature is experimental
490+
491+
When using GraphQl, with `event_listeners_backward_compatibility_layer: true`, mutation resolver gets called before validation, when using `false` (the future default) validation occurs on the user's input.
492+
398493
## v3.2.0-beta.2
399494

400495
### Bug fixes

behat.yml.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ legacy:
203203
- 'Behat\MinkExtension\Context\MinkContext'
204204
- 'behatch:context:rest'
205205
filters:
206-
tags: '~@postgres&&~@mongodb&&~@elasticsearch&&~@link_security'
206+
tags: '~@postgres&&~@mongodb&&~@elasticsearch&&~@link_security&&~@use_listener'
207207
extensions:
208208
'FriendsOfBehat\SymfonyExtension':
209209
bootstrap: 'tests/Fixtures/app/bootstrap.php'

docs/config/packages/framework.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ api_platform:
88
docs_formats:
99
jsonopenapi: ['application/vnd.openapi+json']
1010
keep_legacy_inflector: false
11+
http_cache:
12+
invalidation:
13+
enabled: true
14+
public: true
15+
use_symfony_listeners: false
1116
defaults:
1217
extra_properties:
1318
rfc_7807_compliant_errors: true

docs/guides/http-cache-tags.php

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
// ---
3+
// slug: http-cache-tags
4+
// name: Customize HTTP Cache Tags
5+
// executable: true
6+
// position: 21
7+
// ---
8+
9+
// This guide describes how to customize HTTP Cache Tags included in the API response header by registering a TagCollector service. In this example, the Id
10+
// of a resource will be used to generate the cache tag instead of its IRI.
11+
//
12+
// If you only want to add additional tags without changing them, it might be sufficient to follow the [standard documentation on http cache](https://api-platform.com/docs/core/performance/#extending-cache-tags-for-invalidation).
13+
//
14+
// First, we need our book resource. For the sake of this example, we assume the id to be unique across all resources (otherwise replacing the IRI with Id would make no sense).
15+
namespace App\ApiResource {
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\ApiProperty;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\Operation;
20+
21+
#[ApiResource(
22+
operations: [
23+
new Get(provider: Book::class.'::provide'),
24+
],
25+
)]
26+
class Book
27+
{
28+
#[ApiProperty(identifier: true)]
29+
public string $id = "unique-id-1";
30+
31+
public static function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
32+
{
33+
return (new self());
34+
}
35+
}
36+
}
37+
38+
// Now, we define our TagCollector service. Check the PHPDoc in TagCollectorInterface to see the various information, which is available within `$context`.
39+
//
40+
// Add any tags you want to be included in the response to `$context['resources']`.
41+
42+
namespace App\Service {
43+
use ApiPlatform\Serializer\TagCollectorInterface;
44+
use App\ApiResource\Book;
45+
46+
class TagCollector implements TagCollectorInterface
47+
{
48+
public function collect(array $context = []): void
49+
{
50+
if (isset($context['property_metadata'])) {
51+
return;
52+
}
53+
54+
$iri = $context['iri'] ?? null;
55+
$object = $context['object'] ?? null;
56+
57+
if ($object && $object instanceof Book) {
58+
$iri = $object->id;
59+
}
60+
61+
if (!$iri) {
62+
return;
63+
}
64+
65+
$context['resources'][$iri] = $iri;
66+
}
67+
}
68+
}
69+
70+
// Replace the service 'api_platform.http_cache.tag_collector' with your new TagCollector class.
71+
72+
namespace App\DependencyInjection {
73+
use App\Service\TagCollector;
74+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
75+
76+
function configure(ContainerConfigurator $configurator): void
77+
{
78+
$services = $configurator->services();
79+
$services->set('api_platform.http_cache.tag_collector', TagCollector::class);
80+
}
81+
}
82+
83+
84+
// Request the book. When using Swagger UI, you should see that your 'Cache-Tags' header now includes the Id only instead of the full Iri.
85+
86+
namespace App\Playground {
87+
use Symfony\Component\HttpFoundation\Request;
88+
89+
function request(): Request
90+
{
91+
return Request::create('/books/unique-id-1.jsonld', 'GET');
92+
}
93+
}
94+
95+
// ...or verify directly in testing.
96+
97+
namespace App\Tests {
98+
use ApiPlatform\Playground\Test\TestGuideTrait;
99+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
100+
101+
final class BookTest extends ApiTestCase
102+
{
103+
use TestGuideTrait;
104+
105+
public function testAsAnonymousICanAccessTheDocumentation(): void
106+
{
107+
$response = static::createClient()->request('GET', '/books/unique-id-1.jsonld');
108+
109+
$this->assertResponseIsSuccessful();
110+
$this->assertResponseHeaderSame('Cache-Tags', 'unique-id-1');
111+
$this->assertJsonContains([
112+
'@id' => '/books/unique-id-1',
113+
'@type' => 'Book',
114+
'id' => 'unique-id-1',
115+
]);
116+
}
117+
}
118+
}
119+
120+
121+
// If you rely on invalidation from Api-Platform, don't forget that you'd also need to have your own implementation of `PurgeHttpCacheListener`. Otherwise the wrong tags will be purged.

docs/guides/test-your-api.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function testGetCollection(): void
3838
// We provide assertions based on your resource's JSON Schema to save time asserting that the data
3939
// matches an expected format, for example here with a collection.
4040
$this->assertMatchesResourceCollectionJsonSchema(Book::class);
41-
// PHPUnit default assertios are also available.
41+
// PHPUnit default assertions are also available.
4242
$this->assertCount(0, $response->toArray()['hydra:member']);
4343
}
4444
}

features/graphql/mutation.feature

+18
Original file line numberDiff line numberDiff line change
@@ -1034,3 +1034,21 @@ Feature: GraphQL mutation support
10341034
"""
10351035
Then the response status code should be 200
10361036
And the JSON node "data.uploadMultipleMediaObject.mediaObject.contentUrl" should be equal to "test.gif"
1037+
1038+
@!mongodb
1039+
Scenario: Delete an invalid item through a mutation
1040+
When I send the following GraphQL request:
1041+
"""
1042+
mutation {
1043+
deleteActivityLog(input: {id: "/activity_logs/1"}) {
1044+
activityLog {
1045+
id
1046+
}
1047+
}
1048+
}
1049+
"""
1050+
Then the response status code should be 200
1051+
And the response should be in JSON
1052+
And the header "Content-Type" should be equal to "application/json"
1053+
And the JSON node "errors" should not exist
1054+
And the JSON node "data.deleteActivityLog.activityLog" should exist

features/jsonld/no_output.feature

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Feature: Disable Id generation on anonymous resource collections
2+
3+
@!mongodb
4+
Scenario: Post to an output false should not generate an IRI
5+
When I add "Content-Type" header equal to "application/ld+json"
6+
And I send a "POST" request to "/no_iri_messages" with body:
7+
"""
8+
{}
9+
"""
10+
Then the response status code should be 202

features/main/input_output.feature

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Feature: DTO input and output
2+
In order to use a hypermedia API
3+
As a client software developer
4+
I need to be able to use DTOs on my resources as Input or Output objects.
5+
6+
Background:
7+
Given I add "Accept" header equal to "application/ld+json"
8+
And I add "Content-Type" header equal to "application/ld+json"
9+
10+
@!mongodb
11+
Scenario: Fetch a collection of outputs with an entityClass as state option
12+
When I send a "GET" request to "/output_and_entity_classes"
13+
And the JSON node "hydra:member[0].@type" should be equal to "OutputAndEntityClassEntity"
14+
15+

features/main/patch.feature

+17
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,20 @@ Feature: Sending PATCH requets
8080
"alpha": "/alphas/2"
8181
}
8282
"""
83+
84+
@use_listener
85+
@controller
86+
# Previously to 3.3 it was not possible to disable a read, this test is ignored on the
87+
# legacy test suite (EVENT_LISTENERS_BACKWARD_COMPATIBILITY_LAYER=1)
88+
Scenario: Patch a non-readable resource
89+
When I add "Content-Type" header equal to "application/merge-patch+json"
90+
And I send a "PATCH" request to "/order_products/1/count" with body:
91+
"""
92+
{
93+
"id": 1,
94+
"count": 10
95+
}
96+
97+
"""
98+
Then the response status code should be 200
99+
And the JSON node "id" should contain "1"

src/GraphQl/Resolver/Factory/ResolverFactory.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\GraphQl\Resolver\Factory;
1515

1616
use ApiPlatform\GraphQl\State\Provider\NoopProvider;
17+
use ApiPlatform\Metadata\DeleteOperationInterface;
1718
use ApiPlatform\Metadata\GraphQl\Mutation;
1819
use ApiPlatform\Metadata\GraphQl\Operation;
1920
use ApiPlatform\Metadata\GraphQl\Query;
@@ -75,7 +76,7 @@ private function resolve(?array $source, array $args, ResolveInfo $info, ?string
7576
$context = ['source' => $source, 'args' => $args, 'info' => $info, 'root_class' => $rootClass, 'graphql_context' => &$graphQlContext];
7677

7778
if (null === $operation->canValidate()) {
78-
$operation = $operation->withValidate($operation instanceof Mutation);
79+
$operation = $operation->withValidate($operation instanceof Mutation && !$operation instanceof DeleteOperationInterface);
7980
}
8081

8182
$body ??= $this->provider->provide($operation, [], $context);

src/Metadata/FilterInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ interface FilterInterface
5353
*
5454
* @see \ApiPlatform\OpenApi\Factory\OpenApiFactory::getFiltersParameters
5555
*
56+
* @param class-string $resourceClass
57+
*
5658
* @return array<string, array{property?: string, type?: string, required?: bool, description?: string, strategy?: string, is_collection?: bool, swagger?: array<string, mixed>, openapi?: array<string, mixed>|\ApiPlatform\OpenApi\Model\Parameter, schema?: array<string, mixed>}>
5759
*/
5860
public function getDescription(string $resourceClass): array;

0 commit comments

Comments
 (0)