Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions app/Http/Middleware/AcceptJsonMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class AcceptJsonMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): SymfonyResponse
{
// Check if the Accept header includes application/json
if (! $request->acceptsJson()) {
return response()->json([
'message' => 'This endpoint only accepts JSON. Please include "Accept: application/json" in your request headers.',
'error' => 'Unsupported Media Type',
], Response::HTTP_NOT_ACCEPTABLE);
}

// Ensure the response is JSON
$response = $next($request);

// Force JSON content type if not already set
if (! $response->headers->has('Content-Type') ||
! str_contains($response->headers->get('Content-Type'), 'application/json')) {
$response->headers->set('Content-Type', 'application/json');
}

return $response;
}
}
8 changes: 8 additions & 0 deletions app/OpenApi/Annotations/V1/OoklaAnnotations.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class OoklaAnnotations
description: 'Returns an array of available Ookla speedtest servers. Requires an API token with `ookla:list-servers` scope.',
operationId: 'listOoklaServers',
tags: ['Servers'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
],
responses: [
new OA\Response(
response: Response::HTTP_OK,
Expand All @@ -33,6 +36,11 @@ class OoklaAnnotations
description: 'Forbidden',
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
]
)]
public function listServers(): void {}
Expand Down
20 changes: 20 additions & 0 deletions app/OpenApi/Annotations/V1/ResultsAnnotations.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ResultsAnnotations
operationId: 'listResults',
tags: ['Results'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
new OA\Parameter(
name: 'per_page',
in: 'query',
Expand Down Expand Up @@ -104,6 +105,11 @@ class ResultsAnnotations
description: 'Unauthenticated',
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
new OA\Response(
response: Response::HTTP_UNPROCESSABLE_ENTITY,
description: 'Validation failed',
Expand All @@ -119,6 +125,7 @@ public function index(): void {}
operationId: 'getResult',
tags: ['Results'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
new OA\Parameter(
name: 'id',
in: 'path',
Expand All @@ -143,6 +150,11 @@ public function index(): void {}
description: 'Unauthenticated',
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
new OA\Response(
response: Response::HTTP_NOT_FOUND,
description: 'Result not found',
Expand All @@ -157,6 +169,9 @@ public function show(): void {}
summary: 'Get the most recent result',
operationId: 'getLatestResult',
tags: ['Results'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
],
responses: [
new OA\Response(
response: Response::HTTP_OK,
Expand All @@ -173,6 +188,11 @@ public function show(): void {}
description: 'Unauthenticated',
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
new OA\Response(
response: Response::HTTP_NOT_FOUND,
description: 'No result found',
Expand Down
14 changes: 14 additions & 0 deletions app/OpenApi/Annotations/V1/SpeedtestAnnotations.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SpeedtestAnnotations
operationId: 'runSpeedtest',
tags: ['Speedtests'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
new OA\Parameter(
name: 'server_id',
in: 'query',
Expand All @@ -41,6 +42,11 @@ class SpeedtestAnnotations
description: 'Forbidden',
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
new OA\Response(
response: Response::HTTP_UNPROCESSABLE_ENTITY,
description: 'Validation error',
Expand All @@ -58,6 +64,9 @@ public function run(): void
summary: 'List available Ookla speedtest servers',
operationId: 'listSpeedtestServers',
tags: ['Speedtests'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
],
responses: [
new OA\Response(
response: Response::HTTP_OK,
Expand All @@ -77,6 +86,11 @@ public function run(): void
example: ['message' => 'You do not have permission to view speedtest servers.']
)
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
]
)]
public function listServers(): void {}
Expand Down
6 changes: 6 additions & 0 deletions app/OpenApi/Annotations/V1/StatsAnnotations.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class StatsAnnotations
operationId: 'getStats',
tags: ['Stats'],
parameters: [
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
new OA\Parameter(
name: 'start_at',
in: 'query',
Expand Down Expand Up @@ -48,6 +49,11 @@ class StatsAnnotations
description: 'Forbidden',
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
),
new OA\Response(
response: Response::HTTP_NOT_ACCEPTABLE,
description: 'Not Acceptable - Missing or invalid Accept header',
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
),
new OA\Response(
response: Response::HTTP_UNPROCESSABLE_ENTITY,
description: 'Validation error',
Expand Down
3 changes: 2 additions & 1 deletion app/OpenApi/OpenApiDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
],
parameters: [
new OA\Parameter(
parameter: 'AcceptHeader',
name: 'Accept',
in: 'header',
required: true,
schema: new OA\Schema(type: 'string', default: 'application/json'),
description: 'Expected response format'
description: 'Must be "application/json" - this API only accepts and returns JSON'
),
]
),
Expand Down
24 changes: 24 additions & 0 deletions app/OpenApi/Schemas/NotAcceptableErrorSchema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\OpenApi\Schemas;

use OpenApi\Attributes as OA;

#[OA\Schema(
schema: 'NotAcceptableError',
description: 'Error response when the Accept header is missing or invalid',
type: 'object',
properties: [
new OA\Property(
property: 'message',
type: 'string',
example: 'This endpoint only accepts JSON. Please include "Accept: application/json" in your request headers.'
),
new OA\Property(
property: 'error',
type: 'string',
example: 'Unsupported Media Type'
),
]
)]
class NotAcceptableErrorSchema {}
1 change: 1 addition & 0 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
$middleware->alias([
'getting-started' => App\Http\Middleware\GettingStarted::class,
'public-dashboard' => App\Http\Middleware\PublicDashboard::class,
'accept-json' => App\Http\Middleware\AcceptJsonMiddleware::class,
]);

$middleware->prependToGroup('api', [
Expand Down
Loading