Skip to content

Commit 90e71f0

Browse files
committed
Merge 3.3
2 parents e463d0b + 25c5f22 commit 90e71f0

Some content is hidden

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

49 files changed

+511
-119
lines changed

CHANGELOG.md

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

3+
## v3.3.7
4+
5+
### Bug fixes
6+
7+
* [0ed1b637a](https://github.com/api-platform/core/commit/0ed1b637a84d0860c2e014e2473b52d17de9276b) fix(metadata): wrong schema generated if openapicontext set on array (#6431)
8+
* [57f930c65](https://github.com/api-platform/core/commit/57f930c65cb067b1727488ba6db6188c6c5a01c6) fix(openapi): parameters can disable openapi (#6440)
9+
* [69b4d35b9](https://github.com/api-platform/core/commit/69b4d35b9764e36dab0484e1a7332886e1de9b2c) fix(state): provider as ParameterProviderInterface (#6430)
10+
* [842091ddf](https://github.com/api-platform/core/commit/842091ddf5b41c7a0d76bfbcddccff2920a84c35) fix(jsonschema): make all required properties optional in PATCH operation with 'json' format (#6394)
11+
* [af34e72ed](https://github.com/api-platform/core/commit/af34e72ed6bb0d5f7235fcc9c2a6c7810ba4f9c2) fix(openapi): yaml openapi export should have numeric keys as string (#6436)
12+
* [b42e25f2d](https://github.com/api-platform/core/commit/b42e25f2dcb5e0c1759c9482c08c9929ef86dc90) fix(state): parameter decorates main chain (#6434)
13+
* [c922ba3f5](https://github.com/api-platform/core/commit/c922ba3f5cf6117e85548697a982fb1cbe3dde2e) fix(symfony): check method for readonly routes (#6437)
14+
315
## v3.3.6
416

517
### Bug fixes
@@ -223,6 +235,12 @@ api_platform:
223235
form: ['multipart/form-data']
224236
```
225237

238+
## v3.2.25
239+
240+
### Bug fixes
241+
242+
* [0ed1b637a](https://github.com/api-platform/core/commit/0ed1b637a84d0860c2e014e2473b52d17de9276b) fix(metadata): wrong schema generated if openapicontext set on array (#6431)
243+
226244
## v3.2.24
227245

228246
### Bug fixes

features/graphql/query.feature

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,16 @@ Feature: GraphQL query support
664664
And the header "Content-Type" should be equal to "application/json"
665665
And the JSON node "data.dummyDifferentGraphQlSerializationGroup.name" should be equal to "Name #1"
666666
And the JSON node "data.dummyDifferentGraphQlSerializationGroup.title" should be equal to "Title #1"
667+
668+
Scenario: Call security after resolver
669+
When I send the following GraphQL request:
670+
"""
671+
{
672+
getSecurityAfterResolver(id: "/security_after_resolvers/1") {
673+
name
674+
}
675+
}
676+
"""
677+
Then the response status code should be 200
678+
And the header "Content-Type" should be equal to "application/json"
679+
And the JSON node "data.getSecurityAfterResolver.name" should be equal to "test"

features/jsonld/interface_as_resource.feature

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ Feature: JSON-LD using interface as resource
1515
"code": "WONDERFUL_TAXON"
1616
}
1717
"""
18-
When I send a "GET" request to "/taxa/WONDERFUL_TAXON"
18+
When I send a "GET" request to "/taxons/WONDERFUL_TAXON"
1919
Then the response status code should be 200
2020
And the response should be in JSON
2121
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
2222
And the JSON should be equal to:
2323
"""
2424
{
2525
"@context": "/contexts/Taxon",
26-
"@id": "/taxa/WONDERFUL_TAXON",
26+
"@id": "/taxons/WONDERFUL_TAXON",
2727
"@type": "Taxon",
2828
"code": "WONDERFUL_TAXON"
2929
}
@@ -49,7 +49,7 @@ Feature: JSON-LD using interface as resource
4949
"@type": "Product",
5050
"code": "GREAT_PRODUCT",
5151
"mainTaxon": {
52-
"@id": "/taxa/WONDERFUL_TAXON",
52+
"@id": "/taxons/WONDERFUL_TAXON",
5353
"@type": "Taxon",
5454
"code": "WONDERFUL_TAXON"
5555
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2020
use ApiPlatform\Metadata\ResourceClassResolverInterface;
2121
use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
22+
use Doctrine\Common\Collections\ArrayCollection;
2223
use Ramsey\Uuid\UuidInterface;
2324
use Symfony\Component\PropertyInfo\Type;
2425
use Symfony\Component\Uid\Ulid;
@@ -100,15 +101,21 @@ public function create(string $resourceClass, string $property, array $options =
100101
$propertySchema['example'] = $propertySchema['default'];
101102
}
102103

103-
// never override the following keys if at least one is already set
104+
// never override the following keys if at least one is already set or if there's a custom openapi context
104105
if ([] === $types
105106
|| ($propertySchema['type'] ?? $propertySchema['$ref'] ?? $propertySchema['anyOf'] ?? $propertySchema['allOf'] ?? $propertySchema['oneOf'] ?? false)
107+
|| ($propertyMetadata->getOpenapiContext() ?? false)
106108
) {
107109
return $propertyMetadata->withSchema($propertySchema);
108110
}
109111

110112
$valueSchema = [];
111113
foreach ($types as $type) {
114+
// Temp fix for https://github.com/symfony/symfony/pull/52699
115+
if (ArrayCollection::class === $type->getClassName()) {
116+
$type = new Type($type->getBuiltinType(), $type->isNullable(), $type->getClassName(), true, $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
117+
}
118+
112119
if ($isCollection = $type->isCollection()) {
113120
$keyType = $type->getCollectionKeyTypes()[0] ?? null;
114121
$valueType = $type->getCollectionValueTypes()[0] ?? null;

src/JsonSchema/SchemaFactory.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use ApiPlatform\Metadata\CollectionOperationInterface;
1919
use ApiPlatform\Metadata\HttpOperation;
2020
use ApiPlatform\Metadata\Operation;
21+
use ApiPlatform\Metadata\Patch;
2122
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2223
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2324
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
@@ -33,6 +34,9 @@
3334
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
3435
{
3536
use ResourceMetadataTrait;
37+
38+
private const PATCH_SCHEMA_POSTFIX = '.patch';
39+
3640
private ?TypeFactoryInterface $typeFactory = null;
3741
private ?SchemaFactoryInterface $schemaFactory = null;
3842
// Edge case where the related resource is not readable (for example: NotExposed) but we have groups to read the whole related object
@@ -88,6 +92,12 @@ public function buildSchema(string $className, string $format = 'json', string $
8892
return $schema;
8993
}
9094

95+
$isJsonMergePatch = 'json' === $format && $operation instanceof Patch && Schema::TYPE_INPUT === $type;
96+
97+
if ($isJsonMergePatch) {
98+
$definitionName .= self::PATCH_SCHEMA_POSTFIX;
99+
}
100+
91101
if (!isset($schema['$ref']) && !isset($schema['type'])) {
92102
$ref = Schema::VERSION_OPENAPI === $version ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
93103
if ($forceCollection || ('POST' !== $method && $operation instanceof CollectionOperationInterface)) {
@@ -136,7 +146,7 @@ public function buildSchema(string $className, string $format = 'json', string $
136146
}
137147

138148
$normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName, $inputOrOutputClass, $format, $serializerContext) : $propertyName;
139-
if ($propertyMetadata->isRequired()) {
149+
if ($propertyMetadata->isRequired() && !$isJsonMergePatch) {
140150
$definition['required'][] = $normalizedPropertyName;
141151
}
142152

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\JsonSchema\Tests\Fixtures;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
19+
/*
20+
* This file is part of the API Platform project.
21+
*
22+
* (c) Kévin Dunglas <[email protected]>
23+
*
24+
* For the full copyright and license information, please view the LICENSE
25+
* file that was distributed with this source code.
26+
*/
27+
28+
#[ApiResource]
29+
class DummyWithCustomOpenApiContext
30+
{
31+
#[ApiProperty(openapiContext: ['type' => 'object', 'properties' => ['alpha' => ['type' => 'integer']]])]
32+
public $acme;
33+
}

src/JsonSchema/Tests/Metadata/Property/Factory/SchemaPropertyMetadataFactoryTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\JsonSchema\Tests\Metadata\Property\Factory;
1515

1616
use ApiPlatform\JsonSchema\Metadata\Property\Factory\SchemaPropertyMetadataFactory;
17+
use ApiPlatform\JsonSchema\Tests\Fixtures\DummyWithCustomOpenApiContext;
1718
use ApiPlatform\JsonSchema\Tests\Fixtures\DummyWithEnum;
1819
use ApiPlatform\JsonSchema\Tests\Fixtures\Enum\IntEnumAsIdentifier;
1920
use ApiPlatform\Metadata\ApiProperty;
@@ -34,4 +35,18 @@ public function testEnum(): void
3435
$apiProperty = $schemaPropertyMetadataFactory->create(DummyWithEnum::class, 'intEnumAsIdentifier');
3536
$this->assertEquals(['type' => ['integer', 'null'], 'enum' => [1, 2, null]], $apiProperty->getSchema());
3637
}
38+
39+
public function testWithCustomOpenApiContext(): void
40+
{
41+
$resourceClassResolver = $this->createMock(ResourceClassResolverInterface::class);
42+
$apiProperty = new ApiProperty(
43+
builtinTypes: [new Type(builtinType: 'object', nullable: true, class: IntEnumAsIdentifier::class)],
44+
openapiContext: ['type' => 'object', 'properties' => ['alpha' => ['type' => 'integer']]],
45+
);
46+
$decorated = $this->createMock(PropertyMetadataFactoryInterface::class);
47+
$decorated->expects($this->once())->method('create')->with(DummyWithCustomOpenApiContext::class, 'acme')->willReturn($apiProperty);
48+
$schemaPropertyMetadataFactory = new SchemaPropertyMetadataFactory($resourceClassResolver, $decorated);
49+
$apiProperty = $schemaPropertyMetadataFactory->create(DummyWithCustomOpenApiContext::class, 'acme');
50+
$this->assertEquals([], $apiProperty->getSchema());
51+
}
3752
}

src/Metadata/GraphQl/Operation.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public function __construct(
4141
protected ?array $extraArgs = null,
4242
protected ?array $links = null,
4343
protected ?bool $validateAfterResolver = null,
44+
protected ?string $securityAfterResolver = null,
45+
protected ?string $securityMessageAfterResolver = null,
4446

4547
?string $shortName = null,
4648
?string $class = null,
@@ -209,4 +211,30 @@ public function withValidateAfterResolver(bool $validateAfterResolver = true): s
209211

210212
return $self;
211213
}
214+
215+
public function getSecurityAfterResolver(): ?string
216+
{
217+
return $this->securityAfterResolver;
218+
}
219+
220+
public function withSecurityAfterResolver(string $securityAfterResolver): self
221+
{
222+
$self = clone $this;
223+
$self->securityAfterResolver = $securityAfterResolver;
224+
225+
return $self;
226+
}
227+
228+
public function getSecurityMessageAfterResolver(): ?string
229+
{
230+
return $this->securityMessageAfterResolver;
231+
}
232+
233+
public function withSecurityMessageAfterResolver(string $securityMessageAfterResolver): self
234+
{
235+
$self = clone $this;
236+
$self->securityMessageAfterResolver = $securityMessageAfterResolver;
237+
238+
return $self;
239+
}
212240
}

src/Metadata/GraphQl/Query.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public function __construct(
2424
?array $args = null,
2525
?array $extraArgs = null,
2626
?array $links = null,
27+
?string $securityAfterResolver = null,
28+
?string $securityMessageAfterResolver = null,
2729

2830
?string $shortName = null,
2931
?string $class = null,
@@ -79,6 +81,8 @@ public function __construct(
7981
args: $args,
8082
extraArgs: $extraArgs,
8183
links: $links,
84+
securityAfterResolver: $securityAfterResolver,
85+
securityMessageAfterResolver: $securityMessageAfterResolver,
8286
shortName: $shortName,
8387
class: $class,
8488
paginationEnabled: $paginationEnabled,

src/Metadata/GraphQl/QueryCollection.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public function __construct(
2525
?array $args = null,
2626
?array $extraArgs = null,
2727
?array $links = null,
28+
?string $securityAfterResolver = null,
29+
?string $securityMessageAfterResolver = null,
2830

2931
?string $shortName = null,
3032
?string $class = null,
@@ -80,6 +82,8 @@ public function __construct(
8082
args: $args,
8183
extraArgs: $extraArgs,
8284
links: $links,
85+
securityAfterResolver: $securityAfterResolver,
86+
securityMessageAfterResolver: $securityMessageAfterResolver,
8387
shortName: $shortName,
8488
class: $class,
8589
paginationEnabled: $paginationEnabled,

0 commit comments

Comments
 (0)