From b74bc79c02d277e67a18bd931edbc36209b52eca Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Sun, 26 Jul 2026 11:10:24 +0100 Subject: [PATCH 1/4] Add QUERY support and versioned OpenAPI specs --- .../core/query/QueryFilterParams.java | 10 + pom.xml | 2 +- .../swaggerizer/SwaggerizerTest.java | 27 +- .../crudui/ApiDocumentationPage.java | 2 + .../thingifier/crudui/CrudUiController.java | 6 + .../crudui/OpenApiDocumentation.java | 10 +- .../thingifier/crudui/SwaggerUiPage.java | 7 +- .../adapter/javalin/CrudUiApplication.java | 13 + .../crudui/CrudUiControllerTest.java | 27 ++ .../thingifier/crudui/e2e/CrudUiApiIT.java | 5 + .../adapter/httpserver/HttpRouteVerb.java | 1 + .../adapter/httpserver/ServerRoutes.java | 4 + .../httpserver/SimpleHttpRouteCreator.java | 7 +- .../ThingifierAutoDocGenRouting.java | 31 ++- .../httpserver/ThingifierHttpApiRoutings.java | 47 +++- .../internalhttp/InternalHttpMethod.java | 1 + .../conversion/ThingifierHttpApiBridge.java | 4 + .../adapter/javalin/JavalinHttpServer.java | 2 + .../api/ThingifierRestAPIHandler.java | 8 + .../ApiRoutingDefinitionDocGenerator.java | 51 +++- .../thingifier/api/docgen/RoutingVerb.java | 1 + .../api/http/ApiRequestEnvelope.java | 17 +- .../thingifier/api/http/HttpApiRequest.java | 1 + .../api/http/HttpApiRequestValidator.java | 40 +++ .../api/http/ThingifierHttpApi.java | 10 + .../api/http/UrlQueryParamParser.java | 33 ++- .../headerparser/ContentTypeHeaderParser.java | 4 + .../restapihandlers/RestApiGetHandler.java | 15 +- .../restapihandlers/RestApiQueryHandler.java | 67 +++++ .../RestApiDocumentationGenerator.java | 12 + .../OpenApiSpecificationVersion.java | 40 +++ .../swaggerizer/SwaggerGenerationConfig.java | 2 + .../thingifier/swaggerizer/SwaggerUiPage.java | 27 +- .../thingifier/swaggerizer/Swaggerizer.java | 61 ++++- .../javalin/JavalinHttpServerTest.java | 33 +++ .../ApiRoutingDefinitionDocGeneratorTest.java | 23 ++ .../api/http/ApiRequestEnvelopeTest.java | 17 ++ .../RestApiQueryHandlerTest.java | 247 ++++++++++++++++++ .../swaggerizer/SwaggerUiPageTest.java | 5 + 39 files changed, 874 insertions(+), 46 deletions(-) create mode 100644 thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiQueryHandler.java create mode 100644 thingifier/src/main/java/uk/co/compendiumdev/thingifier/swaggerizer/OpenApiSpecificationVersion.java create mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiQueryHandlerTest.java diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryFilterParams.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryFilterParams.java index 95480fd7..bef3ecfb 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryFilterParams.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryFilterParams.java @@ -39,6 +39,16 @@ public void add(FilterBy aFilterBy) { filterBys.add(aFilterBy); } + public void addAll(final QueryFilterParams params) { + if (params == null) { + return; + } + + for (FilterBy filterBy : params.toList()) { + add(new FilterBy(filterBy.fieldName, filterBy.filterOperation + filterBy.fieldValue)); + } + } + public FilterBy get(int i) { return filterBys.get(i); } diff --git a/pom.xml b/pom.xml index d9c97fca..606a48cc 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ UTF-8 1.5.6-SNAPSHOT - 2.2.27 + 2.2.52 2.8.6 3.53.2.0 2.6 diff --git a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/SwaggerizerTest.java b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/SwaggerizerTest.java index 47e8fee1..7cf0d704 100644 --- a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/SwaggerizerTest.java +++ b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/SwaggerizerTest.java @@ -1,5 +1,7 @@ package uk.co.compendiumdev.thingifier.swaggerizer; +import com.google.gson.JsonParser; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.docgen.RoutingDefinition; @@ -26,6 +28,29 @@ public void canCreateSwagger() { apiDefn.addServer("http://localhost:4567", "local execution"); apiDefn.setVersion("1.0.1"); - System.out.println(new Swaggerizer(apiDefn).asJson()); + String swagger = new Swaggerizer(apiDefn).asJson(); + + Assertions.assertTrue(openApiVersion(swagger).startsWith("3.1.")); + Assertions.assertTrue(swagger.contains("\"x-query-operation\"")); + Assertions.assertTrue(swagger.contains("application/x-www-form-urlencoded")); + } + + @Test + public void canCreateOpenApi30Swagger() { + Thingifier t = new TodoListThingifierTestModel().get(); + + ThingifierApiDocumentationDefn apiDefn = new ThingifierApiDocumentationDefn(); + apiDefn.setThingifier(t); + apiDefn.setVersion("1.0.1"); + + String swagger = new Swaggerizer(apiDefn).asJson(OpenApiSpecificationVersion.OPENAPI_3_0); + + Assertions.assertTrue(openApiVersion(swagger).startsWith("3.0.")); + Assertions.assertTrue(swagger.contains("\"x-query-operation\"")); + Assertions.assertTrue(swagger.contains("application/x-www-form-urlencoded")); + } + + private String openApiVersion(final String swagger) { + return JsonParser.parseString(swagger).getAsJsonObject().get("openapi").getAsString(); } } diff --git a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/ApiDocumentationPage.java b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/ApiDocumentationPage.java index c6b15375..3e3be68b 100644 --- a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/ApiDocumentationPage.java +++ b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/ApiDocumentationPage.java @@ -52,6 +52,8 @@ private void appendHeader(final StringBuilder html) { html.append(" Swagger UI\n"); html.append(" Download OpenAPI\n"); html.append(" Raw OpenAPI JSON\n"); + html.append(" OpenAPI 3.1 JSON\n"); + html.append(" OpenAPI 3.0 JSON\n"); html.append(" \n"); html.append("

").append(escape(title())).append(" API Documentation

\n"); if (!description().isEmpty()) { diff --git a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/CrudUiController.java b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/CrudUiController.java index 1191a8a7..58dd7c95 100644 --- a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/CrudUiController.java +++ b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/CrudUiController.java @@ -2,6 +2,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import uk.co.compendiumdev.thingifier.swaggerizer.OpenApiSpecificationVersion; import uk.co.compendiumdev.thingifier.yaml.ThingifierYamlException; public final class CrudUiController { @@ -188,6 +189,11 @@ public UiHttpResponse openApiJson() { 200, new OpenApiDocumentation(workspace.snapshot()).openApiJson()); } + public UiHttpResponse openApiJson(final OpenApiSpecificationVersion version) { + return UiHttpResponse.json( + 200, new OpenApiDocumentation(workspace.snapshot()).openApiJson(version)); + } + public UiHttpResponse downloadOpenApi(final boolean permissive) { OpenApiDocumentation openApi = new OpenApiDocumentation(workspace.snapshot()); Map headers = new LinkedHashMap<>(); diff --git a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/OpenApiDocumentation.java b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/OpenApiDocumentation.java index 145705a3..1140b6c2 100644 --- a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/OpenApiDocumentation.java +++ b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/OpenApiDocumentation.java @@ -2,6 +2,7 @@ import java.util.Locale; import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; +import uk.co.compendiumdev.thingifier.swaggerizer.OpenApiSpecificationVersion; import uk.co.compendiumdev.thingifier.swaggerizer.Swaggerizer; public final class OpenApiDocumentation { @@ -15,11 +16,16 @@ public OpenApiDocumentation(final WorkspaceSnapshot snapshot) { } public String openApiJson() { - return new Swaggerizer(apiDefinition()).asJson(); + return openApiJson(OpenApiSpecificationVersion.OPENAPI_3_1); + } + + public String openApiJson(final OpenApiSpecificationVersion version) { + return new Swaggerizer(apiDefinition()).asJson(version); } public String permissiveOpenApiJson() { - return new Swaggerizer(apiDefinition()).asJson(true); + return new Swaggerizer(apiDefinition()) + .asJson(OpenApiSpecificationVersion.OPENAPI_3_1, true); } public String downloadFilename(final boolean permissive) { diff --git a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/SwaggerUiPage.java b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/SwaggerUiPage.java index 0b3f3b50..34c92da6 100644 --- a/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/SwaggerUiPage.java +++ b/thingifier-crud-ui/src/main/java/uk/co/compendiumdev/thingifier/crudui/SwaggerUiPage.java @@ -33,7 +33,12 @@ public String html() { + "