diff --git a/src/Api/FilterInterface.php b/src/Api/FilterInterface.php index ece1fe01407..b06f91deaa5 100644 --- a/src/Api/FilterInterface.php +++ b/src/Api/FilterInterface.php @@ -29,20 +29,17 @@ interface FilterInterface * - required: if this filter is required * - strategy (optional): the used strategy * - is_collection (optional): is this filter is collection - * - swagger (optional): additional parameters for the path operation, - * e.g. 'swagger' => [ - * 'description' => 'My Description', - * 'name' => 'My Name', - * 'type' => 'integer', - * ] - * - openapi (optional): additional parameters for the path operation in the version 3 spec, - * e.g. 'openapi' => [ - * 'description' => 'My Description', - * 'name' => 'My Name', - * 'schema' => [ - * 'type' => 'integer', - * ] + * - schema (optional): additional parameters related to schema description + * e.g. 'schema' => [ + * 'type' => 'string', + * 'enum' => [ + * 'value1', + * 'value2', + * ], * ] + * - description (optional): a string describing filter usage + * - swagger (optional/deprecated): additional parameters for the path operation + * - openapi (optional/deprecated): additional parameters for the path operation in the version 3 * The description can contain additional data specific to a filter. * * @see \ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer::getFiltersParameters diff --git a/src/Serializer/Filter/GroupFilter.php b/src/Serializer/Filter/GroupFilter.php index 5d504a6c17b..b6bab2de2b0 100644 --- a/src/Serializer/Filter/GroupFilter.php +++ b/src/Serializer/Filter/GroupFilter.php @@ -65,13 +65,17 @@ public function apply(Request $request, bool $normalization, array $attributes, */ public function getDescription(string $resourceClass): array { - return [ - "$this->parameterName[]" => [ - 'property' => null, - 'type' => 'string', - 'is_collection' => true, - 'required' => false, - ], + $description = [ + 'property' => null, + 'type' => 'string', + 'is_collection' => true, + 'required' => false, ]; + + if ($this->whitelist) { + $description['schema'] = ['type' => 'array', 'items' => ['type' => 'string', 'enum' => $this->whitelist]]; + } + + return ["$this->parameterName[]" => $description]; } } diff --git a/src/Serializer/Filter/PropertyFilter.php b/src/Serializer/Filter/PropertyFilter.php index 229ae82571b..6013fcd2cf5 100644 --- a/src/Serializer/Filter/PropertyFilter.php +++ b/src/Serializer/Filter/PropertyFilter.php @@ -82,24 +82,7 @@ public function getDescription(string $resourceClass): array 'type' => 'string', 'is_collection' => true, 'required' => false, - 'swagger' => [ - 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: '.$example, - 'name' => "$this->parameterName[]", - 'type' => 'array', - 'items' => [ - 'type' => 'string', - ], - ], - 'openapi' => [ - 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: '.$example, - 'name' => "$this->parameterName[]", - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'string', - ], - ], - ], + 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: '.$example, ], ]; } diff --git a/src/Swagger/Serializer/DocumentationNormalizer.php b/src/Swagger/Serializer/DocumentationNormalizer.php index 7fdc800b180..8c5bdf9cf08 100644 --- a/src/Swagger/Serializer/DocumentationNormalizer.php +++ b/src/Swagger/Serializer/DocumentationNormalizer.php @@ -706,12 +706,19 @@ private function getFiltersParameters(bool $v3, string $resourceClass, string $o 'required' => $data['required'], ]; + if (isset($data['description'])) { + $parameter['description'] = $data['description']; + } + $type = \in_array($data['type'], Type::$builtinTypes, true) ? $this->jsonSchemaTypeFactory->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false)) : ['type' => 'string']; $v3 ? $parameter['schema'] = $type : $parameter += $type; if ($v3 && isset($data['schema'])) { $parameter['schema'] = $data['schema']; } + if (!$v3 && isset($data['schema']['items'])) { + $parameter['items'] = $data['schema']['items']; + } if ('array' === ($type['type'] ?? '')) { $deepObject = \in_array($data['type'], [Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT], true); @@ -724,6 +731,7 @@ private function getFiltersParameters(bool $v3, string $resourceClass, string $o } } + /** @deprecated schema key should be used instead */ $key = $v3 ? 'openapi' : 'swagger'; if (isset($data[$key])) { $parameter = $data[$key] + $parameter; diff --git a/tests/Serializer/Filter/GroupFilterTest.php b/tests/Serializer/Filter/GroupFilterTest.php index cede5207ce9..b7d36d201ee 100644 --- a/tests/Serializer/Filter/GroupFilterTest.php +++ b/tests/Serializer/Filter/GroupFilterTest.php @@ -125,4 +125,26 @@ public function testGetDescription() $this->assertEquals($expectedDescription, $groupFilter->getDescription(DummyGroup::class)); } + + public function testGetDescriptionForWhitelistedGroups() + { + $groupFilter = new GroupFilter('whitlisted_groups', false, ['foo', 'bar']); + $expectedDescription = [ + 'whitlisted_groups[]' => [ + 'property' => null, + 'type' => 'string', + 'is_collection' => true, + 'required' => false, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => ['foo', 'bar'], + ], + ], + ], + ]; + + $this->assertEquals($expectedDescription, $groupFilter->getDescription(DummyGroup::class)); + } } diff --git a/tests/Serializer/Filter/PropertyFilterTest.php b/tests/Serializer/Filter/PropertyFilterTest.php index 30ea824f556..22232b31ca3 100644 --- a/tests/Serializer/Filter/PropertyFilterTest.php +++ b/tests/Serializer/Filter/PropertyFilterTest.php @@ -218,24 +218,7 @@ public function testGetDescription() 'type' => 'string', 'is_collection' => true, 'required' => false, - 'swagger' => [ - 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: custom_properties[]={propertyName}&custom_properties[]={anotherPropertyName}&custom_properties[{nestedPropertyParent}][]={nestedProperty}', - 'name' => 'custom_properties[]', - 'type' => 'array', - 'items' => [ - 'type' => 'string', - ], - ], - 'openapi' => [ - 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: custom_properties[]={propertyName}&custom_properties[]={anotherPropertyName}&custom_properties[{nestedPropertyParent}][]={nestedProperty}', - 'name' => 'custom_properties[]', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'string', - ], - ], - ], + 'description' => 'Allows you to reduce the response to contain only the properties you need. If your desired property is nested, you can address it using nested arrays. Example: custom_properties[]={propertyName}&custom_properties[]={anotherPropertyName}&custom_properties[{nestedPropertyParent}][]={nestedProperty}', ], ]; diff --git a/tests/Swagger/Serializer/DocumentationNormalizerV2Test.php b/tests/Swagger/Serializer/DocumentationNormalizerV2Test.php index b8e1b605ae9..be79052c164 100644 --- a/tests/Swagger/Serializer/DocumentationNormalizerV2Test.php +++ b/tests/Swagger/Serializer/DocumentationNormalizerV2Test.php @@ -1655,6 +1655,23 @@ public function testFilters(): void 'required' => true, 'strategy' => 'exact', ]]), + 'f4' => new DummyFilter(['serializer' => [ + 'property' => 'group', + 'type' => 'string', + 'required' => false, + 'strategy' => 'exact', + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + ], + ]]), ]; foreach ($filters as $filterId => $filter) { @@ -1662,7 +1679,7 @@ public function testFilters(): void $filterLocatorProphecy->get($filterId)->willReturn($filter)->shouldBeCalled(); } - $filterLocatorProphecy->has('f4')->willReturn(false)->shouldBeCalled(); + $filterLocatorProphecy->has('f5')->willReturn(false)->shouldBeCalled(); $this->normalizeWithFilters($filterLocatorProphecy->reveal()); } @@ -1694,6 +1711,23 @@ public function testFiltersWithDeprecatedFilterCollection(): void 'required' => true, 'strategy' => 'exact', ]]), + 'f4' => new DummyFilter(['serializer' => [ + 'property' => 'group', + 'type' => 'string', + 'required' => false, + 'strategy' => 'exact', + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + ], + ]]), ])); } @@ -2051,7 +2085,7 @@ private function normalizeWithFilters($filterLocator): void 'This is a dummy.', null, [], - ['get' => ['method' => 'GET', 'filters' => ['f1', 'f2', 'f3', 'f4']] + self::OPERATION_FORMATS] + ['get' => ['method' => 'GET', 'filters' => ['f1', 'f2', 'f3', 'f4', 'f5']] + self::OPERATION_FORMATS] ); $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $resourceMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn($dummyMetadata); @@ -2119,6 +2153,20 @@ private function normalizeWithFilters($filterLocator): void ], 'collectionFormat' => 'csv', ], + [ + 'name' => 'serializer', + 'in' => 'query', + 'required' => false, + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + 'collectionFormat' => 'multi', + ], [ 'name' => 'page', 'in' => 'query', diff --git a/tests/Swagger/Serializer/DocumentationNormalizerV3Test.php b/tests/Swagger/Serializer/DocumentationNormalizerV3Test.php index d6250fb3b90..78ecda6f444 100644 --- a/tests/Swagger/Serializer/DocumentationNormalizerV3Test.php +++ b/tests/Swagger/Serializer/DocumentationNormalizerV3Test.php @@ -1512,6 +1512,23 @@ public function testFilters(): void 'enum' => ['asc', 'desc'], ], ]]), + 'f5' => new DummyFilter(['serializer' => [ + 'property' => 'group', + 'type' => 'string', + 'required' => false, + 'strategy' => 'exact', + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + ], + ]]), ]; foreach ($filters as $filterId => $filter) { @@ -1519,7 +1536,7 @@ public function testFilters(): void $filterLocatorProphecy->get($filterId)->willReturn($filter)->shouldBeCalled(); } - $filterLocatorProphecy->has('f5')->willReturn(false)->shouldBeCalled(); + $filterLocatorProphecy->has('f6')->willReturn(false)->shouldBeCalled(); $this->normalizeWithFilters($filterLocatorProphecy->reveal()); } @@ -1560,6 +1577,23 @@ public function testFiltersWithDeprecatedFilterCollection(): void 'enum' => ['asc', 'desc'], ], ]]), + 'f5' => new DummyFilter(['serializer' => [ + 'property' => 'group', + 'type' => 'string', + 'required' => false, + 'strategy' => 'exact', + 'is_collection' => true, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + ], + ]]), ])); } @@ -2031,7 +2065,7 @@ private function normalizeWithFilters($filterLocator): void 'This is a dummy.', null, [], - ['get' => ['method' => 'GET', 'filters' => ['f1', 'f2', 'f3', 'f4', 'f5']] + self::OPERATION_FORMATS] + ['get' => ['method' => 'GET', 'filters' => ['f1', 'f2', 'f3', 'f4', 'f5', 'f6']] + self::OPERATION_FORMATS] ); $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $resourceMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn($dummyMetadata); @@ -2130,6 +2164,23 @@ private function normalizeWithFilters($filterLocator): void 'enum' => ['asc', 'desc'], ], ], + [ + 'name' => 'serializer', + 'in' => 'query', + 'required' => false, + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => [ + 'group1', + 'group2', + ], + ], + ], + 'style' => 'form', + 'explode' => true, + ], [ 'name' => 'page', 'in' => 'query',