Skip to content

Commit 01641cd

Browse files
committed
Merge 3.4
2 parents 2d59c63 + 86c97ca commit 01641cd

File tree

14 files changed

+154
-90
lines changed

14 files changed

+154
-90
lines changed

features/security/strong_typing.feature

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ Feature: Handle properly invalid data submitted to the API
8989
And the response should be in JSON
9090
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
9191

92+
Scenario: Ignore date with wrong format
93+
When I add "Content-Type" header equal to "application/ld+json"
94+
And I send a "POST" request to "/dummies" with body:
95+
"""
96+
{
97+
"name": "Invalid date format",
98+
"dummyDateWithFormat": "2020-01-01T00:00:00+00:00"
99+
}
100+
"""
101+
Then the response status code should be 400
102+
And the response should be in JSON
103+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
104+
92105
Scenario: Send non-array data when an array is expected
93106
When I add "Content-Type" header equal to "application/ld+json"
94107
And I send a "POST" request to "/dummies" with body:

src/Elasticsearch/Serializer/NameConverter/InnerFieldsNameConverter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*
2525
* @author Baptiste Meyer <[email protected]>
2626
*/
27-
final class InnerFieldsNameConverter implements AdvancedNameConverterInterface
27+
final class InnerFieldsNameConverter implements NameConverterInterface, AdvancedNameConverterInterface
2828
{
2929
public function __construct(private readonly NameConverterInterface $inner = new CamelCaseToSnakeCaseNameConverter())
3030
{

src/JsonApi/Serializer/ReservedAttributeNameConverter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*
2323
* @author Baptiste Meyer <[email protected]>
2424
*/
25-
final class ReservedAttributeNameConverter implements AdvancedNameConverterInterface
25+
final class ReservedAttributeNameConverter implements NameConverterInterface, AdvancedNameConverterInterface
2626
{
2727
public const JSON_API_RESERVED_ATTRIBUTES = [
2828
'id' => '_id',

src/JsonLd/Serializer/ErrorNormalizer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public function supportsNormalization(mixed $data, ?string $format = null, array
5757

5858
public function getSupportedTypes(?string $format): array
5959
{
60-
return $this->inner->getSupportedTypes($format);
60+
if (method_exists($this->inner, 'getSupportedTypes')) {
61+
return $this->inner->getSupportedTypes($format);
62+
}
63+
64+
return [];
6165
}
6266
}

src/Metadata/Util/PropertyInfoToTypeInfoHelper.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\TypeInfo\Exception\InvalidArgumentException;
1818
use Symfony\Component\TypeInfo\Type;
1919
use Symfony\Component\TypeInfo\Type\BuiltinType;
20+
use Symfony\Component\TypeInfo\Type\NullableType;
2021
use Symfony\Component\TypeInfo\Type\UnionType;
2122
use Symfony\Component\TypeInfo\TypeIdentifier;
2223

@@ -126,11 +127,16 @@ public static function createTypeFromLegacyValues(string $builtinType, bool $nul
126127

127128
public static function unwrapNullableType(Type $type): Type
128129
{
129-
if (!$type instanceof UnionType) {
130+
// BC layer for "symfony/type-info" < 7.2
131+
if (method_exists($type, 'asNonNullable')) {
132+
return (!$type instanceof UnionType) ? $type : $type->asNonNullable();
133+
}
134+
135+
if (!$type instanceof NullableType) {
130136
return $type;
131137
}
132138

133-
return $type->asNonNullable();
139+
return $type->getWrappedType();
134140
}
135141

136142
/**

src/OpenApi/Factory/OpenApiFactory.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
3333
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
3434
use ApiPlatform\OpenApi\Attributes\Webhook;
35-
use ApiPlatform\OpenApi\Model;
3635
use ApiPlatform\OpenApi\Model\Components;
3736
use ApiPlatform\OpenApi\Model\Contact;
3837
use ApiPlatform\OpenApi\Model\Info;
@@ -415,11 +414,11 @@ private function buildOpenApiResponse(array $existingResponses, int|string $stat
415414
}
416415

417416
/**
418-
* @return \ArrayObject<Model\MediaType>
417+
* @return \ArrayObject<MediaType>
419418
*/
420419
private function buildContent(array $responseMimeTypes, array $operationSchemas): \ArrayObject
421420
{
422-
/** @var \ArrayObject<Model\MediaType> $content */
421+
/** @var \ArrayObject<MediaType> $content */
423422
$content = new \ArrayObject();
424423

425424
foreach ($responseMimeTypes as $mimeType => $format) {
@@ -506,11 +505,11 @@ private function getPathDescription(string $resourceShortName, string $method, b
506505
/**
507506
* @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#linkObject.
508507
*
509-
* @return \ArrayObject<Model\Link>
508+
* @return \ArrayObject<Link>
510509
*/
511510
private function getLinks(ResourceMetadataCollection $resourceMetadataCollection, HttpOperation $currentOperation): \ArrayObject
512511
{
513-
/** @var \ArrayObject<Model\Link> $links */
512+
/** @var \ArrayObject<Link> $links */
514513
$links = new \ArrayObject();
515514

516515
// Only compute get links for now

src/Serializer/AbstractItemNormalizer.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
298298
foreach ($constructorParameters as $constructorParameter) {
299299
$paramName = $constructorParameter->name;
300300
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
301+
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
302+
$attributeContext['deserialization_path'] = $attributeContext['deserialization_path'] ?? $key;
301303

302304
$allowed = false === $allowedAttributes || (\is_array($allowedAttributes) && \in_array($paramName, $allowedAttributes, true));
303305
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
@@ -310,10 +312,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
310312
$params[] = $data[$paramName];
311313
}
312314
} elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
313-
$constructorContext = $context;
314-
$constructorContext['deserialization_path'] = $context['deserialization_path'] ?? $key;
315315
try {
316-
$params[] = $this->createConstructorArgument($data[$key], $key, $constructorParameter, $constructorContext, $format);
316+
$params[] = $this->createConstructorArgument($data[$key], $key, $constructorParameter, $attributeContext, $format);
317317
} catch (NotNormalizableValueException $exception) {
318318
if (!isset($context['not_normalizable_value_exceptions'])) {
319319
throw $exception;
@@ -332,7 +332,6 @@ protected function instantiateObject(array &$data, string $class, array &$contex
332332
$missingConstructorArguments[] = $constructorParameter->name;
333333
}
334334

335-
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
336335
$constructorParameterType = 'unknown';
337336
$reflectionType = $constructorParameter->getType();
338337
if ($reflectionType instanceof \ReflectionNamedType) {
@@ -343,7 +342,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
343342
\sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name),
344343
null,
345344
[$constructorParameterType],
346-
$attributeContext['deserialization_path'] ?? null,
345+
$attributeContext['deserialization_path'],
347346
true
348347
);
349348
$context['not_normalizable_value_exceptions'][] = $exception;
@@ -386,7 +385,7 @@ protected function getClassDiscriminatorResolvedClass(array $data, string $class
386385
return $mappedClass;
387386
}
388387

389-
protected function createConstructorArgument($parameterData, string $key, \ReflectionParameter $constructorParameter, array &$context, ?string $format = null): mixed
388+
protected function createConstructorArgument($parameterData, string $key, \ReflectionParameter $constructorParameter, array $context, ?string $format = null): mixed
390389
{
391390
return $this->createAndValidateAttributeValue($constructorParameter->name, $parameterData, $format, $context);
392391
}

src/State/Provider/ContentNegotiationProvider.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
6262
private function addRequestFormats(Request $request, array $formats): void
6363
{
6464
foreach ($formats as $format => $mimeTypes) {
65-
$request->setFormat($format, (array) $mimeTypes);
65+
$existingMimeTypes = $request->getMimeTypes($format);
66+
$newMimeTypes = array_unique(array_merge((array) $mimeTypes, $existingMimeTypes));
67+
$request->setFormat($format, $newMimeTypes);
6668
}
6769
}
6870

src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
<service id="api_platform.doctrine_mongodb.odm.state.collection_provider" class="ApiPlatform\Doctrine\Odm\State\CollectionProvider" public="false">
155155
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory" />
156156
<argument type="service" id="doctrine_mongodb" />
157-
<argument type="tagged" tag="api_platform.doctrine_mongodb.odm.aggregation_extension.collection" />
157+
<argument type="tagged_iterator" tag="api_platform.doctrine_mongodb.odm.aggregation_extension.collection" />
158158
<argument type="tagged_locator" tag="api_platform.doctrine.odm.links_handler" index-by="key" />
159159

160160
<tag name="api_platform.state_provider" priority="-100" key="ApiPlatform\Doctrine\Odm\State\CollectionProvider" />
@@ -165,7 +165,7 @@
165165
<service id="api_platform.doctrine_mongodb.odm.state.item_provider" class="ApiPlatform\Doctrine\Odm\State\ItemProvider" public="false">
166166
<argument type="service" id="api_platform.metadata.resource.metadata_collection_factory" />
167167
<argument type="service" id="doctrine_mongodb" />
168-
<argument type="tagged" tag="api_platform.doctrine_mongodb.odm.aggregation_extension.item" />
168+
<argument type="tagged_iterator" tag="api_platform.doctrine_mongodb.odm.aggregation_extension.item" />
169169
<argument type="tagged_locator" tag="api_platform.doctrine.odm.links_handler" index-by="key" />
170170

171171
<tag name="api_platform.state_provider" priority="-100" key="ApiPlatform\Doctrine\Odm\State\ItemProvider" />

src/Symfony/Bundle/Resources/views/DataCollector/request.html.twig

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -139,67 +139,62 @@
139139
</div>
140140
</div>
141141
{% if dataCollected.resourceMetadataCollection is not empty %}
142-
<div class="sf-tabs">
143-
<div class="tab">
144-
<h3 class="tab-title metadata-tab-title">Metadata</h3>
145-
<h3>Resources</h3>
146-
<div class="tab-content metadata-tab-content">
147-
<div class="sf-tabs">
148-
{% for resourceMetadata in dataCollected.resourceMetadataCollection %}
149-
<div class="tab">
150-
<h3 class="tab-title">
151-
{{ resourceMetadata.resource.uriTemplate ?? resourceMetadata.resource.shortName }}
152-
</h3>
153-
<div class="tab-content">
154-
<table>
155-
<thead>
156-
<tr>
157-
<th scope="col" class="key">Resource</th>
158-
</tr>
159-
</thead>
160-
<tbody>
161-
<tr>
162-
<td>{{- profiler_dump(resourceMetadata.resource, 1) -}}</td>
163-
</tr>
164-
</tbody>
165-
</table>
166-
{{ apiPlatform.operationTable(resourceMetadata.operations, '', dataCollected.requestAttributes.operation_name|default('')) }}
167-
<table>
168-
<thead>
169-
<tr>
170-
<th scope="col" class="key">Filters</th>
171-
<th scope="col"></th>
172-
</tr>
173-
</thead>
174-
<tbody>
175-
{% if dataCollected.filters and loop.index0 in dataCollected.filters|keys %}
176-
{% for id, filter in dataCollected.filters[loop.index0] %}
177-
{% set ignored_filter = filter is same as(null) %}
178-
<tr{% if ignored_filter %} class="status-warning"{% endif %}>
179-
<td>
180-
{{ id }}
181-
{% if ignored_filter %}
182-
<span class="newline text-muted">ignored filter</span>
183-
{% else %}
184-
{{ dump(filter) }}
185-
{% endif %}
186-
</td>
187-
</tr>
188-
{% endfor %}
189-
{% else %}
190-
<tr>
191-
<td class="text-muted" colspan="2">
192-
No available filter declared for this resource.
142+
<h3>Resources</h3>
143+
<div class="tab-content metadata-tab-content">
144+
<div class="sf-tabs">
145+
{% for resourceMetadata in dataCollected.resourceMetadataCollection %}
146+
<div class="tab">
147+
<h3 class="tab-title">
148+
{{ resourceMetadata.resource.uriTemplate ?: resourceMetadata.resource.shortName }}
149+
</h3>
150+
<div class="tab-content">
151+
<table>
152+
<thead>
153+
<tr>
154+
<th scope="col" class="key">Resource</th>
155+
</tr>
156+
</thead>
157+
<tbody>
158+
<tr>
159+
<td>{{- profiler_dump(resourceMetadata.resource, 1) -}}</td>
160+
</tr>
161+
</tbody>
162+
</table>
163+
{{ apiPlatform.operationTable(resourceMetadata.operations, '', dataCollected.requestAttributes.operation_name|default('')) }}
164+
<table>
165+
<thead>
166+
<tr>
167+
<th scope="col" class="key">Filters</th>
168+
<th scope="col"></th>
169+
</tr>
170+
</thead>
171+
<tbody>
172+
{% if dataCollected.filters and loop.index0 in dataCollected.filters|keys %}
173+
{% for id, filter in dataCollected.filters[loop.index0] %}
174+
{% set ignored_filter = filter is same as(null) %}
175+
<tr{% if ignored_filter %} class="status-warning"{% endif %}>
176+
<td>
177+
{{ id }}
178+
{% if ignored_filter %}
179+
<span class="newline text-muted">ignored filter</span>
180+
{% else %}
181+
{{ dump(filter) }}
182+
{% endif %}
193183
</td>
194184
</tr>
195-
{% endif %}
196-
</tbody>
197-
</table>
198-
</div>
199-
</div>
200-
{% endfor %}
185+
{% endfor %}
186+
{% else %}
187+
<tr>
188+
<td class="text-muted" colspan="2">
189+
No available filter declared for this resource.
190+
</td>
191+
</tr>
192+
{% endif %}
193+
</tbody>
194+
</table>
195+
</div>
201196
</div>
202-
</div>
197+
{% endfor %}
203198
</div>
204199
</div>
205200
{% endif %}

0 commit comments

Comments
 (0)