Skip to content

Commit

Permalink
Merge 3.4
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Oct 21, 2024
2 parents 4171d5f + 77d3ff3 commit d9cb665
Show file tree
Hide file tree
Showing 18 changed files with 574 additions and 17 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ Notes:
### Features

* [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882)
=======
## v3.4.4

### Bug fixes

* [550347867](https://github.com/api-platform/core/commit/550347867f30611b673d8df99f65186d013919dd) fix(graphql): register query parameter arguments with filters (#6727)
* [99262dce7](https://github.com/api-platform/core/commit/99262dce739800bd841c95e026848b587ba25801) fix(jsonschema): handle @id when genId is false (#6716)
* [ad5efa535](https://github.com/api-platform/core/commit/ad5efa535a4dcbaad64ecff89514eaa6e07f5b7c) fix: multiple parameter provider #6673 (#6732)
* [d34cd7be8](https://github.com/api-platform/core/commit/d34cd7be8e7a12fd08a8b10270a614c06c10aa89) fix: use stateOptions when retrieving a Parameter filter (#6728)
* [e7fb04fab](https://github.com/api-platform/core/commit/e7fb04fab05bc077e2dbeb0fa0fc2c1d28c96105) fix(symfony): fetch api-platform/symfony version debug bar (#6722)
* [e96623ebf](https://github.com/api-platform/core/commit/e96623ebfd8691ba943bdb56a4d91e160497a311) fix(jsonld): prefix error @type with hydra: (#6721)

## v3.4.3

Expand Down
2 changes: 1 addition & 1 deletion features/main/relation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ Feature: Relations support
"properties": {
"@type": {
"type": "string",
"pattern": "^Error$"
"pattern": "^hydra:Error$"
},
"title": {
"type": "string",
Expand Down
10 changes: 5 additions & 5 deletions features/security/strong_typing.feature
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "NULL" given.'

Expand All @@ -71,7 +71,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'Invalid IRI "1".'
And the JSON node "trace" should exist
Expand Down Expand Up @@ -102,7 +102,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "relatedDummies" attribute must be "array", "string" given.'
And the JSON node "trace" should exist
Expand All @@ -120,7 +120,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the key "a" must be "int", "string" given.'

Expand All @@ -136,7 +136,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "integer" given.'

Expand Down
2 changes: 1 addition & 1 deletion features/serializer/vo_relations.feature
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Feature: Value object as ApiResource
"properties": {
"@type": {
"type": "string",
"pattern": "^Error$"
"pattern": "^hydra:Error$"
},
"title": {
"type": "string",
Expand Down
7 changes: 6 additions & 1 deletion src/JsonLd/Serializer/ErrorNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ public function __construct(private readonly NormalizerInterface $inner, private

public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
$context += $this->defaultContext;
$normalized = $this->inner->normalize($object, $format, $context);
$hydraPrefix = $this->getHydraPrefix($context + $this->defaultContext);
$hydraPrefix = $this->getHydraPrefix($context);
if (!$hydraPrefix) {
return $normalized;
}

if ('Error' === $normalized['@type']) {
$normalized['@type'] = 'hydra:Error';
}

if (isset($normalized['description'])) {
$normalized['hydra:description'] = $normalized['description'];
}
Expand Down
8 changes: 8 additions & 0 deletions src/JsonSchema/SchemaFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
continue;
}

if (false === $propertyMetadata->getGenId()) {
$subDefinitionName = $this->definitionNameFactory->create($className, $format, $className, null, $serializerContext);

if (isset($subSchema->getDefinitions()[$subDefinitionName])) {
unset($subSchema->getDefinitions()[$subDefinitionName]['properties']['@id']);
}
}

if ($isCollection) {
$propertySchema['items']['$ref'] = $subSchema['$ref'];
unset($propertySchema['items']['type']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private function getDefaultParameters(Operation $operation, string $resourceClas
if (':property' === $key) {
foreach ($propertyNames as $property) {
$converted = $this->nameConverter?->denormalize($property) ?? $property;
$propertyParameter = $this->setDefaults($converted, $parameter, $resourceClass, $properties);
$propertyParameter = $this->setDefaults($converted, $parameter, $resourceClass, $properties, $operation);
$priority = $propertyParameter->getPriority() ?? $internalPriority--;
$parameters->add($converted, $propertyParameter->withPriority($priority)->withKey($converted));
}
Expand All @@ -133,7 +133,7 @@ private function getDefaultParameters(Operation $operation, string $resourceClas
$parameter = $parameter->withExtraProperties($parameter->getExtraProperties() + ['_properties' => $p]);
}

$parameter = $this->setDefaults($key, $parameter, $resourceClass, $properties);
$parameter = $this->setDefaults($key, $parameter, $resourceClass, $properties, $operation);
$priority = $parameter->getPriority() ?? $internalPriority--;
$parameters->add($key, $parameter->withPriority($priority));
}
Expand Down Expand Up @@ -171,7 +171,7 @@ private function addFilterMetadata(Parameter $parameter): Parameter
/**
* @param array<string, ApiProperty> $properties
*/
private function setDefaults(string $key, Parameter $parameter, string $resourceClass, array $properties): Parameter
private function setDefaults(string $key, Parameter $parameter, string $resourceClass, array $properties, Operation $operation): Parameter
{
if (null === $parameter->getKey()) {
$parameter = $parameter->withKey($key);
Expand All @@ -187,7 +187,7 @@ private function setDefaults(string $key, Parameter $parameter, string $resource
}

// Read filter description to populate the Parameter
$description = $filter instanceof FilterInterface ? $filter->getDescription($resourceClass) : [];
$description = $filter instanceof FilterInterface ? $filter->getDescription($this->getFilterClass($operation)) : [];
if (($schema = $description[$key]['schema'] ?? null) && null === $parameter->getSchema()) {
$parameter = $parameter->withSchema($schema);
}
Expand Down Expand Up @@ -220,4 +220,17 @@ private function setDefaults(string $key, Parameter $parameter, string $resource

return $this->addFilterMetadata($parameter);
}

private function getFilterClass(Operation $operation): ?string
{
$stateOptions = $operation->getStateOptions();
if ($stateOptions instanceof DoctrineOrmOptions) {

Check failure on line 227 in src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.3)

Class ApiPlatform\Metadata\Resource\Factory\DoctrineOrmOptions not found.
return $stateOptions->getEntityClass();

Check failure on line 228 in src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.3)

Call to method getEntityClass() on an unknown class ApiPlatform\Metadata\Resource\Factory\DoctrineOrmOptions.
}
if ($stateOptions instanceof DoctrineOdmOptions) {

Check failure on line 230 in src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.3)

Class ApiPlatform\Metadata\Resource\Factory\DoctrineOdmOptions not found.
return $stateOptions->getDocumentClass();

Check failure on line 231 in src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.3)

Call to method getDocumentClass() on an unknown class ApiPlatform\Metadata\Resource\Factory\DoctrineOdmOptions.
}

return $operation->getClass();
}
}
2 changes: 1 addition & 1 deletion src/State/Provider/ParameterProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
$request->attributes->set('_api_header_parameters', $request->headers->all());
}

$context = ['operation' => $operation] + $context;
$parameters = $operation->getParameters();
foreach ($parameters ?? [] as $parameter) {
$extraProperties = $parameter->getExtraProperties();
unset($extraProperties['_api_values']);
$parameters->add($parameter->getKey(), $parameter = $parameter->withExtraProperties($extraProperties));

$context = ['operation' => $operation] + $context;
$values = $this->getParameterValues($parameter, $request, $context);
$value = $this->extractParameterValues($parameter, $values);

Expand Down
29 changes: 26 additions & 3 deletions src/Symfony/Bundle/DataCollector/RequestDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\State\Util\RequestAttributesExtractor;
use Composer\InstalledVersions;
use PackageVersions\Versions;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -67,16 +68,38 @@ private function setFilters(ApiResource $resourceMetadata, int $index, array &$f
}
}

// TODO: 4.1 remove Versions as its deprecated
public function getVersion(): ?string
{
if (class_exists(InstalledVersions::class)) {
return InstalledVersions::getPrettyVersion('api-platform/symfony') ?? InstalledVersions::getPrettyVersion('api-platform/core');
}

if (!class_exists(Versions::class)) {
return null;
}

$version = Versions::getVersion('api-platform/core');
preg_match('/^v(.*?)@/', (string) $version, $output);
try {
$version = strtok(Versions::getVersion('api-platform/symfony'), '@');
} catch (\OutOfBoundsException) {
$version = false;
}

if (false === $version) {
try {
$version = strtok(Versions::getVersion('api-platform/core'), '@');
} catch (\OutOfBoundsException) {
$version = false;
}
}

if (false === $version) {
return null;
}

preg_match('/^v(.*?)$/', $version, $output);

return $output[1] ?? strtok($version, '@');
return $output[1] ?? $version;
}

/**
Expand Down
90 changes: 90 additions & 0 deletions tests/Fixtures/TestBundle/ApiResource/AgentApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;

use ApiPlatform\Doctrine\Odm\Filter\DateFilter as OdmDateFilter;
use ApiPlatform\Doctrine\Odm\State\Options as OdmOptions;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\State\Options;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\QueryParameter;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\AgentDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Agent;

#[ApiFilter(DateFilter::class, properties: ['birthday'], alias: 'app_filter_date')]
#[ApiResource(
shortName: 'Agent',
operations: [
new GetCollection(parameters: [
'birthday' => new QueryParameter(filter: 'app_filter_date'),
]),
],
stateOptions: new Options(entityClass: Agent::class)
)]
#[ApiFilter(OdmDateFilter::class, properties: ['birthday'], alias: 'app_filter_date_odm')]
#[ApiResource(
shortName: 'AgentDocument',
operations: [
new GetCollection(parameters: [
'birthday' => new QueryParameter(filter: 'app_filter_date_odm'),
]),
],
stateOptions: new OdmOptions(documentClass: AgentDocument::class)
)]
class AgentApi
{
private ?int $id = null;

private ?string $name = null;

private ?\DateTimeInterface $birthday = null;

public function getId(): ?int
{
return $this->id;
}

public function setId(?int $id): self
{
$this->id = $id;

return $this;
}

public function getName(): ?string
{
return $this->name;
}

public function setName(?string $name): self
{
$this->name = $name;

return $this;
}

public function getBirthday(): ?\DateTimeInterface
{
return $this->birthday;
}

public function setBirthday(?\DateTimeInterface $birthday): self
{
$this->birthday = $birthday;

return $this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6673;

use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Parameter;
use ApiPlatform\Metadata\QueryParameter;

#[GetCollection(
uriTemplate: 'issue6673_multiple_parameter_provider',
shortName: 'multiple_parameter_provider',
outputFormats: ['json'],
parameters: [
'a' => new QueryParameter(
provider: [self::class, 'parameterOneProvider'],
),
'b' => new QueryParameter(
provider: [self::class, 'parameterTwoProvider'],
),
],
provider: [self::class, 'provide']
)]
final class MutlipleParameterProvider
{
public function __construct(public readonly string $id)
{
}

public static function provide(Operation $operation): ?array
{
return $operation->getNormalizationContext();
}

public static function parameterOneProvider(Parameter $parameter, array $parameters = [], array $context = []): ?Operation
{
$operation = $context['operation'];
$context = $operation->getNormalizationContext() ?? [];
$context['a'] = $parameter->getValue();

return $operation->withNormalizationContext($context);
}

public static function parameterTwoProvider(Parameter $parameter, array $parameters = [], array $context = []): ?Operation
{
$operation = $context['operation'];
$context = $operation->getNormalizationContext() ?? [];
$context['b'] = $parameter->getValue();

return $operation->withNormalizationContext($context);
}
}
Loading

0 comments on commit d9cb665

Please sign in to comment.