Skip to content

Commit 8f4972b

Browse files
committed
refactor(openapi): parameter's filter description
1 parent 34604f4 commit 8f4972b

File tree

4 files changed

+120
-68
lines changed

4 files changed

+120
-68
lines changed

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use ApiPlatform\Doctrine\Odm\State\Options as DoctrineOdmOptions;
1717
use ApiPlatform\Doctrine\Orm\State\Options as DoctrineOrmOptions;
1818
use ApiPlatform\Metadata\FilterInterface;
19-
use ApiPlatform\Metadata\HeaderParameterInterface;
2019
use ApiPlatform\Metadata\HttpOperation;
2120
use ApiPlatform\Metadata\Operation;
2221
use ApiPlatform\Metadata\Parameter;

src/OpenApi/Factory/OpenApiFactory.php

Lines changed: 92 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
use ApiPlatform\OpenApi\Options;
5656
use ApiPlatform\OpenApi\Serializer\NormalizeOperationNameTrait;
5757
use ApiPlatform\State\Pagination\PaginationOptions;
58+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\FilterWithStateOptions;
5859
use Psr\Container\ContainerInterface;
5960
use Symfony\Component\PropertyInfo\Type;
6061
use Symfony\Component\Routing\RouteCollection;
@@ -161,6 +162,9 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
161162
}
162163

163164
foreach ($resource->getOperations() as $operationName => $operation) {
165+
if (FilterWithStateOptions::class !== $operation->getClass()) {
166+
continue;
167+
}
164168
$resourceShortName = $operation->getShortName();
165169
// No path to return
166170
if (null === $operation->getUriTemplate() && null === $operation->getRouteName()) {
@@ -331,12 +335,26 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
331335
}
332336
}
333337

338+
$entityClass = $this->getFilterClass($operation);
334339
$openapiParameters = $openapiOperation->getParameters();
335340
foreach ($operation->getParameters() ?? [] as $key => $p) {
336341
if (false === $p->getOpenApi()) {
337342
continue;
338343
}
339344

345+
if (($f = $p->getFilter()) && \is_string($f) && $this->filterLocator->has($f)) {
346+
$filter = $this->filterLocator->get($f);
347+
foreach ($filter->getDescription($entityClass) as $name => $description) {
348+
if ($prop = $p->getProperty()) {
349+
$name = str_replace($prop, $key, $name);
350+
}
351+
352+
$openapiParameters[] = $this->getFilterParameter($name, $description, $operation->getShortName(), $f);
353+
}
354+
355+
continue;
356+
}
357+
340358
$in = $p instanceof HeaderParameterInterface ? 'header' : 'query';
341359
$parameter = new Parameter($key, $in, $p->getDescription() ?? "$resourceShortName $key", $p->getRequired() ?? false, false, false, $p->getSchema() ?? ['type' => 'string']);
342360

@@ -654,92 +672,102 @@ private function getLinks(ResourceMetadataCollection $resourceMetadataCollection
654672
private function getFiltersParameters(CollectionOperationInterface|HttpOperation $operation): array
655673
{
656674
$parameters = [];
657-
658675
$resourceFilters = $operation->getFilters();
676+
$entityClass = $this->getFilterClass($operation);
677+
659678
foreach ($resourceFilters ?? [] as $filterId) {
660679
if (!$this->filterLocator->has($filterId)) {
661680
continue;
662681
}
663682

664683
$filter = $this->filterLocator->get($filterId);
665-
$entityClass = $operation->getClass();
666-
if ($options = $operation->getStateOptions()) {
667-
if ($options instanceof DoctrineOptions && $options->getEntityClass()) {
668-
$entityClass = $options->getEntityClass();
669-
}
670-
671-
if ($options instanceof DoctrineODMOptions && $options->getDocumentClass()) {
672-
$entityClass = $options->getDocumentClass();
673-
}
684+
foreach ($filter->getDescription($entityClass) as $name => $description) {
685+
$parameters[] = $this->getFilterParameter($name, $description, $operation->getShortName(), $filterId);
674686
}
687+
}
675688

676-
foreach ($filter->getDescription($entityClass) as $name => $data) {
677-
if (isset($data['swagger'])) {
678-
trigger_deprecation('api-platform/core', '4.0', \sprintf('Using the "swagger" field of the %s::getDescription() (%s) is deprecated.', $filter::class, $operation->getShortName()));
679-
}
689+
return $parameters;
690+
}
680691

681-
if (!isset($data['openapi']) || $data['openapi'] instanceof Parameter) {
682-
$schema = $data['schema'] ?? [];
692+
private function getFilterClass(HttpOperation $operation): string
693+
{
694+
$entityClass = $operation->getClass();
695+
if ($options = $operation->getStateOptions()) {
696+
if ($options instanceof DoctrineOptions && $options->getEntityClass()) {
697+
return $options->getEntityClass();
698+
}
683699

684-
if (isset($data['type']) && \in_array($data['type'] ?? null, Type::$builtinTypes, true) && !isset($schema['type'])) {
685-
$schema += $this->jsonSchemaTypeFactory ? $this->jsonSchemaTypeFactory->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false)) : $this->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false));
686-
}
700+
if ($options instanceof DoctrineODMOptions && $options->getDocumentClass()) {
701+
return $options->getDocumentClass();
702+
}
703+
}
687704

688-
if (!isset($schema['type'])) {
689-
$schema['type'] = 'string';
690-
}
705+
return $entityClass;
706+
}
691707

692-
$style = 'array' === ($schema['type'] ?? null) && \in_array(
693-
$data['type'],
694-
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
695-
true
696-
) ? 'deepObject' : 'form';
708+
private function getFilterParameter(string $name, array $description, string $shortName, string $filter): Parameter
709+
{
710+
if (isset($description['swagger'])) {
711+
trigger_deprecation('api-platform/core', '4.0', \sprintf('Using the "swagger" field of the %s::getDescription() (%s) is deprecated.', $filter, $shortName));
712+
}
697713

698-
$parameter = isset($data['openapi']) && $data['openapi'] instanceof Parameter ? $data['openapi'] : new Parameter(in: 'query', name: $name, style: $style, explode: $data['is_collection'] ?? false);
714+
if (!isset($description['openapi']) || $description['openapi'] instanceof Parameter) {
715+
$schema = $description['schema'] ?? [];
699716

700-
if ('' === $parameter->getDescription() && ($description = $data['description'] ?? '')) {
701-
$parameter = $parameter->withDescription($description);
702-
}
717+
if (isset($description['type']) && \in_array($description['type'] ?? null, Type::$builtinTypes, true) && !isset($schema['type'])) {
718+
$schema += $this->jsonSchemaTypeFactory ? $this->jsonSchemaTypeFactory->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false)) : $this->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false));
719+
}
703720

704-
if (false === $parameter->getRequired() && false !== ($required = $data['required'] ?? false)) {
705-
$parameter = $parameter->withRequired($required);
706-
}
721+
if (!isset($schema['type'])) {
722+
$schema['type'] = 'string';
723+
}
707724

708-
$parameters[] = $parameter->withSchema($schema);
709-
continue;
710-
}
725+
$style = 'array' === ($schema['type'] ?? null) && \in_array(
726+
$description['type'],
727+
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
728+
true
729+
) ? 'deepObject' : 'form';
711730

712-
trigger_deprecation('api-platform/core', '4.0', \sprintf('Not using "%s" on the "openapi" field of the %s::getDescription() (%s) is deprecated.', Parameter::class, $filter::class, $operation->getShortName()));
713-
if ($this->jsonSchemaTypeFactory) {
714-
$schema = $data['schema'] ?? (\in_array($data['type'], Type::$builtinTypes, true) ? $this->jsonSchemaTypeFactory->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false), 'openapi') : ['type' => 'string']);
715-
} else {
716-
$schema = $data['schema'] ?? (\in_array($data['type'], Type::$builtinTypes, true) ? $this->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false)) : ['type' => 'string']);
717-
}
731+
$parameter = isset($description['openapi']) && $description['openapi'] instanceof Parameter ? $description['openapi'] : new Parameter(in: 'query', name: $name, style: $style, explode: $description['is_collection'] ?? false);
718732

719-
$parameters[] = new Parameter(
720-
$name,
721-
'query',
722-
$data['description'] ?? '',
723-
$data['required'] ?? false,
724-
$data['openapi']['deprecated'] ?? false,
725-
$data['openapi']['allowEmptyValue'] ?? true,
726-
$schema,
727-
'array' === $schema['type'] && \in_array(
728-
$data['type'],
729-
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
730-
true
731-
) ? 'deepObject' : 'form',
732-
$data['openapi']['explode'] ?? ('array' === $schema['type']),
733-
$data['openapi']['allowReserved'] ?? false,
734-
$data['openapi']['example'] ?? null,
735-
isset(
736-
$data['openapi']['examples']
737-
) ? new \ArrayObject($data['openapi']['examples']) : null
738-
);
733+
if ('' === $parameter->getDescription() && ($description = $description['description'] ?? '')) {
734+
$parameter = $parameter->withDescription($description);
735+
}
736+
737+
if (false === $parameter->getRequired() && false !== ($required = $description['required'] ?? false)) {
738+
$parameter = $parameter->withRequired($required);
739739
}
740+
741+
return $parameter->withSchema($schema);
740742
}
741743

742-
return $parameters;
744+
trigger_deprecation('api-platform/core', '4.0', \sprintf('Not using "%s" on the "openapi" field of the %s::getDescription() (%s) is deprecated.', Parameter::class, $filter, $shortName));
745+
if ($this->jsonSchemaTypeFactory) {
746+
$schema = $description['schema'] ?? (\in_array($description['type'], Type::$builtinTypes, true) ? $this->jsonSchemaTypeFactory->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false), 'openapi') : ['type' => 'string']);
747+
} else {
748+
$schema = $description['schema'] ?? (\in_array($description['type'], Type::$builtinTypes, true) ? $this->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false)) : ['type' => 'string']);
749+
}
750+
751+
return new Parameter(
752+
$name,
753+
'query',
754+
$description['description'] ?? '',
755+
$description['required'] ?? false,
756+
$description['openapi']['deprecated'] ?? false,
757+
$description['openapi']['allowEmptyValue'] ?? true,
758+
$schema,
759+
'array' === $schema['type'] && \in_array(
760+
$description['type'],
761+
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
762+
true
763+
) ? 'deepObject' : 'form',
764+
$description['openapi']['explode'] ?? ('array' === $schema['type']),
765+
$description['openapi']['allowReserved'] ?? false,
766+
$description['openapi']['example'] ?? null,
767+
isset(
768+
$description['openapi']['examples']
769+
) ? new \ArrayObject($description['openapi']['examples']) : null
770+
);
743771
}
744772

745773
private function getPaginationParameters(CollectionOperationInterface|HttpOperation $operation): array

tests/Fixtures/TestBundle/ApiResource/FilterWithStateOptions.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
<?php
22

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+
314
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;
415

516
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
@@ -19,5 +30,7 @@
1930
#[ApiFilter(DateFilter::class, alias: 'filter_with_state_options_date', properties: ['dummyDate' => DateFilter::EXCLUDE_NULL])]
2031
final readonly class FilterWithStateOptions
2132
{
22-
public function __construct(public string $id, public \DateImmutable $dummyDate, public string $name) {}
33+
public function __construct(public string $id, public \DateTimeImmutable $dummyDate, public string $name)
34+
{
35+
}
2336
}

tests/Fixtures/TestBundle/Entity/FilterWithStateOptionsEntity.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
<?php
22

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+
314
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;
415

516
use Doctrine\ORM\Mapping as ORM;
@@ -15,6 +26,7 @@ public function __construct(
1526
#[ORM\Column(type: 'date_immutable', nullable: true)]
1627
public ?\DateTimeImmutable $dummyDate = null,
1728
#[ORM\Column(type: 'string', nullable: true)]
18-
public ?string $name = null
19-
) {}
29+
public ?string $name = null,
30+
) {
31+
}
2032
}

0 commit comments

Comments
 (0)