Skip to content

Commit 6d823ce

Browse files
authored
Merge pull request #1 from Tobion/configuration-plus-tests
Better configurability + tests
2 parents 13fc83d + 8063740 commit 6d823ce

15 files changed

Lines changed: 723 additions & 39 deletions

File tree

.gitattributes

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
/tests export-ignore
2-
.editorconfig export-ignore
3-
.gitattributes export-ignore
4-
.gitignore export-ignore
5-
phpunit.xml export-ignore
1+
/.github/ export-ignore
2+
/tests/ export-ignore
3+
/.editorconfig export-ignore
4+
/.gitattributes export-ignore
5+
/.gitignore export-ignore
6+
/phpunit.xml export-ignore

.github/workflows/ci.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
jobs:
10+
build-lowest-version:
11+
name: Build lowest version
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Set up PHP
16+
uses: shivammathur/setup-php@v2
17+
with:
18+
php-version: '7.2'
19+
coverage: 'none'
20+
21+
- name: Checkout code
22+
uses: actions/checkout@v2
23+
24+
- name: Install dependencies
25+
run: composer update --no-interaction --prefer-stable --prefer-lowest --no-progress --prefer-dist
26+
27+
- name: Run tests
28+
run: vendor/bin/simple-phpunit
29+
30+
build:
31+
name: Build
32+
runs-on: ubuntu-latest
33+
strategy:
34+
max-parallel: 10
35+
matrix:
36+
php: ['7.2', '7.3', '7.4']
37+
38+
steps:
39+
- name: Set up PHP
40+
uses: shivammathur/setup-php@v2
41+
with:
42+
php-version: ${{ matrix.php }}
43+
coverage: 'none'
44+
45+
- name: Checkout code
46+
uses: actions/checkout@v2
47+
48+
- name: Install dependencies
49+
run: composer update --no-interaction --prefer-stable --no-progress --prefer-dist
50+
51+
- name: Run tests
52+
run: vendor/bin/simple-phpunit
53+
54+
build-php8:
55+
name: Build (PHP 8)
56+
runs-on: ubuntu-latest
57+
58+
steps:
59+
- name: Set up PHP
60+
uses: shivammathur/setup-php@v2
61+
with:
62+
php-version: 8.0
63+
coverage: 'none'
64+
65+
- name: Checkout code
66+
uses: actions/checkout@v2
67+
68+
- name: Install dependencies
69+
run: composer update --no-interaction --no-progress --prefer-dist
70+
71+
- name: Run tests
72+
run: vendor/bin/simple-phpunit

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
"symfony/finder": "^4.4|^5.0",
1717
"symfony/framework-bundle": "^4.4|^5.0",
1818
"symfony/routing": "^4.4|^5.0",
19-
"zircote/swagger-php": "^2.0"
19+
"zircote/swagger-php": "^2.0.14"
2020
},
2121
"require-dev": {
2222
"symfony/phpunit-bridge": "^5.0@dev"
2323
},
24+
"minimum-stability": "dev",
2425
"autoload": {
2526
"psr-4": {
2627
"Tobion\\OpenApiSymfonyRouting\\": "src/"

src/FormatSuffixConfig.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tobion\OpenApiSymfonyRouting;
6+
7+
use Swagger\Annotations\AbstractAnnotation;
8+
9+
/**
10+
* @internal
11+
*/
12+
class FormatSuffixConfig
13+
{
14+
/**
15+
* @var bool
16+
*/
17+
public $enabled;
18+
19+
/**
20+
* @var string|null
21+
*/
22+
public $pattern;
23+
24+
public function __construct(
25+
bool $enabled,
26+
?string $pattern
27+
) {
28+
$this->enabled = $enabled;
29+
$this->pattern = $pattern;
30+
}
31+
32+
public static function fromAnnotation(AbstractAnnotation $annotation, ?self $parent = null): self
33+
{
34+
if (!isset($annotation->x['format-suffix'])) {
35+
return new self($parent ? $parent->enabled : false, $parent ? $parent->pattern : null);
36+
}
37+
38+
if (is_bool($annotation->x['format-suffix'])) {
39+
return new self($annotation->x['format-suffix'], $parent ? $parent->pattern : null);
40+
}
41+
42+
return new self(
43+
$annotation->x['format-suffix']['enabled'] ?? ($parent ? $parent->enabled : false),
44+
$annotation->x['format-suffix']['pattern'] ?? ($parent ? $parent->pattern : null)
45+
);
46+
}
47+
}

src/OpenApiRouteLoader.php

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,77 +13,83 @@
1313
class OpenApiRouteLoader implements RouteLoaderInterface
1414
{
1515
/**
16-
* @var string[]
16+
* @var Finder
1717
*/
18-
private $sourceDirectories;
19-
20-
/**
21-
* @var string
22-
*/
23-
private $sourcePattern;
18+
private $finder;
2419

2520
/**
2621
* @var array<string, int>
2722
*/
2823
private $routeNames = [];
2924

30-
/**
31-
* @param string[] $sourceDirectories
32-
*/
3325
public function __construct(
34-
array $sourceDirectories,
35-
string $sourcePattern = '/\.php/'
26+
?Finder $finder = null
3627
) {
37-
$this->sourceDirectories = $sourceDirectories;
38-
$this->sourcePattern = $sourcePattern;
28+
if (null === $finder) {
29+
// try to use the symfony flex default src directory based on a composer install
30+
$srcDir = __DIR__.'/../../../../src';
31+
$realPath = realpath($srcDir);
32+
if (!$realPath || !is_dir($realPath)) {
33+
throw new \LogicException(sprintf('The default directory to look for OpenAPI/Swagger annotations "%s" does not exist. Please configure the finder explicitly.'));
34+
}
35+
36+
$finder = (new Finder())->in($realPath)->files()->name('*.php')->sortByName()->followLinks();
37+
}
38+
39+
$this->finder = $finder;
3940
}
4041

4142
public function __invoke(): RouteCollection
4243
{
43-
$finder = new Finder();
44-
$finder->in($this->sourceDirectories)->path($this->sourcePattern);
45-
46-
$fullSwagger = \Swagger\scan($finder);
44+
$fullSwagger = \Swagger\scan($this->finder);
4745
$routeCollection = new RouteCollection();
4846

47+
$globalFormatSuffixConfig = FormatSuffixConfig::fromAnnotation($fullSwagger);
48+
4949
foreach ($fullSwagger->paths as $path) {
50-
$this->addRouteFromSwaggerOperation($routeCollection, $path->get);
51-
$this->addRouteFromSwaggerOperation($routeCollection, $path->put);
52-
$this->addRouteFromSwaggerOperation($routeCollection, $path->post);
53-
$this->addRouteFromSwaggerOperation($routeCollection, $path->delete);
54-
$this->addRouteFromSwaggerOperation($routeCollection, $path->options);
55-
$this->addRouteFromSwaggerOperation($routeCollection, $path->head);
56-
$this->addRouteFromSwaggerOperation($routeCollection, $path->patch);
50+
$pathFormatSuffixConfig = FormatSuffixConfig::fromAnnotation($path, $globalFormatSuffixConfig);
51+
52+
$this->addRouteFromSwaggerOperation($routeCollection, $path->get, $pathFormatSuffixConfig);
53+
$this->addRouteFromSwaggerOperation($routeCollection, $path->put, $pathFormatSuffixConfig);
54+
$this->addRouteFromSwaggerOperation($routeCollection, $path->post, $pathFormatSuffixConfig);
55+
$this->addRouteFromSwaggerOperation($routeCollection, $path->delete, $pathFormatSuffixConfig);
56+
$this->addRouteFromSwaggerOperation($routeCollection, $path->options, $pathFormatSuffixConfig);
57+
$this->addRouteFromSwaggerOperation($routeCollection, $path->head, $pathFormatSuffixConfig);
58+
$this->addRouteFromSwaggerOperation($routeCollection, $path->patch, $pathFormatSuffixConfig);
5759
}
5860

5961
$this->routeNames = [];
6062

6163
return $routeCollection;
6264
}
6365

64-
private function addRouteFromSwaggerOperation(RouteCollection $routeCollection, ?Operation $operation): void
66+
private function addRouteFromSwaggerOperation(RouteCollection $routeCollection, ?Operation $operation, FormatSuffixConfig $parentFormatSuffixConfig): void
6567
{
6668
if (null === $operation) {
6769
return;
6870
}
6971

7072
$controller = $this->getControllerFromSwaggerOperation($operation);
7173
$name = $this->getRouteName($operation, $controller);
72-
$route = $this->createRoute($operation, $controller);
74+
$route = $this->createRoute($operation, $controller, $parentFormatSuffixConfig);
7375
$routeCollection->add($name, $route);
7476
}
7577

76-
private function createRoute(Operation $operation, string $controller): Route
78+
private function createRoute(Operation $operation, string $controller, FormatSuffixConfig $parentFormatSuffixConfig): Route
7779
{
78-
$formatSuffix = $operation->x['format-suffix'] ?? true;
79-
$path = $formatSuffix ? $operation->path.'.{_format}' : $operation->path;
80+
$formatSuffixConfig = FormatSuffixConfig::fromAnnotation($operation, $parentFormatSuffixConfig);
81+
82+
$path = $formatSuffixConfig->enabled ? $operation->path.'.{_format}' : $operation->path;
8083
$route = new Route($path);
8184
$route->setMethods($operation->method);
8285
$route->setDefault('_controller', $controller);
83-
if ($formatSuffix) {
84-
$formatPattern = $operation->x['format-pattern'] ?? 'json|xml';
86+
87+
if ($formatSuffixConfig->enabled) {
8588
$route->setDefault('_format', null);
86-
$route->setRequirement('_format', $formatPattern);
89+
90+
if (null !== $formatSuffixConfig->pattern) {
91+
$route->setRequirement('_format', $formatSuffixConfig->pattern);
92+
}
8793
}
8894
if (null !== $operation->parameters) {
8995
foreach ($operation->parameters as $parameter) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\Basic;
6+
7+
use Swagger\Annotations as SWG;
8+
9+
/**
10+
* @SWG\Swagger(
11+
* @SWG\Info(
12+
* title="My API",
13+
* version="1.0"
14+
* )
15+
* )
16+
*/
17+
class Controller
18+
{
19+
/**
20+
* @SWG\Get(
21+
* path="/foobar",
22+
* @SWG\Response(
23+
* response="200",
24+
* description="Success"
25+
* )
26+
* )
27+
*/
28+
public function __invoke(): void
29+
{
30+
}
31+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\FormatSuffix;
6+
7+
use Swagger\Annotations as SWG;
8+
9+
/**
10+
* @SWG\Swagger(
11+
* @SWG\Info(
12+
* title="My API",
13+
* version="1.0"
14+
* ),
15+
* x={"format-suffix": {
16+
* "enabled": true
17+
* }}
18+
* )
19+
*/
20+
class Controller
21+
{
22+
/**
23+
* @SWG\Get(
24+
* path="/a",
25+
* @SWG\Response(
26+
* response="200",
27+
* description="Success"
28+
* )
29+
* )
30+
*/
31+
public function inheritEnabledFormatSuffix(): void
32+
{
33+
}
34+
35+
/**
36+
* @SWG\Get(
37+
* path="/b",
38+
* x={"format-suffix": {
39+
* "pattern": "json|xml"
40+
* }},
41+
* @SWG\Response(
42+
* response="200",
43+
* description="Success"
44+
* )
45+
* )
46+
*/
47+
public function defineFormatPattern(): void
48+
{
49+
}
50+
51+
/**
52+
* @SWG\Get(
53+
* path="/c",
54+
* x={"format-suffix": false},
55+
* @SWG\Response(
56+
* response="200",
57+
* description="Success"
58+
* )
59+
* )
60+
*/
61+
public function disableFormatSuffix(): void
62+
{
63+
}
64+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tobion\OpenApiSymfonyRouting\Tests\Fixtures\OperationId;
6+
7+
use Swagger\Annotations as SWG;
8+
9+
/**
10+
* @SWG\Swagger(
11+
* @SWG\Info(
12+
* title="My API",
13+
* version="1.0"
14+
* )
15+
* )
16+
*/
17+
class Controller
18+
{
19+
/**
20+
* @SWG\Get(
21+
* path="/foobar",
22+
* operationId="my-name",
23+
* @SWG\Response(
24+
* response="200",
25+
* description="Success"
26+
* )
27+
* )
28+
*/
29+
public function __invoke(): void
30+
{
31+
}
32+
}

0 commit comments

Comments
 (0)