Skip to content

Commit 1ac0c3d

Browse files
committed
Merge 4.0
2 parents fcd9a07 + 5db40ae commit 1ac0c3d

File tree

76 files changed

+2180
-135
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2180
-135
lines changed

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
->in(__DIR__)
1616
->exclude([
1717
'src/Core/Bridge/Symfony/Maker/Resources/skeleton',
18+
'src/Laravel/Console/Maker/Resources/skeleton',
1819
'src/Laravel/config',
1920
'tests/Fixtures/app/var',
2021
'docs/guides',

CHANGELOG.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Changelog
22

3+
## v4.0.5
4+
5+
### Bug fixes
6+
7+
* [4171d5f9c](https://github.com/api-platform/core/commit/4171d5f9cd41731b857c53a186270ba0626baedf) fix(graphql): register query parameter arguments with filters (#6726)
8+
* [48ab53816](https://github.com/api-platform/core/commit/48ab53816c55e6116aa64ac81f522f4b7b9bb9f6) fix(laravel): make command writes to app instead of src (#6723)
9+
10+
### Features
11+
12+
## v4.0.4
13+
14+
### Bug fixes
15+
16+
* [2e8287dad](https://github.com/api-platform/core/commit/2e8287dad0c0315dd6527279a6359c0a22f40d93) fix(laravel): allow serializer attributes through ApiProperty (#6680)
17+
* [439c188ea](https://github.com/api-platform/core/commit/439c188ea1685676d5e705a49a4b835f35a84d72) fix(laravel): match integer type (#6715)
18+
* [4ad7a50aa](https://github.com/api-platform/core/commit/4ad7a50aaabf0d85e2eb5bb3a6d4ef8d5b7b39a7) fix(laravel): openapi Options binding (#6714)
19+
* [ec6e64512](https://github.com/api-platform/core/commit/ec6e6451299a50fcab397e86fafe6db132ce7519) fix(laravel): skip resource path when not available (#6697)
20+
21+
### Features
22+
23+
* [5aa799321](https://github.com/api-platform/core/commit/5aa7993219a6fb55f11476a031963a542b2d3586) feat(laravel): command to generate state providers/processors (#6708)
24+
325
## v4.0.3
426

527
### Bug fixes
@@ -130,6 +152,25 @@ Notes:
130152

131153
* [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882)
132154

155+
## v3.4.4
156+
157+
### Bug fixes
158+
159+
* [550347867](https://github.com/api-platform/core/commit/550347867f30611b673d8df99f65186d013919dd) fix(graphql): register query parameter arguments with filters (#6727)
160+
* [99262dce7](https://github.com/api-platform/core/commit/99262dce739800bd841c95e026848b587ba25801) fix(jsonschema): handle @id when genId is false (#6716)
161+
* [ad5efa535](https://github.com/api-platform/core/commit/ad5efa535a4dcbaad64ecff89514eaa6e07f5b7c) fix: multiple parameter provider #6673 (#6732)
162+
* [d34cd7be8](https://github.com/api-platform/core/commit/d34cd7be8e7a12fd08a8b10270a614c06c10aa89) fix: use stateOptions when retrieving a Parameter filter (#6728)
163+
* [e7fb04fab](https://github.com/api-platform/core/commit/e7fb04fab05bc077e2dbeb0fa0fc2c1d28c96105) fix(symfony): fetch api-platform/symfony version debug bar (#6722)
164+
* [e96623ebf](https://github.com/api-platform/core/commit/e96623ebfd8691ba943bdb56a4d91e160497a311) fix(jsonld): prefix error @type with hydra: (#6721)
165+
166+
## v3.4.3
167+
168+
### Bug fixes
169+
170+
* [3ca599158](https://github.com/api-platform/core/commit/3ca599158139d56fbd6ee66f2de3e586120d735c) fix(hydra): hydra_prefix on errors (#6704)
171+
* [f7f605dc8](https://github.com/api-platform/core/commit/f7f605dc8b798b975d2286c970c9091436d7f890) fix: check that api-platform/ramsey-uuid is installed before registering related services (#6696)
172+
* [fbb53e5e3](https://github.com/api-platform/core/commit/fbb53e5e35ca0ec3de26ddc7de7ea4d1dda5c20b) fix(symfony): metadata aware name converter has 0 arguments by default (#6711)
173+
133174
## v3.4.2
134175

135176
### Bug fixes
@@ -142,9 +183,6 @@ Notes:
142183
* [afe7d47d7](https://github.com/api-platform/core/commit/afe7d47d7b7ba6c8591bfb60137a65d1fa1fe38f) fix(metadata): passing class as parameter in XML ApiResource's definition (#6659)
143184
* [b93ee467c](https://github.com/api-platform/core/commit/b93ee467c69253e0cfe60e75b48a5c7aa683474a) fix(metadata): overwriting XML ApiResource definition by YAML ApiResource definition (#6660)
144185

145-
> [!WARNING]
146-
> Hydra prefix on errors is breaking, read `title` not `hydra:title`. The `hydra_prefix` flag doesn't apply to errors as it provided redundant information (both `hydra:title` and `title` were available)
147-
148186
## v3.4.1
149187

150188
### Bug fixes

docs/guides/error-resource.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ class MyDomainException extends \Exception implements ProblemExceptionInterface
1818
{
1919
public function getType(): string
2020
{
21-
return 'teapot';
21+
return '/errors/418';
2222
}
2323

2424
public function getTitle(): ?string
2525
{
26-
return null;
26+
return 'Teapot error';
2727
}
2828

2929
public function getStatus(): ?int
@@ -40,6 +40,8 @@ public function getInstance(): ?string
4040
{
4141
return null;
4242
}
43+
44+
public string $myCustomField = 'I usually prefer coffee.';
4345
}
4446

4547
use ApiPlatform\Metadata\ApiResource;
@@ -83,7 +85,12 @@ public function testBookDoesNotExists(): void
8385
// you can override this by looking at the [Error Provider guide](/docs/guides/error-provider).
8486
$this->assertResponseStatusCodeSame(418);
8587
$this->assertJsonContains([
88+
'@id' => '/my_domain_exceptions',
89+
'@type' => 'MyDomainException',
90+
'type' => '/errors/418',
91+
'title' => 'Teapot error',
8692
'detail' => 'I am teapot',
93+
'myCustomField' => 'I usually prefer coffee.'
8794
]);
8895
}
8996
}

features/hydra/error.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ Feature: Error handling
1717
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
1818
And the JSON node "type" should exist
1919
And the JSON node "title" should be equal to "An error occurred"
20+
And the JSON node "hydra:title" should be equal to "An error occurred"
2021
And the JSON node "detail" should exist
2122
And the JSON node "description" should exist
23+
And the JSON node "hydra:description" should exist
2224
And the JSON node "trace" should exist
2325
And the JSON node "status" should exist
2426
And the JSON node "@context" should exist
@@ -48,6 +50,8 @@ Feature: Error handling
4850
],
4951
"detail": "name: This value should not be blank.",
5052
"title": "An error occurred",
53+
"hydra:title": "An error occurred",
54+
"hydra:description": "name: This value should not be blank.",
5155
"description": "name: This value should not be blank.",
5256
"type": "/validation_errors/c1051bb4-d103-4f74-8988-acbcafc7fdc3"
5357
}

features/main/relation.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ Feature: Relations support
493493
"properties": {
494494
"@type": {
495495
"type": "string",
496-
"pattern": "^Error$"
496+
"pattern": "^hydra:Error$"
497497
},
498498
"title": {
499499
"type": "string",

features/mongodb/filters.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Feature: Filters on collections
1212
And the response should be in JSON
1313
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
1414
And the JSON node "@context" should be equal to "/contexts/Error"
15-
And the JSON node "@type" should be equal to "Error"
15+
And the JSON node "@type" should be equal to "hydra:Error"
1616
And the JSON node "title" should be equal to "An error occurred"
1717
And the JSON node "description" should be equal to "Cannot use reference 'badFourthLevel' in class 'ThirdLevel' for lookup or graphLookup: dbRef references are not supported."
1818
And the JSON node "trace" should exist
@@ -23,7 +23,7 @@ Feature: Filters on collections
2323
And the response should be in JSON
2424
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
2525
And the JSON node "@context" should be equal to "/contexts/Error"
26-
And the JSON node "@type" should be equal to "Error"
26+
And the JSON node "@type" should be equal to "hydra:Error"
2727
And the JSON node "title" should be equal to "An error occurred"
2828
And the JSON node "description" should be equal to "Cannot use reference 'badThirdLevel' in class 'FourthLevel' for lookup or graphLookup: dbRef references are not supported."
2929
And the JSON node "trace" should exist

features/security/strong_typing.feature

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Feature: Handle properly invalid data submitted to the API
5454
And the response should be in JSON
5555
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
5656
And the JSON node "@context" should be equal to "/contexts/Error"
57-
And the JSON node "@type" should be equal to "Error"
57+
And the JSON node "@type" should be equal to "hydra:Error"
5858
And the JSON node "title" should be equal to "An error occurred"
5959
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "NULL" given.'
6060

@@ -71,7 +71,7 @@ Feature: Handle properly invalid data submitted to the API
7171
And the response should be in JSON
7272
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
7373
And the JSON node "@context" should be equal to "/contexts/Error"
74-
And the JSON node "@type" should be equal to "Error"
74+
And the JSON node "@type" should be equal to "hydra:Error"
7575
And the JSON node "title" should be equal to "An error occurred"
7676
And the JSON node "description" should be equal to 'Invalid IRI "1".'
7777
And the JSON node "trace" should exist
@@ -102,7 +102,7 @@ Feature: Handle properly invalid data submitted to the API
102102
And the response should be in JSON
103103
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
104104
And the JSON node "@context" should be equal to "/contexts/Error"
105-
And the JSON node "@type" should be equal to "Error"
105+
And the JSON node "@type" should be equal to "hydra:Error"
106106
And the JSON node "title" should be equal to "An error occurred"
107107
And the JSON node "description" should be equal to 'The type of the "relatedDummies" attribute must be "array", "string" given.'
108108
And the JSON node "trace" should exist
@@ -120,7 +120,7 @@ Feature: Handle properly invalid data submitted to the API
120120
And the response should be in JSON
121121
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
122122
And the JSON node "@context" should be equal to "/contexts/Error"
123-
And the JSON node "@type" should be equal to "Error"
123+
And the JSON node "@type" should be equal to "hydra:Error"
124124
And the JSON node "title" should be equal to "An error occurred"
125125
And the JSON node "description" should be equal to 'The type of the key "a" must be "int", "string" given.'
126126

@@ -136,7 +136,7 @@ Feature: Handle properly invalid data submitted to the API
136136
And the response should be in JSON
137137
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
138138
And the JSON node "@context" should be equal to "/contexts/Error"
139-
And the JSON node "@type" should be equal to "Error"
139+
And the JSON node "@type" should be equal to "hydra:Error"
140140
And the JSON node "title" should be equal to "An error occurred"
141141
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "integer" given.'
142142

features/serializer/vo_relations.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Feature: Value object as ApiResource
148148
"properties": {
149149
"@type": {
150150
"type": "string",
151-
"pattern": "^Error$"
151+
"pattern": "^hydra:Error$"
152152
},
153153
"title": {
154154
"type": "string",

src/GraphQl/Type/FieldsBuilder.php

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -287,61 +287,6 @@ public function resolveResourceArgs(array $args, Operation $operation): array
287287
$args[$id]['type'] = $this->typeConverter->resolveType($arg['type']);
288288
}
289289

290-
/*
291-
* This is @experimental, read the comment on the parameterToObjectType function as additional information.
292-
*/
293-
foreach ($operation->getParameters() ?? [] as $parameter) {
294-
$key = $parameter->getKey();
295-
296-
if (str_contains($key, ':property')) {
297-
if (!($filterId = $parameter->getFilter()) || !$this->filterLocator->has($filterId)) {
298-
continue;
299-
}
300-
301-
$filter = $this->filterLocator->get($filterId);
302-
$parsedKey = explode('[:property]', $key);
303-
$flattenFields = [];
304-
305-
if ($filter instanceof FilterInterface) {
306-
foreach ($filter->getDescription($operation->getClass()) as $name => $value) {
307-
$values = [];
308-
parse_str($name, $values);
309-
if (isset($values[$parsedKey[0]])) {
310-
$values = $values[$parsedKey[0]];
311-
}
312-
313-
$name = key($values);
314-
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
315-
}
316-
317-
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
318-
}
319-
320-
if ($filter instanceof OpenApiParameterFilterInterface) {
321-
foreach ($filter->getOpenApiParameters($parameter) as $value) {
322-
$values = [];
323-
parse_str($value->getName(), $values);
324-
if (isset($values[$parsedKey[0]])) {
325-
$values = $values[$parsedKey[0]];
326-
}
327-
328-
$name = key($values);
329-
$flattenFields[] = ['name' => $name, 'required' => $value->getRequired(), 'description' => $value->getDescription(), 'leafs' => $values[$name], 'type' => $value->getSchema()['type'] ?? 'string'];
330-
}
331-
332-
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0].$operation->getShortName().$operation->getName());
333-
}
334-
335-
continue;
336-
}
337-
338-
$args[$key] = ['type' => GraphQLType::string()];
339-
340-
if ($parameter->getRequired()) {
341-
$args[$key]['type'] = GraphQLType::nonNull($args[$key]['type']);
342-
}
343-
}
344-
345290
return $args;
346291
}
347292

@@ -463,12 +408,15 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
463408

464409
$args = [];
465410

466-
if (!$input && !$rootOperation instanceof Mutation && !$rootOperation instanceof Subscription && !$isStandardGraphqlType && $isCollectionType) {
467-
if (!$this->isEnumClass($resourceClass) && $this->pagination->isGraphQlEnabled($resourceOperation)) {
468-
$args = $this->getGraphQlPaginationArgs($resourceOperation);
469-
}
411+
if (!$input && !$rootOperation instanceof Mutation && !$rootOperation instanceof Subscription && !$isStandardGraphqlType) {
412+
if ($isCollectionType) {
413+
if (!$this->isEnumClass($resourceClass) && $this->pagination->isGraphQlEnabled($resourceOperation)) {
414+
$args = $this->getGraphQlPaginationArgs($resourceOperation);
415+
}
470416

471-
$args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth);
417+
$args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth);
418+
$args = $this->getParameterArgs($rootOperation, $args);
419+
}
472420
}
473421

474422
if ($isStandardGraphqlType || $input) {
@@ -491,6 +439,67 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
491439
return null;
492440
}
493441

442+
/*
443+
* This function is @experimental, read the comment on the parameterToObjectType function for additional information.
444+
* @experimental
445+
*/
446+
private function getParameterArgs(Operation $operation, array $args = []): array
447+
{
448+
foreach ($operation->getParameters() ?? [] as $parameter) {
449+
$key = $parameter->getKey();
450+
451+
if (!str_contains($key, ':property')) {
452+
$args[$key] = ['type' => GraphQLType::string()];
453+
454+
if ($parameter->getRequired()) {
455+
$args[$key]['type'] = GraphQLType::nonNull($args[$key]['type']);
456+
}
457+
458+
continue;
459+
}
460+
461+
if (!($filterId = $parameter->getFilter()) || !$this->filterLocator->has($filterId)) {
462+
continue;
463+
}
464+
465+
$filter = $this->filterLocator->get($filterId);
466+
$parsedKey = explode('[:property]', $key);
467+
$flattenFields = [];
468+
469+
if ($filter instanceof FilterInterface) {
470+
foreach ($filter->getDescription($operation->getClass()) as $name => $value) {
471+
$values = [];
472+
parse_str($name, $values);
473+
if (isset($values[$parsedKey[0]])) {
474+
$values = $values[$parsedKey[0]];
475+
}
476+
477+
$name = key($values);
478+
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
479+
}
480+
481+
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
482+
}
483+
484+
if ($filter instanceof OpenApiParameterFilterInterface) {
485+
foreach ($filter->getOpenApiParameters($parameter) as $value) {
486+
$values = [];
487+
parse_str($value->getName(), $values);
488+
if (isset($values[$parsedKey[0]])) {
489+
$values = $values[$parsedKey[0]];
490+
}
491+
492+
$name = key($values);
493+
$flattenFields[] = ['name' => $name, 'required' => $value->getRequired(), 'description' => $value->getDescription(), 'leafs' => $values[$name], 'type' => $value->getSchema()['type'] ?? 'string'];
494+
}
495+
496+
$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0].$operation->getShortName().$operation->getName());
497+
}
498+
}
499+
500+
return $args;
501+
}
502+
494503
private function getGraphQlPaginationArgs(Operation $queryOperation): array
495504
{
496505
$paginationType = $this->pagination->getGraphQlPaginationType($queryOperation);

src/GraphQl/Type/TypeConverter.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,14 @@ private function getResourceType(Type $type, bool $input, Operation $rootOperati
168168
try {
169169
$operation = $resourceMetadataCollection->getOperation($operationName);
170170
} catch (OperationNotFoundException) {
171-
$operation = $resourceMetadataCollection->getOperation($isCollection ? 'collection_query' : 'item_query');
171+
try {
172+
$operation = $resourceMetadataCollection->getOperation($isCollection ? 'collection_query' : 'item_query');
173+
} catch (OperationNotFoundException) {
174+
throw new OperationNotFoundException(\sprintf('A GraphQl operation named "%s" should exist on the type "%s" as we reference this type in another query.', $isCollection ? 'collection_query' : 'item_query', $resourceClass));
175+
}
172176
}
173177
if (!$operation instanceof Operation) {
174-
throw new OperationNotFoundException();
178+
throw new OperationNotFoundException(\sprintf('A GraphQl operation named "%s" should exist on the type "%s" as we reference this type in another query.', $operationName, $resourceClass));
175179
}
176180

177181
return $this->typeBuilder->getResourceObjectType($resourceMetadataCollection, $operation, $propertyMetadata, [

0 commit comments

Comments
 (0)