From cebad9b77b62a586c0e4361067f24699c995d39c Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 22:45:03 +0000 Subject: [PATCH] feat(api): honour header toggles for versioning Co-Authored-By: Virgil --- .../src/Front/Api/Middleware/ApiSunset.php | 4 ++ .../src/Front/Api/Middleware/ApiVersion.php | 31 +++++++++---- src/php/tests/Feature/ApiSunsetTest.php | 21 +++++++++ .../tests/Feature/ApiVersionHeadersTest.php | 45 +++++++++++++++++++ 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 src/php/tests/Feature/ApiVersionHeadersTest.php diff --git a/src/php/src/Front/Api/Middleware/ApiSunset.php b/src/php/src/Front/Api/Middleware/ApiSunset.php index b8510d7..fbf39d1 100644 --- a/src/php/src/Front/Api/Middleware/ApiSunset.php +++ b/src/php/src/Front/Api/Middleware/ApiSunset.php @@ -37,6 +37,10 @@ class ApiSunset /** @var Response $response */ $response = $next($request); + if (! (bool) config('api.headers.include_deprecation', true)) { + return $response; + } + $response->headers->set('Deprecation', 'true'); if ($sunsetDate !== '') { diff --git a/src/php/src/Front/Api/Middleware/ApiVersion.php b/src/php/src/Front/Api/Middleware/ApiVersion.php index 52c659b..b334afd 100644 --- a/src/php/src/Front/Api/Middleware/ApiVersion.php +++ b/src/php/src/Front/Api/Middleware/ApiVersion.php @@ -105,11 +105,16 @@ class ApiVersion /** @var Response $response */ $response = $next($request); - // Add version header to response - $response->headers->set('X-API-Version', (string) $version); + $includeVersionHeader = (bool) config('api.headers.include_version', true); + $includeDeprecationHeaders = (bool) config('api.headers.include_deprecation', true); + + // Add version header to response when enabled + if ($includeVersionHeader) { + $response->headers->set('X-API-Version', (string) $version); + } // Add deprecation headers if applicable - if (in_array($version, $deprecated, true)) { + if ($includeDeprecationHeaders && in_array($version, $deprecated, true)) { $response->headers->set('Deprecation', 'true'); $response->headers->set('X-API-Warn', "API version {$version} is deprecated. Please upgrade to v{$current}."); @@ -217,6 +222,12 @@ class ApiVersion */ protected function unsupportedVersion(int $requested, array $supported, int $current): Response { + $headers = []; + + if ((bool) config('api.headers.include_version', true)) { + $headers['X-API-Version'] = (string) $current; + } + return response()->json([ 'error' => 'unsupported_api_version', 'message' => "API version {$requested} is not supported.", @@ -224,9 +235,7 @@ class ApiVersion 'supported_versions' => $supported, 'current_version' => $current, 'hint' => 'Use Accept-Version header or URL prefix (e.g., /api/v1/) to specify version.', - ], 400, [ - 'X-API-Version' => (string) $current, - ]); + ], 400, $headers); } /** @@ -234,13 +243,17 @@ class ApiVersion */ protected function versionTooLow(int $requested, int $required): Response { + $headers = []; + + if ((bool) config('api.headers.include_version', true)) { + $headers['X-API-Version'] = (string) $requested; + } + return response()->json([ 'error' => 'api_version_too_low', 'message' => "This endpoint requires API version {$required} or higher.", 'requested_version' => $requested, 'minimum_version' => $required, - ], 400, [ - 'X-API-Version' => (string) $requested, - ]); + ], 400, $headers); } } diff --git a/src/php/tests/Feature/ApiSunsetTest.php b/src/php/tests/Feature/ApiSunsetTest.php index 8af518d..33bdb5d 100644 --- a/src/php/tests/Feature/ApiSunsetTest.php +++ b/src/php/tests/Feature/ApiSunsetTest.php @@ -4,9 +4,12 @@ declare(strict_types=1); use Core\Front\Api\Middleware\ApiSunset; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Config; use Symfony\Component\HttpFoundation\Response; it('adds deprecation headers without a sunset date', function () { + Config::set('api.headers.include_deprecation', true); + $middleware = new ApiSunset(); $request = Request::create('/legacy-endpoint', 'GET'); @@ -19,6 +22,8 @@ it('adds deprecation headers without a sunset date', function () { }); it('adds a replacement link without a sunset date', function () { + Config::set('api.headers.include_deprecation', true); + $middleware = new ApiSunset(); $request = Request::create('/old-endpoint', 'GET'); @@ -31,6 +36,8 @@ it('adds a replacement link without a sunset date', function () { }); it('formats the sunset date and keeps the replacement link', function () { + Config::set('api.headers.include_deprecation', true); + $middleware = new ApiSunset(); $request = Request::create('/legacy-endpoint', 'GET'); @@ -41,3 +48,17 @@ it('formats the sunset date and keeps the replacement link', function () { expect($response->headers->get('Link'))->toBe('; rel="successor-version"'); expect($response->headers->get('X-API-Warn'))->toBe('This endpoint is deprecated and will be removed on 2025-06-01.'); }); + +it('skips deprecation headers when they are disabled in configuration', function () { + Config::set('api.headers.include_deprecation', false); + + $middleware = new ApiSunset(); + $request = Request::create('/legacy-endpoint', 'GET'); + + $response = $middleware->handle($request, fn () => new Response('OK'), '2025-06-01', '/api/v2/users'); + + expect($response->headers->has('Deprecation'))->toBeFalse(); + expect($response->headers->has('Sunset'))->toBeFalse(); + expect($response->headers->has('Link'))->toBeFalse(); + expect($response->headers->has('X-API-Warn'))->toBeFalse(); +}); diff --git a/src/php/tests/Feature/ApiVersionHeadersTest.php b/src/php/tests/Feature/ApiVersionHeadersTest.php new file mode 100644 index 0000000..cd5a645 --- /dev/null +++ b/src/php/tests/Feature/ApiVersionHeadersTest.php @@ -0,0 +1,45 @@ + '2025-06-01', + ]); + Config::set('api.headers.include_version', true); + Config::set('api.headers.include_deprecation', true); +}); + +it('skips the api version header when it is disabled in configuration', function () { + Config::set('api.headers.include_version', false); + + $middleware = new ApiVersion(); + $request = Request::create('/api/users', 'GET'); + + $response = $middleware->handle($request, fn () => new Response('OK')); + + expect($response->headers->has('X-API-Version'))->toBeFalse(); +}); + +it('skips deprecation headers when they are disabled in configuration', function () { + Config::set('api.headers.include_deprecation', false); + + $middleware = new ApiVersion(); + $request = Request::create('/api/v1/users', 'GET'); + + $response = $middleware->handle($request, fn () => new Response('OK')); + + expect($response->headers->get('X-API-Version'))->toBe('1'); + expect($response->headers->has('Deprecation'))->toBeFalse(); + expect($response->headers->has('Sunset'))->toBeFalse(); + expect($response->headers->has('X-API-Warn'))->toBeFalse(); +});