diff --git a/CHANGELOG.md b/CHANGELOG.md index cb9c59e8..d1b9f221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this #### Fixed -* *Nothing* +* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path. ## 1.21.1 - 2020-01-02 diff --git a/module/Rest/config/auth.config.php b/module/Rest/config/auth.config.php index dd0d5369..99141364 100644 --- a/module/Rest/config/auth.config.php +++ b/module/Rest/config/auth.config.php @@ -12,6 +12,7 @@ return [ 'routes_whitelist' => [ Action\HealthAction::class, Action\ShortUrl\SingleStepCreateShortUrlAction::class, + ConfigProvider::UNVERSIONED_HEALTH_ENDPOINT_NAME, ], 'plugins' => [ diff --git a/module/Rest/src/ConfigProvider.php b/module/Rest/src/ConfigProvider.php index b94991ca..570eab85 100644 --- a/module/Rest/src/ConfigProvider.php +++ b/module/Rest/src/ConfigProvider.php @@ -6,6 +6,8 @@ namespace Shlinkio\Shlink\Rest; use Closure; +use function Functional\first; +use function Functional\map; use function Shlinkio\Shlink\Common\loadConfigFromGlob; use function sprintf; @@ -13,6 +15,7 @@ class ConfigProvider { private const ROUTES_PREFIX = '/rest/v{version:1|2}'; private const UNVERSIONED_ROUTES_PREFIX = '/rest'; + public const UNVERSIONED_HEALTH_ENDPOINT_NAME = 'unversioned_health'; private Closure $loadConfig; @@ -29,18 +32,33 @@ class ConfigProvider private function applyRoutesPrefix(array $config): array { - $routes =& $config['routes'] ?? []; + $routes = $config['routes'] ?? []; + $healthRoute = $this->buildUnversionedHealthRouteFromExistingRoutes($routes); - // Prepend the routes prefix to every path - foreach ($routes as $key => $route) { + $prefixRoute = static function (array $route) { ['path' => $path] = $route; - $routes[$key]['path'] = sprintf( - '%s%s', - $path === '/health' ? self::UNVERSIONED_ROUTES_PREFIX : self::ROUTES_PREFIX, - $path, - ); - } + $route['path'] = sprintf('%s%s', self::ROUTES_PREFIX, $path); + + return $route; + }; + $prefixedRoutes = map($routes, $prefixRoute); + + $config['routes'] = $healthRoute !== null ? [...$prefixedRoutes, $healthRoute] : $prefixedRoutes; return $config; } + + private function buildUnversionedHealthRouteFromExistingRoutes(array $routes): ?array + { + $healthRoute = first($routes, fn (array $route) => $route['path'] === '/health'); + if ($healthRoute === null) { + return null; + } + + $path = $healthRoute['path']; + $healthRoute['path'] = sprintf('%s%s', self::UNVERSIONED_ROUTES_PREFIX, $path); + $healthRoute['name'] = self::UNVERSIONED_HEALTH_ENDPOINT_NAME; + + return $healthRoute; + } } diff --git a/module/Rest/test/ConfigProviderTest.php b/module/Rest/test/ConfigProviderTest.php index 80919c30..8032a854 100644 --- a/module/Rest/test/ConfigProviderTest.php +++ b/module/Rest/test/ConfigProviderTest.php @@ -25,25 +25,47 @@ class ConfigProviderTest extends TestCase $this->assertArrayHasKey('dependencies', $config); } - /** @test */ - public function routesAreProperlyPrefixed(): void + /** + * @test + * @dataProvider provideRoutesConfig + */ + public function routesAreProperlyPrefixed(array $routes, array $expected): void { - $configProvider = new ConfigProvider(fn () => [ - 'routes' => [ + $configProvider = new ConfigProvider(fn () => ['routes' => $routes]); + + $config = $configProvider(); + + $this->assertEquals($expected, $config['routes']); + } + + public function provideRoutesConfig(): iterable + { + yield 'health action present' => [ + [ ['path' => '/foo'], ['path' => '/bar'], ['path' => '/baz/foo'], ['path' => '/health'], ], - ]); - - $config = $configProvider(); - - $this->assertEquals([ - ['path' => '/rest/v{version:1|2}/foo'], - ['path' => '/rest/v{version:1|2}/bar'], - ['path' => '/rest/v{version:1|2}/baz/foo'], - ['path' => '/rest/health'], - ], $config['routes']); + [ + ['path' => '/rest/v{version:1|2}/foo'], + ['path' => '/rest/v{version:1|2}/bar'], + ['path' => '/rest/v{version:1|2}/baz/foo'], + ['path' => '/rest/v{version:1|2}/health'], + ['path' => '/rest/health', 'name' => ConfigProvider::UNVERSIONED_HEALTH_ENDPOINT_NAME], + ], + ]; + yield 'health action not present' => [ + [ + ['path' => '/foo'], + ['path' => '/bar'], + ['path' => '/baz/foo'], + ], + [ + ['path' => '/rest/v{version:1|2}/foo'], + ['path' => '/rest/v{version:1|2}/bar'], + ['path' => '/rest/v{version:1|2}/baz/foo'], + ], + ]; } }