Skip to content

Commit be7fbac

Browse files
committed
Merge 3.2
2 parents cbc803c + ff1f282 commit be7fbac

File tree

42 files changed

+552
-77
lines changed

Some content is hidden

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

42 files changed

+552
-77
lines changed

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
'no_superfluous_elseif' => true,
8383
'no_superfluous_phpdoc_tags' => [
8484
'allow_mixed' => false,
85+
'allow_unused_params' => true,
8586
],
8687
'no_unset_cast' => true,
8788
'no_unset_on_property' => true,

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## v3.2.8
4+
5+
### Bug fixes
6+
7+
* [2a43268f9](https://github.com/api-platform/core/commit/2a43268f997e79a8407992c0bd7704a19d860479) fix(jsonschema): fix invalid "int" type to "integer" (#6049)
8+
* [9660a190a](https://github.com/api-platform/core/commit/9660a190a264102f7d1cfa1eae41f397ec559391) fix(serializer): concat context on wrong id (#6050)
9+
* [a35f0da11](https://github.com/api-platform/core/commit/a35f0da118444e7d735a37de5e918e8927a99b5e) fix(jsonld): remove link to ApiDocumentation when doc is disabled (#6029)
10+
* [a9a06897b](https://github.com/api-platform/core/commit/a9a06897b38a1b7a68db0e217d659d2dc5450851) fix(doctrine): get reference with identifier value (#6019)
11+
* [aac883e93](https://github.com/api-platform/core/commit/aac883e9331adac959c097796277e8a6d3e63ef0) fix(symfony): bypass symfony exception listener (#6056)
12+
* [b1926f533](https://github.com/api-platform/core/commit/b1926f533f4dd1f979285ac6bb26e339fe9d908f) fix(symfony): do not use metadata when merging schema constraints in Collection constraint (#6057)
13+
* [cc16a1ced](https://github.com/api-platform/core/commit/cc16a1ced3300a6080fbc428bade0291ca5bcb82) fix(jsonschema): iri example (#5901)
14+
* [ccf52c199](https://github.com/api-platform/core/commit/ccf52c19953874fbafbb398de0a3419244079f48) fix: item_uri_template conflict with context on relation (#6015)
15+
* [dcce75121](https://github.com/api-platform/core/commit/dcce75121153b32401d9301d8502d43ef46a8b17) fix(doctrine): OrderFilterTrait - properties null value causing error in foreach
16+
* [dcfd3c5ca](https://github.com/api-platform/core/commit/dcfd3c5ca34c4add63d299a8400f94795461c982) fix(jsonschema): keep format subschema generation (#6055)
17+
* [c13c88e5c](https://github.com/api-platform/core/commit/c13c88e5c2c8206664bda2d708e43c995968ae84) fix(metadata): throw exception if itemUriTemplate if used on invalid operation (xml/yaml formats) (#6053)
18+
319
## v3.2.7
420

521
Symfony 7 support.
@@ -228,6 +244,14 @@ Notes:
228244
* [92a81f024](https://github.com/api-platform/core/commit/92a81f024541054b9322e7457b75c721261e14e0) feat(graphql): allow to disable the introspection query (#5711)
229245
* [d793ffb92](https://github.com/api-platform/core/commit/d793ffb9228a21655ee35f0b90a959f93281a4cf) feat: union/intersect types (#5470)
230246

247+
## v3.1.24
248+
249+
### Bug fixes
250+
251+
* [9660a190a](https://github.com/api-platform/core/commit/9660a190a264102f7d1cfa1eae41f397ec559391) fix(serializer): concat context on wrong id (#6050)
252+
* [a9a06897b](https://github.com/api-platform/core/commit/a9a06897b38a1b7a68db0e217d659d2dc5450851) fix(doctrine): get reference with identifier value (#6019)
253+
* [cc16a1ced](https://github.com/api-platform/core/commit/cc16a1ced3300a6080fbc428bade0291ca5bcb82) fix(jsonschema): iri example (#5901)
254+
231255
## v3.1.23
232256

233257
### Bug fixes

features/hydra/error.feature

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,44 @@ Feature: Error handling
6969
And the JSON node "title" should be equal to "An error occurred"
7070
And the JSON node "detail" should exist
7171

72+
Scenario: Get an rfc 7807 not found error
73+
When I add "Accept" header equal to "application/ld+json"
74+
And I send a "POST" request to "/does_not_exist" with body:
75+
"""
76+
{}
77+
"""
78+
Then the response status code should be 404
79+
And the response should be in JSON
80+
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
81+
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
82+
And the JSON node "@context" should not exist
83+
And the JSON node "type" should exist
84+
And the JSON node "title" should be equal to "An error occurred"
85+
And the JSON node "hydra:title" should be equal to "An error occurred"
86+
And the JSON node "detail" should exist
87+
And the JSON node "hydra:description" should exist
88+
89+
Scenario: Get an rfc 7807 bad method error
90+
When I add "Content-Type" header equal to "application/ld+json"
91+
And I add "Accept" header equal to "application/ld+json"
92+
And I send a "PATCH" request to "/dummy_problems" with body:
93+
"""
94+
{}
95+
"""
96+
Then the response status code should be 405
97+
And the response should be in JSON
98+
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
99+
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
100+
And the JSON node "@context" should not exist
101+
And the JSON node "type" should exist
102+
And the JSON node "title" should be equal to "An error occurred"
103+
And the JSON node "hydra:title" should be equal to "An error occurred"
104+
And the JSON node "detail" should exist
105+
And the JSON node "hydra:description" should exist
106+
72107
Scenario: Get an rfc 7807 validation error
73108
When I add "Content-Type" header equal to "application/ld+json"
109+
And I add "Accept" header equal to "application/ld+json"
74110
And I send a "POST" request to "/validation_exception_problems" with body:
75111
"""
76112
{}
@@ -84,4 +120,3 @@ Feature: Error handling
84120
And the JSON node "title" should be equal to "An error occurred"
85121
And the JSON node "detail" should exist
86122
And the JSON node "violations" should exist
87-

features/hydra/error_legacy.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Feature: Error handling
142142

143143
Scenario: Get an error because of sending bad type property
144144
When I add "Content-Type" header equal to "application/json"
145+
And I add "Accept" header equal to "application/ld+json"
145146
And I send a "POST" request to "/greetings" with body:
146147
"""
147148
{

features/hydra/item_uri_template.feature

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
@!mongodb
22
@v3
33
Feature: Exposing a collection of objects should use the specified operation to generate the IRI
4+
Background:
5+
Given I add "Accept" header equal to "application/ld+json"
46

57
Scenario: Get a collection of objects without any itemUriTemplate should generate the IRI from the first Get operation
68
When I send a "GET" request to "/cars"
@@ -220,3 +222,16 @@ Feature: Exposing a collection of objects should use the specified operation to
220222
]
221223
}
222224
"""
225+
226+
Scenario: Create an object with an itemUriTemplate should generate the IRI according to the specified itemUriTemplate
227+
When I add "Content-Type" header equal to "application/ld+json"
228+
And I send a "POST" request to "/issue5662/books/a/reviews" with body:
229+
"""
230+
{
231+
"body": "Good book"
232+
}
233+
"""
234+
Then the response status code should be 201
235+
And the response should be in JSON
236+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
237+
And the JSON node "@id" should be equal to "/issue5662/books/a/reviews/0"

features/jsonapi/errors.feature

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,17 @@ Feature: JSON API error handling
4747
And the JSON node "errors[0].title" should be equal to "An error occurred"
4848
And the JSON node "errors[0].status" should be equal to 400
4949
And the JSON node "errors[0].detail" should exist
50-
And the JSON node "errors[0].detail" should exist
50+
And the JSON node "errors[0].type" should exist
5151

52+
Scenario: Get an rfc 7807 error
53+
When I send a "POST" request to "/does_not_exist" with body:
54+
"""
55+
{}
56+
"""
57+
Then the response status code should be 404
58+
And the response should be in JSON
59+
And the header "Content-Type" should be equal to "application/vnd.api+json; charset=utf-8"
60+
And the JSON node "errors[0].title" should be equal to "An error occurred"
61+
And the JSON node "errors[0].status" should be equal to 404
62+
And the JSON node "errors[0].detail" should exist
63+
And the JSON node "errors[0].type" should exist

features/main/standard_put.feature

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,48 @@ Feature: Spec-compliant PUT support
4747
"bar": ""
4848
}
4949
"""
50+
51+
@createSchema
52+
@!mongodb
53+
Scenario: Create a new resource identified by an uid
54+
When I add "Content-Type" header equal to "application/ld+json"
55+
And I send a "PUT" request to "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335" with body:
56+
"""
57+
{
58+
"name": "test"
59+
}
60+
"""
61+
Then the response status code should be 201
62+
And the response should be in JSON
63+
And the JSON should be equal to:
64+
"""
65+
{
66+
"@context": "/contexts/UidIdentified",
67+
"@id": "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335",
68+
"@type": "UidIdentified",
69+
"id": "fbcf5910-d915-4f7d-ba39-6b2957c57335",
70+
"name": "test"
71+
}
72+
"""
73+
74+
@!mongodb
75+
Scenario: Replace an existing resource
76+
When I add "Content-Type" header equal to "application/ld+json"
77+
And I send a "PUT" request to "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335" with body:
78+
"""
79+
{
80+
"name": "bar"
81+
}
82+
"""
83+
Then the response status code should be 200
84+
And the response should be in JSON
85+
And the JSON should be equal to:
86+
"""
87+
{
88+
"@context": "/contexts/UidIdentified",
89+
"@id": "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335",
90+
"@type": "UidIdentified",
91+
"id": "fbcf5910-d915-4f7d-ba39-6b2957c57335",
92+
"name": "bar"
93+
}
94+
"""

features/openapi/docs.feature

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ Feature: Documentation support
100100
{
101101
"owl:maxCardinality": 1,
102102
"type": "string",
103-
"format": "iri-reference"
103+
"format": "iri-reference",
104+
"example": "https://example.com/"
104105
}
105106
"""
106107
# Enable these tests when SF 4.4 / PHP 7.1 support is dropped

src/Doctrine/Common/Filter/OrderFilterTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function getDescription(string $resourceClass): array
4343
$properties = array_fill_keys($fieldNames, null);
4444
}
4545

46-
foreach ($properties as $property => $propertyOptions) {
46+
foreach ($properties ?? [] as $property => $propertyOptions) {
4747
if (!$this->isPropertyMapped($property, $resourceClass)) {
4848
continue;
4949
}

src/Doctrine/Common/State/PersistProcessor.php

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,46 +50,46 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
5050
// https://github.com/doctrine/orm/issues/8461#issuecomment-1250233555
5151
if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false)) {
5252
\assert(method_exists($manager, 'getReference'));
53-
// TODO: the call to getReference is most likely to fail with complex identifiers
5453
$newData = $data;
55-
if ($previousData = $context['previous_data']) {
56-
$newData = 1 === \count($uriVariables) ? $manager->getReference($class, current($uriVariables)) : clone $previousData;
57-
}
58-
5954
$identifiers = array_reverse($uriVariables);
6055
$links = $this->getLinks($class, $operation, $context);
6156
$reflectionProperties = $this->getReflectionProperties($data);
6257

63-
if (!$previousData) {
64-
foreach (array_reverse($links) as $link) {
65-
if ($link->getExpandedValue() || !$link->getFromClass()) {
58+
// TODO: the call to getReference is most likely to fail with complex identifiers
59+
if ($previousData = $context['previous_data']) {
60+
$classMetadata = $manager->getClassMetadata($class);
61+
$identifiers = $classMetadata->getIdentifierValues($previousData);
62+
$newData = 1 === \count($identifiers) ? $manager->getReference($class, current($identifiers)) : clone $previousData;
63+
64+
foreach ($reflectionProperties as $propertyName => $reflectionProperty) {
65+
// // Don't override the property if it's part of the subresource system
66+
if (isset($identifiers[$propertyName]) || isset($uriVariables[$propertyName])) {
6667
continue;
6768
}
6869

69-
$identifierProperties = $link->getIdentifiers();
70-
$hasCompositeIdentifiers = 1 < \count($identifierProperties);
70+
// Skip URI variables as sometime an uri variable is not the doctrine identifier
71+
foreach ($links as $link) {
72+
if (\in_array($propertyName, $link->getIdentifiers(), true)) {
73+
continue 2;
74+
}
75+
}
7176

72-
foreach ($identifierProperties as $identifierProperty) {
73-
$reflectionProperty = $reflectionProperties[$identifierProperty];
74-
$reflectionProperty->setValue($newData, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
77+
if (($newValue = $reflectionProperty->getValue($data)) !== $reflectionProperty->getValue($newData)) {
78+
$reflectionProperty->setValue($newData, $newValue);
7579
}
7680
}
7781
} else {
78-
foreach ($reflectionProperties as $propertyName => $reflectionProperty) {
79-
// Don't override the property if it's part of the subresource system
80-
if (isset($uriVariables[$propertyName])) {
82+
foreach (array_reverse($links) as $link) {
83+
if ($link->getExpandedValue() || !$link->getFromClass()) {
8184
continue;
8285
}
8386

84-
foreach ($links as $link) {
85-
$identifierProperties = $link->getIdentifiers();
86-
if (\in_array($propertyName, $identifierProperties, true)) {
87-
continue;
88-
}
87+
$identifierProperties = $link->getIdentifiers();
88+
$hasCompositeIdentifiers = 1 < \count($identifierProperties);
8989

90-
if (($newValue = $reflectionProperty->getValue($data)) !== $reflectionProperty->getValue($newData)) {
91-
$reflectionProperty->setValue($newData, $newValue);
92-
}
90+
foreach ($identifierProperties as $identifierProperty) {
91+
$reflectionProperty = $reflectionProperties[$identifierProperty];
92+
$reflectionProperty->setValue($newData, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
9393
}
9494
}
9595
}
@@ -113,7 +113,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
113113
private function isDeferredExplicit(DoctrineObjectManager $manager, $data): bool
114114
{
115115
$classMetadata = $manager->getClassMetadata($this->getObjectClass($data));
116-
if (method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) {
116+
if ($classMetadata && method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) { // @phpstan-ignore-line metadata can be null
117117
return $classMetadata->isChangeTrackingDeferredExplicit();
118118
}
119119

src/Hal/JsonSchema/SchemaFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Hal\JsonSchema;
1515

1616
use ApiPlatform\JsonSchema\Schema;
17+
use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface;
1718
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
1819
use ApiPlatform\Metadata\Operation;
1920

@@ -46,6 +47,9 @@ final class SchemaFactory implements SchemaFactoryInterface
4647
public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
4748
{
4849
$this->addDistinctFormat('jsonhal');
50+
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
51+
$this->schemaFactory->setSchemaFactory($this);
52+
}
4953
}
5054

5155
/**

src/Hydra/JsonSchema/SchemaFactory.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\JsonLd\ContextBuilder;
1717
use ApiPlatform\JsonSchema\Schema;
1818
use ApiPlatform\JsonSchema\SchemaFactory as BaseSchemaFactory;
19+
use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface;
1920
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
2021
use ApiPlatform\Metadata\Operation;
2122

@@ -60,6 +61,9 @@ final class SchemaFactory implements SchemaFactoryInterface
6061
public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
6162
{
6263
$this->addDistinctFormat('jsonld');
64+
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
65+
$this->schemaFactory->setSchemaFactory($this);
66+
}
6367
}
6468

6569
/**

src/JsonApi/Serializer/ErrorNormalizer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public function normalize(mixed $object, string $format = null, array $context =
5656
$jsonApiObject = $this->itemNormalizer->normalize($object, $format, $context);
5757
$error = $jsonApiObject['data']['attributes'];
5858
$error['id'] = $jsonApiObject['data']['id'];
59+
$error['type'] = $jsonApiObject['data']['id'];
5960

6061
return ['errors' => [$error]];
6162
}

src/JsonSchema/Metadata/Property/Factory/SchemaPropertyMetadataFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ private function getClassType(?string $className, bool $nullable, ?bool $readabl
244244
if (!$this->isResourceClass($className) && is_a($className, \BackedEnum::class, true)) {
245245
$enumCases = array_map(static fn (\BackedEnum $enum): string|int => $enum->value, $className::cases());
246246

247-
$type = \is_string($enumCases[0] ?? '') ? 'string' : 'int';
247+
$type = \is_string($enumCases[0] ?? '') ? 'string' : 'integer';
248248

249249
if ($nullable) {
250250
$enumCases[] = null;
@@ -260,6 +260,7 @@ private function getClassType(?string $className, bool $nullable, ?bool $readabl
260260
return [
261261
'type' => 'string',
262262
'format' => 'iri-reference',
263+
'example' => 'https://example.com/',
263264
];
264265
}
265266

src/JsonSchema/SchemaFactory.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@
3333
*
3434
* @author Kévin Dunglas <[email protected]>
3535
*/
36-
final class SchemaFactory implements SchemaFactoryInterface
36+
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
3737
{
3838
use ResourceClassInfoTrait;
3939
private array $distinctFormats = [];
4040
private ?TypeFactoryInterface $typeFactory = null;
41+
private ?SchemaFactoryInterface $schemaFactory = null;
4142
// Edge case where the related resource is not readable (for example: NotExposed) but we have groups to read the whole related object
4243
public const FORCE_SUBSCHEMA = '_api_subschema_force_readable_link';
4344
public const OPENAPI_DEFINITION_NAME = 'openapi_definition_name';
@@ -217,7 +218,8 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
217218
continue;
218219
}
219220

220-
$subSchema = $this->buildSchema($className, $format, $parentType, null, $subSchema, $serializerContext + [self::FORCE_SUBSCHEMA => true], false);
221+
$subSchemaFactory = $this->schemaFactory ?: $this;
222+
$subSchema = $subSchemaFactory->buildSchema($className, $format, $parentType, null, $subSchema, $serializerContext + [self::FORCE_SUBSCHEMA => true], false);
221223
if (!isset($subSchema['$ref'])) {
222224
continue;
223225
}
@@ -399,4 +401,9 @@ private function getShortClassName(string $fullyQualifiedName): string
399401

400402
return end($parts);
401403
}
404+
405+
public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
406+
{
407+
$this->schemaFactory = $schemaFactory;
408+
}
402409
}

0 commit comments

Comments
 (0)