refactor(core): make boot and middleware ordering deterministic
CI / Build & Test (push) Failing after 4m51s
CI / Build & Test (pull_request) Canceled after 23s

This commit is contained in:
Zakaria El Orche
2026-08-12 16:42:49 +00:00
parent d7f36a7aea
commit 891ef99b8e
51 changed files with 1395 additions and 504 deletions
@@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import dev.relism.flash.extension.FlashContext;
import dev.relism.flash.extension.FlashExtension;
import dev.relism.flash.extension.FlashRegistrar;
import dev.relism.flash.extension.FlashExtension;
import dev.relism.flash.extension.RouteEvent;
import dev.relism.flash.http.ContentType;
import dev.relism.flash.http.HttpMethod;
@@ -65,7 +65,7 @@ public class OpenApiExtension implements FlashExtension {
// ── FlashExtension ────────────────────────────────────────────────────────
@Override
public void provide(FlashContext ctx) {
public void configure(FlashRegistrar<?> app, FlashContext ctx) {
OpenApiBuilder builder = new OpenApiBuilder().title(title).version(version).description(description);
OpenApiContributorRegistry registry = new OpenApiContributorRegistry();
@@ -76,22 +76,20 @@ public class OpenApiExtension implements FlashExtension {
// Collect operation metadata from final compiled routes.
// This guarantees full runtime paths (namespaces/prefixes/rewrites) in the spec.
ctx.addRouteListener(event -> addOperationFromEvent(builder, event));
}
@Override
public void routes(FlashRegistrar<?> app, FlashContext ctx) {
ObjectMapper jsonMapper = ctx.find(ObjectMapper.class).orElseGet(() -> JsonMapper.builder().build());
YAMLMapper yamlMapper = new YAMLMapper();
OpenApiBuilder builder = ctx.require(OpenApiBuilder.class);
ctx.onReady(() -> {
ObjectMapper jsonMapper = ctx.find(ObjectMapper.class).orElseGet(() -> JsonMapper.builder().build());
YAMLMapper yamlMapper = new YAMLMapper();
OpenApiBuilder resolvedBuilder = ctx.require(OpenApiBuilder.class);
String jsonPath = basePath + ".json";
String yamlPath = basePath + ".yaml";
String swaggerPath = basePath + "/swagger";
String swaggerHtml = buildSwaggerHtml(jsonPath);
app.get(jsonPath, (req, res) -> { res.type(ContentType.JSON); return jsonMapper.writeValueAsString(builder.build()); });
app.get(yamlPath, (req, res) -> { res.type(YAML_CONTENT_TYPE); return yamlMapper.writeValueAsString(builder.build()); });
app.get(swaggerPath, (req, res) -> { res.type(ContentType.TEXT_HTML); return swaggerHtml; });
app.get(jsonPath, (req, res) -> { res.type(ContentType.JSON); return jsonMapper.writeValueAsString(resolvedBuilder.build()); });
app.get(yamlPath, (req, res) -> { res.type(YAML_CONTENT_TYPE); return yamlMapper.writeValueAsString(resolvedBuilder.build()); });
app.get(swaggerPath, (req, res) -> { res.type(ContentType.TEXT_HTML); return swaggerHtml; });
});
}
// ── Swagger UI HTML ───────────────────────────────────────────────────────
@@ -11,7 +11,7 @@ import dev.relism.flash.http.HttpMethod;
import dev.relism.flash.models.RequestHandler;
import dev.relism.flash.models.Response;
import dev.relism.flash.routing.GET;
import dev.relism.flash.routing.Middleware;
import dev.relism.flash.routing.MiddlewareNode;
import dev.relism.flash.websocket.WebSocketEndpoint;
import org.junit.jupiter.api.Test;
@@ -41,12 +41,10 @@ class OpenApiExtensionTest {
void provide_collects_operations_and_routes_serve_json_yaml_swagger() throws Exception {
FlashContext ctx = new FlashContext();
OpenApiExtension ext = new OpenApiExtension("/docs", "My API", "2.0.0", "desc");
ext.provide(ctx);
emitRoute(ctx, HttpMethod.GET, "/health", "/", HealthHandler.class);
TestRegistrar app = new TestRegistrar(ctx);
ext.routes(app, ctx);
ext.configure(app, ctx);
emitRoute(ctx, HttpMethod.GET, "/health", "/", HealthHandler.class);
ctx.complete();
assertNotNull(app.route(HttpMethod.GET, "/docs.json"));
assertNotNull(app.route(HttpMethod.GET, "/docs.yaml"));
@@ -77,9 +75,9 @@ class OpenApiExtensionTest {
ObjectMapper mapper = new ObjectMapper();
ctx.provide(ObjectMapper.class, mapper);
ext.provide(ctx);
TestRegistrar app = new TestRegistrar(ctx);
ext.routes(app, ctx);
ext.configure(app, ctx);
ctx.complete();
Response jsonRes = new Response(200, ContentType.NONE);
Object jsonBody = app.route(HttpMethod.GET, "/openapi.json").handle(null, jsonRes);
@@ -99,10 +97,11 @@ class OpenApiExtensionTest {
void collects_full_runtime_path_from_route_event() {
FlashContext ctx = new FlashContext();
OpenApiExtension ext = new OpenApiExtension();
ext.provide(ctx);
ext.configure(new TestRegistrar(ctx), ctx);
emitRoute(ctx, HttpMethod.GET, "/api/v1/users", "/api/v1", ScopedUsersHandler.class);
ctx.complete();
OpenApiBuilder builder = ctx.require(OpenApiBuilder.class);
Map<String, Object> spec = builder.build();
Map<String, Object> paths = cast(spec.get("paths"));
@@ -114,10 +113,11 @@ class OpenApiExtensionTest {
void normalizes_double_slash_paths_from_events() {
FlashContext ctx = new FlashContext();
OpenApiExtension ext = new OpenApiExtension();
ext.provide(ctx);
ext.configure(new TestRegistrar(ctx), ctx);
emitRoute(ctx, HttpMethod.GET, "//blogs", "/", ScopedUsersHandler.class);
ctx.complete();
OpenApiBuilder builder = ctx.require(OpenApiBuilder.class);
Map<String, Object> spec = builder.build();
Map<String, Object> paths = cast(spec.get("paths"));
@@ -150,7 +150,7 @@ class OpenApiExtensionTest {
private static final class TestRegistrar extends FlashRegistrar<TestRegistrar> {
private final FlashContext ctx;
private final Map<String, RequestHandler> routes = new HashMap<>();
private final List<Middleware> middlewares = new ArrayList<>();
private final List<MiddlewareNode> middlewares = new ArrayList<>();
private TestRegistrar(FlashContext ctx) {
this.ctx = ctx;
@@ -162,7 +162,7 @@ class OpenApiExtensionTest {
}
@Override
protected void addRoute(HttpMethod method, String path, RequestHandler handler, List<Middleware> mw) {
protected void addRoute(HttpMethod method, String path, RequestHandler handler, List<MiddlewareNode> mw) {
routes.put(method.name() + " " + path, handler);
}
@@ -172,7 +172,7 @@ class OpenApiExtensionTest {
}
@Override
protected void addMiddleware(Middleware mw) {
protected void addMiddleware(MiddlewareNode mw) {
middlewares.add(mw);
}