From 636faf49844a34962ab0b27852a51aa4a81a4284 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 8 Oct 2024 08:04:04 +0530 Subject: [PATCH] FISH-9548 Maintain OpenAI state to create ER diagram --- PayaraStarterGenerator/pom.xml | 8 +- .../starter/application/domain/Attribute.java | 114 +++++---- .../starter/application/domain/Constant.java | 19 ++ .../starter/application/domain/ERModel.java | 95 ++++++-- .../starter/application/domain/Entity.java | 80 ++++--- .../starter/application/domain/KeyValue.java | 45 ---- .../application/domain/Relationship.java | 54 +++-- .../generator/CRUDAppGenerator.java | 24 +- .../generator/ERDiagramParser.java | 55 ++--- .../starter/application/util/JPAUtil.java | 158 +++++++++++- .../resources/template/html/about-us.html.ftl | 2 +- .../resources/template/html/entity.html.ftl | 12 +- .../resources/template/html/home.html.ftl | 2 +- starter-ui/pom.xml | 9 +- .../fish/payara/starter/ERDiagramChat.java | 226 ------------------ .../payara/starter/ERDiagramPlainChat.java | 8 + .../payara/starter/ERDiagramResource.java | 12 + .../payara/starter/LangChainChatService.java | 109 +++++++++ .../resources/ApplicationConfiguration.java | 1 + .../resources/ApplicationGenerator.java | 37 ++- .../starter/resources/LoggerOutputStream.java | 30 +++ .../META-INF/microprofile-config.properties | 7 +- starter-ui/src/main/webapp/index.html | 193 ++++++++++++--- 23 files changed, 808 insertions(+), 492 deletions(-) create mode 100644 PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Constant.java delete mode 100644 PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/KeyValue.java delete mode 100644 starter-ui/src/main/java/fish/payara/starter/ERDiagramChat.java create mode 100644 starter-ui/src/main/java/fish/payara/starter/resources/LoggerOutputStream.java diff --git a/PayaraStarterGenerator/pom.xml b/PayaraStarterGenerator/pom.xml index 8ea292f..44dae78 100644 --- a/PayaraStarterGenerator/pom.xml +++ b/PayaraStarterGenerator/pom.xml @@ -4,7 +4,7 @@ fish.payara.starter payara-starter-parent - 1.0-beta8 + 1.0-beta9 payara-starter-generator Payara Starter Generator @@ -16,6 +16,12 @@ 2.3.31 jar + + jakarta.platform + jakarta.jakartaee-api + 10.0.0 + provided + diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Attribute.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Attribute.java index 2e722d9..b9249a7 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Attribute.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Attribute.java @@ -16,9 +16,12 @@ package fish.payara.starter.application.domain; import fish.payara.starter.application.util.AttributeType; +import static fish.payara.starter.application.util.AttributeType.LOCAL_DATE; +import static fish.payara.starter.application.util.AttributeType.LOCAL_DATE_TIME; import static fish.payara.starter.application.util.StringHelper.pluralize; import static fish.payara.starter.application.util.StringHelper.startCase; import static fish.payara.starter.application.util.StringHelper.titleCase; +import jakarta.json.bind.annotation.JsonbTransient; import java.util.ArrayList; import java.util.List; @@ -28,51 +31,55 @@ */ public class Attribute { - private final String name; - private final String type; - private boolean isPrimaryKey; + private String name; + private String type; + private boolean primaryKey; private boolean required; - private List _import = new ArrayList<>(); - private Entity relation; private boolean multi; + private String tooltip; + private Boolean display; + private String htmlLabel; - private final List property; + public Attribute() { + } - public Attribute(String name, String type, boolean isPrimaryKey, List property) { + public Attribute(String name, String type, boolean isPrimaryKey) { this.name = name; this.type = type; - this.isPrimaryKey = isPrimaryKey; - this.property = property; + this.primaryKey = isPrimaryKey; } - public Attribute(String name, Entity relation, boolean multi, List property) { + public Attribute(String name, boolean multi, String relation) { this.name = name; - this.type = relation.getName(); - this.relation = relation; + this.type = relation; this.multi = multi; - this.property = property; } public String getName() { return name; } + @JsonbTransient public String getStartCaseName() { return startCase(name); } + @JsonbTransient public String getLowerCaseName() { return name.toLowerCase(); } + @JsonbTransient public String getTitleCaseName() { return titleCase(name); } + @JsonbTransient public String getLowerCasePluralizeName() { return pluralize(name.toLowerCase()); } + @JsonbTransient public String getTitleCasePluralizeName() { return pluralize(titleCase(name)); } @@ -81,71 +88,90 @@ public String getType() { return type; } + @JsonbTransient public boolean isNumber() { return AttributeType.isNumber(type); } public boolean isPrimaryKey() { - return isPrimaryKey; + return primaryKey; } + @JsonbTransient public boolean isRequired() { return required; } + public void setRequired(boolean required) { + this.required = required; + } + public boolean isMulti() { return multi; } - public Entity getRelation() { - return relation; + @JsonbTransient + public String getToolTipText() { + return tooltip; } - public List getProperty() { - return property; + @JsonbTransient + public boolean isToolTip() { + return getToolTipText() != null && !getToolTipText().trim().isEmpty(); } - public String getProperty(String key) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue.getValue(); - } - } - return null; - } - - public String getProperty(String key, String defaultValue) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue.getValue() == null ? defaultValue : keyValue.getValue(); - } - } - return defaultValue; + public void setTooltip(String tooltip) { + this.tooltip = tooltip; } - - public String getToolTipText() { - return getProperty("tooltip", ""); + + @JsonbTransient + public String getHtmlLabel() { + return htmlLabel; } - public boolean isToolTip() { - return !getToolTipText().trim().isEmpty(); + public void setHtmlLabel(String htmllabel) { + this.htmlLabel = htmllabel; + } + + @JsonbTransient + public Boolean isDisplay() { + return display; + } + + public void setDisplay(Boolean display) { + this.display = display; } + @JsonbTransient public List getImports() { + List _import = new ArrayList<>(); + if (type.equals("LocalDate")) { + _import.add(LOCAL_DATE); + } else if (type.equals("LocalDateTime")) { + _import.add(LOCAL_DATE_TIME); + } return _import; } - public boolean addImport(String e) { - return _import.add(e); + public void setName(String name) { + this.name = name; } - public boolean removeImport(String o) { - return _import.remove(o); + public void setType(String type) { + this.type = type; + } + + public void setPrimaryKey(boolean isPrimaryKey) { + this.primaryKey = isPrimaryKey; + } + + public void setMulti(boolean multi) { + this.multi = multi; } @Override public String toString() { - return "\n\t\tAttribute{name=" + name + ", type=" + type + ", isPrimaryKey=" + isPrimaryKey + ", property=" + property + '}'; + return "\n\t\tAttribute{name=" + name + ", type=" + type + ", isPrimaryKey=" + primaryKey + '}'; } } diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Constant.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Constant.java new file mode 100644 index 0000000..bce96aa --- /dev/null +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Constant.java @@ -0,0 +1,19 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package fish.payara.starter.application.domain; + +/** + * + * @author Gaurav Gupta + */ +public class Constant { + + public static final String icon_default = "circle"; + public static final String title_default = "Jakarta EE Sample"; + public static final String longTitle_default = "Jakarta EE Sample"; + public static final String homePageDescription_default = "Unlock the full potential of your application by harnessing the power of Jakarta EE"; + public static final String aboutUsPageDescription_default = "Welcome to our About Us page, where innovation meets reliability with Payara Jakarta EE. As a team passionate about delivering unparalleled solutions, we specialize in harnessing the power of Jakarta EE to create robust, scalable, and secure applications. With a deep understanding of enterprise-grade development, we are committed to crafting tailored solutions that drive business growth and exceed client expectations. Backed by years of experience and a dedication to staying at the forefront of technology, we take pride in our ability to transform ideas into reality, empowering businesses to thrive in the digital landscape. Discover more about our journey, expertise, and the vision that propels us forward."; + +} diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/ERModel.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/ERModel.java index 8a1235d..b206c0c 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/ERModel.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/ERModel.java @@ -19,15 +19,24 @@ * * @author Gaurav Gupta */ +import static fish.payara.starter.application.domain.Constant.aboutUsPageDescription_default; +import static fish.payara.starter.application.domain.Constant.homePageDescription_default; +import static fish.payara.starter.application.domain.Constant.icon_default; +import static fish.payara.starter.application.domain.Constant.longTitle_default; +import static fish.payara.starter.application.domain.Constant.title_default; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import jakarta.json.bind.annotation.JsonbTransient; + public class ERModel { - private final List entities = new ArrayList<>(); - private final List relationships = new ArrayList<>(); - private List property = Collections.EMPTY_LIST; + private List entities = new ArrayList<>(); + private List relationships = new ArrayList<>(); + + + public ERModel() { + } public void addEntity(Entity entity) { entities.add(entity); @@ -54,41 +63,79 @@ public List getRelationships() { return relationships; } - public List getProperty() { - return property; + public void setEntities(List entities) { + this.entities = entities; } - public void setProperty(List property) { - this.property = property; + public void setRelationships(List relationships) { + this.relationships = relationships; } - - public String getProperty(String key, String defaultValue) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue.getValue() == null ? defaultValue : keyValue.getValue(); - } - } - return defaultValue; + + + private String icon; + private String title; + private String longTitle; + private String homePageDescription; + private String aboutUsPageDescription; + private List topBarMenuOptions = new ArrayList<>(); + + + @JsonbTransient + public String getIcon() { + return icon == null ? icon_default : icon; } - public String getIcon() { - return getProperty("icon", "circle"); + public void setIcon(String icon) { + this.icon = icon; } + @JsonbTransient public String getTitle() { - return getProperty("title", "Jakarta EE Sample"); + return title == null ? title_default : title; + } + + @JsonbTransient + public String getTitle(String defaultValue) { + return title != null ? title : defaultValue; + } + + public void setTitle(String title) { + this.title = title; } + @JsonbTransient public String getLongTitle() { - return getProperty("long-title", getProperty("title", "EE Sample")); + return longTitle == null ? longTitle_default : longTitle; + } + + public void setLongTitle(String longTitle) { + this.longTitle = longTitle; + } + + @JsonbTransient + public String getHomePageDescription() { + return homePageDescription == null ? homePageDescription_default : homePageDescription; + } + + public void setHomePageDescription(String homePageDescription) { + this.homePageDescription = homePageDescription; + } + + @JsonbTransient + public String getAboutUsPageDescription() { + return aboutUsPageDescription == null ? aboutUsPageDescription_default : aboutUsPageDescription; + } + + public void setAboutUsPageDescription(String aboutUsPageDescription) { + this.aboutUsPageDescription = aboutUsPageDescription; } - public String getDescription() { - return getProperty("home-page-description", "Unlock the full potential of your application by harnessing the power of Jakarta EE"); + public List getTopBarMenuOptions() { + return topBarMenuOptions; } - public String getAboutUsDescription() { - return getProperty("about-us-page-description", "Welcome to our About Us page, where innovation meets reliability with Payara Jakarta EE. As a team passionate about delivering unparalleled solutions, we specialize in harnessing the power of Jakarta EE to create robust, scalable, and secure applications. With a deep understanding of enterprise-grade development, we are committed to crafting tailored solutions that drive business growth and exceed client expectations. Backed by years of experience and a dedication to staying at the forefront of technology, we take pride in our ability to transform ideas into reality, empowering businesses to thrive in the digital landscape. Discover more about our journey, expertise, and the vision that propels us forward."); + public void setTopBarMenuOptions(List topBarMenuOptions) { + this.topBarMenuOptions = topBarMenuOptions; } @Override diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Entity.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Entity.java index ce9b826..214d9de 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Entity.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Entity.java @@ -19,6 +19,7 @@ import static fish.payara.starter.application.util.StringHelper.pluralize; import static fish.payara.starter.application.util.StringHelper.startCase; import static fish.payara.starter.application.util.StringHelper.titleCase; +import jakarta.json.bind.annotation.JsonbTransient; import java.util.ArrayList; import java.util.List; @@ -28,57 +29,74 @@ */ public class Entity { - public String name; - private final List attributes = new ArrayList<>(); - private final List property; + private String name; + private List attributes = new ArrayList<>(); + private String icon = "circle"; + private String title; + private String description = ""; - public Entity(String name, List property) { + public Entity() { + } + + public Entity(String name) { this.name = name; - this.property = property; } public String getName() { return name; } + @JsonbTransient public String getStartCaseName() { return startCase(name); } + @JsonbTransient public String getLowerCaseName() { return name.toLowerCase(); } + @JsonbTransient public String getTitleCaseName() { return titleCase(name); } + @JsonbTransient public String getLowerCasePluralizeName() { return pluralize(name.toLowerCase()); } + @JsonbTransient public String getTitleCasePluralizeName() { - return getProperty("title", pluralize(titleCase(name))); + return pluralize(getTitle()); } public List getAttributes() { return attributes; } + public void setAttributes(List attributes) { + this.attributes = attributes; + } + + @JsonbTransient public String getPrimaryKeyType() { return attributes.stream().filter(a -> a.isPrimaryKey()).map(a -> a.getType()).findFirst().orElse("Long"); } + @JsonbTransient public String getPrimaryKeyName() { return attributes.stream().filter(a -> a.isPrimaryKey()).map(a -> a.getName()).findFirst().orElse(null); } + @JsonbTransient public String getPrimaryKeyFirstUpperName() { return firstUpper(getPrimaryKeyName()); } - + + @JsonbTransient public String getDisplayName() { - String displayName = attributes.stream().filter(a -> Boolean.valueOf(a.getProperty("display"))).map(a -> a.getName()).findFirst().orElse(null); + String displayName = attributes.stream().filter(a -> a.isDisplay() != null && a.isDisplay()).map(a -> a.getName()).findFirst().orElse(null); if(displayName == null) { displayName = attributes.stream().filter(a -> !a.isPrimaryKey()).map(a -> a.getName()).findFirst().orElse(null); @@ -90,43 +108,45 @@ public void addAttribute(Attribute attribute) { attributes.add(attribute); } - public List getProperty() { - return property; + @JsonbTransient + public String getIcon() { + return icon; } - public KeyValue getProperty(String key) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue; - } - } - return null; + public void setIcon(String icon) { + this.icon = icon; } - public String getProperty(String key, String defaultValue) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue.getValue() == null ? defaultValue : keyValue.getValue(); - } - } - return defaultValue; + @JsonbTransient + public String getDescription() { + return description; } - public String getIcon() { - return getProperty("icon", "circle"); + public void setDescription(String description) { + this.description = description; } + @JsonbTransient public String getTitle() { - return getProperty("title", titleCase(name)); + return getTitle(titleCase(name)); + } + + @JsonbTransient + public String getTitle(String defaultValue) { + return title != null ? title : defaultValue; } - public String getDescription() { - return getProperty("description", ""); + public void setTitle(String title) { + this.title = title; + } + + public void setName(String name) { + this.name = name; } @Override public String toString() { - return "\n\tEntity{" + "name=" + name + ", attributes=" + attributes + ", property=\n\t\t" + property + '}'; + return "\n\tEntity{" + "name=" + name + ", attributes=" + attributes + '}'; } } diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/KeyValue.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/KeyValue.java deleted file mode 100644 index 39f9a82..0000000 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/KeyValue.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2024 the original author or authors from the Jeddict project (https://jeddict.github.io/). - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package fish.payara.starter.application.domain; - -/** - * - * @author Gaurav Gupta - */ -public class KeyValue { - - private final String key; - private final String value; - - public KeyValue(String key, String value) { - this.key = key; - this.value = value; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return "{" + key + "=" + value + '}'; - } - -} diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Relationship.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Relationship.java index 73352d5..7a0b0a3 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Relationship.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/domain/Relationship.java @@ -23,18 +23,21 @@ */ public class Relationship { - private final String firstEntity; - private final String secondEntity; - private final String relationshipType; - private final String relationshipLabel; - private final List property; + private String firstEntity; + private String secondEntity; + private String relationshipType; + private String relationshipLabel; + private String relationshipVarNameInFirstEntity; + private String relationshipVarNameInSecondEntity; - public Relationship(String firstEntity, String secondEntity, String relationshipType, String relationshipLabel, List property) { + public Relationship() { + } + + public Relationship(String firstEntity, String secondEntity, String relationshipType, String relationshipLabel) { this.firstEntity = firstEntity; this.secondEntity = secondEntity; this.relationshipType = relationshipType; this.relationshipLabel = relationshipLabel; - this.property = property; } public String getFirstEntity() { @@ -53,17 +56,36 @@ public String getRelationshipLabel() { return relationshipLabel; } - public List getProperty() { - return property; + public String getRelationshipVarNameInFirstEntity() { + return relationshipVarNameInFirstEntity; + } + + public void setRelationshipVarNameInFirstEntity(String relationshipVarNameInFirstEntity) { + this.relationshipVarNameInFirstEntity = relationshipVarNameInFirstEntity; + } + + public String getRelationshipVarNameInSecondEntity() { + return relationshipVarNameInSecondEntity; + } + + public void setRelationshipVarNameInSecondEntity(String relationshipVarNameInSecondEntity) { + this.relationshipVarNameInSecondEntity = relationshipVarNameInSecondEntity; + } + + public void setFirstEntity(String firstEntity) { + this.firstEntity = firstEntity; + } + + public void setSecondEntity(String secondEntity) { + this.secondEntity = secondEntity; } - public KeyValue getProperty(String key) { - for (KeyValue keyValue : property) { - if (keyValue.getKey().equals(key)) { - return keyValue; - } - } - return null; + public void setRelationshipType(String relationshipType) { + this.relationshipType = relationshipType; + } + + public void setRelationshipLabel(String relationshipLabel) { + this.relationshipLabel = relationshipLabel; } @Override diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java index 39f190e..36a8b27 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java @@ -21,7 +21,7 @@ import fish.payara.starter.application.domain.Attribute; import static fish.payara.starter.application.util.AttributeType.isBoolean; import static fish.payara.starter.application.util.AttributeType.isPrimitive; -import static fish.payara.starter.application.util.JPAUtil.SQL_RESERVED_KEYWORDS; +import static fish.payara.starter.application.util.JPAUtil.ALL_RESERVED_KEYWORDS; import static fish.payara.starter.application.util.JavaUtil.getIntrospectionPrefix; import static fish.payara.starter.application.util.JavaUtil.getMethodName; import static fish.payara.starter.application.util.StringHelper.firstLower; @@ -44,7 +44,7 @@ import java.util.Set; public class CRUDAppGenerator { - + private final String _package; private final String domainLayer; private final String repositoryLayer; @@ -151,7 +151,7 @@ public void generate(File projectDir, boolean generateJPA, boolean generateRepos generateJPAClass(_package, model, entity, java); } Map dataModel = new HashMap<>(); - dataModel.put("appPU", model.getProperty("title", "app") + "PU"); + dataModel.put("appPU", model.getTitle("app") + "PU"); generate("template/descriptor", "persistence.xml.ftl", "persistence.xml", dataModel, metainf); if (generateRepository) { @@ -187,10 +187,10 @@ private void generateFrontendBase(ERModel model, File outputDir) { private void generateFrontend(Entity entity, File outputDir) { Map dataModel = new HashMap<>(); dataModel.put("entity", entity); - dataModel.put("entityNameLowerCase", entity.name.toLowerCase()); - dataModel.put("entityNameTitleCase", titleCase(entity.name)); - dataModel.put("entityNameTitleCasePluralize", pluralize(titleCase(entity.name))); - dataModel.put("entityNameLowerCasePluralize", pluralize(entity.name.toLowerCase())); + dataModel.put("entityNameLowerCase", entity.getName().toLowerCase()); + dataModel.put("entityNameTitleCase", titleCase(entity.getName())); + dataModel.put("entityNameTitleCasePluralize", pluralize(titleCase(entity.getName()))); + dataModel.put("entityNameLowerCasePluralize", pluralize(entity.getName().toLowerCase())); generate("template/html", "entity.html.ftl", dataModel.get("entityNameLowerCase") + ".html", dataModel, outputDir); } @@ -393,7 +393,7 @@ private void generate(String templatePath, String templateName, String outputFil } private String escapeReservedKeyword(String name) { - return SQL_RESERVED_KEYWORDS.contains(name.toUpperCase()) ? "\\\"" + name + "\\\"" : name; + return ALL_RESERVED_KEYWORDS.contains(name.toUpperCase()) ? "\\\"" + name + "\\\"" : name; } private void generateJPAClass(String _package, ERModel model, Entity entity, File outputDir) throws IOException { @@ -492,15 +492,13 @@ private void appendRelationship(StringBuilder sb, StringBuilder sbfunc, Set parseKeyValuePairs(String metadata) { - List keyValues = new ArrayList<>(); - if(metadata != null) { - Pattern keyValuePattern = Pattern.compile("([\\w-]+)\\[(.+?)\\]"); - Matcher matcher = keyValuePattern.matcher(metadata.trim()); - while (matcher.find()) { - String key = matcher.group(1); - String value = matcher.group(2); - keyValues.add(new KeyValue(key, value)); - } - } - return keyValues; - } +// private List parseKeyValuePairs(String metadata) { +// List keyValues = new ArrayList<>(); +// if(metadata != null) { +// Pattern keyValuePattern = Pattern.compile("([\\w-]+)\\[(.+?)\\]"); +// Matcher matcher = keyValuePattern.matcher(metadata.trim()); +// while (matcher.find()) { +// String key = matcher.group(1); +// String value = matcher.group(2); +// keyValues.add(new KeyValue(key, value)); +// } +// } +// return keyValues; +// } } diff --git a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/util/JPAUtil.java b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/util/JPAUtil.java index 7f87729..5e71c48 100644 --- a/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/util/JPAUtil.java +++ b/PayaraStarterGenerator/src/main/java/fish/payara/starter/application/util/JPAUtil.java @@ -15,6 +15,7 @@ */ package fish.payara.starter.application.util; +import java.util.HashSet; import java.util.Set; /** @@ -23,12 +24,155 @@ */ public class JPAUtil { - public static final Set SQL_RESERVED_KEYWORDS = Set.of( - "ADD", "ALL", "ALTER", "AND", "ANY", "AS", "ASC", "BACKUP", "BETWEEN", "BY", "CASE", "CHECK", - "COLUMN", "CONSTRAINT", "CREATE", "DATABASE", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP", - "EXEC", "EXISTS", "FOREIGN", "FROM", "FULL", "GROUP", "HAVING", "IN", "INDEX", "INNER", "INSERT", - "INTO", "IS", "JOIN", "LEFT", "LIKE", "LIMIT", "NOT", "NULL", "ON", "OR", "ORDER", "OUTER", - "PRIMARY", "PROCEDURE", "RIGHT", "ROWNUM", "SELECT", "SET", "TABLE", "TOP", "TRUNCATE", "UNION", - "UNIQUE", "UPDATE", "VALUES", "VIEW", "WHERE" + private static final Set JPA_RESERVED_KEYWORDS = Set.of( + "ENTITY", "TRANSACTION", "PERSISTENCE", "QUERY", "TABLE", "COLUMN", "ID", "VERSION", + "JOINCOLUMN", "MAPPEDBY", "ONETOMANY", "MANYTOONE", "ONETOONE", "MANYTOMANY", "CASCADETYPE", + "FETCHTYPE", "ENTITYMANAGER", "NAMEDQUERY", "QUERYHINT", "EMBEDDABLE", "EMBEDDED", + "TEMPORAL", "TEMPORALTYPE", "ENUMTYPE", "INHERITANCE", "DISCRIMINATORCOLUMN", "INHERITANCETYPE", + "PREPERSIST", "POSTPERSIST", "PREUPDATE", "POSTUPDATE", "PREREMOVE", "POSTREMOVE", + "PRELOAD", "POSTLOAD", "ACCESS", "ACCESSTYPE", "ATTRIBUTEOVERRIDE", "ASSOCIATIONOVERRIDE", + "ELEMENTCOLLECTION", "ENUMERATED", "SEQUENCEGENERATOR", "TABLEGENERATOR", "GENERATEDVALUE", + "GENERATIONTYPE", "IDCLASS", "JOINTABLE" ); + + private static final Set SQL_RESERVED_KEYWORDS = Set.of( + "ADD", "ALL", "ALTER", "AND", "ANY", "AS", "ASC", "BACKUP", "BEGIN", "BETWEEN", "BIGINT", "BINARY", + "BIT", "BY", "CASCADE", "CASE", "CHAR", "CHECK", "COLUMN", "CONSTRAINT", "CREATE", "CROSS", + "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "DATABASE", "DEC", "DECIMAL", + "DEFAULT", "DELETE", "DESC", "DISTINCT", "DOUBLE", "DROP", "ELSE", "END", "ESCAPE", "EXISTS", + "FALSE", "FETCH", "FLOAT", "FOR", "FOREIGN", "FROM", "FULL", "GROUP", "HAVING", "IF", "IN", "INDEX", + "INNER", "INSERT", "INT", "INTEGER", "INTERSECT", "INTO", "IS", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", + "NOT", "NULL", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PRIMARY", "PROCEDURE", "REAL", "REFERENCES", + "RIGHT", "ROLLBACK", "SELECT", "SET", "SMALLINT", "TABLE", "THEN", "TIME", "TIMESTAMP", "TO", + "TRANSACTION", "TRUE", "UNION", "UNIQUE", "UPDATE", "USING", "VALUES", "VARCHAR", "VIEW", "WHERE", + "WITH", "WORK", "YEAR" + ); + + private static final Set MYSQL_RESERVED_KEYWORDS = Set.of( + "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE", + "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOL", "BOOLEAN", "BY", "CALL", "CASCADE", + "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", "CONDITION", + "CONNECTION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", + "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATABASE", "DATABASES", + "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", + "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", + "DISTINCT", "DISTINCTROW", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "EMPTY", + "ENCLOSED", "ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH", "FLOAT", + "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", "GRANT", "GROUP", "HAVING", + "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", + "IN", "INOUT", "INNER", "INPLACE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", + "INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE", "JOIN", "KEY", "KEYS", + "KILL", "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", + "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", + "MATCH", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", + "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", "NO_WRITE_TO_BINLOG", + "NULL", "ON", "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", + "OUTFILE", "PARTITION", "PRIMARY", "PROCEDURE", "PURGE", "RANGE", "READ", + "READS", "REAL", "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPLACE", + "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "ROW", "ROWS", "SELECT", + "SET", "SHOW", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", + "SQLSTATE", "SQLWARNING", "STRAIGHT_JOIN", "TABLE", "TABLES", "TABLESPACE", + "THEN", "TIME", "TIMESTAMP", "TIMESTAMPDIFF", "TIMESTAMPADD", "TO", + "TRIGGER", "TRUE", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", + "USAGE", "USE", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", + "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "WHEN", + "WHENEVER", "WHERE", "WHILE", "WITH", "WRITE", "X509", "XML", "YEAR_MONTH" + ); + + private static final Set POSTGRESQL_RESERVED_KEYWORDS = Set.of( + "ABORT", "ABS", "ALIAS", "ALL", "ALTER", "AND", "ANY", "ARRAY", "AS", "ASC", + "ASSERTION", "AT", "AUTHORIZATION", "BACKWARD", "BEFORE", "BEGIN", "BETWEEN", + "BOTH", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "CONCURRENTLY", "CONSTRAINT", + "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME", + "CURRENT_USER", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DISTINCT", + "DO", "ELSE", "END", "EXCEPT", "EXCLUDE", "EXISTS", "EXTRACT", "FALSE", + "FETCH", "FOR", "FOREIGN", "FROM", "FULL", "GRANT", "GROUP", "HAVING", + "ILIKE", "IN", "INHERIT", "INOUT", "INSERT", "INT", "INTERSECT", "INTO", + "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "LISTEN", "LOAD", + "LOCAL", "LOCK", "MINUTE", "MONTH", "NAMES", "NOT", "NOTNULL", "NULL", + "OFFSET", "ON", "ONLY", "OPEN", "OR", "ORDER", "OUTER", "OVER", "PARTITION", + "PLACING", "PRIMARY", "RETURN", "REVOKE", "RIGHT", "ROLLBACK", "ROW", + "ROWS", "SELECT", "SESSION_USER", "SET", "TABLE", "TABLES", "THEN", + "TO", "UNION", "UNIQUE", "UPDATE", "USER", "USING", "VERBOSE", + "VIEW", "WHEN", "WHERE", "WITH" + ); + + private static final Set SQLSERVER_RESERVED_KEYWORDS = Set.of( + "ADD", "ALL", "ALTER", "AND", "ANY", "AS", "ASC", "AUTHORIZATION", "BACKUP", + "BEGIN", "BETWEEN", "BINARY", "BIT", "BREAK", "BROWSE", "BULK", "BY", + "CASCADE", "CASE", "CHECK", "CHECKPOINT", "CLOSE", "CLUSTERED", "COALESCE", + "COLUMN", "COMMIT", "COMPUTE", "CONSTRAINT", "CONTAINS", "CONVERT", + "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", "CURRENT_TIMESTAMP", + "CURSOR", "DATABASE", "DBCC", "DEALLOCATE", "DECLARE", "DEFAULT", + "DELETE", "DENY", "DESC", "DISTINCT", "DOUBLE", "DROP", "DUMP", "ELSE", + "END", "ERRLVL", "ESCAPE", "EXISTS", "EXIT", "EXTERNAL", "FETCH", + "FILE", "FILLFACTOR", "FOR", "FOREIGN", "FROM", "FULL", "GOTO", + "GRANT", "GROUP", "HAVING", "HOLDLOCK", "IDENTITY", "IDENTITYCOL", + "IF", "IN", "INDEX", "INNER", "INSERT", "INTERSECT", "INTO", + "IS", "JOIN", "KEY", "KILL", "LEFT", "LIKE", "LINENO", "MERGE", + "NATIONAL", "NOCHECK", "NONCLUSTERED", "NOT", "NULL", "OFF", + "OFFSETS", "ON", "OPEN", "OPTION", "OR", "ORDER", "OUTER", + "OVER", "PERCENT", "PIVOT", "PLAN", "PRIMARY", "PRINT", + "PROC", "PROCEDURE", "RAISERROR", "READ", "READTEXT", + "RECONFIGURE", "REFERENCES", "REPEATABLE", "RESTRICT", + "RETURN", "REVERT", "REVOKE", "RIGHT", "ROLLBACK", + "ROWCOUNT", "ROWS", "SAVE", "SCHEMA", "SELECT", "SESSION_USER", + "SET", "SHUTDOWN", "SOME", "TABLE", "TABLESAMPLE", "THEN", + "TO", "TOP", "TRAN", "TRANSACTION", "TRIGGER", "TRUNCATE", + "UNION", "UNIQUE", "UPDATE", "USE", "USER", "VALUES", + "VIEW", "WAITFOR", "WHEN", "WHERE", "WHILE", "WITH", + "WITHIN", "WORK", "WRITE", "XOR" + ); + + private static final Set ORACLE_RESERVED_KEYWORDS = Set.of( + "ACCESS", "ADD", "ALL", "ALTER", "AND", "ANY", "AS", "ASC", "AUDIT", + "BETWEEN", "BFILE", "BINARY_FLOAT", "BINARY_DOUBLE", "BLOB", + "BOOLEAN", "BY", "CHAR", "CHARACTER", "CHECK", "CLUSTER", + "COLUMN", "COMMIT", "CONNECT", "CREATE", "CURRENT", + "DATE", "DAY", "DELETE", "DESC", "DISTINCT", "DOUBLE", + "DROP", "ELSE", "END", "EXISTS", "EXTRACT", "FALSE", + "FLOAT", "FOR", "FROM", "FULL", "GRANT", "GROUP", + "HAVING", "IDENTIFIED", "IF", "IN", "INDEX", "INSERT", + "INTO", "IS", "JOIN", "LEVEL", "LIKE", "LONG", + "MAX", "MIN", "MONTH", "NATURAL", "NOT", "NULL", + "NUMBER", "OF", "ON", "OPTION", "OR", "ORDER", + "PLS_INTEGER", "PRIMARY", "PROCEDURE", "PUBLIC", "RAW", + "RENAME", "REPLACE", "SELECT", "SESSION", "SET", + "TABLE", "THEN", "TO", "TRIGGER", "TRUE", "UNION", + "UPDATE", "USER", "VALIDATE", "VIEW", "WHEN", "WHERE", + "WITH" + ); + + private static final Set SQLITE_RESERVED_KEYWORDS = Set.of( + "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", + "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", + "CASCADE", "CASE", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", + "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", + "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DELETE", + "DESC", "DETACH", "DISTINCT", "DO", "DROP", "ELSE", "END", + "EXCEPT", "EXCLUDE", "EXISTS", "EXPR", "FAIL", "FOR", "FOREIGN", + "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", + "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", + "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", + "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", + "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", + "ORDER", "PRAGMA", "RAISE", "RECURSIVE", "REFERENCES", + "REGEXP", "REINDEX", "REPLACE", "RESTRICT", "ROLLBACK", + "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET", "TABLE", + "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", + "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", + "VALUES", "VIEW", "WHEN", "WHERE", "WITH", "WITHOUT" + ); + + public static final Set ALL_RESERVED_KEYWORDS = new HashSet<>(); + + static { + ALL_RESERVED_KEYWORDS.addAll(JPA_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(SQL_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(MYSQL_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(POSTGRESQL_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(SQLSERVER_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(ORACLE_RESERVED_KEYWORDS); + ALL_RESERVED_KEYWORDS.addAll(SQLITE_RESERVED_KEYWORDS); + } } diff --git a/PayaraStarterGenerator/src/main/resources/template/html/about-us.html.ftl b/PayaraStarterGenerator/src/main/resources/template/html/about-us.html.ftl index 9d27bd9..294c309 100644 --- a/PayaraStarterGenerator/src/main/resources/template/html/about-us.html.ftl +++ b/PayaraStarterGenerator/src/main/resources/template/html/about-us.html.ftl @@ -15,5 +15,5 @@ -->

About Us

-

${model.getAboutUsDescription()}

+

${model.getAboutUsPageDescription()}

\ No newline at end of file diff --git a/PayaraStarterGenerator/src/main/resources/template/html/entity.html.ftl b/PayaraStarterGenerator/src/main/resources/template/html/entity.html.ftl index e50d010..5feaf6c 100644 --- a/PayaraStarterGenerator/src/main/resources/template/html/entity.html.ftl +++ b/PayaraStarterGenerator/src/main/resources/template/html/entity.html.ftl @@ -143,12 +143,12 @@ // Open modal for new ${entityNameLowerCase} $('#new${entityNameTitleCase}Button').on('click', function () { $('#${entityNameLowerCase}Form')[0].reset(); - $('#${entity.getPrimaryKeyName()}').prop('disabled', false); + $('#${entity.getPrimaryKeyName()}').closest('.form-group').hide(); $('#${entityNameLowerCase}ModalLabel').text('Add ${entity.getTitle()}'); $('#saveButton').show(); $('#updateButton').hide(); $('#${entityNameLowerCase}Modal').modal('show'); -<#list entity.attributes as attribute> + <#list entity.attributes as attribute> <#if attribute.relation??> <#if attribute.multi> <#else> @@ -171,7 +171,9 @@ }, <#else> - ${attribute.name}: $('#${attribute.name}').val(), + <#if entity.getPrimaryKeyName() != attribute.name> + ${attribute.name}: $('#${attribute.name}').val(), + }; @@ -224,8 +226,6 @@ method: 'GET', contentType: 'application/json', success: function (${entityNameLowerCase}) { - <#list entity.attributes as attribute> - <#list entity.attributes as attribute> <#if attribute.relation??> <#if attribute.multi> @@ -233,7 +233,7 @@ $('#${attribute.name}Select').val(${entityNameLowerCase}.${attribute.name}.${attribute.relation.getPrimaryKeyName()}); <#else> - $('#${attribute.name}').val(${entityNameLowerCase}.${attribute.name})<#if attribute.isPrimaryKey()>.prop('disabled', true); + $('#${attribute.name}').val(${entityNameLowerCase}.${attribute.name})<#if attribute.isPrimaryKey()>.prop('disabled', true).closest('.form-group').show(); $('#${entityNameLowerCase}ModalLabel').text('Edit ${entity.getTitle()}'); diff --git a/PayaraStarterGenerator/src/main/resources/template/html/home.html.ftl b/PayaraStarterGenerator/src/main/resources/template/html/home.html.ftl index d3db51e..a233554 100644 --- a/PayaraStarterGenerator/src/main/resources/template/html/home.html.ftl +++ b/PayaraStarterGenerator/src/main/resources/template/html/home.html.ftl @@ -15,5 +15,5 @@ -->

${model.getLongTitle()}

-

${model.getDescription()}

+

${model.getHomePageDescription()}

diff --git a/starter-ui/pom.xml b/starter-ui/pom.xml index ad4a996..31052f7 100644 --- a/starter-ui/pom.xml +++ b/starter-ui/pom.xml @@ -69,7 +69,7 @@ org.apache.maven.resolver maven-resolver-connector-basic - 2.0.1 + 1.9.10 org.apache.maven.resolver @@ -190,6 +190,13 @@ fish.payara.maven.plugins payara-micro-maven-plugin 2.4 + + + + + fish.payara.maven.plugins diff --git a/starter-ui/src/main/java/fish/payara/starter/ERDiagramChat.java b/starter-ui/src/main/java/fish/payara/starter/ERDiagramChat.java deleted file mode 100644 index b1e1fa5..0000000 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramChat.java +++ /dev/null @@ -1,226 +0,0 @@ -package fish.payara.starter; - -import dev.langchain4j.service.SystemMessage; - -public interface ERDiagramChat { - - @SystemMessage(""" -You are an API server that responds in a Mermaid erDiagram format. -Don't say anything else. Respond only with the erDiagram. - -Entity Relationship Diagrams -An entity–relationship model (or ER model) describes interrelated things of interest in a specific domain of knowledge. A basic ER model is composed of entity types (which classify the things of interest) and specifies relationships that can exist between entities (instances of those entity types). -Comments can be added using %%{ comments here }%% -So after Defining relation add comment with variable name for JPA Entities of that relation seprated by comma. -1- Add comment after erDiagram ends with application's bootstrap icon,title, website home-page description,about-us page description(5-10 lines), Top bar menu options(e.g Home, Product, Serices, contact us, about us, ) to show on final website of application. - 1(a) -Add Top bar menu options only to global comment after erDiagram ends not After Defining Entity - 1(b) -Add website home-page description,about-us page description(5-10 lines) only to global comment after erDiagram ends not After Defining Entity -2- After Defining Entity - Each must have one primary key PK attribute - Add comment relate to bootstrap icon (e.g people, person, cart, cash, house), entity title and entity description to show on final website of application. -3- After Defining Attribute: -3(a) Add htmllabel if variable name is not descriptive itself. Add it only if attribute is not slef descriptive. - Valid Example: string custNumber %%{ htmllabel[Customer Number] }%% - Valid Example: date dob %%{ htmllabel[Date of Birth] }%%) - HTML label should not be the same as the attribute name by spliting with space; it should provide more meaning and description. - Invalid Example: date appointmentDate %%{ htmllabel[Appointment Date] }%% - Not required for appointmentDate as it is self descriptive - Invalid Example: string phoneNumber %%{ htmllabel[Phone Number] }%% - Not required for phoneNumber as it is self descriptive - Invalid Example: string doctorId PK %%{ htmllabel[Doctor ID] }%% - Not required for doctorId as it is self descriptive -3(b) Each entity must have only one label or name field. Add a 'display[true]' property to the comment that will serve as the label for the entity. - Do not add the 'display[true]' property to more than one attribute. - Look for attributes that start or end with 'name' and add the 'display[true]' property to one of them. - If no attribute is found that starts or ends with 'name', then decide which other attribute can represent the label. -3(c) Add 'required[true]' property if attribute is required for entity. - Do not add to more than 50% of attribute in entity. - Do not add to Primary key PK. - Invalid Example: string doctorId PK %%{ required[true] }%% -3(d) Add 'tooltip[description of attribute]' to attribute to show it on html input UI which will help end user. - tooltip must be added to all attributes except FK or PK primary key. - -Example: -CUSTOMER { %%{ icon[people],title[abc],description[xyz] }%% - string name %%{ display[true],required[true] }%% - long custNumber PK %%{ htmllabel[Customer Number],required[true] }%% -} -4- After Defining relation add comment with variable name for JPA Entities of that relation. -Example: -CUSTOMER ||--o{ ORDER : places %%{ CUSTOMER[orders],ORDER[customer] }%% -In this Example, CUSTOMER and ORDER entity have relatationship where as comment defines variable name in JPA Entity classes, suggest varaible name related to label which is places in this example. - -Note that practitioners of ER modelling almost always refer to entity types simply as entities. For example the CUSTOMER entity type would be referred to simply as the CUSTOMER entity. This is so common it would be inadvisable to do anything else, but technically an entity is an abstract instance of an entity type, and this is what an ER diagram shows - abstract instances, and the relationships between them. This is why entities are always named using singular nouns. - -Entity names are often capitalised, although there is no accepted standard on this, and it is not required in Mermaid. - -Relationships between entities are represented by lines with end markers representing cardinality. Mermaid uses the most popular crow's foot notation. The crow's foot intuitively conveys the possibility of many instances of the entity that it connects to. - -ER diagrams can be used for various purposes, ranging from abstract logical models devoid of any implementation details, through to physical models of relational database tables. It can be useful to include attribute definitions on ER diagrams to aid comprehension of the purpose and meaning of entities. These do not necessarily need to be exhaustive; often a small subset of attributes is enough. Mermaid allows them to be defined in terms of their type and name. - -Code: -erDiagram - CUSTOMER ||--o{ ORDER : places %%{ CUSTOMER[orders],ORDER[customer] }%% - CUSTOMER { %%{ icon[people],title[abc],description[xyz] }%% - long custNumber PK %%{ htmllabel[Customer Number],required[true] }%% - string name %%{ display[true],required[true],tooltip[description of attribute] }%% - string sector %%{ tooltip[description of attribute] }%% - } - ORDER ||--|{ LINE-ITEM : contains %%{ ORDER[items],LINE-ITEM[order] }%% - ORDER { %%{ icon[cart],title[abc],description[xyz] }%% - int orderNumber PK %%{ display[true] }%% - string deliveryAddress %%{ tooltip[description of attribute] }%% - } - LINE-ITEM { %%{ icon[folder],title[abc],description[xyz] }%% - string productCode PK - string productName %%{ display[true],required[true],tooltip[description of attribute] }%% - int quantity %%{ tooltip[description of attribute] }%% - float pricePerUnit %%{ tooltip[description of attribute] }%% - } -%%{ icon[person],title[abc],home-page-description[xyz],about-us-page-description[xyz],menu[home, services, about us, contact us] }%% - - -When including attributes on ER diagrams, you must decide whether to include foreign keys as attributes. This probably depends on how closely you are trying to represent relational table structures. If your diagram is a logical model which is not meant to imply a relational implementation, then it is better to leave these out because the associative relationships already convey the way that entities are associated. For example, a JSON data structure can implement a one-to-many relationship without the need for foreign key properties, using arrays. Similarly an object-oriented programming language may use pointers or references to collections. Even for models that are intended for relational implementation, you might decide that inclusion of foreign key attributes duplicates information already portrayed by the relationships, and does not add meaning to entities. Ultimately, it's your choice. - - -Syntax -Entities and Relationships -Mermaid syntax for ER diagrams is compatible with PlantUML, with an extension to label the relationship. Each statement consists of the following parts: - - [ : ] -Where: - -first-entity is the name of an entity. Names must begin with an alphabetic character or an underscore (from v10.5.0+), and may also contain digits and hyphens. -relationship describes the way that both entities inter-relate. See below. -second-entity is the name of the other entity. -relationship-label describes the relationship from the perspective of the first entity. -Ensure the relationship label is always a single word: - - [ : ] - -If you want the relationship label to be more than one word, you must use double quotes around the phrase -If you don't want a label at all on a relationship, you must use an empty double-quoted string - -For example: - - PROPERTY ||--|{ ROOM : contains -This statement can be read as a property contains one or more rooms, and a room is part of one and only one property. You can see that the label here is from the first entity's perspective: a property contains a room, but a room does not contain a property. When considered from the perspective of the second entity, the equivalent label is usually very easy to infer. (Some ER diagrams label relationships from both perspectives, but this is not supported here, and is usually superfluous). - -Only the first-entity part of a statement is mandatory. This makes it possible to show an entity with no relationships, which can be useful during iterative construction of diagrams. If any other parts of a statement are specified, then all parts are mandatory. - -Relationship Syntax -The relationship part of each statement can be broken down into three sub-components: - -the cardinality of the first entity with respect to the second, -whether the relationship confers identity on a 'child' entity -the cardinality of the second entity with respect to the first -Cardinality is a property that describes how many elements of another entity can be related to the entity in question. In the above example a PROPERTY can have one or more ROOM instances associated to it, whereas a ROOM can only be associated with one PROPERTY. In each cardinality marker there are two characters. The outermost character represents a maximum value, and the innermost character represents a minimum value. The table below summarises possible cardinalities. Ensure all relationships use valid cardinality markers: - -Value (left) Value (right) Meaning -|o o| Zero or one -|| || Exactly one -}o o{ Zero or more (no upper limit) -}| |{ One or more (no upper limit) - -Valid Cardinality Symbols for left: -Each cardinality symbol is composed of two characters that define the minimum and maximum relationships: - -|o: Zero or one -||: Exactly one -}o: Zero or more -}|: One or more - -Valid Cardinality Symbols for right: -Each cardinality symbol is composed of two characters that define the minimum and maximum relationships: - -o|: Zero or one -||: Exactly one -o{: Zero or more -|{: One or more - -Identification -Relationships may be classified as either identifying or non-identifying and these are rendered with either solid or dashed lines respectively. This is relevant when one of the entities in question can not have independent existence without the other. For example a firm that insures people to drive cars might need to store data on NAMED-DRIVERs. In modelling this we might start out by observing that a CAR can be driven by many PERSON instances, and a PERSON can drive many CARs - both entities can exist without the other, so this is a non-identifying relationship that we might specify in Mermaid as: PERSON }|..|{ CAR : "driver". Note the two dots in the middle of the relationship that will result in a dashed line being drawn between the two entities. But when this many-to-many relationship is resolved into two one-to-many relationships, we observe that a NAMED-DRIVER cannot exist without both a PERSON and a CAR - the relationships become identifying and would be specified using hyphens, which translate to a solid line: - -Aliases - -Value Alias for -to identifying -optionally to non-identifying -Code: -erDiagram - CAR ||--o{ NAMED-DRIVER : allows - PERSON ||--o{ NAMED-DRIVER : is - - -Attributes -Attributes can be defined for entities by specifying the entity name followed by a block containing multiple type name pairs, where a block is delimited by an opening { and a closing }. The attributes are rendered inside the entity boxes. For example: - -Code: -erDiagram - CAR ||--o{ NAMED-DRIVER : allows %%{ CAR[drivers],Driver[vehicle] }%% - CAR { %%{ icon[car-front],title[abc],description[xyz] }%% - string registrationNumber %%{ display[true] }%% - string make %%{ tooltip[description of attribute] }%% - string model %%{ tooltip[description of attribute] }%% - } - PERSON ||--o{ NAMED-DRIVER : is %%{ Person[drivers],Driver[owner] }%% - PERSON { %%{ icon[people],title[abc],description[xyz] }%% - string firstName %%{ display[true] }%% - string lastName %%{ tooltip[description of attribute] }%% - int age %%{ tooltip[description of attribute] }%% - } -%%{ icon[pin-map],title[abc],home-page-description[xyz],about-us-page-description[xyz],menu[home, services, about us, contact us] }%% - - -The type values must begin with an alphabetic character and may contain digits, hyphens, underscores, parentheses and square brackets. The name values follow a similar format to type, but may start with an asterisk as another option to indicate an attribute is a primary key. Other than that, there are no restrictions, and there is no implicit set of valid data types. - -Entity Name Aliases (v10.5.0+) -An alias can be added to an entity using square brackets. If provided, the alias will be showed in the diagram instead of the entity name. - -Code: -erDiagram - p[Person] { - string firstName - string lastName - } - a["Customer Account"] { - string email - } - p ||--o| a : has - - -Attribute Keys and Comments -Attributes may also have a key or comment defined. Keys can be PK, FK or UK, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., PK, FK).. A comment is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them. - -Code: -erDiagram - CAR ||--o{ NAMED-DRIVER : allows %%{ CAR[drivers],Driver[vehicle] }%% - CAR { %%{ icon[car-front],title[abc],description[xyz] }%% - string registrationNumber %%{ display[true] }%% - string make - string model - string[] parts - } - PERSON ||--o{ NAMED-DRIVER : is %%{ Person[drivers],Driver[owner] }%% - PERSON { %%{ icon[people],title[abc],description[xyz] }%% - string driversLicense PK "The license #" - string(99) firstName "Only 99 characters are allowed" %%{ display[true] }%% - string lastName - string phone UK - int age - } - NAMED-DRIVER { %%{ icon[person],title[abc],description[xyz] }%% - string carRegistrationNumber PK, FK - string driverLicence FK %%{ display[true] }%% - } -%%{ icon[pin-map],title[abc],home-page-description[xyz],about-us-page-description[xyz],menu[home, services, about us, contact us] }%% - - -The user will provide you with a prompt of er diagram context they have. Based on the given prompt containing application usecase, -suggest erDiagram. -Whatever the prompt is, look for erDiagram and make your suggestions based on that and any other context you can understand from the prompt. - -Remember you are an API server, you only respond in erDiagram. - - -Don't add anything else in the end after you respond with the erDiagram. - """) - String generateERDiagram(String userMessage); -} diff --git a/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java b/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java index 9c0145a..9941672 100644 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java +++ b/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java @@ -244,4 +244,12 @@ Attributes may also have a key or comment defined. Keys can be PK, FK or UK, for """) String shrinkERDiagram(@UserMessage String erDiagram, @UserMessage String diagramName); + @SystemMessage(""" + You are an API server that modifies the ER diagram as per the user's request. + Ensure that the existing diagram is updated instead of generating a new version. + Please update the diagram strictly in Mermaid 'erDiagram' format. + Respond only in Mermaid erDiagram format and the response adheres to valid Mermaid syntax. + """) + String updateERDiagram(@UserMessage String erDiagram, @UserMessage String userRequestToModifyTheDiagram); + } diff --git a/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java b/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java index 63970cb..77a7fcf 100644 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java +++ b/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java @@ -58,5 +58,17 @@ public String shrinkERDiagramSize(@QueryParam("name") String diagramName, String throw new BadRequestException("Please provide the diagram"); } } + + @POST + @Consumes(MediaType.TEXT_PLAIN) + @Path("/update") + public String updateERDiagramSize(@QueryParam("request") String userRequest, String erDiagram) { + if (erDiagram != null && !erDiagram.isBlank() + && userRequest != null && !userRequest.isBlank()) { + return langChainChatService.updateERDiagramSuggestion(userRequest, erDiagram); + } else { + throw new BadRequestException("Please provide the diagram"); + } + } } diff --git a/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java b/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java index aa8606c..aa48bbe 100644 --- a/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java +++ b/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java @@ -5,6 +5,8 @@ import jakarta.inject.Inject; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.service.AiServices; +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; @ApplicationScoped public class LangChainChatService { @@ -31,4 +33,111 @@ public String shrinkERDiagramSuggestion(String diagramName, String erDiagram) { return erDiagramChat.shrinkERDiagram(erDiagram, diagramName); } + public String updateERDiagramSuggestion(String userRequest, String erDiagram) { + return model.generate(String.format(""" + You are an API server that modifies the ER diagram as per the user's request '%s'. + Ensure that the existing diagram is updated instead of generating a new version. + Please update the diagram strictly in Mermaid 'erDiagram' format. + Respond only in Mermaid erDiagram format and the response adheres to valid Mermaid syntax.\n\n + Existing ER Diagram:\n %s + """, userRequest, erDiagram)); + } + + public String addFronEndDetailsToERDiagram(String erDiagram) { + + return model.generate(""" + You are an API server that modifies the ER diagram json. + 1- Add application's bootstrap icon,title, longTitle, website homePageDescription,aboutUsPageDescription(5-10 lines), Top bar menu options(e.g Home, Product, Serices, contact us, about us, ) to show on final website of application to root of json. + 1(a) -Add Top bar menu options only to root of json + 1(b) -Add website home-page description,about-us page description(5-10 lines) only to root of json + 2- After following property to each Entity + Each must have one primary key PK attribute + Add property related to bootstrap icon (e.g people, person, cart, cash, house), entity title and entity description to show on final website of application. + 3- After following property to Attribute: + 3(a) Add htmllabel if variable name is not descriptive itself. Add it only if attribute is not itself descriptive. + Valid Example: For string custNumber add property htmllabel=Customer Number + Valid Example: For date dob add property htmllabel=Date of Birth + HTML label should not be the same as the attribute name by spliting with space; it should provide more meaning and description. + Invalid Example: For date appointmentDate dont' add htmllabel=Appointment Date, Not required for appointmentDate as it is self descriptive + 3(b) Each entity must have only one label or name attribute. Add a 'display=true' property to the attribute that will serve as the label for the entity. + Do not add the 'display=true' property to more than one attribute. + Look for attributes that start or end with 'name' and add the 'display=true' property to one of them. + If no attribute is found that starts or ends with 'name', then decide which other attribute can represent the label. + 3(c) Add 'required=true' property if attribute is required for entity. + Do not add to more than 50% of attribute in entity. + Do not add to Primary key PK. + 3(d) Add 'tooltip=description of attribute' to attribute to show it on html input UI which will help end user. + tooltip must be added to all attributes except FK or PK primary key. + 4- Add variable name to relation of JPA Entities with property relationshipVarNameInFirstEntity and relationshipVarNameInSecondEntity. + + Sample JSON Structure: + { + "icon": "person", + "title": "My Application", + "longTitle": "My Jakarta EE Application", + "homePageDescription": "", + "aboutUsPageDescription": "", + "topBarMenuOptions": [], + "entities": [ + { + "attributes": [ + { + "multi": false, + "name": "userId", + "primaryKey": true, + "type": "String" + }, + { + "multi": false, + "name": "username", + "primaryKey": false, + "type": "String", + "display": true, + "tooltip": "The name used by the user to log in" + } + ], + "name": "User", + "icon": "people", + "title": "User", + "description": "Represents the users of the application." + }, + { + "attributes": [ + { + "multi": false, + "name": "commentId", + "primaryKey": true, + "type": "String" + }, + { + "multi": false, + "name": "content", + "primaryKey": false, + "type": "String", + "tooltip": "The content of the comment" + } + ], + "name": "Comment", + "icon": "chat", + "title": "Comment", + "description": "Represents comments made on streams." + } + ], + "relationships": [ + { + "firstEntity": "User", + "relationshipLabel": "has", + "relationshipVarNameInFirstEntity": "comments", + "relationshipVarNameInSecondEntity": "user", + "relationshipType": "||--o{", + "secondEntity": "Comment" + } + ] + }\n\n\n\n + + Return only the modified json and do not add additional text.\n\n + Existing ER Diagram Json which should be updated:\n + """ + erDiagram); + } + } diff --git a/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationConfiguration.java b/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationConfiguration.java index 2397d56..b2a54c3 100644 --- a/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationConfiguration.java +++ b/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationConfiguration.java @@ -73,6 +73,7 @@ public class ApplicationConfiguration { public static final String DEPLOY_WAR = "deployWar"; public static final String CONTEXT_ROOT = "contextRoot"; public static final String AUTH = "auth"; + public static final String ENTITIES = "entities"; public static final String PAYARA_VERSION_6_2023_11 = "6.2023.11"; diff --git a/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationGenerator.java b/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationGenerator.java index 1dcc8f0..2344355 100644 --- a/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationGenerator.java +++ b/starter-ui/src/main/java/fish/payara/starter/resources/ApplicationGenerator.java @@ -38,6 +38,7 @@ */ package fish.payara.starter.resources; +import fish.payara.starter.LangChainChatService; import fish.payara.starter.application.domain.ERModel; import fish.payara.starter.application.generator.CRUDAppGenerator; import fish.payara.starter.application.generator.ERDiagramParser; @@ -64,10 +65,12 @@ import jakarta.enterprise.concurrent.ManagedExecutorDefinition; import jakarta.enterprise.concurrent.ManagedExecutorService; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.nio.file.Files; import java.util.Arrays; import java.util.LinkedList; @@ -79,6 +82,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.maven.cli.MavenCli; +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbException; /** * @@ -102,6 +108,9 @@ public class ApplicationGenerator { @Resource(name = "java:comp/DefaultManagedExecutorService") private ManagedExecutorService executorService; + @Inject + private LangChainChatService langChainChatService; + public Future generate(ApplicationConfiguration appProperties) { System.out.println("executorService " + executorService); return executorService.submit(() -> { @@ -111,15 +120,29 @@ public Future generate(ApplicationConfiguration appProperties) { workingDirectory.deleteOnExit(); LOGGER.log(Level.INFO, "Executing Maven Archetype from working directory: {0}", new Object[]{workingDirectory.getAbsolutePath()}); Properties properties = buildMavenProperties(appProperties); + ERModel erModel = null; + if (appProperties.getErDiagram() != null) { + ERDiagramParser parser = new ERDiagramParser(); + erModel = parser.parse(appProperties.getErDiagram()); + if (erModel != null && !erModel.getEntities().isEmpty()) { + properties.put(ApplicationConfiguration.ENTITIES, appProperties.getAuth()); + } + } invokeMavenArchetype(ARCHETYPE_GROUP_ID, ARCHETYPE_ARTIFACT_ID, ARCHETYPE_VERSION, properties, workingDirectory); LOGGER.info("Creating a compressed application bundle."); applicationDir = new File(workingDirectory, appProperties.getArtifactId()); - if (appProperties.getErDiagram() != null) { - ERDiagramParser parser = new ERDiagramParser(); - ERModel erModel = parser.parse(appProperties.getErDiagram()); - + if (erModel != null && !erModel.getEntities().isEmpty()) { + Jsonb jsonb = JsonbBuilder.create(); + String jsonString = jsonb.toJson(erModel); + String outputJson = langChainChatService.addFronEndDetailsToERDiagram(jsonString); + if (outputJson != null && !outputJson.isEmpty()) { + String updatedJson = outputJson.strip().replaceAll("^```json|```$", ""); + // convert json to ERModel + + erModel = jsonb.fromJson(updatedJson, ERModel.class); + } CRUDAppGenerator generator = new CRUDAppGenerator(erModel, appProperties.getPackageName(), appProperties.getJpaSubpackage(), @@ -132,12 +155,14 @@ public Future generate(ApplicationConfiguration appProperties) { appProperties.isGenerateRepository(), appProperties.isGenerateRest(), appProperties.isGenerateWeb()); - } catch (IOException e) { + } catch (Exception e) { e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Error generating application: {0}", e.getMessage()); } } return zipDirectory(applicationDir, workingDirectory); - } catch (IOException ie) { + } catch (Exception ie) { + ie.printStackTrace(); throw new RuntimeException("Failed to generate application.", ie); } finally { if (applicationDir != null) { diff --git a/starter-ui/src/main/java/fish/payara/starter/resources/LoggerOutputStream.java b/starter-ui/src/main/java/fish/payara/starter/resources/LoggerOutputStream.java new file mode 100644 index 0000000..0147d45 --- /dev/null +++ b/starter-ui/src/main/java/fish/payara/starter/resources/LoggerOutputStream.java @@ -0,0 +1,30 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package fish.payara.starter.resources; + +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LoggerOutputStream extends OutputStream { + private final Logger logger; + private final Level level; + private final StringBuilder buffer = new StringBuilder(); + + public LoggerOutputStream(Logger logger, Level level) { + this.logger = logger; + this.level = level; + } + + @Override + public void write(int b) { + if (b == '\n') { + logger.log(level, buffer.toString()); + buffer.setLength(0); + } else { + buffer.append((char) b); + } + } +} diff --git a/starter-ui/src/main/resources/META-INF/microprofile-config.properties b/starter-ui/src/main/resources/META-INF/microprofile-config.properties index 9f8683b..8fa58f9 100644 --- a/starter-ui/src/main/resources/META-INF/microprofile-config.properties +++ b/starter-ui/src/main/resources/META-INF/microprofile-config.properties @@ -1,11 +1,6 @@ -#vaadin.whitelisted-packages=com.vaadin,org.vaadin,dev.hilla,com.example.application -# OpenAI API key -# Timeout in seconds for requests to OpenAI. Set to zero to disable the timeout. openai.timeout=45 -gpt.model=gpt-3.5-turbo -#gpt.model=gpt-4 +gpt.model=gpt-4o-mini gpt.image.mode=dall-e-3 model.temperature=0.7 cache.enabled=false - diff --git a/starter-ui/src/main/webapp/index.html b/starter-ui/src/main/webapp/index.html index e424ea3..c4988ef 100644 --- a/starter-ui/src/main/webapp/index.html +++ b/starter-ui/src/main/webapp/index.html @@ -537,7 +537,7 @@

Simply select your options, and click ‘Generate’ - and Payara Pl Generate - +

@@ -615,30 +615,59 @@

Simply select your options, and click ‘Generate’ - and Payara Pl