From 8d19acb7dbe2207cc44337d50982f97e061145d6 Mon Sep 17 00:00:00 2001 From: John Charman Date: Fri, 11 Oct 2024 11:03:23 +0100 Subject: [PATCH] Add ignoreServers option --- composer.json | 2 +- src/Console/Command/CacheOpenAPIRoutes.php | 53 +++- src/Console/Service/CacheOpenAPIRoutes.php | 18 +- .../Command/CacheOpenAPIRoutesTest.php | 263 +++--------------- .../Service/CacheOpenAPIRoutesTest.php | 235 ++-------------- tests/fixtures/ProvidesApiAndRoutes.php | 179 ++++++++++++ 6 files changed, 297 insertions(+), 453 deletions(-) create mode 100644 tests/fixtures/ProvidesApiAndRoutes.php diff --git a/composer.json b/composer.json index 0e4503d..a054f4d 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "require": { "php": "^8.1.0", "cebe/php-openapi": "^1.7", - "membrane/openapi-reader": "^2.0.0", + "membrane/openapi-reader": "2.1.0", "psr/http-message": "^1.0 || ^2.0", "psr/log": "^3.0", "symfony/console": "^6.0 || ^7.0" diff --git a/src/Console/Command/CacheOpenAPIRoutes.php b/src/Console/Command/CacheOpenAPIRoutes.php index 6bc6288..1a44dc6 100644 --- a/src/Console/Command/CacheOpenAPIRoutes.php +++ b/src/Console/Command/CacheOpenAPIRoutes.php @@ -4,11 +4,13 @@ namespace Membrane\OpenAPIRouter\Console\Command; -use Membrane\OpenAPIRouter\Console\Service\CacheOpenAPIRoutes as CacheOpenAPIRoutesService; +use Membrane\OpenAPIRouter\Console\Service; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; @@ -20,29 +22,52 @@ class CacheOpenAPIRoutes extends Command { protected function configure(): void { - self::addArgument( - 'openAPI', - InputArgument::REQUIRED, - 'The absolute filepath to your OpenAPI' - ); - self::addArgument( - 'destination', - InputArgument::OPTIONAL, - 'The filepath for the generated route collection', - getcwd() . '/cache/routes.php' - ); + $this->setDefinition(new InputDefinition([ + new InputArgument( + name: 'openAPI', + mode: InputArgument::REQUIRED, + description: 'The absolute filepath to your OpenAPI' + ), + new InputArgument( + name: 'destination', + mode: InputArgument::OPTIONAL, + description: 'The filepath for the generated route collection', + default: getcwd() . '/cache/routes.php' + ), + new InputOption( + name: 'ignore-servers', + description: 'ignore servers, only use the default "/" server', + mode: InputOption::VALUE_NONE, + ), + // @todo add support for this in the reader first + // new InputOption( + // name: 'with-hostless-fallback', + // description: 'add the default "/" server, if not already specified', + // ), + ])); } protected function execute(InputInterface $input, OutputInterface $output): int { $openAPIFilePath = $input->getArgument('openAPI'); assert(is_string($openAPIFilePath)); + $destination = $input->getArgument('destination'); assert(is_string($destination)); + $ignoreServers = $input->getOption('ignore-servers'); + assert(is_bool($ignoreServers)); + // @todo add support this in the reader first + // $hostlessFallback = $input->getOption('with-hostless-fallback'); + $logger = new ConsoleLogger($output); - $service = new CacheOpenAPIRoutesService($logger); - return $service->cache($openAPIFilePath, $destination) ? Command::SUCCESS : Command::FAILURE; + return (new Service\CacheOpenAPIRoutes($logger))->cache( + $openAPIFilePath, + $destination, + $ignoreServers, + // @todo add support this in the reader first + // $hostlessFallback, + ) ? Command::SUCCESS : Command::FAILURE; } } diff --git a/src/Console/Service/CacheOpenAPIRoutes.php b/src/Console/Service/CacheOpenAPIRoutes.php index c9bf41e..6c6f966 100644 --- a/src/Console/Service/CacheOpenAPIRoutes.php +++ b/src/Console/Service/CacheOpenAPIRoutes.php @@ -19,8 +19,13 @@ public function __construct( ) { } - public function cache(string $openAPIFilePath, string $cacheDestination): bool - { + public function cache( + string $openAPIFilePath, + string $cacheDestination, + bool $ignoreServers = false, + //@todo add support for this in the reader first + // bool $hostlessFallback, + ): bool { $existingFilePath = $cacheDestination; while (!file_exists($existingFilePath)) { $existingFilePath = dirname($existingFilePath); @@ -38,6 +43,15 @@ public function cache(string $openAPIFilePath, string $cacheDestination): bool return false; } + if ($ignoreServers) { + $openApi = $openApi->withoutServers(); + } + + //@todo add support for this in reader first + // if ($hostlessFallback) { + // $openApi = $openApi->withHostlessFallback(); + // } + try { $routeCollection = (new RouteCollector())->collect($openApi); } catch (CannotCollectRoutes $e) { diff --git a/tests/Console/Command/CacheOpenAPIRoutesTest.php b/tests/Console/Command/CacheOpenAPIRoutesTest.php index e21ded4..94e0c20 100644 --- a/tests/Console/Command/CacheOpenAPIRoutesTest.php +++ b/tests/Console/Command/CacheOpenAPIRoutesTest.php @@ -10,9 +10,11 @@ use Membrane\OpenAPIRouter\Route; use Membrane\OpenAPIRouter\RouteCollection; use Membrane\OpenAPIRouter\RouteCollector; +use Membrane\OpenAPIRouter\Tests\Fixtures\ProvidesApiAndRoutes; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; @@ -70,248 +72,59 @@ public function itCannotRouteFromRelativeFilePaths(): void public function itCannotRouteWithoutAnyRoutes(): void { $openAPIFilePath = $this->root . '/openapi.json'; - file_put_contents( - $openAPIFilePath, - json_encode([ - 'openapi' => '3.0.0', - 'info' => ['title' => '', 'version' => '1.0.0'], - 'paths' => [] - ]) - ); - - self::assertTrue(file_exists($openAPIFilePath)); + file_put_contents($openAPIFilePath, json_encode([ + 'openapi' => '3.0.0', + 'info' => ['title' => '', 'version' => '1.0.0'], + 'paths' => [] + ])); $sut = new CommandTester(new CacheOpenAPIRoutes()); - $sut->execute(['openAPI' => $openAPIFilePath, 'destination' => vfsStream::url('cache') . '/routes.php']); + $sut->execute([ + 'openAPI' => $openAPIFilePath, + 'destination' => vfsStream::url('cache/routes.php'), + ]); self::assertSame(Command::FAILURE, $sut->getStatusCode()); } - public static function successfulExecutionProvider(): array + #[Test] + #[DataProviderExternal(ProvidesApiAndRoutes::class, 'defaultBehaviour')] + public function itCachesRoutes(string $openAPI, RouteCollection $expected): void { - $petStoreRoutes = new RouteCollection([ - 'hosted' => [ - 'static' => [ - 'http://petstore.swagger.io/api' => [ - 'static' => [ - '/pets' => [ - 'get' => 'findPets', - 'post' => 'addPet', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/pets/([^/]+)(*MARK:/pets/{id}))$#', - 'paths' => [ - '/pets/{id}' => [ - 'get' => 'find pet by id', - 'delete' => 'deletePet', - ], - ], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)#', - 'servers' => [], - ], - ], - 'hostless' => [ - 'static' => [], - 'dynamic' => [ - 'regex' => '#^(?|)#', - 'servers' => [], - ], - ], - ]); - $weirdAndWonderfulRoutes = new RouteCollection([ - 'hosted' => [ - 'static' => [ - 'http://weirdest.com' => [ - 'static' => [ - '/however' => [ - 'put' => 'put-however', - 'post' => 'post-however', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', - 'paths' => [ - '/and/{name}' => [ - 'get' => 'get-and', - ], - ], - ], - ], - 'http://weirder.co.uk' => [ - 'static' => [ - '/however' => [ - 'get' => 'get-however', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', - 'paths' => [ - '/and/{name}' => [ - 'put' => 'put-and', - 'post' => 'post-and', - ], - ], - ], - ], - 'http://wonderful.io' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - 'http://wonderful.io/and' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - 'http://wonderful.io/or' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|http://weird.io/([^/]+)(*MARK:http://weird.io/{conjunction}))#', - 'servers' => [ - 'http://weird.io/{conjunction}' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - ], - ], - 'hostless' => [ - 'static' => [ - '' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - '/v1' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/([^/]+)(*MARK:/{version}))#', - 'servers' => [ - '/{version}' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - ], - ], - ]); - return [ - 'successfully routes petstore-expanded.json' => [ - __DIR__ . '/../../fixtures/docs/petstore-expanded.json', - vfsStream::url('cache/routes.php'), - Command::SUCCESS, - $petStoreRoutes - ], - 'successfully routes the WeirdAndWonderful.json' => [ - __DIR__ . '/../../fixtures/WeirdAndWonderful.json', - vfsStream::url('cache/routes.php'), - Command::SUCCESS, - $weirdAndWonderfulRoutes - ], - 'successfully routes the WeirdAndWonderful.json and caches in a nested directory' => [ - __DIR__ . '/../../fixtures/WeirdAndWonderful.json', - vfsStream::url('cache/nested-cache/nester-cache/nestest-cache/routes.php'), - Command::SUCCESS, - $weirdAndWonderfulRoutes - ] - ]; + $cachePath = vfsStream::url('cache/routes.php'); + + $sut = new CommandTester(new CacheOpenAPIRoutes()); + + $sut->execute(['openAPI' => $openAPI, 'destination' => $cachePath]); + + self::assertSame(Command::SUCCESS, $sut->getStatusCode()); + + $actual = eval('?>' . file_get_contents($cachePath)); + + self::assertEquals($expected, $actual); } #[Test] - #[DataProvider('successfulExecutionProvider')] - public function successfulExecutionTest( + #[DataProviderExternal(ProvidesApiAndRoutes::class, 'ignoringServers')] + public function itCachesRoutesIgnoringServers( string $openAPI, - string $destination, - int $expectedStatusCode, - RouteCollection $expectedRouteCollection + RouteCollection $expected, ): void { + $cachePath = vfsStream::url('cache/routes.php'); + $sut = new CommandTester(new CacheOpenAPIRoutes()); - $sut->execute(['openAPI' => $openAPI, 'destination' => $destination]); + $sut->execute([ + 'openAPI' => $openAPI, + 'destination' => $cachePath, + '--ignore-servers' => true, + ]); - self::assertSame($expectedStatusCode, $sut->getStatusCode()); + self::assertSame(Command::SUCCESS, $sut->getStatusCode()); - $actualRouteCollection = eval('?>' . file_get_contents($destination)); + $actual = eval('?>' . file_get_contents($cachePath)); - self::assertEquals($expectedRouteCollection, $actualRouteCollection); + self::assertEquals($expected, $actual); } } diff --git a/tests/Console/Service/CacheOpenAPIRoutesTest.php b/tests/Console/Service/CacheOpenAPIRoutesTest.php index 8750078..efba087 100644 --- a/tests/Console/Service/CacheOpenAPIRoutesTest.php +++ b/tests/Console/Service/CacheOpenAPIRoutesTest.php @@ -9,9 +9,11 @@ use Membrane\OpenAPIRouter\Route; use Membrane\OpenAPIRouter\RouteCollection; use Membrane\OpenAPIRouter\RouteCollector; +use Membrane\OpenAPIRouter\Tests\Fixtures\ProvidesApiAndRoutes; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\TestCase; @@ -66,222 +68,33 @@ public function cannotRouteFromRelativeFilePaths(): void self::assertFalse($this->sut->cache($filePath, $this->root . '/cache/routes.php')); } - public static function successfulExecutionProvider(): array - { - $petStoreRoutes = new RouteCollection([ - 'hosted' => [ - 'static' => [ - 'http://petstore.swagger.io/api' => [ - 'static' => [ - '/pets' => [ - 'get' => 'findPets', - 'post' => 'addPet', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/pets/([^/]+)(*MARK:/pets/{id}))$#', - 'paths' => [ - '/pets/{id}' => [ - 'get' => 'find pet by id', - 'delete' => 'deletePet', - ], - ], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)#', - 'servers' => [], - ], - ], - 'hostless' => [ - 'static' => [], - 'dynamic' => [ - 'regex' => '#^(?|)#', - 'servers' => [], - ], - ], - ]); - $weirdAndWonderfulRoutes = new RouteCollection([ - 'hosted' => [ - 'static' => [ - 'http://weirdest.com' => [ - 'static' => [ - '/however' => [ - 'put' => 'put-however', - 'post' => 'post-however', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', - 'paths' => [ - '/and/{name}' => [ - 'get' => 'get-and', - ], - ], - ], - ], - 'http://weirder.co.uk' => [ - 'static' => [ - '/however' => [ - 'get' => 'get-however', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', - 'paths' => [ - '/and/{name}' => [ - 'put' => 'put-and', - 'post' => 'post-and', - ], - ], - ], - ], - 'http://wonderful.io' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - 'http://wonderful.io/and' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - 'http://wonderful.io/or' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|http://weird.io/([^/]+)(*MARK:http://weird.io/{conjunction}))#', - 'servers' => [ - 'http://weird.io/{conjunction}' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - ], - ], - 'hostless' => [ - 'static' => [ - '' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - '/v1' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|/([^/]+)(*MARK:/{version}))#', - 'servers' => [ - '/{version}' => [ - 'static' => [ - '/or' => [ - 'post' => 'post-or', - ], - '/xor' => [ - 'delete' => 'delete-xor', - ], - ], - 'dynamic' => [ - 'regex' => '#^(?|)$#', - 'paths' => [], - ], - ], - ], - ], - ], - ]); - return [ - 'successfully routes petstore-expanded.json' => [ - __DIR__ . '/../../fixtures/docs/petstore-expanded.json', - vfsStream::url('root/cache/routes.php'), - $petStoreRoutes - ], - 'successfully routes the WeirdAndWonderful.json' => [ - __DIR__ . '/../../fixtures/WeirdAndWonderful.json', - vfsStream::url('root/cache/routes.php'), - $weirdAndWonderfulRoutes - ], - 'successfully routes the WeirdAndWonderful.json and caches in a nested directory' => [ - __DIR__ . '/../../fixtures/WeirdAndWonderful.json', - vfsStream::url('root/cache/nested-cache/nester-cache/nestest-cache/routes.php'), - $weirdAndWonderfulRoutes - ] - ]; + #[Test] + #[DataProviderExternal(ProvidesApiAndRoutes::class, 'defaultBehaviour')] + public function itCachesRoutes( + string $apiPath, + RouteCollection $expected, + ): void { + $cachePath = vfsStream::url('root/cache/routes.php'); + + self::assertTrue($this->sut->cache($apiPath, $cachePath)); + + $actualRouteCollection = eval('?>' . file_get_contents($cachePath)); + + self::assertEquals($expected, $actualRouteCollection); } #[Test] - #[DataProvider('successfulExecutionProvider')] - public function successfulExecutionTest( - string $openAPI, - string $destination, - RouteCollection $expectedRouteCollection + #[DataProviderExternal(ProvidesApiAndRoutes::class, 'ignoringServers')] + public function itCachesRoutesIgnoringServers( + string $apiPath, + RouteCollection $expected ): void { - self::assertTrue($this->sut->cache($openAPI, $destination)); + $cachePath = vfsStream::url('root/cache/routes.php'); + + self::assertTrue($this->sut->cache($apiPath, $cachePath, true)); - $actualRouteCollection = eval('?>' . file_get_contents($destination)); + $actual = eval('?>' . file_get_contents($cachePath)); - self::assertEquals($expectedRouteCollection, $actualRouteCollection); + self::assertEquals($expected, $actual); } } diff --git a/tests/fixtures/ProvidesApiAndRoutes.php b/tests/fixtures/ProvidesApiAndRoutes.php new file mode 100644 index 0000000..7138d3c --- /dev/null +++ b/tests/fixtures/ProvidesApiAndRoutes.php @@ -0,0 +1,179 @@ + [self::PETSTORE_EXPANDED, new RouteCollection([ + 'hosted' => [ + 'static' => ['http://petstore.swagger.io/api' => [ + 'static' => [ + '/pets' => ['get' => 'findPets', 'post' => 'addPet'], + ], + 'dynamic' => [ + 'regex' => '#^(?|/pets/([^/]+)(*MARK:/pets/{id}))$#', + 'paths' => [ + '/pets/{id}' => ['get' => 'find pet by id', 'delete' => 'deletePet'], + ], + ], + ]], + 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []], + ], + 'hostless' => ['static' => [], 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []]], + ])]; + + yield 'weird and wonderful' => [self::WEIRD_AND_WONDERFUL, new RouteCollection([ + 'hosted' => [ + 'static' => [ + 'http://weirdest.com' => [ + 'static' => [ + '/however' => ['put' => 'put-however', 'post' => 'post-however'], + ], + 'dynamic' => [ + 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', + 'paths' => [ + '/and/{name}' => ['get' => 'get-and'] + ], + ], + ], + 'http://weirder.co.uk' => [ + 'static' => [ + '/however' => ['get' => 'get-however'] + ], + 'dynamic' => [ + 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', + 'paths' => [ + '/and/{name}' => ['put' => 'put-and', 'post' => 'post-and'], + ], + ], + ], + 'http://wonderful.io' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ], + 'http://wonderful.io/and' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => [ + 'regex' => '#^(?|)$#', + 'paths' => [], + ], + ], + 'http://wonderful.io/or' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ], + ], + 'dynamic' => [ + 'regex' => '#^(?|http://weird.io/([^/]+)(*MARK:http://weird.io/{conjunction}))#', + 'servers' => ['http://weird.io/{conjunction}' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ]], + ], + ], + 'hostless' => [ + 'static' => [ + '' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ], + '/v1' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ], + ], + 'dynamic' => [ + 'regex' => '#^(?|/([^/]+)(*MARK:/{version}))#', + 'servers' => ['/{version}' => [ + 'static' => [ + '/or' => ['post' => 'post-or'], + '/xor' => ['delete' => 'delete-xor'], + ], + 'dynamic' => ['regex' => '#^(?|)$#', 'paths' => []], + ]], + ], + ], + ])]; + } + + public static function ignoringServers(): Generator + { + yield 'petstore-expanded, ignoring servers' => [self::PETSTORE_EXPANDED, new RouteCollection([ + 'hosted' => ['static' => [], 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []]], + 'hostless' => [ + 'static' => ['' => [ + 'static' => [ + '/pets' => ['get' => 'findPets', 'post' => 'addPet'], + ], + 'dynamic' => [ + 'regex' => '#^(?|/pets/([^/]+)(*MARK:/pets/{id}))$#', + 'paths' => [ + '/pets/{id}' => ['get' => 'find pet by id', 'delete' => 'deletePet'], + ], + ], + ]], + 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []], + ], + ])]; + + yield 'weird-and-wonderful, ignoring servers' => [self::WEIRD_AND_WONDERFUL, new RouteCollection([ + 'hosted' => ['static' => [], 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []]], + 'hostless' => [ + 'static' => ['' => [ + 'static' => [ + '/or' => [ + 'post' => 'post-or', + ], + '/xor' => [ + 'delete' => 'delete-xor', + ], + '/however' => [ + 'get' => 'get-however', + 'put' => 'put-however', + 'post' => 'post-however', + ], + ], + 'dynamic' => [ + 'regex' => '#^(?|/and/([^/]+)(*MARK:/and/{name}))$#', + 'paths' => [ + '/and/{name}' => [ + 'get' => 'get-and', + 'put' => 'put-and', + 'post' => 'post-and', + ], + ] + ], + ]], + 'dynamic' => ['regex' => '#^(?|)#', 'servers' => []], + ], + ])]; + } +}