Skip to content

Commit

Permalink
Add example schema-query-parameter-attributes processor (zircote#1569)
Browse files Browse the repository at this point in the history
  • Loading branch information
beeyev authored Apr 18, 2024
1 parent b46a36d commit 1cdb866
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Examples/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace SchemaQueryParameterProcessor;

use OpenApi\Analysis;
use OpenApi\Annotations\Operation;
use OpenApi\Generator;
use OpenApi\Attributes\Parameter;
use OpenApi\Attributes\Schema;

/**
* Custom processor to translate the vendor tag `query-args-$ref` into query parameter annotations.
*
* Details for the parameters are taken from the referenced schema.
*/
class SchemaQueryParameter
{
public const REF = 'query-args-$ref';

public function __invoke(Analysis $analysis): void
{
/** @var Operation[] $operations */
$operations = $analysis->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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App;

use OpenApi\Attributes as OA;

/**
* Uses a custom processor `QueryArgsFromSchema` processor to convert a vendor extension into query parameters.
*
* The parameters are extracted from the schema referenced by the custom extension.
*/
#[OA\OpenApi(
info: new OA\Info(version: '1.0.0', title: 'Example of using a custom processor in swagger-php'),
)]
class OpenApi
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App;

use OpenApi\Attributes as OA;

#[OA\Schema(
title: 'Product',
description: 'A simple product model',
)]
class Product
{
public function __construct(
#[OA\Property(
title: 'The unique identifier of a product in our catalog.',
example: 43,
)]
public int $id,
#[OA\Property(
title: 'The name of the product.',
example: 'Lorem ipsum',
)]
public string|null $name,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace App;

use OpenApi\Attributes as OA;
use OpenApi\Attributes\Response;
use SchemaQueryParameterProcessor\SchemaQueryParameter;

class ProductController
{
#[OA\Get(
path: '/products/{id}',
tags: ['Products'],
parameters: [
new OA\PathParameter(
name: 'id',
required: true,
),
],
responses: [
new Response(
response: 200,
description: 'A single product',
content: new OA\JsonContent(
ref: Product::class
)
),
],
)]
public function getProduct($id)
{
}

#[OA\Get(
path: '/products/search',
tags: ['Products'],
responses: [
new Response(
response: 200,
description: 'A single product',
content: new OA\JsonContent(
type: 'array',
items: new OA\Items(ref: Product::class)
)
),
],
x: [SchemaQueryParameter::REF => Product::class],
)]
public function findProducts($id)
{
}
}
29 changes: 29 additions & 0 deletions Examples/processors/schema-query-parameter-attributes/scan.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use OpenApi\Generator;
use OpenApi\Processors\BuildPaths;
use SchemaQueryParameterProcessor\SchemaQueryParameter;

$classLoader = require __DIR__ . '/../../../vendor/autoload.php';

// register our app namespace...
$classLoader->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();
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -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\\<string, mixed\\>\\) does not accept string\\.$#"
count: 1
path: Examples/processors/schema-query-parameter-attributes/SchemaQueryParameter.php

-
message: "#^Strict comparison using \\=\\=\\= between array\\<OpenApi\\\\Annotations\\\\Parameter\\> 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\\<OpenApi\\\\Annotations\\\\Property\\> 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
Expand Down

0 comments on commit 1cdb866

Please sign in to comment.