Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zircote/swagger-php attributes support #14

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Version < 1.2 requires zircote/swagger-php 2.x which works with the OpenAPI Spec
So tobion/openapi-symfony-routing can be used with both OpenAPI v2 and v3 and composer will select the compatible one for your dependencies.
Route loading stays the same between those versions. You just need to update the annotations when migrating from OpenAPI v2 to v3.

Version >= 1.3 requires zircote/swagger-php >=4.1 which is compatible with the OpenAPI Specification version 3.0.1 and supports attributes. This package need php >= 8.1 for attributes support from zircote/swagger-php.

## Basic Usage

This library allows to (re-)use your OpenAPI documentation to configure the routing of your Symfony-based API.
Expand Down
10 changes: 10 additions & 0 deletions add_composer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

export COMPOSER_HOME=/tmp/composer

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
chmod +x composer.phar
mv composer.phar composer
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/framework-bundle": "^4.4|^5.0|^6.0",
"symfony/routing": "^4.4|^5.0|^6.0",
"zircote/swagger-php": "^3.0.3"
"zircote/swagger-php": "^3.0.3|^4.1"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.2|^6.0"
Expand Down
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
php7:
image: php:7.2-fpm
user: "1000:1000"
working_dir: /var/www/html
volumes:
- ./:/var/www/html
php8.0:
image: php:8.0-fpm
user: "1000:1000"
working_dir: /var/www/html
volumes:
- ./:/var/www/html
php8.1:
image: php:8.1-fpm
working_dir: /var/www/html
user: "1000:1000"
volumes:
- ./:/var/www/html

50 changes: 43 additions & 7 deletions src/OpenApiRouteLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class OpenApiRouteLoader implements RouteLoaderInterface
*/
private $routeNames = [];

/**
* @var null|Generator
*/
private $generator = null;

/**
* @var string
*/
Expand Down Expand Up @@ -86,12 +91,30 @@ private function createOpenApi(): OpenApi
return \OpenApi\scan($this->finder);
}

$processors = array_filter(Analysis::processors(), static function ($processor): bool {
// remove OperationId processor which would hash the controller starting in 3.2.2 breaking the default route name logic
return !$processor instanceof OperationId && !$processor instanceof DocBlockDescriptions;
});
if (null !== $this->generator) {
return $this->generator->generate($this->finder);
}

if (method_exists(Analysis::class, 'processors')) {
$processors = array_filter(Analysis::processors(), static function ($processor): bool {
// remove OperationId processor which would hash the controller starting in 3.2.2 breaking the default route name logic
return !$processor instanceof OperationId && !$processor instanceof DocBlockDescriptions;
});

$this->generator = (new Generator())->setProcessors($this->filterProcessors($processors));

return $this->generator->generate($this->finder);
}

$this->generator = new Generator();

return (new Generator())->setProcessors($processors)->generate($this->finder);
$this->generator->setProcessors(
$this->filterProcessors(
$this->generator->getProcessors()
)
);

return $this->generator->generate($this->finder);
}

/**
Expand Down Expand Up @@ -128,8 +151,14 @@ private function createRoute(Operation $operation, string $controller, FormatSuf
}
if (self::$openApiUndefined !== $operation->parameters) {
foreach ($operation->parameters as $parameter) {
if ('path' === $parameter->in && self::$openApiUndefined !== $parameter->schema && self::$openApiUndefined !== $parameter->schema->pattern) {
$route->setRequirement($parameter->name, $parameter->schema->pattern);
if ('path' === $parameter->in && self::$openApiUndefined !== $parameter->schema) {
if (self::$openApiUndefined !== $parameter->schema->pattern) {
$route->setRequirement($parameter->name, $parameter->schema->pattern);
}

if (self::$openApiUndefined !== $parameter->schema->enum) {
$route->setRequirement($parameter->name, implode('|', $parameter->schema->enum));
}
}
}
}
Expand Down Expand Up @@ -190,4 +219,11 @@ private function getDefaultRouteName(string $controller): string

return $name;
}

private function filterProcessors(array $processors): array
{
return array_filter($processors, static function ($processor): bool {
return !$processor instanceof OperationId && !$processor instanceof DocBlockDescriptions;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\Basic;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\Basic;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\FormatSuffix;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\FormatSuffix;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\OperationId;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\OperationId;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\PathParameterPattern;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\PathParameterPattern;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\Priority;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\Priority;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses\SubNamespace;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses\SubNamespace;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralHttpMethods;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralHttpMethods;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralRoutesOnOneAction;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralRoutesOnOneAction;

use OpenApi\Annotations as OA;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests;
namespace Tobion\OpenApiSymfonyRouting\Tests\Annotations;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Tobion\OpenApiSymfonyRouting\OpenApiRouteLoader;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\Basic\Controller as BasicController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\FormatSuffix\Controller as FormatSuffixController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\OperationId\Controller as OperationIdController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\PathParameterPattern\Controller as PathParameterPatternController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\Priority\Controller as PriorityController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses\BarController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses\FooController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralClasses\SubNamespace\SubController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralHttpMethods\Controller as SeveralHttpMethodsController;
use Tobion\OpenApiSymfonyRouting\Tests\Fixtures\SeveralRoutesOnOneAction\Controller as SeveralRoutesOnOneActionController;

class OpenApiRouteLoaderTest extends TestCase
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\Basic\Controller as BasicController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\FormatSuffix\Controller as FormatSuffixController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\OperationId\Controller as OperationIdController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\PathParameterPattern\Controller as PathParameterPatternController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\Priority\Controller as PriorityController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses\BarController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses\FooController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralClasses\SubNamespace\SubController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralHttpMethods\Controller as SeveralHttpMethodsController;
use Tobion\OpenApiSymfonyRouting\Tests\Annotations\Fixtures\SeveralRoutesOnOneAction\Controller as SeveralRoutesOnOneActionController;

final class OpenApiRouteLoaderAnnotationsTest extends TestCase
{
private const FIXTURES_ROUTE_NAME_PREFIX = 'tobion_openapisymfonyrouting_tests_fixtures_';
private const FIXTURES_ROUTE_NAME_PREFIX = 'tobion_openapisymfonyrouting_tests_annotations_fixtures_';

public function testBasic(): void
sonrac marked this conversation as resolved.
Show resolved Hide resolved
{
Expand Down
17 changes: 17 additions & 0 deletions tests/Attributes/Fixtures/Basic/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Attributes\Fixtures\Basic;

use OpenApi\Attributes as OA;

#[OA\Info(version: "1.0", title: "My API")]
class Controller
{
#[OA\Get(path: "/foobar")]
#[OA\Response(response: 200, description: "OK")]
public function __invoke(): void
{
}
}
39 changes: 39 additions & 0 deletions tests/Attributes/Fixtures/FormatSuffix/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Attributes\Fixtures\FormatSuffix;

use OpenApi\Attributes as OA;

#[OA\OpenApi(
info: new OA\Info(
title: "My API",
version: "1.0"
),
x: [
"format-suffix" => [
"enabled" => true
]
]
)]
class Controller
{
#[OA\Get(path: "/a",)]
#[OA\Response(response: "200", description: "Success")]
public function inheritEnabledFormatSuffix(): void
{
}

#[OA\Get(path: "/b", x: ["format-suffix" => ["pattern" => "json|xml"]])]
#[OA\Response(response: "200", description: "Success")]
public function defineFormatPattern(): void
{
}

#[OA\Get(path: "/c", x: ["format-suffix" => false])]
#[OA\Response(response: "200", description: "Success")]
public function disableFormatSuffix(): void
{
}
}
17 changes: 17 additions & 0 deletions tests/Attributes/Fixtures/OperationId/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Attributes\Fixtures\OperationId;

use OpenApi\Attributes as OA;

#[OA\Info(title: "My API", version: "1.0")]
class Controller
{
#[OA\Get(path: "/foobar", operationId: "my-name")]
#[OA\Response(response: "200", description: "Success")]
public function __invoke(): void
{
}
}
39 changes: 39 additions & 0 deletions tests/Attributes/Fixtures/PathParameterPattern/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Tobion\OpenApiSymfonyRouting\Tests\Attributes\Fixtures\PathParameterPattern;

use mysql_xdevapi\Schema;
use OpenApi\Attributes as OA;

#[OA\Info(title: "My API", version: "1.0")]
class Controller
{
#[OA\Get(path: "/foo/{id}")]
#[OA\Response(response:"200", description:"Success")]
public function noPattern(
#[OA\Parameter]string $id
): void
{
}

#[OA\Get(path: "/baz/{id}")]
#[OA\Response(response:"200", description:"Success")]
public function noSchema(
#[OA\Parameter]string $id
): void
{
}

#[OA\Get(path: "/bar/{id}/{type}")]
#[OA\Response(response:"200", description:"Success")]
public function withPattern(
#[OA\PathParameter(required: true, schema: new OA\Schema(pattern:"^[a-zA-Z0-9]+$"))]
string $id,
#[OA\PathParameter(required: true, schema: new OA\Schema(enum: ["internal", "external"]))]
string $type
): void
{
}
}
Loading