From b7c76762ef941c312085eb1e7c41bc8e11b91e4e Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 28 Jul 2026 22:14:12 +0100 Subject: [PATCH] Configure data explorer response views --- .../compendiumdev/thingifier/Thingifier.java | 8 + .../api/docgen/ApiRoutingDefinition.java | 6 + .../api/ermodelconversion/JsonThing.java | 24 ++- .../api/ermodelconversion/XmlThing.java | 16 +- .../htmlgui/ThingifierGuiConfig.java | 66 ++++++ .../htmlgui/htmlgen/DefaultGuiHtmlPages.java | 199 ++++++++++++------ .../DefaultGuiHtmlPagesRepositoryTest.java | 92 ++++++++ 7 files changed, 338 insertions(+), 73 deletions(-) create mode 100644 thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/ThingifierGuiConfig.java diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index 27611efa..a3a441f6 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -14,6 +14,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.repository.ThingStore; +import uk.co.compendiumdev.thingifier.htmlgui.ThingifierGuiConfig; import uk.co.compendiumdev.thingifier.reporting.ThingReporter; /* Thingifier @@ -33,6 +34,7 @@ public final class Thingifier implements AutoCloseable { private final ThingifierApiConfig apiConfig; private final ThingifierApiConfigProfiles apiConfigProfiles; private final ThingifierApiSpec apiSpec; + private final ThingifierGuiConfig guiConfig; public Thingifier() { this(new EntityRelModel()); @@ -45,6 +47,7 @@ public Thingifier(final EntityRelModel erm) { apiConfig = new ThingifierApiConfig(""); apiConfigProfiles = new ThingifierApiConfigProfiles(); apiSpec = new ThingifierApiSpec(); + guiConfig = new ThingifierGuiConfig(); apiDocsConfig = new ApiDocsConfig(); } @@ -62,6 +65,7 @@ public Thingifier( this.apiConfig = apiConfig; this.apiConfigProfiles = apiConfigProfiles; this.apiSpec = new ThingifierApiSpec(); + this.guiConfig = new ThingifierGuiConfig(); this.apiDocsConfig = apiDocsConfig; } @@ -216,6 +220,10 @@ public ThingifierApiSpec apiSpec() { return apiSpec; } + public ThingifierGuiConfig guiConfig() { + return guiConfig; + } + public void configureWithProfile(final ThingifierApiConfigProfile profileToUse) { if (profileToUse == null) { System.out.println("API System Defaults Used"); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/docgen/ApiRoutingDefinition.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/docgen/ApiRoutingDefinition.java index e262277b..8f9ec2b1 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/docgen/ApiRoutingDefinition.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/docgen/ApiRoutingDefinition.java @@ -7,6 +7,7 @@ import java.util.Map; import uk.co.compendiumdev.thingifier.api.response.ResponseHeader; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityViewDefinition; public final class ApiRoutingDefinition { @@ -58,6 +59,11 @@ public void addObjectSchema(EntityDefinition entityDefn) { // and as plural for array responses objectSchemas.put(entityDefn.getPlural(), entityDefn); + + for (EntityViewDefinition view : entityDefn.getViews()) { + objectSchemas.put(view.getName(), entityDefn); + objectSchemas.put("create_" + view.getName(), entityDefn); + } } public boolean hasObjectSchemaNamed(String aName) { diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java index c239ecd0..fe8f5b00 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java @@ -96,10 +96,17 @@ public JsonObject asJsonObjectTypedArrayWithContentsUntyped( public JsonObject asJsonObjectTypedDraftArrayWithContentsUntyped( final List things, String typeName) { + return asJsonObjectTypedDraftArrayWithContentsUntyped(things, typeName, null); + } + + public JsonObject asJsonObjectTypedDraftArrayWithContentsUntyped( + final List things, + String typeName, + final EntityViewDefinition view) { final JsonObject arrayObj = new JsonObject(); JsonArray drafts = new JsonArray(); for (EntityInstanceDraft draft : things) { - drafts.add(asJsonObject(draft)); + drafts.add(asJsonObject(draft, view)); } arrayObj.add(typeName, drafts); return arrayObj; @@ -252,6 +259,11 @@ private JsonObject asFieldJsonObject( } public JsonObject asJsonObject(final EntityInstanceDraft draft) { + return asJsonObject(draft, null); + } + + public JsonObject asJsonObject( + final EntityInstanceDraft draft, final EntityViewDefinition view) { final JsonObject jsonobj = new JsonObject(); if (draft == null) { @@ -268,6 +280,9 @@ public JsonObject asJsonObject(final EntityInstanceDraft draft) { } for (String fieldName : entity.getFieldNames()) { + if (view != null && !view.isResponseVisible(fieldName)) { + continue; + } Field field = entity.getField(fieldName); String fieldValue = draftValueFor(field, values.get(fieldName.toLowerCase())); if (fieldValue == null) { @@ -524,8 +539,13 @@ public JsonObject asNamedJsonObject( } public JsonObject asNamedJsonObject(final EntityInstanceDraft draft) { + return asNamedJsonObject(draft, null); + } + + public JsonObject asNamedJsonObject( + final EntityInstanceDraft draft, final EntityViewDefinition view) { final JsonObject retObj = new JsonObject(); - retObj.add(draft.getEntity().getName(), asJsonObject(draft)); + retObj.add(draft.getEntity().getName(), asJsonObject(draft, view)); return retObj; } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/XmlThing.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/XmlThing.java index aa3cc52e..46e8cd3f 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/XmlThing.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/XmlThing.java @@ -37,7 +37,12 @@ public String getSingleObjectXml( } public String getSingleObjectXml(final EntityInstanceDraft draft) { - String parseForXMLOutput = jsonConvertor.asNamedJsonObject(draft).toString(); + return getSingleObjectXml(draft, null); + } + + public String getSingleObjectXml( + final EntityInstanceDraft draft, final EntityViewDefinition view) { + String parseForXMLOutput = jsonConvertor.asNamedJsonObject(draft, view).toString(); return XML.toString(new JSONObject(parseForXMLOutput)); } @@ -78,10 +83,17 @@ public String getCollectionOfThings( public String getCollectionOfDrafts( final List thingsToReturn, final EntityDefinition typeOfThingReturned) { + return getCollectionOfDrafts(thingsToReturn, typeOfThingReturned, null); + } + + public String getCollectionOfDrafts( + final List thingsToReturn, + final EntityDefinition typeOfThingReturned, + final EntityViewDefinition view) { String parseForXMLOutput = jsonConvertor .asJsonObjectTypedDraftArrayWithContentsUntyped( - thingsToReturn, typeOfThingReturned.getPlural()) + thingsToReturn, typeOfThingReturned.getPlural(), view) .toString(); String output = XML.toString(new JSONObject(parseForXMLOutput)); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/ThingifierGuiConfig.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/ThingifierGuiConfig.java new file mode 100644 index 00000000..c64c6fcf --- /dev/null +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/ThingifierGuiConfig.java @@ -0,0 +1,66 @@ +package uk.co.compendiumdev.thingifier.htmlgui; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityViewDefinition; + +public final class ThingifierGuiConfig { + + private final DataExplorerConfig dataExplorer; + + public ThingifierGuiConfig() { + dataExplorer = new DataExplorerConfig(); + } + + public DataExplorerConfig dataExplorer() { + return dataExplorer; + } + + public static final class DataExplorerConfig { + private final Map responseViewNamesByEntity; + + private DataExplorerConfig() { + responseViewNamesByEntity = new HashMap<>(); + } + + public DataExplorerConfig responseView(final String entityName, final String viewName) { + final String normalizedEntityName = normalize(entityName); + final String normalizedViewName = viewName == null ? "" : viewName.trim(); + if (normalizedEntityName.isEmpty()) { + throw new IllegalArgumentException("Entity name is required"); + } + if (normalizedViewName.isEmpty()) { + throw new IllegalArgumentException("View name is required"); + } + responseViewNamesByEntity.put(normalizedEntityName, normalizedViewName); + return this; + } + + public Optional responseViewNameFor(final EntityDefinition definition) { + if (definition == null) { + return Optional.empty(); + } + String viewName = responseViewNamesByEntity.get(normalize(definition.getName())); + if (viewName == null) { + viewName = responseViewNamesByEntity.get(normalize(definition.getPlural())); + } + return Optional.ofNullable(viewName); + } + + public EntityViewDefinition responseViewFor(final EntityDefinition definition) { + if (definition == null) { + return null; + } + return responseViewNameFor(definition) + .filter(definition::hasViewNamed) + .map(definition::getViewNamed) + .orElse(null); + } + + private static String normalize(final String value) { + return value == null ? "" : value.trim().toLowerCase(); + } + } +} diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPages.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPages.java index 73d7cb68..2498d209 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPages.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPages.java @@ -5,12 +5,14 @@ import java.util.List; import java.util.Map; import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.api.ermodelconversion.XmlThing; import uk.co.compendiumdev.thingifier.api.http.bodyparser.xml.GenericXMLPrettyPrinter; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityViewDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; @@ -38,10 +40,7 @@ public DefaultGuiHtmlPages( this.jsonThing = new JsonThing(apiConfig.jsonOutput()); this.xmlThing = new XmlThing(jsonThing); this.urlPathPrefix = urlPathPrefix; - String firstEntity = - ((EntityDefinition) - this.thingifier.getERmodel().getEntityDefinitions().toArray()[0]) - .getName(); + String firstEntity = firstVisibleThingName(); String database = EntityRelModel.DEFAULT_DATABASE_NAME; this.tryDefault = " [explore default data]" @@ -88,7 +87,7 @@ private String getInstancesRootMenuHtml(String database) { StringBuilder html = new StringBuilder(); html.append("