AuthMiddleware.install(ctx, config, source) now owns the annotation processor and the flash.auth.policy key, so a second credential source gets annotation-driven authorization without copying the wiring. The key is public: an extension that contributes middleware can order itself around authentication. OidcSession becomes Session in auth-core, carrying claims, an expiry and an opaque attribute map. OpenID Connect keeps its access, id and refresh tokens in that map under its own keys, so renewal stays its business and core has no OAuth2 vocabulary in it. isAccessTokenExpired() becomes isExpired(), with the 30s eager-renewal window it always had and now a test for it. flash-ext-oidc is renamed flash-ext-auth-oidc, matching cache-core/cache-caffeine and data-core/data-hibernate.
155 lines
3.9 KiB
Markdown
155 lines
3.9 KiB
Markdown
# flash-ext-openapi
|
|
|
|
OpenAPI 3.0.3 generation + Swagger UI for Flash.
|
|
|
|
## What it provides
|
|
|
|
| Route | Description |
|
|
|---|---|
|
|
| `GET /openapi.json` | OpenAPI spec JSON |
|
|
| `GET /openapi.yaml` | OpenAPI spec YAML |
|
|
| `GET /openapi/swagger` | Swagger UI |
|
|
|
|
## Install
|
|
|
|
```java
|
|
FlashApp.create(8080)
|
|
.install(new JacksonExtension())
|
|
.install(new OpenApiExtension("/openapi", "My API", "1.0.0"))
|
|
.scan("com.acme.handlers")
|
|
.startAndBlock();
|
|
```
|
|
|
|
## Operation annotation
|
|
|
|
```java
|
|
@GET("/users/{id}")
|
|
@ApiOperation(summary = "Get user", description = "Returns one user", tags = {"users"})
|
|
@Parameter(name = "expand", in = ParameterIn.QUERY, type = SchemaType.STRING, examples = {"roles", "permissions"})
|
|
@APIResponse(
|
|
responseCode = "200",
|
|
description = "User found",
|
|
content = @Content(contentType = ContentType.JSON, schema = UserDto.class)
|
|
)
|
|
public final class GetUser extends RequestHandler { ... }
|
|
```
|
|
|
|
## Response patterns
|
|
|
|
### Single object
|
|
|
|
```java
|
|
@APIResponse(
|
|
responseCode = "200",
|
|
description = "User found",
|
|
content = @Content(contentType = ContentType.JSON, schema = UserDto.class)
|
|
)
|
|
```
|
|
|
|
### Array
|
|
|
|
```java
|
|
@APIResponse(
|
|
responseCode = "200",
|
|
description = "Users listed",
|
|
content = @Content(contentType = ContentType.JSON, schema = UserDto.class, array = true)
|
|
)
|
|
```
|
|
|
|
### No content
|
|
|
|
```java
|
|
@APIResponse(
|
|
responseCode = "204",
|
|
description = "Deleted",
|
|
content = @Content(contentType = ContentType.NONE)
|
|
)
|
|
```
|
|
|
|
### Inferred from handler return type
|
|
|
|
```java
|
|
@APIResponse(
|
|
responseCode = "200",
|
|
content = @Content
|
|
)
|
|
```
|
|
|
|
If `content.schema` is omitted, schema is inferred from the handler `handle(...)` return type.
|
|
Explicit `content.schema` always wins over inference.
|
|
|
|
Inference defaults:
|
|
|
|
- `UserDto` -> object schema for `UserDto`
|
|
- `List<UserDto>` / `Set<UserDto>` / `UserDto[]` -> `array` with `items: UserDto`
|
|
- `Map<String, UserDto>` -> `object` with `additionalProperties: UserDto`
|
|
|
|
## DTO schema metadata
|
|
|
|
```java
|
|
@Schema(name = "User", title = "User DTO", description = "Public user", deprecated = false)
|
|
public class UserDto {
|
|
|
|
@SchemaProperty(title = "ID", required = true, example = "USR-100", enumeration = {"USR-100", "USR-101"})
|
|
public String id;
|
|
|
|
@SchemaProperty(hidden = true)
|
|
public String internalDebug;
|
|
}
|
|
```
|
|
|
|
Supported field-level exclusion:
|
|
|
|
- `@Schema(hidden = true)` / `@SchemaProperty(hidden = true)`
|
|
- `@JsonIgnore`
|
|
- `@JsonIgnoreProperties(...)`
|
|
- `transient` / `static`
|
|
|
|
## Contributor API
|
|
|
|
OpenAPI is extension-agnostic. Other extensions contribute with `OpenApiContributor` via
|
|
`OpenApiContributorRegistry`.
|
|
|
|
Supported contribution surfaces:
|
|
|
|
- `components` fragments (merged with last-wins)
|
|
- operation `security` requirements (additive)
|
|
- operation `responses` and response `headers` (additive)
|
|
|
|
Merge policy:
|
|
|
|
- contributor collisions use **last-wins**
|
|
- manual `@APIResponse` description always wins over contributors for the same status
|
|
|
|
## OIDC interop
|
|
|
|
When `flash-ext-auth-oidc` is installed, OpenAPI integrates automatically:
|
|
|
|
- security scheme under `components.securitySchemes`
|
|
- per-operation `security`
|
|
- auto responses (class-based handlers):
|
|
- `401 Authentication required`
|
|
- `403` role/scope required messages when applicable
|
|
|
|
Manual `@APIResponse` for the same status code always wins.
|
|
|
|
## Limiter interop
|
|
|
|
When `flash-ext-limiter` is installed, handlers with `@Limit` automatically get response
|
|
headers documented in OpenAPI:
|
|
|
|
- `X-RateLimit-Limit`
|
|
- `X-RateLimit-Remaining`
|
|
- `X-RateLimit-Reset`
|
|
- `Retry-After` on `429`
|
|
|
|
If `429` is missing, it is auto-added as `Too Many Requests`.
|
|
|
|
## Notes
|
|
|
|
- Operations are collected from final boot-time routes for class-based handlers with `@ApiOperation`.
|
|
- Documented paths always match runtime paths (including scope namespaces/prefixes/rewrites).
|
|
- Route path params are auto-discovered from `/{id}`.
|
|
- Parameter annotations are mainly for query/header/cookie enrichment.
|
|
- Output responses are sorted by numeric status code.
|