(fix) Umbau zu SCS
### 1.1 OpenAPI-Dokumentation implementieren - Swagger/OpenAPI in bestehenden Ktor-Diensten integrieren - Zentrale API-Dokumentationsseite im API-Gateway erstellen - CI/CD-Pipeline um automatische API-Dokumentationsgenerierung erweitern - Entwickler-Guidelines für API-Dokumentation erstellen
This commit is contained in:
@@ -10,9 +10,12 @@ import io.ktor.server.routing.*
|
||||
*
|
||||
* This module configures the OpenAPI specification generation and Swagger UI
|
||||
* for the API Gateway, providing comprehensive API documentation.
|
||||
*
|
||||
* The OpenAPI specification is loaded from a static YAML file located at:
|
||||
* resources/openapi/documentation.yaml
|
||||
*/
|
||||
fun Application.configureOpenApi() {
|
||||
// Configure OpenAPI using a static file
|
||||
// Configure OpenAPI endpoint using the static YAML file
|
||||
routing {
|
||||
// Serve the OpenAPI specification from a file
|
||||
openAPI(path = "openapi", swaggerFile = "openapi/documentation.yaml") {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package at.mocode.gateway
|
||||
|
||||
import at.mocode.gateway.config.configureOpenApi
|
||||
import at.mocode.gateway.config.configureSwagger
|
||||
import at.mocode.gateway.routing.docRoutes
|
||||
import at.mocode.shared.config.AppConfig
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.http.content.*
|
||||
import io.ktor.server.plugins.calllogging.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
@@ -41,6 +45,10 @@ fun Application.module() {
|
||||
install(CallLogging)
|
||||
}
|
||||
|
||||
// OpenAPI und Swagger UI konfigurieren
|
||||
configureOpenApi()
|
||||
configureSwagger()
|
||||
|
||||
routing {
|
||||
// Hauptrouten
|
||||
get("/") {
|
||||
@@ -49,5 +57,14 @@ fun Application.module() {
|
||||
ContentType.Text.Plain
|
||||
)
|
||||
}
|
||||
|
||||
// Static resources for documentation
|
||||
static("/docs") {
|
||||
resources("static/docs")
|
||||
defaultResource("static/docs/index.html")
|
||||
}
|
||||
|
||||
// API Documentation routes
|
||||
docRoutes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package at.mocode.gateway.routing
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Sets up routes for API documentation
|
||||
*/
|
||||
fun Routing.docRoutes() {
|
||||
// Central API documentation endpoint - HTML version
|
||||
get("/api") {
|
||||
call.respondRedirect("/docs", permanent = false)
|
||||
}
|
||||
|
||||
// JSON API documentation endpoint for backward compatibility
|
||||
get("/api/json") {
|
||||
val apiDocumentation = ApiDocumentationData(
|
||||
title = "Meldestelle Self-Contained Systems API",
|
||||
description = "Unified API Gateway for all bounded contexts",
|
||||
contexts = listOf(
|
||||
ApiContext(
|
||||
name = "Authentication Context",
|
||||
path = "/auth",
|
||||
description = "User authentication, registration, and profile management"
|
||||
),
|
||||
ApiContext(
|
||||
name = "Master Data Context",
|
||||
path = "/api/masterdata",
|
||||
description = "Reference data management (countries, states, age classes, venues)"
|
||||
),
|
||||
ApiContext(
|
||||
name = "Horse Registry Context",
|
||||
path = "/api/horses",
|
||||
description = "Horse registration, ownership, and pedigree management"
|
||||
),
|
||||
ApiContext(
|
||||
name = "Event Management Context",
|
||||
path = "/api/events",
|
||||
description = "Event creation, management, and participant registration"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
call.respond(
|
||||
ApiResponse.success(
|
||||
data = apiDocumentation,
|
||||
message = "API documentation retrieved successfully"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for API documentation response
|
||||
*/
|
||||
@Serializable
|
||||
data class ApiDocumentationData(
|
||||
val title: String,
|
||||
val description: String,
|
||||
val contexts: List<ApiContext>
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class for API context information
|
||||
*/
|
||||
@Serializable
|
||||
data class ApiContext(
|
||||
val name: String,
|
||||
val path: String,
|
||||
val description: String
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,385 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Meldestelle API Documentation</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--secondary-color: #2c3e50;
|
||||
--accent-color: #e74c3c;
|
||||
--light-bg: #f5f5f5;
|
||||
--dark-bg: #2c3e50;
|
||||
--text-color: #333;
|
||||
--light-text: #f5f5f5;
|
||||
--border-color: #ddd;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--light-bg);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--dark-bg);
|
||||
color: var(--light-text);
|
||||
padding: 20px 0;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
header .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
color: var(--light-text);
|
||||
text-decoration: none;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.hero {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--light-text);
|
||||
padding: 50px 0;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 36px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 18px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--light-text);
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.3s;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.section h2 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 15px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card .endpoints {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.card .endpoints h4 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card .endpoints ul {
|
||||
list-style: none;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.card .endpoints ul li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.card .endpoints .method {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.card .endpoints .get {
|
||||
background-color: #61affe;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card .endpoints .post {
|
||||
background-color: #49cc90;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card .endpoints .put {
|
||||
background-color: #fca130;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card .endpoints .delete {
|
||||
background-color: #f93e3e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.resources {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.resource-card {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.resource-card h3 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.resource-card p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--dark-bg);
|
||||
color: var(--light-text);
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<div class="logo">Meldestelle API</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="#overview">Overview</a></li>
|
||||
<li><a href="#contexts">API Contexts</a></li>
|
||||
<li><a href="#resources">Resources</a></li>
|
||||
<li><a href="/swagger" target="_blank">Swagger UI</a></li>
|
||||
<li><a href="/openapi" target="_blank">OpenAPI Spec</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1>Meldestelle Self-Contained Systems API</h1>
|
||||
<p>Unified API Gateway for all bounded contexts of the Austrian Equestrian Federation's Meldestelle system.</p>
|
||||
<div>
|
||||
<a href="/swagger" class="btn" target="_blank">Interactive API Documentation</a>
|
||||
<a href="/openapi" class="btn" target="_blank">OpenAPI Specification</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container">
|
||||
<section id="overview" class="section">
|
||||
<h2>Overview</h2>
|
||||
<div class="card">
|
||||
<p>The Meldestelle API provides a unified interface to various bounded contexts while maintaining the independence of each context. This API Gateway aggregates all bounded context APIs and provides a single entry point for clients.</p>
|
||||
<p>The API follows REST principles and uses JSON for data exchange. All responses are wrapped in a consistent format using the <code>BaseDto</code> wrapper.</p>
|
||||
<p>Authentication is handled using JWT (JSON Web Token) based authentication. Most endpoints require authentication, which can be obtained by registering and logging in through the Authentication Context.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contexts" class="section">
|
||||
<h2>API Contexts</h2>
|
||||
|
||||
<div class="card">
|
||||
<h3>Authentication Context</h3>
|
||||
<p>User authentication, registration, and profile management</p>
|
||||
<p><strong>Base Path:</strong> /auth</p>
|
||||
<div class="endpoints">
|
||||
<h4>Key Endpoints:</h4>
|
||||
<ul>
|
||||
<li><span class="method post">POST</span> /auth/register - User registration</li>
|
||||
<li><span class="method post">POST</span> /auth/login - User authentication</li>
|
||||
<li><span class="method get">GET</span> /auth/profile - Get user profile</li>
|
||||
<li><span class="method put">PUT</span> /auth/profile - Update user profile</li>
|
||||
<li><span class="method post">POST</span> /auth/change-password - Change password</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Master Data Context</h3>
|
||||
<p>Reference data management (countries, states, age classes, venues)</p>
|
||||
<p><strong>Base Path:</strong> /api/masterdata</p>
|
||||
<div class="endpoints">
|
||||
<h4>Key Endpoints:</h4>
|
||||
<ul>
|
||||
<li><span class="method get">GET</span> /api/masterdata/countries - Get all countries</li>
|
||||
<li><span class="method get">GET</span> /api/masterdata/countries/active - Get active countries</li>
|
||||
<li><span class="method get">GET</span> /api/masterdata/countries/{id} - Get country by ID</li>
|
||||
<li><span class="method post">POST</span> /api/masterdata/countries - Create country</li>
|
||||
<li><span class="method put">PUT</span> /api/masterdata/countries/{id} - Update country</li>
|
||||
<li><span class="method delete">DELETE</span> /api/masterdata/countries/{id} - Delete country</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Horse Registry Context</h3>
|
||||
<p>Horse registration, ownership, and pedigree management</p>
|
||||
<p><strong>Base Path:</strong> /api/horses</p>
|
||||
<div class="endpoints">
|
||||
<h4>Key Endpoints:</h4>
|
||||
<ul>
|
||||
<li><span class="method get">GET</span> /api/horses - Get all horses</li>
|
||||
<li><span class="method get">GET</span> /api/horses/active - Get active horses</li>
|
||||
<li><span class="method get">GET</span> /api/horses/{id} - Get horse by ID</li>
|
||||
<li><span class="method get">GET</span> /api/horses/search - Search horses by name</li>
|
||||
<li><span class="method post">POST</span> /api/horses - Create horse</li>
|
||||
<li><span class="method put">PUT</span> /api/horses/{id} - Update horse</li>
|
||||
<li><span class="method delete">DELETE</span> /api/horses/{id} - Delete horse</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Event Management Context</h3>
|
||||
<p>Event creation, management, and participant registration</p>
|
||||
<p><strong>Base Path:</strong> /api/events</p>
|
||||
<div class="endpoints">
|
||||
<h4>Key Endpoints:</h4>
|
||||
<ul>
|
||||
<li><span class="method get">GET</span> /api/events - Get all events</li>
|
||||
<li><span class="method get">GET</span> /api/events/stats - Get event statistics</li>
|
||||
<li><span class="method post">POST</span> /api/events - Create event</li>
|
||||
<li><span class="method get">GET</span> /api/events/{id} - Get event by ID</li>
|
||||
<li><span class="method put">PUT</span> /api/events/{id} - Update event</li>
|
||||
<li><span class="method delete">DELETE</span> /api/events/{id} - Delete event</li>
|
||||
<li><span class="method get">GET</span> /api/events/search - Search events</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="resources" class="section">
|
||||
<h2>Documentation Resources</h2>
|
||||
<div class="resources">
|
||||
<div class="resource-card">
|
||||
<h3>Swagger UI</h3>
|
||||
<p>Interactive documentation for exploring and testing the API endpoints.</p>
|
||||
<a href="/swagger" class="btn" target="_blank">Open Swagger UI</a>
|
||||
</div>
|
||||
<div class="resource-card">
|
||||
<h3>OpenAPI Specification</h3>
|
||||
<p>Raw OpenAPI 3.0.3 specification in YAML format for code generation or import into other tools.</p>
|
||||
<a href="/openapi" class="btn" target="_blank">View OpenAPI Spec</a>
|
||||
</div>
|
||||
<div class="resource-card">
|
||||
<h3>Postman Collection</h3>
|
||||
<p>Comprehensive API collection covering all endpoints with pre-configured request examples.</p>
|
||||
<a href="/docs/postman/Meldestelle_API_Collection.json" class="btn" target="_blank">Download Collection</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>Getting Started</h2>
|
||||
<div class="card">
|
||||
<h3>Authentication</h3>
|
||||
<p>The API uses JWT (JSON Web Token) based authentication:</p>
|
||||
<ol>
|
||||
<li>Register a new user via <code>POST /auth/register</code></li>
|
||||
<li>Login with credentials via <code>POST /auth/login</code></li>
|
||||
<li>Extract the JWT token from the login response</li>
|
||||
<li>Include the token in the <code>Authorization</code> header: <code>Bearer <token></code></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Response Format</h3>
|
||||
<p>All API responses follow a consistent format using the <code>BaseDto</code> wrapper:</p>
|
||||
<pre><code>{
|
||||
"success": true,
|
||||
"data": {
|
||||
"example": "Actual response data goes here"
|
||||
},
|
||||
"message": "Operation completed successfully",
|
||||
"timestamp": "2024-01-15T10:30:00Z"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© 2024 Meldestelle API. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user