Skip to content

Commit c59e275

Browse files
authored
Merge 4.0
Merge 4.0
2 parents 6bdc01c + 4fac6f5 commit c59e275

File tree

24 files changed

+349
-35
lines changed

24 files changed

+349
-35
lines changed

features/filter/filter_validation.feature

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ Feature: Validate filters based upon filter description
2525
Scenario: Required filter should throw an error if not set
2626
When I am on "/array_filter_validators"
2727
Then the response status code should be 422
28-
And the JSON node "detail" should be equal to 'arrayRequired: This value should not be blank.\nindexedArrayRequired: This value should not be blank.'
28+
And the JSON node "detail" should be equal to 'arrayRequired[]: This value should not be blank.\nindexedArrayRequired[foo]: This value should not be blank.'
2929

3030
When I am on "/array_filter_validators?arrayRequired[foo]=foo"
3131
Then the response status code should be 422
32-
And the JSON node "detail" should be equal to 'indexedArrayRequired: This value should not be blank.'
32+
And the JSON node "detail" should be equal to 'indexedArrayRequired[foo]: This value should not be blank.'
3333

3434
When I am on "/array_filter_validators?arrayRequired[]=foo"
3535
Then the response status code should be 422
36-
And the JSON node "detail" should be equal to 'indexedArrayRequired: This value should not be blank.'
36+
And the JSON node "detail" should be equal to 'indexedArrayRequired[foo]: This value should not be blank.'
3737

3838
Scenario: Test filter bounds: maximum
3939
When I am on "/filter_validators?required=foo&required-allow-empty&maximum=10"
@@ -49,7 +49,7 @@ Feature: Validate filters based upon filter description
4949

5050
When I am on "/filter_validators?required=foo&required-allow-empty&exclusiveMaximum=10"
5151
Then the response status code should be 422
52-
And the JSON node "detail" should be equal to 'maximum: This value should be less than 10.'
52+
And the JSON node "detail" should be equal to 'exclusiveMaximum: This value should be less than 10.'
5353

5454
Scenario: Test filter bounds: minimum
5555
When I am on "/filter_validators?required=foo&required-allow-empty&minimum=5"

src/Doctrine/Odm/Extension/ParameterExtension.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public function __construct(
4444
private function applyFilter(Builder $aggregationBuilder, ?string $resourceClass = null, ?Operation $operation = null, array &$context = []): void
4545
{
4646
foreach ($operation->getParameters() ?? [] as $parameter) {
47-
// TODO: remove the null equality as a parameter can have a null value
4847
if (null === ($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
4948
continue;
5049
}

src/Doctrine/Orm/Extension/ParameterExtension.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public function __construct(
4646
private function applyFilter(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
4747
{
4848
foreach ($operation?->getParameters() ?? [] as $parameter) {
49-
// TODO: remove the null equality as a parameter can have a null value
5049
if (null === ($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
5150
continue;
5251
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\JsonApi\Serializer;
15+
16+
use ApiPlatform\Exception\ErrorCodeSerializableInterface;
17+
use Symfony\Component\ErrorHandler\Exception\FlattenException;
18+
use Symfony\Component\HttpFoundation\Response;
19+
20+
/**
21+
* @deprecated
22+
*/
23+
trait ErrorNormalizerTrait
24+
{
25+
private function getErrorMessage($object, array $context, bool $debug = false): string
26+
{
27+
$message = $object->getMessage();
28+
29+
if ($debug) {
30+
return $message;
31+
}
32+
33+
if ($object instanceof FlattenException) {
34+
$statusCode = $context['statusCode'] ?? $object->getStatusCode();
35+
if ($statusCode >= 500 && $statusCode < 600) {
36+
$message = Response::$statusTexts[$statusCode] ?? Response::$statusTexts[Response::HTTP_INTERNAL_SERVER_ERROR];
37+
}
38+
}
39+
40+
return $message;
41+
}
42+
43+
private function getErrorCode(object $object): ?string
44+
{
45+
if ($object instanceof FlattenException) {
46+
$exceptionClass = $object->getClass();
47+
} else {
48+
$exceptionClass = $object::class;
49+
}
50+
51+
if (is_a($exceptionClass, ErrorCodeSerializableInterface::class, true)) {
52+
return $exceptionClass::getErrorCode();
53+
}
54+
55+
return null;
56+
}
57+
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ final class SchemaPropertyMetadataFactory implements PropertyMetadataFactoryInte
3434

3535
public const JSON_SCHEMA_USER_DEFINED = 'user_defined_schema';
3636

37-
public function __construct(ResourceClassResolverInterface $resourceClassResolver, private readonly ?PropertyMetadataFactoryInterface $decorated = null)
38-
{
37+
public function __construct(
38+
ResourceClassResolverInterface $resourceClassResolver,
39+
private readonly ?PropertyMetadataFactoryInterface $decorated = null,
40+
) {
3941
$this->resourceClassResolver = $resourceClassResolver;
4042
}
4143

@@ -198,6 +200,8 @@ private function typeToArray(Type $type, ?bool $readableLink = null): array
198200
* Gets the JSON Schema document which specifies the data type corresponding to the given PHP class, and recursively adds needed new schema to the current schema if provided.
199201
*
200202
* Note: if the class is not part of exceptions listed above, any class is considered as a resource.
203+
*
204+
* @throws PropertyNotFoundException
201205
*/
202206
private function getClassType(?string $className, bool $nullable, ?bool $readableLink): array
203207
{
@@ -240,7 +244,8 @@ private function getClassType(?string $className, bool $nullable, ?bool $readabl
240244
];
241245
}
242246

243-
if (!$this->isResourceClass($className) && is_a($className, \BackedEnum::class, true)) {
247+
$isResourceClass = $this->isResourceClass($className);
248+
if (!$isResourceClass && is_a($className, \BackedEnum::class, true)) {
244249
$enumCases = array_map(static fn (\BackedEnum $enum): string|int => $enum->value, $className::cases());
245250

246251
$type = \is_string($enumCases[0] ?? '') ? 'string' : 'integer';
@@ -255,15 +260,14 @@ private function getClassType(?string $className, bool $nullable, ?bool $readabl
255260
];
256261
}
257262

258-
if (true !== $readableLink && $this->isResourceClass($className)) {
263+
if (true !== $readableLink && $isResourceClass) {
259264
return [
260265
'type' => 'string',
261266
'format' => 'iri-reference',
262267
'example' => 'https://example.com/',
263268
];
264269
}
265270

266-
// TODO: add propertyNameCollectionFactory and recurse to find the underlying schema? Right now SchemaFactory does the job so we don't compute anything here.
267271
return ['type' => Schema::UNKNOWN_TYPE];
268272
}
269273

src/JsonSchema/SchemaFactory.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
179179
$propertySchemaType = $propertySchema['type'] ?? false;
180180

181181
$isUnknown = Schema::UNKNOWN_TYPE === $propertySchemaType
182-
|| ('array' === $propertySchemaType && Schema::UNKNOWN_TYPE === ($propertySchema['items']['type'] ?? null));
182+
|| ('array' === $propertySchemaType && Schema::UNKNOWN_TYPE === ($propertySchema['items']['type'] ?? null))
183+
|| ('object' === $propertySchemaType && Schema::UNKNOWN_TYPE === ($propertySchema['additionalProperties']['type'] ?? null));
183184

184185
if (
185186
!$isUnknown && (
@@ -231,8 +232,9 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
231232
}
232233

233234
if ($isCollection) {
234-
$propertySchema['items']['$ref'] = $subSchema['$ref'];
235-
unset($propertySchema['items']['type']);
235+
$key = ($propertySchema['type'] ?? null) === 'object' ? 'additionalProperties' : 'items';
236+
$propertySchema[$key]['$ref'] = $subSchema['$ref'];
237+
unset($propertySchema[$key]['type']);
236238
break;
237239
}
238240

src/Metadata/Parameter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ public function getSecurityMessage(): ?string
124124
*
125125
* @readonly
126126
*/
127-
public function getValue(): mixed
127+
public function getValue(mixed $default = new ParameterNotFound()): mixed
128128
{
129-
return $this->extraProperties['_api_values'] ?? new ParameterNotFound();
129+
return $this->extraProperties['_api_values'] ?? $default;
130130
}
131131

132132
/**

src/Metadata/Parameters.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function add(string $key, Parameter $value): self
7070
/**
7171
* @param class-string $parameterClass
7272
*/
73-
public function remove(string $key, string $parameterClass): self
73+
public function remove(string $key, string $parameterClass = QueryParameter::class): self
7474
{
7575
foreach ($this->parameters as $i => [$parameterName, $parameter]) {
7676
if ($parameterName === $key && $parameterClass === $parameter::class) {
@@ -86,7 +86,7 @@ public function remove(string $key, string $parameterClass): self
8686
/**
8787
* @param class-string $parameterClass
8888
*/
89-
public function get(string $key, string $parameterClass): ?Parameter
89+
public function get(string $key, string $parameterClass = QueryParameter::class): ?Parameter
9090
{
9191
foreach ($this->parameters as [$parameterName, $parameter]) {
9292
if ($parameterName === $key && $parameterClass === $parameter::class) {
@@ -100,7 +100,7 @@ public function get(string $key, string $parameterClass): ?Parameter
100100
/**
101101
* @param class-string $parameterClass
102102
*/
103-
public function has(string $key, string $parameterClass): bool
103+
public function has(string $key, string $parameterClass = QueryParameter::class): bool
104104
{
105105
foreach ($this->parameters as [$parameterName, $parameter]) {
106106
if ($parameterName === $key && $parameterClass === $parameter::class) {

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ private function addFilterMetadata(Parameter $parameter): Parameter
152152
return $parameter;
153153
}
154154

155+
if (!\is_object($filterId) && !$this->filterLocator->has($filterId)) {
156+
return $parameter;
157+
}
158+
155159
$filter = \is_object($filterId) ? $filterId : $this->filterLocator->get($filterId);
156160

157161
if (!$filter) {
@@ -162,7 +166,7 @@ private function addFilterMetadata(Parameter $parameter): Parameter
162166
$parameter = $parameter->withSchema($schema);
163167
}
164168

165-
if (null === $parameter->getOpenApi() && $filter instanceof OpenApiParameterFilterInterface && ($openApiParameter = $filter->getOpenApiParameters($parameter)) && $openApiParameter instanceof OpenApiParameter) {
169+
if (null === $parameter->getOpenApi() && $filter instanceof OpenApiParameterFilterInterface && ($openApiParameter = $filter->getOpenApiParameters($parameter))) {
166170
$parameter = $parameter->withOpenApi($openApiParameter);
167171
}
168172

@@ -186,7 +190,6 @@ private function setDefaults(string $key, Parameter $parameter, string $resource
186190
if ($filter instanceof SerializerFilterInterface && null === $parameter->getProvider()) {
187191
$parameter = $parameter->withProvider('api_platform.serializer.filter_parameter_provider');
188192
}
189-
190193
$currentKey = $key;
191194
if (null === $parameter->getProperty() && isset($properties[$key])) {
192195
$parameter = $parameter->withProperty($key);

src/Metadata/Tests/ParameterTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\Metadata\Tests;
15+
16+
use ApiPlatform\Metadata\QueryParameter;
17+
use ApiPlatform\State\ParameterNotFound;
18+
use PHPUnit\Framework\TestCase;
19+
20+
class ParameterTest extends TestCase
21+
{
22+
public function testDefaultValue(): void
23+
{
24+
$parameter = new QueryParameter();
25+
$this->assertSame('test', $parameter->getValue('test'));
26+
$this->assertInstanceOf(ParameterNotFound::class, $parameter->getValue());
27+
}
28+
}

0 commit comments

Comments
 (0)