Commit Graph
22 Commits
Author SHA1 Message Date
Zakaria El OrcheandClaude Opus 5 5fe6fb46c8 fix(ext-openapi): one shared answer per status, not a numbered family
Hoisting named a shared response after its status and disambiguated with a
counter, so a document with three wordings for 403 grew Forbidden, Forbidden2
and Forbidden3 in its components. Numbered names say nothing and move as soon
as a route is added.

The answer a status is usually given is now the one hoisted, under that
status's own name, and a route that answers the same status differently keeps
its wording inline where it belongs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 16:34:13 +00:00
Zakaria El OrcheandClaude Opus 5 9090ba59f5 feat(ext-openapi): a route can say it is not part of the API
Every class-based route is documented, which is what keeps a document from
lying by omission. Some routes are not API at all — a health check, an
internal callback, something on its way out — and @Undocumented says so, once,
where the handler is. Inherited, so a base class leaves out every handler
written against it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:47:35 +00:00
Zakaria El OrcheandClaude Opus 5 60dd4eab6a fix(ext-openapi): an answer's media type is the handler's, not the body's
@Consumes says what a route reads. It was also deciding what the document said
a route answers, through a JSON default nothing could override: a handler that
takes a JSON body is not thereby a handler that answers JSON.

@Produces now says that, beside @Consumes and as descriptive as it is — on the
handler, or once on a base class. Every response takes its media type from it,
JSON when nothing declares one, and the error object stays JSON because that is
what Flash answers a failure with whatever the route produces.

Content loses contentType with it. One handler answers in one format and a
status code does not change that, so the media type was in the wrong place; a
response with no schema and no return type to infer one from is a response
with no body, which is what a 204 was using it to say.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 14:41:56 +00:00
Zakaria El OrcheandClaude Opus 5 2fbe65fcc5 feat(ext-openapi): document what the code already says
Every class-based route is documented now, annotated or not: a route without
@ApiOperation used to be dropped with a warning, which made the document lie by
omission.

Read off the handler: the request body from the type it declares (or from the
new @RequestBody, for one that reads the body itself), the success schema from
the most specific handle it implements, and the media type from @Consumes.

Failures are described too. Any 4xx or 5xx without an explicit schema documents
the error object Flash actually answers with, written once under
components.schemas.Error — before this, a declared 4xx inherited the success
schema, which was simply wrong. And an answer two or more operations give
identically is hoisted into components.responses and referenced, so the 401 of
every guarded route appears once rather than on every path.

@Content gained an example, and the schema registry moved into its own class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:31:28 +00:00
Zakaria El Orche e0795299fc refactor(ext-oidc): replace auth modules with security extensions 2026-09-16 15:54:19 +00:00
Zakaria El Orche 096098b33c fix(ext-openapi): allow contributors to enrich default responses 2026-09-16 15:54:10 +00:00
Zakaria El Orche 9d39e24ccb refactor(ext-auth): generic sessions, shared annotation wiring, rename to flash-ext-auth-oidc
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.
2026-09-10 19:06:15 +00:00
Zakaria El OrcheandClaude Opus 5 f68e661296 feat(ext-validation): add request validation with compiled constraints
Standard jakarta.validation annotations, compiled once per type into a flat
check table. No configuration: constraints come from the annotations already on
your types, and ValidationException extends HttpException with status 422 so
the default handler renders it without this extension registering anything.

    record CreateUser(@NotBlank @Size(max = 80) String name,
                      @Email String email,
                      @Min(18) int age) {}

    CreateUser dto = validation.body(req, CreateUser.class);

Annotations only — Hibernate Validator's engine is deliberately absent. It
resolves constraints reflectively per call and pulls ~2 MB plus EL, which is
the per-request cost this module exists to avoid. jakarta.validation-api is
~90 KB of annotations.

The passing path allocates nothing. Constraints resolve at first use into an
opcode plus operands cached in a ClassValue, so there is no map lookup and no
lock. Fields are read through MethodHandles adapted to an exact signature —
(Object)Object for references, (Object)long for primitive integrals — so
invokeExact neither boxes nor builds the argument array Field.get and
Method.invoke allocate. Checks are a flat array walked by a tableswitch rather
than a class hierarchy behind a virtual call. @Size reads a length the object
already knows and @Email scans with indexOf, because Pattern.matcher allocates
a matcher and two int arrays per call. Messages are pre-rendered at compile
time. The violation list and the exception exist only once something fails.

@Pattern is the marked exception: its regex compiles once but matcher()
allocates per call.

Constraints are read from declared fields, so records and plain classes take
one code path — a constraint on a record component propagates to its backing
field.

Jakarta null semantics are exact: only @NotNull rejects null.

flash-ext-openapi now mirrors the same annotations into the generated schema —
minLength, maxLength, minItems, minimum, maximum, pattern, format: email and
required — via an optional jakarta.validation dependency detected at boot. A
type declares its rules once and both the validator and the published contract
read them. An explicit @Schema still wins; the bridge only fills keys nobody
set, and without the annotations on the classpath the bridge class is never
loaded.

flash-ext-jackson is optional too: validate(value) works without it, only
body(req, type) needs a codec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 12:20:33 +00:00
Zakaria El OrcheandClaude Sonnet 5 74169e40f4 feat(core): add any() route registration, warn on undocumented OpenAPI routes, log unhandled 500s
Publish Maven packages / publish (push) Failing after 3m35s
FlashRegistrar#any() registers a handler under every HTTP method for
verb-indifferent handlers (e.g. a reverse proxy). OpenApiExtension now warns
when a class-based handler has no @ApiOperation instead of silently omitting
it from the spec. AbstractRouter's default production exception handler now
logs unhandled exceptions server-side instead of only returning a JSON 500.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 11:56:51 +00:00
Zakaria El Orche 891ef99b8e refactor(core): make boot and middleware ordering deterministic
CI / Build & Test (pull_request) Canceled after 23s
CI / Build & Test (push) Failing after 4m51s
2026-08-12 16:42:49 +00:00
Relism ccc5550598 feat: introduce WebSocket support with new endpoints and transaction propagation enhancements 2026-05-11 16:14:26 +02:00
github-actions[bot] a4a16bdb00 chore(release): prepare 2.1.0-SNAPSHOT 2026-05-11 13:54:19 +00:00
github-actions[bot] f941eb63f4 chore(release): 2.0.0 2026-05-11 13:54:16 +00:00
Relism 945633e5bd ci: setup CI/CD pipelines, versioning and agent guidelines 2026-05-11 14:19:54 +02:00
Relism 8a52c4f143 refactor: rename packages and files to use 'flash' prefix for consistency 2026-04-26 12:34:49 +02:00
Relism 9e19f439be add core view extension with JTE and Thymeleaf support 2026-04-21 00:32:09 +02:00
Relism 34fd74068a implement OpenAPI contributor integration for rate limiting and response headers 2026-04-19 23:42:50 +02:00
Relism e161497f2c i spent the last year just spinning 2026-04-17 18:56:06 +02:00
Relism 9efbe38c0c weeks of bullshit 2026-04-17 08:18:11 +02:00
Relism b5d4481502 preparing for another refactoring... 2026-03-29 23:16:41 +02:00
Relism 2edd68b0aa preparing for a conceptual refactoring... 2026-03-28 14:11:12 +01:00
Relism 7b996b552b pre-major refactoring + ext api. 2026-03-26 14:10:53 +01:00