From 1cdb866e71bb0b57684449643edb12c12d072757 Mon Sep 17 00:00:00 2001 From: Alexander Tebiev <326840+beeyev@users.noreply.github.com> Date: Fri, 19 Apr 2024 01:00:03 +0200 Subject: [PATCH] Add example schema-query-parameter-attributes processor (#1569) --- Examples/Readme.md | 8 ++ .../SchemaQueryParameter.php | 84 +++++++++++++++++++ .../app/OpenApi.php | 17 ++++ .../app/Product.php | 26 ++++++ .../app/ProductController.php | 54 ++++++++++++ .../scan.php | 29 +++++++ .../schema-query-parameter.yaml | 69 +++++++++++++++ phpstan-baseline.neon | 20 +++++ 8 files changed, 307 insertions(+) create mode 100644 Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php create mode 100644 Examples/processors/schema-query-parameter-attributes/app/OpenApi.php create mode 100644 Examples/processors/schema-query-parameter-attributes/app/Product.php create mode 100644 Examples/processors/schema-query-parameter-attributes/app/ProductController.php create mode 100644 Examples/processors/schema-query-parameter-attributes/scan.php create mode 100644 Examples/processors/schema-query-parameter-attributes/schema-query-parameter.yaml diff --git a/Examples/Readme.md b/Examples/Readme.md index b038e1015..c53728591 100644 --- a/Examples/Readme.md +++ b/Examples/Readme.md @@ -66,6 +66,14 @@ class MyCustomProcessor [source](processors/schema-query-parameter) +* **schema-query-parameter-attributes processor** + + Same as the `schema-query-parameter` processor but uses php attributes instead of annotations. + A processor that takes a vendor tag (expecting a schema `#ref`) and injects all properties of that given schema as + query parameter to the [request definition](processors/schema-query-parameter-attributes/SchemaQueryParameter.php). + + [source](processors/schema-query-parameter-attributes) + * **sort-components processor** A processor that sorts components so they appear in alphabetical order. diff --git a/Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php b/Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php new file mode 100644 index 000000000..d1fb56ecd --- /dev/null +++ b/Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php @@ -0,0 +1,84 @@ +getAnnotationsOfType(Operation::class); + + foreach ($operations as $operation) { + if ($operation->x !== Generator::UNDEFINED && array_key_exists(self::REF, $operation->x)) { + if (!is_string($operation->x[self::REF])) { + throw new \InvalidArgumentException('Value of `x.' . self::REF . '` must be a string'); + } + + $schema = $analysis->getSchemaForSource($operation->x[self::REF]); + if (!$schema instanceof Schema) { + throw new \InvalidArgumentException('Value of `x.' . self::REF . "` contains reference to unknown schema: `{$operation->x[self::REF]}`"); + } + + $this->expandQueryArgs($operation, $schema); + $this->cleanUp($operation); + } + } + } + + /** + * Expand the given operation by injecting parameters for all properties of the given schema. + */ + private function expandQueryArgs(Operation $operation, Schema $schema): void + { + if ($schema->properties === Generator::UNDEFINED || !$schema->properties) { + return; + } + + $operation->parameters = $operation->parameters === Generator::UNDEFINED ? [] : $operation->parameters; + + foreach ($schema->properties as $property) { + $isNullable = $property->nullable !== Generator::UNDEFINED ? $property->nullable : false; + $schema = new Schema( + type: $property->format !== Generator::UNDEFINED ? $property->format : $property->type, + nullable: $isNullable + ); + $schema->_context = $operation->_context; // inherit context from operation, required to pretend to be a parameter + + $parameter = new Parameter( + name: $property->property, + description: $property->description !== Generator::UNDEFINED ? $property->description : null, + in: 'query', + required: !$isNullable, + schema: $schema, + example: $property->example, + ); + $parameter->_context = $operation->_context; // inherit context from operation, required to pretend to be a parameter + + $operation->parameters[] = $parameter; + } + } + + private function cleanUp(Operation $operation): void + { + unset($operation->x[self::REF]); + if (!$operation->x) { + $operation->x = Generator::UNDEFINED; + } + } +} diff --git a/Examples/processors/schema-query-parameter-attributes/app/OpenApi.php b/Examples/processors/schema-query-parameter-attributes/app/OpenApi.php new file mode 100644 index 000000000..fe5875c45 --- /dev/null +++ b/Examples/processors/schema-query-parameter-attributes/app/OpenApi.php @@ -0,0 +1,17 @@ + Product::class], + )] + public function findProducts($id) + { + } +} diff --git a/Examples/processors/schema-query-parameter-attributes/scan.php b/Examples/processors/schema-query-parameter-attributes/scan.php new file mode 100644 index 000000000..081396be8 --- /dev/null +++ b/Examples/processors/schema-query-parameter-attributes/scan.php @@ -0,0 +1,29 @@ +addPsr4('App\\', __DIR__ . '/app'); +// and our custom processor +$classLoader->addPsr4('SchemaQueryParameterProcessor\\', __DIR__); + +$openapiGenerator = new Generator(); +$processors = []; +foreach ($openapiGenerator->getProcessors() as $processor) { + $processors[] = $processor; + if ($processor instanceof BuildPaths) { + $processors[] = new SchemaQueryParameter(); + } +} + +$openapi = $openapiGenerator + ->setProcessors($processors) + ->generate([__DIR__ . '/app']); + +file_put_contents(__DIR__ . '/schema-query-parameter.yaml', $openapi->toYaml()); + +echo $openapi->toYaml(); diff --git a/Examples/processors/schema-query-parameter-attributes/schema-query-parameter.yaml b/Examples/processors/schema-query-parameter-attributes/schema-query-parameter.yaml new file mode 100644 index 000000000..852a5708c --- /dev/null +++ b/Examples/processors/schema-query-parameter-attributes/schema-query-parameter.yaml @@ -0,0 +1,69 @@ +openapi: 3.0.0 +info: + title: 'Example of using a custom processor in swagger-php' + version: 1.0.0 +paths: + '/products/{id}': + get: + tags: + - Products + operationId: 399b71a7672f0a46be1b5f4c120c355d + parameters: + - + name: id + in: path + required: true + responses: + '200': + description: 'A single product' + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + /products/search: + get: + tags: + - Products + operationId: 178f74de3417eec20dee95709821e6ca + parameters: + - + name: id + in: query + required: true + schema: + type: integer + nullable: false + example: 43 + - + name: name + in: query + required: false + schema: + type: string + nullable: true + example: 'Lorem ipsum' + responses: + '200': + description: 'A single product' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Product' +components: + schemas: + Product: + title: Product + description: 'A simple product model' + properties: + id: + title: 'The unique identifier of a product in our catalog.' + type: integer + example: 43 + name: + title: 'The name of the product.' + type: string + example: 'Lorem ipsum' + nullable: true + type: object diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9a54f0842..3e34c3bca 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,25 @@ parameters: ignoreErrors: + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php + + - + message: "#^Property OpenApi\\\\Annotations\\\\AbstractAnnotation\\:\\:\\$x \\(array\\\\) does not accept string\\.$#" + count: 1 + path: Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php + + - + message: "#^Strict comparison using \\=\\=\\= between array\\ and '@OA\\\\\\\\Generator\\:…' will always evaluate to false\\.$#" + count: 1 + path: Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php + + - + message: "#^Strict comparison using \\=\\=\\= between array\\ and '@OA\\\\\\\\Generator\\:…' will always evaluate to false\\.$#" + count: 1 + path: Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php + - message: "#^Result of && is always true\\.$#" count: 1