feat(api): add Stoplight docs renderer
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
5d5ca8aa51
commit
c9cf407530
9 changed files with 112 additions and 1 deletions
|
|
@ -34,6 +34,7 @@ class DocumentationController
|
|||
return match ($defaultUi) {
|
||||
'swagger' => $this->swagger($request),
|
||||
'redoc' => $this->redoc($request),
|
||||
'stoplight' => $this->stoplight($request),
|
||||
default => $this->scalar($request),
|
||||
};
|
||||
}
|
||||
|
|
@ -74,6 +75,19 @@ class DocumentationController
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Stoplight Elements.
|
||||
*/
|
||||
public function stoplight(Request $request): View
|
||||
{
|
||||
$config = config('api-docs.ui.stoplight', []);
|
||||
|
||||
return view('api-docs::stoplight', [
|
||||
'specUrl' => route('api.docs.openapi.json'),
|
||||
'config' => $config,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenAPI specification as JSON.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Route::get('/', [DocumentationController::class, 'index'])->name('api.docs');
|
|||
Route::get('/swagger', [DocumentationController::class, 'swagger'])->name('api.docs.swagger');
|
||||
Route::get('/scalar', [DocumentationController::class, 'scalar'])->name('api.docs.scalar');
|
||||
Route::get('/redoc', [DocumentationController::class, 'redoc'])->name('api.docs.redoc');
|
||||
Route::get('/stoplight', [DocumentationController::class, 'stoplight'])->name('api.docs.stoplight');
|
||||
|
||||
// OpenAPI specification routes
|
||||
Route::get('/openapi.json', [DocumentationController::class, 'openApiJson'])
|
||||
|
|
|
|||
34
src/php/src/Api/Documentation/Views/stoplight.blade.php
Normal file
34
src/php/src/Api/Documentation/Views/stoplight.blade.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="API Documentation - Stoplight Elements">
|
||||
<title>{{ config('api-docs.info.title', 'API Documentation') }} - Stoplight</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
elements-api {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<elements-api
|
||||
apiDescriptionUrl="{{ $specUrl }}"
|
||||
router="hash"
|
||||
layout="{{ $config['layout'] ?? 'sidebar' }}"
|
||||
theme="{{ $config['theme'] ?? 'dark' }}"
|
||||
hideTryIt="{{ ($config['hide_try_it'] ?? false) ? 'true' : 'false' }}"
|
||||
></elements-api>
|
||||
|
||||
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -268,6 +268,13 @@ return [
|
|||
'hide_download_button' => false,
|
||||
'hide_models' => false,
|
||||
],
|
||||
|
||||
// Stoplight Elements specific options
|
||||
'stoplight' => [
|
||||
'theme' => 'dark', // 'dark' or 'light'
|
||||
'layout' => 'sidebar', // 'sidebar' or 'stacked'
|
||||
'hide_try_it' => false,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
|||
21
src/php/src/Api/Tests/Feature/DocumentationStoplightTest.php
Normal file
21
src/php/src/Api/Tests/Feature/DocumentationStoplightTest.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
it('renders Stoplight Elements when selected as the default documentation ui', function () {
|
||||
config(['api-docs.ui.default' => 'stoplight']);
|
||||
|
||||
$response = $this->get('/api/docs');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('elements-api', false);
|
||||
$response->assertSee('@stoplight/elements', false);
|
||||
});
|
||||
|
||||
it('renders the dedicated Stoplight documentation route', function () {
|
||||
$response = $this->get('/api/docs/stoplight');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('elements-api', false);
|
||||
$response->assertSee('@stoplight/elements', false);
|
||||
});
|
||||
|
|
@ -65,6 +65,11 @@ class DocsController
|
|||
return view('api::redoc');
|
||||
}
|
||||
|
||||
public function stoplight(): View
|
||||
{
|
||||
return view('api::stoplight');
|
||||
}
|
||||
|
||||
public function openapi(OpenApiGenerator $generator): JsonResponse
|
||||
{
|
||||
return response()->json($generator->generate());
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ Route::get('/scalar', [DocsController::class, 'scalar'])->name('api.scalar');
|
|||
// ReDoc (three-panel API reference)
|
||||
Route::get('/redoc', [DocsController::class, 'redoc'])->name('api.redoc');
|
||||
|
||||
// Stoplight Elements API reference
|
||||
Route::get('/stoplight', [DocsController::class, 'stoplight'])->name('api.stoplight');
|
||||
|
||||
// OpenAPI spec (rate limited - expensive to generate)
|
||||
Route::get('/openapi.json', [DocsController::class, 'openapi'])
|
||||
->middleware('throttle:60,1')
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
|
||||
<button
|
||||
@click="open = !open"
|
||||
class="text-sm flex items-center gap-1 {{ request()->routeIs('api.swagger', 'api.scalar', 'api.redoc') ? 'font-medium text-cyan-600 dark:text-cyan-400' : 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200' }}"
|
||||
class="text-sm flex items-center gap-1 {{ request()->routeIs('api.swagger', 'api.scalar', 'api.redoc', 'api.stoplight') ? 'font-medium text-cyan-600 dark:text-cyan-400' : 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200' }}"
|
||||
>
|
||||
API Explorer
|
||||
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': open }" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
||||
|
|
@ -109,6 +109,9 @@
|
|||
<a href="{{ route('api.redoc') }}" class="block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-700 {{ request()->routeIs('api.redoc') ? 'bg-zinc-100 dark:bg-zinc-700' : '' }}">
|
||||
<i class="fa-solid fa-book w-4 mr-2 text-zinc-400"></i>ReDoc
|
||||
</a>
|
||||
<a href="{{ route('api.stoplight') }}" class="block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-700 {{ request()->routeIs('api.stoplight') ? 'bg-zinc-100 dark:bg-zinc-700' : '' }}">
|
||||
<i class="fa-solid fa-layer-group w-4 mr-2 text-zinc-400"></i>Stoplight
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
23
src/php/src/Website/Api/View/Blade/stoplight.blade.php
Normal file
23
src/php/src/Website/Api/View/Blade/stoplight.blade.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
@extends('layouts::docs')
|
||||
|
||||
@section('title', 'Stoplight')
|
||||
@section('description', 'Stoplight Elements API reference for the Core API.')
|
||||
|
||||
@section('content')
|
||||
<div class="min-h-[calc(100vh-4rem)]">
|
||||
<elements-api
|
||||
apiDescriptionUrl="{{ route('api.openapi.json') }}"
|
||||
router="hash"
|
||||
layout="sidebar"
|
||||
theme="dark"
|
||||
></elements-api>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('head')
|
||||
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
|
||||
@endpush
|
||||
Loading…
Add table
Reference in a new issue