From d590880facfe7158d681b5d9c93b8b27a06f26cb Mon Sep 17 00:00:00 2001 From: indexxd <35069244+indexxd@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:32:44 +0200 Subject: [PATCH] feat: add arrays of enums and enum callbacks for query params (#2096) This adds support for the `callback` option in Choice constraint in QueryParams to allow providing enum choices at runtime. Also adds support for arrays of enums using the `multiple` QueryParam option. --------- Co-authored-by: indexxd Co-authored-by: Djordy Koert --- src/RouteDescriber/FosRestDescriber.php | 17 +++++- tests/RouteDescriber/FosRestDescriberTest.php | 53 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/RouteDescriber/FosRestDescriber.php b/src/RouteDescriber/FosRestDescriber.php index 3ee28847d..2c6df5451 100644 --- a/src/RouteDescriber/FosRestDescriber.php +++ b/src/RouteDescriber/FosRestDescriber.php @@ -149,6 +149,14 @@ private function getFormat($requirements): ?string private function getEnum($requirements): ?array { if ($requirements instanceof Choice) { + if (null != $requirements->callback) { + if (!\is_callable($choices = $requirements->callback)) { + return null; + } + + return $choices(); + } + return $requirements->choices; } @@ -217,7 +225,14 @@ private function describeCommonSchemaFromAnnotation(OA\Schema $schema, AbstractS $enum = $this->getEnum($annotation->requirements); if (null !== $enum) { - $schema->enum = $enum; + if ($annotation->requirements instanceof Choice) { + if ($annotation->requirements->multiple) { + $schema->type = 'array'; + $schema->items = Util::createChild($schema, OA\Items::class, ['type' => 'string', 'enum' => $enum]); + } else { + $schema->enum = $enum; + } + } } } diff --git a/tests/RouteDescriber/FosRestDescriberTest.php b/tests/RouteDescriber/FosRestDescriberTest.php index 51da37a65..65c3c91b3 100644 --- a/tests/RouteDescriber/FosRestDescriberTest.php +++ b/tests/RouteDescriber/FosRestDescriberTest.php @@ -47,4 +47,57 @@ public function testQueryParamWithChoiceConstraintIsAddedAsEnum(): void self::assertSame($choices, $api->paths[0]->get->parameters[0]->schema->enum); } + + public function testQueryParamWithChoiceConstraintCallbackIsAddedAsEnum(): void + { + $queryParam = new QueryParam(); + $choice = new Choice(); + $choice->callback = function () { + return ['foo', 'bar']; + }; + + $queryParam->requirements = $choice; + $readerMock = $this->createMock(Reader::class); + $readerMock->method('getMethodAnnotations')->willReturn([ + $queryParam, + ]); + + $fosRestDescriber = new FosRestDescriber($readerMock, []); + $api = new OpenApi([]); + + $fosRestDescriber->describe( + $api, + new Route('/'), + $this->createMock(\ReflectionMethod::class) + ); + + self::assertSame(['foo', 'bar'], $api->paths[0]->get->parameters[0]->schema->enum); + } + + public function testQueryParamWithChoiceConstraintAsArray(): void + { + $choices = ['foo', 'bar']; + + $queryParam = new QueryParam(); + $choice = new Choice($choices); + $choice->multiple = true; + $queryParam->requirements = $choice; + + $readerMock = $this->createMock(Reader::class); + $readerMock->method('getMethodAnnotations')->willReturn([ + $queryParam, + ]); + + $fosRestDescriber = new FosRestDescriber($readerMock, []); + $api = new OpenApi([]); + + $fosRestDescriber->describe( + $api, + new Route('/'), + $this->createMock(\ReflectionMethod::class) + ); + + self::assertEquals('array', $api->paths[0]->get->parameters[0]->schema->type); + self::assertSame($choices, $api->paths[0]->get->parameters[0]->schema->items->enum); + } }