Skip to content

Commit ad548ba

Browse files
committed
Merge 4.1
2 parents 757bcbb + fe921cd commit ad548ba

31 files changed

+518
-121
lines changed

CHANGELOG.md

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

3+
## v4.1.18
4+
5+
### Bug fixes
6+
7+
* [0e712d969](https://github.com/api-platform/core/commit/0e712d9692f018b9816764ada9fa4c7b02b5e028) fix(symfony): catch InvalidUriVariableException in IriConverter (#7271)
8+
* [221e5d97f](https://github.com/api-platform/core/commit/221e5d97f9e1114d642c834e29392a35967a9960) fix(openapi): command options mode (`InputOption::VALUE_REQUIRED`) (#7266)
9+
* [3ed6f30f5](https://github.com/api-platform/core/commit/3ed6f30f5ce60ac45a1a1df6bac9372a7b712d51) fix(symfony): fix resolving groups in ValidationGroupsExtractorTrait when GroupSequence (#7272)
10+
* [6e4e46bfe](https://github.com/api-platform/core/commit/6e4e46bfef0aaa87b2bdc56a1b28510fcdaa2914) fix(json-schema): Rebuild sub-schema definition without `@id` property when `genId` is `false` (#7162) (#7251)
11+
* [803165228](https://github.com/api-platform/core/commit/80316522807be2af25657f2936341ce2f527c364) fix(symfony): missing GroupSequence type for validation groups
12+
* [85e18a2d6](https://github.com/api-platform/core/commit/85e18a2d665baf791027a93106d6a6c293464883) fix(metadata): support stateOptions in YAML and XML for Doctrine ORM/ODM (#7217)
13+
* [88e0073bf](https://github.com/api-platform/core/commit/88e0073bf2eb9c27a35d6a04a32919908cdae704) fix(validator): error xml format output
14+
* [bf271d139](https://github.com/api-platform/core/commit/bf271d139aa6cd6c74d2dd0ee5a482d03a1afca4) fix(validator): parameter validation list<string>|string (#7245)
15+
* [d3e73f09a](https://github.com/api-platform/core/commit/d3e73f09afd6bde5763e0c2c7a4a0203f857d7c3) fix(metadata): read every OpenApiOperation attributes in Xml instead of only "deprecated" (#7189)
16+
* [e40bb19a6](https://github.com/api-platform/core/commit/e40bb19a6d80d60e5f06ac02b5bbf1aaac08e46a) fix(laravel): decorate error handler (#7247)
17+
* [f0604a07e](https://github.com/api-platform/core/commit/f0604a07e79ba2ee6a47cee20fcdaebbb68bbcaf) fix: getcontainer return type (#7230)
18+
* [f46c0a3f4](https://github.com/api-platform/core/commit/f46c0a3f47265d48d2863860d0e274f83367c929) fix(jsonld): reset gen_id configuration (#7264)
19+
* [f82464431](https://github.com/api-platform/core/commit/f8246443134b07fe6a2b591642af1ed3a5f40b44) fix(state): depend only on translation contracts (#7262)
20+
* [fa37c1eba](https://github.com/api-platform/core/commit/fa37c1eba871c533c2e3436a915d0f02dd2baa78) fix(state): error xml format output (#7273)
21+
22+
### Experimental Features
23+
24+
* [8885000db](https://github.com/api-platform/core/commit/8885000dbfa610146ff8f3187d41a0dde9b22e26) feat(state): cast parameter values to validate with the Type constraint (#7240)
25+
326
## v4.1.17
427

528
### Bug fixes

src/Doctrine/Odm/phpunit.baseline.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0"?>
22
<files version="1">
3-
<file path="vendor/symfony/contracts/Deprecation/function.php">
4-
<line number="25" hash="c6af5d66288d0667e424978000f29571e4954b81">
5-
<issue><![CDATA[Since symfony/property-info 7.3: The "Symfony\Component\PropertyInfo\Type" class is deprecated. Use "Symfony\Component\TypeInfo\Type" class from "symfony/type-info" instead.]]></issue>
6-
</line>
3+
<file path="vendor/symfony/deprecation-contracts/function.php">
4+
<line number="25" hash="c6af5d66288d0667e424978000f29571e4954b81">
5+
<issue><![CDATA[Since symfony/property-info 7.3: The "Symfony\Component\PropertyInfo\Type" class is deprecated. Use "Symfony\Component\TypeInfo\Type" class from "symfony/type-info" instead.]]></issue>
6+
</line>
77
</file>
88
</files>

src/GraphQl/phpunit.baseline.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0"?>
22
<files version="1">
3-
<file path="vendor/symfony/contracts/Deprecation/function.php">
4-
<line number="25" hash="c6af5d66288d0667e424978000f29571e4954b81">
5-
<issue><![CDATA[Since symfony/property-info 7.3: The "Symfony\Component\PropertyInfo\Type" class is deprecated. Use "Symfony\Component\TypeInfo\Type" class from "symfony/type-info" instead.]]></issue>
6-
</line>
3+
<file path="vendor/symfony/deprecation-contracts/function.php">
4+
<line number="25" hash="c6af5d66288d0667e424978000f29571e4954b81">
5+
<issue><![CDATA[Since symfony/property-info 7.3: The "Symfony\Component\PropertyInfo\Type" class is deprecated. Use "Symfony\Component\TypeInfo\Type" class from "symfony/type-info" instead.]]></issue>
6+
</line>
77
</file>
88
</files>

src/JsonLd/Serializer/ItemNormalizer.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ public function normalize(mixed $object, ?string $format = null, array $context
119119
$metadata = $this->createJsonLdContext($this->contextBuilder, $object, $context);
120120
}
121121

122-
// maybe not needed anymore
123-
if (isset($context['operation']) && $previousResourceClass !== $resourceClass) {
124-
unset($context['operation'], $context['operation_name']);
122+
// Special case: non-resource got serialized and contains a resource therefore we need to reset part of the context
123+
if ($previousResourceClass !== $resourceClass) {
124+
unset($context['operation'], $context['operation_name'], $context['output']);
125125
}
126126

127127
if (true === ($context['output']['gen_id'] ?? true) && true === ($context['force_iri_generation'] ?? true) && $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context)) {
@@ -136,9 +136,12 @@ public function normalize(mixed $object, ?string $format = null, array $context
136136
return $data;
137137
}
138138

139-
if (!isset($metadata['@type']) && $isResourceClass) {
140-
$operation = $context['operation'] ?? $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation();
139+
$operation = $context['operation'] ?? null;
140+
if ($isResourceClass && !$operation) {
141+
$operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation();
142+
}
141143

144+
if (!isset($metadata['@type']) && $operation) {
142145
$types = $operation instanceof HttpOperation ? $operation->getTypes() : null;
143146
if (null === $types) {
144147
$types = [$operation->getShortName()];

src/Laravel/ApiPlatformDeferredProvider.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
use ApiPlatform\State\Provider\ParameterProvider;
8484
use ApiPlatform\State\Provider\SecurityParameterProvider;
8585
use ApiPlatform\State\ProviderInterface;
86-
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerInterface;
86+
use Illuminate\Contracts\Debug\ExceptionHandler;
8787
use Illuminate\Contracts\Foundation\Application;
8888
use Illuminate\Contracts\Support\DeferrableProvider;
8989
use Illuminate\Support\ServiceProvider;
@@ -252,9 +252,9 @@ public function register(): void
252252
);
253253
});
254254

255-
$this->app->singleton(
256-
ExceptionHandlerInterface::class,
257-
function (Application $app) {
255+
$this->app->extend(
256+
ExceptionHandler::class,
257+
function (ExceptionHandler $decorated, Application $app) {
258258
/** @var ConfigRepository */
259259
$config = $app['config'];
260260

@@ -267,7 +267,8 @@ function (Application $app) {
267267
$app->make(Negotiator::class),
268268
$config->get('api-platform.exception_to_status'),
269269
$config->get('app.debug'),
270-
$config->get('api-platform.error_formats')
270+
$config->get('api-platform.error_formats'),
271+
$decorated
271272
);
272273
}
273274
);

src/Laravel/Exception/ErrorHandler.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Illuminate\Auth\Access\AuthorizationException;
2828
use Illuminate\Auth\AuthenticationException;
2929
use Illuminate\Contracts\Container\Container;
30+
use Illuminate\Contracts\Debug\ExceptionHandler;
3031
use Illuminate\Foundation\Exceptions\Handler as ExceptionsHandler;
3132
use Negotiation\Negotiator;
3233
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
@@ -54,6 +55,7 @@ public function __construct(
5455
private readonly ?array $exceptionToStatus = null,
5556
private readonly ?bool $debug = false,
5657
private readonly ?array $errorFormats = null,
58+
private readonly ?ExceptionHandler $decorated = null,
5759
) {
5860
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
5961
$this->negotiator = $negotiator;
@@ -65,7 +67,7 @@ public function render($request, \Throwable $exception)
6567
$apiOperation = $this->initializeOperation($request);
6668

6769
if (!$apiOperation) {
68-
return parent::render($request, $exception);
70+
return $this->decorated ? $this->decorated->render($request, $exception) : parent::render($request, $exception);
6971
}
7072

7173
$formats = $this->errorFormats ?? ['jsonproblem' => ['application/problem+json']];
@@ -157,9 +159,12 @@ public function render($request, \Throwable $exception)
157159
}
158160

159161
try {
160-
return $this->apiPlatformController->__invoke($dup);
162+
$response = $this->apiPlatformController->__invoke($dup);
163+
$this->decorated->render($dup, $exception);
164+
165+
return $response;
161166
} catch (\Throwable $e) {
162-
return parent::render($dup, $e);
167+
return $this->decorated ? $this->decorated->render($request, $exception) : parent::render($request, $exception);
163168
}
164169
}
165170

src/Laravel/workbench/app/Models/Book.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
use Illuminate\Database\Eloquent\Factories\HasFactory;
3838
use Illuminate\Database\Eloquent\Model;
3939
use Illuminate\Database\Eloquent\Relations\BelongsTo;
40+
use Symfony\Component\TypeInfo\Type\BuiltinType;
41+
use Symfony\Component\TypeInfo\TypeIdentifier;
4042
use Workbench\App\Http\Requests\BookFormRequest;
4143

4244
#[ApiResource(
@@ -79,7 +81,7 @@
7981
property: 'name'
8082
)]
8183
#[QueryParameter(key: 'properties', filter: PropertyFilter::class)]
82-
#[QueryParameter(key: 'published', filter: BooleanFilter::class)]
84+
#[QueryParameter(key: 'published', filter: BooleanFilter::class, nativeType: new BuiltinType(TypeIdentifier::BOOL))]
8385
class Book extends Model
8486
{
8587
use HasFactory;

src/Metadata/Parameter.php

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ abstract class Parameter
2727
* @param (array<string, mixed>&array{type?: string, default?: string})|null $schema
2828
* @param array<string, mixed> $extraProperties
2929
* @param ParameterProviderInterface|callable|string|null $provider
30-
* @param list<string> $properties a list of properties this parameter applies to (works with the :property placeholder)
30+
* @param list<string> $properties a list of properties this parameter applies to (works with the :property placeholder)
3131
* @param FilterInterface|string|null $filter
32-
* @param mixed $constraints an array of Symfony constraints, or an array of Laravel rules
33-
* @param Type $nativeType the PHP native type, we cast values to an array if its a CollectionType, if not and it's an array with a single value we use it (eg: HTTP Header)
32+
* @param mixed $constraints an array of Symfony constraints, or an array of Laravel rules
33+
* @param Type $nativeType the PHP native type, we cast values to an array if its a CollectionType, if not and it's an array with a single value we use it (eg: HTTP Header)
34+
* @param ?bool $castToNativeType whether API Platform should cast your parameter to the nativeType declared
35+
* @param ?callable(mixed): mixed $castFn the closure used to cast your parameter, this gets called only when $castToNativeType is set
3436
*/
3537
public function __construct(
3638
protected ?string $key = null,
@@ -51,6 +53,8 @@ public function __construct(
5153
protected array|string|null $filterContext = null,
5254
protected ?Type $nativeType = null,
5355
protected ?bool $castToArray = null,
56+
protected ?bool $castToNativeType = null,
57+
protected mixed $castFn = null,
5458
) {
5559
}
5660

@@ -332,4 +336,33 @@ public function withCastToArray(bool $castToArray): self
332336

333337
return $self;
334338
}
339+
340+
public function getCastToNativeType(): ?bool
341+
{
342+
return $this->castToNativeType;
343+
}
344+
345+
public function withCastToNativeType(bool $castToNativeType): self
346+
{
347+
$self = clone $this;
348+
$self->castToNativeType = $castToNativeType;
349+
350+
return $self;
351+
}
352+
353+
public function getCastFn(): ?callable
354+
{
355+
return $this->castFn;
356+
}
357+
358+
/**
359+
* @param callable(mixed): mixed $castFn
360+
*/
361+
public function withCastFn(mixed $castFn): self
362+
{
363+
$self = clone $this;
364+
$self->castFn = $castFn;
365+
366+
return $self;
367+
}
335368
}

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
2929
use ApiPlatform\OpenApi\Model\Parameter as OpenApiParameter;
3030
use ApiPlatform\Serializer\Filter\FilterInterface as SerializerFilterInterface;
31+
use ApiPlatform\State\Parameter\ValueCaster;
3132
use ApiPlatform\State\Util\StateOptionsTrait;
3233
use Psr\Container\ContainerInterface;
3334
use Psr\Log\LoggerInterface;
3435
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
3536
use Symfony\Component\TypeInfo\Type;
37+
use Symfony\Component\TypeInfo\TypeIdentifier;
3638

3739
/**
3840
* Prepares Parameters documentation by reading its filter details and declaring an OpenApi parameter.
@@ -158,11 +160,27 @@ private function getDefaultParameters(Operation $operation, string $resourceClas
158160
$parameter = $parameter->withNativeType(Type::string());
159161
} elseif ('boolean' === ($parameter->getSchema()['type'] ?? null)) {
160162
$parameter = $parameter->withNativeType(Type::bool());
163+
} elseif ('integer' === ($parameter->getSchema()['type'] ?? null)) {
164+
$parameter = $parameter->withNativeType(Type::int());
165+
} elseif ('number' === ($parameter->getSchema()['type'] ?? null)) {
166+
$parameter = $parameter->withNativeType(Type::float());
161167
} else {
162168
$parameter = $parameter->withNativeType(Type::union(Type::string(), Type::list(Type::string())));
163169
}
164170
}
165171

172+
if ($parameter->getCastToNativeType() && null === $parameter->getCastFn() && ($nativeType = $parameter->getNativeType())) {
173+
if ($nativeType->isIdentifiedBy(TypeIdentifier::BOOL)) {
174+
$parameter = $parameter->withCastFn([ValueCaster::class, 'toBool']);
175+
}
176+
if ($nativeType->isIdentifiedBy(TypeIdentifier::INT)) {
177+
$parameter = $parameter->withCastFn([ValueCaster::class, 'toInt']);
178+
}
179+
if ($nativeType->isIdentifiedBy(TypeIdentifier::FLOAT)) {
180+
$parameter = $parameter->withCastFn([ValueCaster::class, 'toFloat']);
181+
}
182+
}
183+
166184
$priority = $parameter->getPriority() ?? $internalPriority--;
167185
$parameters->add($key, $parameter->withPriority($priority));
168186
}

src/Metadata/UriVariablesConverterInterface.php

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

1616
use ApiPlatform\Metadata\Exception\InvalidIdentifierException;
17+
use ApiPlatform\Metadata\Exception\InvalidUriVariableException;
1718

1819
/**
1920
* Identifier converter.
@@ -29,6 +30,7 @@ interface UriVariablesConverterInterface
2930
* @param string $class The class to which the URI variables belong to
3031
*
3132
* @throws InvalidIdentifierException
33+
* @throws InvalidUriVariableException
3234
*
3335
* @return array Array indexed by identifiers properties with their values denormalized
3436
*/

0 commit comments

Comments
 (0)