diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e7c986..df4bdd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,8 @@ on: - cron: '0 11 26 * *' jobs: - build-lowest-version: - name: Build lowest version + build-lowest-dependencies: + name: With lowest dependencies runs-on: ubuntu-latest steps: @@ -53,3 +53,27 @@ jobs: - name: Run tests run: vendor/bin/simple-phpunit + + build-dev-dependencies: + name: With dev dependencies & PHP + runs-on: ubuntu-latest + + steps: + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + coverage: 'none' + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Allow dev dependencies + run: composer config minimum-stability dev + + - name: Install dependencies + run: composer update --no-interaction --no-progress --prefer-dist + + - name: Run tests + continue-on-error: true + run: vendor/bin/simple-phpunit diff --git a/src/OpenApiRouteLoader.php b/src/OpenApiRouteLoader.php index 4923e50..ad6deb4 100644 --- a/src/OpenApiRouteLoader.php +++ b/src/OpenApiRouteLoader.php @@ -4,7 +4,12 @@ namespace Tobion\OpenApiSymfonyRouting; +use OpenApi\Analysis; +use OpenApi\Annotations\OpenApi; use OpenApi\Annotations\Operation; +use OpenApi\Generator; +use OpenApi\Processors\DocBlockDescriptions; +use OpenApi\Processors\OperationId; use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\Routing\Route; @@ -22,9 +27,18 @@ class OpenApiRouteLoader implements RouteLoaderInterface */ private $routeNames = []; + /** + * @var string + */ + private static $openApiUndefined; + public function __construct(Finder $finder) { $this->finder = $finder; + + if (!isset(self::$openApiUndefined)) { + self::$openApiUndefined = \class_exists(Generator::class) ? Generator::UNDEFINED : \OpenApi\UNDEFINED; + } } public static function fromDirectories(string $dir, string ...$moreDirs): self @@ -44,7 +58,7 @@ public static function fromSrcDirectory(): self public function __invoke(): RouteCollection { - $openApi = \OpenApi\scan($this->finder); + $openApi = $this->createOpenApi(); $routeCollection = new RouteCollection(); $globalFormatSuffixConfig = FormatSuffixConfig::fromAnnotation($openApi); @@ -66,12 +80,26 @@ public function __invoke(): RouteCollection return $routeCollection; } + private function createOpenApi(): OpenApi + { + if (!\class_exists(Generator::class)) { + 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; + }); + + return (new Generator())->setProcessors($processors)->generate($this->finder); + } + /** * @param Operation|string $operation */ private function addRouteFromOpenApiOperation(RouteCollection $routeCollection, $operation, FormatSuffixConfig $parentFormatSuffixConfig): void { - if (\OpenApi\UNDEFINED === $operation || !$operation instanceof Operation) { + if (self::$openApiUndefined === $operation || !$operation instanceof Operation) { return; } @@ -98,9 +126,9 @@ private function createRoute(Operation $operation, string $controller, FormatSuf $route->setRequirement('_format', $formatSuffixConfig->pattern); } } - if (\OpenApi\UNDEFINED !== $operation->parameters) { + if (self::$openApiUndefined !== $operation->parameters) { foreach ($operation->parameters as $parameter) { - if ('path' === $parameter->in && \OpenApi\UNDEFINED !== $parameter->schema && \OpenApi\UNDEFINED !== $parameter->schema->pattern) { + if ('path' === $parameter->in && self::$openApiUndefined !== $parameter->schema && self::$openApiUndefined !== $parameter->schema->pattern) { $route->setRequirement($parameter->name, $parameter->schema->pattern); } } @@ -121,7 +149,7 @@ private function getRouteName(Operation $operation, string $controller): string // swagger-php v3 adds the controller as operationId automatically, see \OpenApi\Processors\OperationId. // This must be ignored as it is not viable with multiple annotations on the same controller. - return \OpenApi\UNDEFINED === $operation->operationId || $controller === $operation->operationId ? $this->getDefaultRouteName($controller) : $operation->operationId; + return self::$openApiUndefined === $operation->operationId || $controller === $operation->operationId ? $this->getDefaultRouteName($controller) : $operation->operationId; } private function getRoutePriority(Operation $operation): int