diff --git a/starter-archetype/src/main/resources/META-INF/archetype-post-generate.groovy b/starter-archetype/src/main/resources/META-INF/archetype-post-generate.groovy index 7ff38a8..08f2729 100644 --- a/starter-archetype/src/main/resources/META-INF/archetype-post-generate.groovy +++ b/starter-archetype/src/main/resources/META-INF/archetype-post-generate.groovy @@ -17,6 +17,7 @@ def mpFaultTolerance = request.properties["mpFaultTolerance"].trim() def mpMetrics = request.properties["mpMetrics"].trim() def auth = request.properties["auth"].trim() def erDiagram = request.properties["erDiagram"].trim() +def restSubpackage = request.properties["restSubpackage"].trim() def outputDirectory = new File(request.getOutputDirectory(), request.getArtifactId()) @@ -109,6 +110,10 @@ private generateSource(build, _package, platform, jakartaEEVersion, } def packagePath = _package.replaceAll("\\.", "/") + + File oldFolder = new File(outputDirectory.path + "/src/main/java/" + packagePath + "/resource") + File newFolder = new File(outputDirectory.path + "/src/main/java/" + packagePath + "/" + restSubpackage) + renameFolder(oldFolder, newFolder) if (!auth.equals("formAuthDB")) { FileUtils.forceDelete(new File(outputDirectory.path + "/src/main/java/" + packagePath + "/secured/DatabaseSetup.java")) @@ -137,6 +142,19 @@ private generateSource(build, _package, platform, jakartaEEVersion, } } +private void renameFolder(File oldFolder, File newFolder) { + if (oldFolder.exists() && !newFolder.exists()) { + boolean success = oldFolder.renameTo(newFolder) + if (success) { + println "Folder renamed from ${oldFolder.name} to ${newFolder.name}" + } else { + println "Failed to rename folder ${oldFolder.name}" + } + } else { + println "Folder ${oldFolder.name} does not exist or the new folder ${newFolder.name} already exists." + } +} + private void bindEEPackage(String jakartaEEVersion, String mpConfig, String mpOpenAPI, String mpFaultTolerance, String mpMetrics, String auth, String erDiagram, File outputDirectory) { def eePackage = (jakartaEEVersion == '8') ? 'javax' : 'jakarta' println "Binding EE package: $eePackage" diff --git a/starter-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/starter-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml index ad994cb..268f705 100644 --- a/starter-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml +++ b/starter-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -145,6 +145,9 @@ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + resource + diff --git a/starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/HelloWorldResource.java b/starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/HelloWorldResource.java similarity index 98% rename from starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/HelloWorldResource.java rename to starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/HelloWorldResource.java index 10121ad..b70156b 100644 --- a/starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/HelloWorldResource.java +++ b/starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/HelloWorldResource.java @@ -1,4 +1,4 @@ -package ${package}.hello; +package ${package}.${restSubpackage}; <% if (mpConfig) { %> import ${eePackage}.inject.Inject;<% } %> import ${eePackage}.ws.rs.GET; diff --git a/starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/RestConfiguration.java b/starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/RestConfiguration.java similarity index 86% rename from starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/RestConfiguration.java rename to starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/RestConfiguration.java index 000dbb6..994d749 100644 --- a/starter-archetype/src/main/resources/archetype-resources/src/main/java/hello/RestConfiguration.java +++ b/starter-archetype/src/main/resources/archetype-resources/src/main/java/resource/RestConfiguration.java @@ -1,4 +1,4 @@ -package ${package}.hello; +package ${package}.${restSubpackage}; import ${eePackage}.ws.rs.ApplicationPath; import ${eePackage}.ws.rs.core.Application; diff --git a/starter-generator/src/main/java/fish/payara/starter/application/domain/Attribute.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/Attribute.java index b9249a7..ef550d1 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/domain/Attribute.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/Attribute.java @@ -22,6 +22,7 @@ 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 jakarta.json.bind.annotation.JsonbTypeSerializer; import java.util.ArrayList; import java.util.List; @@ -29,13 +30,14 @@ * * @author Gaurav Gupta */ +@JsonbTypeSerializer(AttributeSerializer.class) public class Attribute { private String name; private String type; private boolean primaryKey; - private boolean required; private boolean multi; + private boolean required; private String tooltip; private Boolean display; private String htmlLabel; diff --git a/starter-generator/src/main/java/fish/payara/starter/application/domain/AttributeSerializer.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/AttributeSerializer.java new file mode 100644 index 0000000..9491790 --- /dev/null +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/AttributeSerializer.java @@ -0,0 +1,64 @@ +/* + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.starter.application.domain; + +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +public class AttributeSerializer implements JsonbSerializer { + + @Override + public void serialize(Attribute attribute, JsonGenerator generator, SerializationContext ctx) { + generator.writeStartObject(); + + // Always write the name and type + generator.write("name", attribute.getName()); + generator.write("type", attribute.getType()); + + if (attribute.isPrimaryKey()) { + generator.write("primaryKey", true); + } + if (attribute.isMulti()) { + generator.write("multi", true); + } + + generator.writeEnd(); + } +} \ No newline at end of file diff --git a/starter-generator/src/main/java/fish/payara/starter/application/domain/Constant.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/Constant.java index bce96aa..46ac1e5 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/domain/Constant.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/Constant.java @@ -1,6 +1,40 @@ /* - * 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 + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. */ package fish.payara.starter.application.domain; @@ -9,11 +43,11 @@ * @author Gaurav Gupta */ public class Constant { - - public static final String icon_default = "circle"; + + 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/starter-generator/src/main/java/fish/payara/starter/application/domain/ERModel.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/ERModel.java index b206c0c..5bf3d43 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/domain/ERModel.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/ERModel.java @@ -27,13 +27,20 @@ import java.util.ArrayList; import java.util.List; import jakarta.json.bind.annotation.JsonbTransient; - +import java.util.LinkedHashSet; +import java.util.Set; public class ERModel { private List entities = new ArrayList<>(); - private List relationships = new ArrayList<>(); - + private Set relationships = new LinkedHashSet<>(); + + private String icon; + private String title; + private String longTitle; + private String homePageDescription; + private String aboutUsPageDescription; + private List topBarMenuOptions = new ArrayList<>(); public ERModel() { } @@ -59,7 +66,7 @@ public List getEntities() { return entities; } - public List getRelationships() { + public Set getRelationships() { return relationships; } @@ -67,19 +74,10 @@ public void setEntities(List entities) { this.entities = entities; } - public void setRelationships(List relationships) { + public void setRelationships(Set relationships) { this.relationships = relationships; } - - 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; diff --git a/starter-generator/src/main/java/fish/payara/starter/application/domain/Entity.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/Entity.java index 214d9de..7f5cb75 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/domain/Entity.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/Entity.java @@ -46,6 +46,18 @@ public String getName() { return name; } + @JsonbTransient + public String getClassName() { + String[] parts = name.split("_"); + StringBuilder result = new StringBuilder(); + for (String part : parts) { + if (!part.isEmpty()) { + result.append(part.substring(0, 1).toUpperCase()).append(part.substring(1).toLowerCase()); + } + } + return result.toString(); + } + @JsonbTransient public String getStartCaseName() { return startCase(name); diff --git a/starter-generator/src/main/java/fish/payara/starter/application/domain/Relationship.java b/starter-generator/src/main/java/fish/payara/starter/application/domain/Relationship.java index 7a0b0a3..1d4148e 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/domain/Relationship.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/domain/Relationship.java @@ -15,7 +15,9 @@ */ package fish.payara.starter.application.domain; +import jakarta.json.bind.annotation.JsonbTransient; import java.util.List; +import java.util.Objects; /** * @@ -48,6 +50,28 @@ public String getSecondEntity() { return secondEntity; } + @JsonbTransient + public String getFirstEntityClass() { + return getClassName(firstEntity); + } + + @JsonbTransient + public String getSecondEntityClass() { + return getClassName(secondEntity); + } + + @JsonbTransient + public String getClassName(String name) { + String[] parts = name.split("_"); + StringBuilder result = new StringBuilder(); + for (String part : parts) { + if (!part.isEmpty()) { + result.append(part.substring(0, 1).toUpperCase()).append(part.substring(1).toLowerCase()); + } + } + return result.toString(); + } + public String getRelationshipType() { return relationshipType; } @@ -88,6 +112,35 @@ public void setRelationshipLabel(String relationshipLabel) { this.relationshipLabel = relationshipLabel; } + @Override + public int hashCode() { + return Objects.hash(firstEntity, secondEntity, relationshipType, relationshipLabel); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Relationship other = (Relationship)obj; + if (!Objects.equals(this.firstEntity, other.firstEntity)) { + return false; + } + if (!Objects.equals(this.secondEntity, other.secondEntity)) { + return false; + } + if (!Objects.equals(this.relationshipType, other.relationshipType)) { + return false; + } + return Objects.equals(this.relationshipLabel, other.relationshipLabel); + } + @Override public String toString() { return "Relationship{" + "firstEntity=" + firstEntity + ", secondEntity=" + secondEntity + ", relationshipType=" + relationshipType + ", relationshipLabel=" + relationshipLabel + '}'; diff --git a/starter-generator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java b/starter-generator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java index 36a8b27..9ef656a 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/generator/CRUDAppGenerator.java @@ -70,59 +70,78 @@ public CRUDAppGenerator(ERModel model, String _package, String domainLayer, Stri } public static void main(String[] args) { - String mermaidCode = "erDiagram\n" + -" USER ||--o{ JOB_APPLICATION : applies %%{ USER[applications],JOB_APPLICATION[user] }%%\n" + -" USER { %%{ icon[person],title[Recruitment Management System],description[A system for managing recruitment processes. Post job openings, receive applications, and manage candidates efficiently.],menu[Home, Jobs, Candidates, About Us, Contact Us] }%%\n" + -" string userId PK %%{ htmllabel[User ID],required[true] }%%\n" + -" string username %%{ display[true],required[true],tooltip[Username for login] }%%\n" + -" string email %%{ tooltip[User's email address] }%%\n" + -" }\n" + -" JOB_APPLICATION ||--|{ JOB : applies_for %%{ JOB_APPLICATION[job],JOB[applications] }%%\n" + -" JOB_APPLICATION { %%{ icon[file-text],title[Job Application],description[Track job applications submitted by candidates.],menu[My Applications, Jobs, About Us, Contact Us] }%%\n" + -" int applicationId PK %%{ display[true] }%%\n" + -" string status %%{ tooltip[Application status] }%%\n" + -" string resume %%{ tooltip[Link to candidate's resume] }%%\n" + -" }\n" + -" JOB { %%{ icon[briefcase],title[Job],description[Manage job openings and applications.],menu[Jobs, Candidates, About Us, Contact Us] }%% \n" + -" int jobId PK \n" + -" string title %%{ display[true],required[true],tooltip[Job title] }%%\n" + -" string description %%{ tooltip[Job description] }%%\n" + -" date startDate %%{ tooltip[Start date of employment] }%%\n" + -" date endDate %%{ tooltip[End date of employment] }%%\n" + -" }\n" + -" CANDIDATE ||--o{ JOB_APPLICATION : submits %%{ CANDIDATE[applications],JOB_APPLICATION[candidate] }%%\n" + -" CANDIDATE { %%{ icon[person],title[Candidate],description[Manage candidate profiles and applications.],menu[Candidates, Jobs, About Us, Contact Us] }%% \n" + -" int candidateId PK \n" + -" string name %%{ display[true],required[true],tooltip[Candidate's name] }%%\n" + -" string email %%{ tooltip[Candidate's email address] }%%\n" + -" string phone %%{ tooltip[Candidate's phone number] }%%\n" + -" }\n" + -" INTERVIEW ||--|{ CANDIDATE : schedules %%{ INTERVIEW[candidate],CANDIDATE[interviews] }%%\n" + -" INTERVIEW { %%{ icon[calendar],title[Interview],description[Schedule and manage interviews with candidates.],menu[Interviews, Candidates, About Us, Contact Us] }%% \n" + -" int interviewId PK \n" + -" date date %%{ display[true],tooltip[Interview date] }%%\n" + -" string location %%{ tooltip[Interview location] }%%\n" + -" }\n" + -" RECRUITER ||--o{ INTERVIEW : schedules %%{ RECRUITER[interviews],INTERVIEW[recruiter] }%%\n" + -" RECRUITER { %%{ icon[person],title[Recruiter],description[Manage recruiter profiles and interview schedules.],menu[Recruiters, Interviews, About Us, Contact Us] }%% \n" + -" int recruiterId PK \n" + -" string name %%{ display[true],required[true],tooltip[Recruiter's name] }%%\n" + -" string department %%{ tooltip[Recruiter's department] }%%\n" + -" }\n" + -" OFFER ||--o{ CANDIDATE : extends %%{ OFFER[candidate],CANDIDATE[offer] }%%\n" + -" OFFER { %%{ icon[document],title[Job Offer],description[Create and manage job offers extended to candidates.],menu[Offers, Candidates, About Us, Contact Us] }%% \n" + -" int offerId PK \n" + -" string status %%{ tooltip[Offer status] }%%\n" + -" float salary %%{ tooltip[Offered salary] }%%\n" + -" }\n" + -" FEEDBACK ||--|{ INTERVIEW : provides %%{ FEEDBACK[interview],INTERVIEW[feedback] }%%\n" + -" FEEDBACK { %%{ icon[comment],title[Interview Feedback],description[Provide feedback on candidate interviews.],menu[Feedback, Interviews, About Us, Contact Us] }%% \n" + -" int feedbackId PK \n" + -" string comments %%{ display[true],tooltip[Interviewer's comments] }%%\n" + -" int rating %%{ tooltip[Interview rating] }%%\n" + -" }\n" + -"%%{ icon[briefcase],title[Recruitment Management System],home-page-description[A system for managing recruitment processes. Post job openings, receive applications, and manage candidates efficiently.],about-us-page-description[Explore our recruitment management system and streamline your hiring process. Connect with talented candidates and make informed decisions.],menu[Home, Jobs, Candidates, About Us, Contact Us] }%% \n" + -""; + String mermaidCode = """ +erDiagram + STUDENT ||--o{ ENROLLMENT : enrolls + STUDENT { + string studentID PK + string name + string address + int age + } + ENROLLMENT ||--|{ COURSE : contains + ENROLLMENT { + int enrollmentID PK + string semester + } + COURSE { + string courseCode PK + string courseName + int credits + } + TEACHER ||--o{ COURSE : teaches + TEACHER { + string teacherID PK + string name + string specialization + } + CLASSROOM ||--o{ COURSE : hosts + CLASSROOM { + string classroomID PK + string building + int capacity + } + STUDENT ||--o{ ATTENDANCE : records + ATTENDANCE { + int attendanceID PK + date date + boolean present + } + COURSE ||--o{ ASSIGNMENT : includes + ASSIGNMENT { + int assignmentID PK + string title + date dueDate + int maxScore + } + TEACHER ||--o{ ASSIGNMENT : assigns + STUDENT ||--o{ SUBMISSION : submits + SUBMISSION { + int submissionID PK + int score + date submissionDate + } + STUDENT ||--o{ PROJECT : participates + PROJECT { + int projectID PK + string projectName + date startDate + date endDate + string description + } + COURSE ||--o{ PROJECT : involves + TEACHER ||--o{ PROJECT : supervises + PROJECT ||--o{ STUDENT : has + STUDENT ||--o{ EXAM : takes + EXAM { + int examID PK + string subject + date examDate + int totalMarks + } + COURSE ||--o{ EXAM : includes + TEACHER ||--o{ EXAM : administers + """; ERDiagramParser parser = new ERDiagramParser(); ERModel erModel = parser.parse(mermaidCode); @@ -187,10 +206,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.getName().toLowerCase()); - dataModel.put("entityNameTitleCase", titleCase(entity.getName())); - dataModel.put("entityNameTitleCasePluralize", pluralize(titleCase(entity.getName()))); - dataModel.put("entityNameLowerCasePluralize", pluralize(entity.getName().toLowerCase())); + dataModel.put("entityNameLowerCase", entity.getClassName().toLowerCase()); + dataModel.put("entityNameTitleCase", titleCase(entity.getClassName())); + dataModel.put("entityNameTitleCasePluralize", pluralize(titleCase(entity.getClassName()))); + dataModel.put("entityNameLowerCasePluralize", pluralize(entity.getClassName().toLowerCase())); generate("template/html", "entity.html.ftl", dataModel.get("entityNameLowerCase") + ".html", dataModel, outputDir); } @@ -218,20 +237,20 @@ private void generateEntityController(String _package, Entity entity, File outpu String repositoryPackage = _package + "." + repositoryLayer; String controllerPackage = _package + "." + controllerLayer; - String entityInstance = firstLower(entity.getName()); + String entityInstance = firstLower(entity.getClassName()); String entityNameSpinalCased = kebabCase(entityInstance); Map dataModel = new HashMap<>(); dataModel.put("package", controllerPackage); dataModel.put("entity", entity); - dataModel.put("EntityClass", entity.getName()); - dataModel.put("EntityClassPlural", pluralize(firstUpper(entity.getName()))); - dataModel.put("EntityClass_FQN", _package + "."+ domainLayer+"." + entity.getName()); + dataModel.put("EntityClass", entity.getClassName()); + dataModel.put("EntityClassPlural", pluralize(firstUpper(entity.getClassName()))); + dataModel.put("EntityClass_FQN", _package + "."+ domainLayer+"." + entity.getClassName()); dataModel.put("entityInstance", entityInstance); dataModel.put("entityInstancePlural", pluralize(entityInstance)); dataModel.put("entityTranslationKey", entityInstance); - String repositoryFileName = entity.getName() + firstUpper(repositoryLayer); - String controllerFileName = entity.getName() + firstUpper(controllerLayer); + String repositoryFileName = entity.getClassName() + firstUpper(repositoryLayer); + String controllerFileName = entity.getClassName() + firstUpper(controllerLayer); dataModel.put("controllerClass", controllerFileName); dataModel.put("controllerClassHumanized", startCase(controllerFileName)); @@ -244,7 +263,7 @@ private void generateEntityController(String _package, Entity entity, File outpu dataModel.put("EntityRepositorySuffix", firstUpper(repositoryLayer)); boolean dto = false; - dataModel.put("instanceType", dto ? entity.getName() + "DTO" : entity.getName()); + dataModel.put("instanceType", dto ? entity.getClassName() + "DTO" : entity.getClassName()); dataModel.put("instanceName", dto ? entityInstance + "DTO" : entityInstance); dataModel.put("pagination", "no"); @@ -307,9 +326,9 @@ private void generateEntityRepository(String _package, Entity entity, File outpu dataModel.put("cdi", true); dataModel.put("named", false); dataModel.put("entityInstance", "exampleEntityRepository"); - dataModel.put("EntityClass", entity.getName()); - dataModel.put("EntityRepository", entity.getName() + firstUpper(repositoryLayer)); - dataModel.put("EntityClass_FQN", _package + "."+domainLayer+"." + entity.getName()); + dataModel.put("EntityClass", entity.getClassName()); + dataModel.put("EntityRepository", entity.getClassName()+ firstUpper(repositoryLayer)); + dataModel.put("EntityClass_FQN", _package + "."+domainLayer+"." + entity.getClassName()); dataModel.put("EntityPKClass", entity.getPrimaryKeyType()); dataModel.put("EntityPKClass_FQN", ""); dataModel.put("AbstractRepository", "Abstract" + firstUpper(repositoryLayer)); @@ -397,8 +416,8 @@ private String escapeReservedKeyword(String name) { } private void generateJPAClass(String _package, ERModel model, Entity entity, File outputDir) throws IOException { - List relationships = model.getRelationships(); - String className = entity.getName(); + Set relationships = model.getRelationships(); + String className = entity.getClassName(); StringBuilder sbHeader = new StringBuilder(); StringBuilder sbfunc = new StringBuilder(); String entityPackage = _package + "." + domainLayer; @@ -408,7 +427,7 @@ private void generateJPAClass(String _package, ERModel model, Entity entity, Fil StringBuilder sbBody = new StringBuilder(); // Generate named queries sbBody.append(generateNamedQueries(entity)); - String entityName = entity.getName(); + String entityName = entity.getClassName(); String escapedEntityName = escapeReservedKeyword(entityName); if (entityName.length() < escapedEntityName.length()) { sbBody.append("@Table(name = \"").append(escapedEntityName).append("\")\n"); @@ -476,8 +495,8 @@ private String generateNamedQueries(Entity entity) { for (Attribute attribute : entity.getAttributes()) { String capitalized = attribute.getName().substring(0, 1).toUpperCase() + attribute.getName().substring(1); - String queryName = entity.getName() + ".findBy" + capitalized; - String queryString = "SELECT e FROM " + entity.getName() + " e WHERE e." + attribute.getName() + " = :" + attribute.getName(); + String queryName = entity.getClassName() + ".findBy" + capitalized; + String queryString = "SELECT e FROM " + entity.getClassName() + " e WHERE e." + attribute.getName() + " = :" + attribute.getName(); sb.append(" @NamedQuery(name = \"").append(queryName).append("\", query = \"").append(queryString).append("\"),\n"); } @@ -490,8 +509,8 @@ private String generateNamedQueries(Entity entity) { private void appendRelationship(StringBuilder sb, StringBuilder sbfunc, Set _imports, ERModel model, Entity entity, Relationship relationship, boolean isFirstEntity) { String relationshipType = relationship.getRelationshipType(); - String firstEntity = relationship.getFirstEntity(); - String secondEntity = relationship.getSecondEntity(); + String firstEntity = relationship.getFirstEntityClass(); + String secondEntity = relationship.getSecondEntityClass(); if (isFirstEntity) { switch (relationshipType) { @@ -511,17 +530,17 @@ private void appendRelationship(StringBuilder sb, StringBuilder sbfunc, Set get").append(secondEntity).append("() {\n"); - sbfunc.append(" return ").append(secondEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public List<").append(secondEntity).append("> get").append(pluralize(secondEntity)).append("() {\n"); + sbfunc.append(" return ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); - sbfunc.append(" public void set").append(secondEntity).append("(List<").append(secondEntity).append("> ").append(secondEntity.toLowerCase()).append("s) {\n"); - sbfunc.append(" this.").append(secondEntity.toLowerCase()).append("s = ").append(secondEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public void set").append(pluralize(secondEntity)).append("(List<").append(secondEntity).append("> ").append(pluralize(secondEntity.toLowerCase())).append(") {\n"); + sbfunc.append(" this.").append(pluralize(secondEntity.toLowerCase())).append(" = ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); _imports.add("jakarta.json.bind.annotation.JsonbTransient"); _imports.add("java.util.List"); sb.append(" @JsonbTransient\n"); sb.append(" @OneToMany(mappedBy = \"").append(firstEntity.toLowerCase()).append("\")\n"); - sb.append(" private List<").append(secondEntity).append("> ").append(secondEntity.toLowerCase()).append("s;\n"); + sb.append(" private List<").append(secondEntity).append("> ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); break; case "}|--||": // One or more to exactly one case "}o--||": // Zero or more to exactly one @@ -540,17 +559,17 @@ private void appendRelationship(StringBuilder sb, StringBuilder sbfunc, Set get").append(secondEntity).append("() {\n"); - sbfunc.append(" return ").append(secondEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public List<").append(secondEntity).append("> get").append(pluralize(secondEntity)).append("() {\n"); + sbfunc.append(" return ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); - sbfunc.append(" public void set").append(secondEntity).append("(List<").append(secondEntity).append("> ").append(secondEntity.toLowerCase()).append("s) {\n"); - sbfunc.append(" this.").append(secondEntity.toLowerCase()).append("s = ").append(secondEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public void set").append(pluralize(secondEntity)).append("(List<").append(secondEntity).append("> ").append(pluralize(secondEntity.toLowerCase())).append(") {\n"); + sbfunc.append(" this.").append(pluralize(secondEntity.toLowerCase())).append(" = ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); _imports.add("jakarta.json.bind.annotation.JsonbTransient"); _imports.add("java.util.List"); sb.append(" @JsonbTransient\n"); sb.append(" @ManyToMany(mappedBy = \"").append(firstEntity.toLowerCase()).append("\")\n"); - sb.append(" private List<").append(secondEntity).append("> ").append(secondEntity.toLowerCase()).append("s;\n"); + sb.append(" private List<").append(secondEntity).append("> ").append(pluralize(secondEntity.toLowerCase())).append(";\n"); break; } } else { @@ -595,18 +614,18 @@ private void appendRelationship(StringBuilder sb, StringBuilder sbfunc, Set get").append(firstEntity).append("() {\n"); - sbfunc.append(" return ").append(firstEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public List<").append(firstEntity).append("> get").append(pluralize(firstEntity)).append("() {\n"); + sbfunc.append(" return ").append(pluralize(firstEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); - sbfunc.append(" public void set").append(firstEntity).append("(List<").append(firstEntity).append("> ").append(firstEntity.toLowerCase()).append("s) {\n"); - sbfunc.append(" this.").append(firstEntity.toLowerCase()).append("s = ").append(firstEntity.toLowerCase()).append("s;\n"); + sbfunc.append(" public void set").append(pluralize(firstEntity)).append("(List<").append(firstEntity).append("> ").append(pluralize(firstEntity.toLowerCase())).append(") {\n"); + sbfunc.append(" this.").append(pluralize(firstEntity.toLowerCase())).append(" = ").append(pluralize(firstEntity.toLowerCase())).append(";\n"); sbfunc.append(" }\n\n"); _imports.add("jakarta.json.bind.annotation.JsonbTransient"); _imports.add("java.util.List"); sb.append(" @JsonbTransient\n"); sb.append(" @ManyToMany\n"); sb.append(" @JoinColumn(name = \"").append(firstEntity.toLowerCase()).append("_id\")\n"); - sb.append(" private List<").append(firstEntity).append("> ").append(firstEntity.toLowerCase()).append("s;\n"); + sb.append(" private List<").append(firstEntity).append("> ").append(pluralize(firstEntity.toLowerCase())).append(";\n"); break; } } diff --git a/starter-generator/src/main/java/fish/payara/starter/application/generator/ERDiagramParser.java b/starter-generator/src/main/java/fish/payara/starter/application/generator/ERDiagramParser.java index b187cfa..fa9246d 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/generator/ERDiagramParser.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/generator/ERDiagramParser.java @@ -19,8 +19,6 @@ import fish.payara.starter.application.domain.Relationship; import fish.payara.starter.application.domain.ERModel; import fish.payara.starter.application.domain.Attribute; -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.AttributeType.getWrapperType; import static fish.payara.starter.application.util.StringHelper.titleCase; import java.util.regex.Matcher; @@ -53,8 +51,11 @@ public ERModel parse(String mermaidCode) { Matcher matcher = ENTITY_PATTERN.matcher(line); if (matcher.find()) { String entityName = matcher.group(1); -// String entityMetadata = matcher.group(2); - Entity entity = new Entity(titleCase(entityName)); + + Entity entity = erModel.getEntity(titleCase(entityName)); + if(entity == null) { + entity = new Entity(titleCase(entityName)); + } while (i < lines.length - 1) { line = lines[++i].trim(); if (line.equals("}")) { diff --git a/starter-generator/src/main/java/fish/payara/starter/application/util/StringUtils.java b/starter-generator/src/main/java/fish/payara/starter/application/util/StringUtils.java index 488647a..efc56a2 100644 --- a/starter-generator/src/main/java/fish/payara/starter/application/util/StringUtils.java +++ b/starter-generator/src/main/java/fish/payara/starter/application/util/StringUtils.java @@ -1,6 +1,40 @@ /* - * 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 + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. */ package fish.payara.starter.application.util; diff --git a/starter-generator/src/main/resources/template/rest/EntityController.java.ftl b/starter-generator/src/main/resources/template/rest/EntityController.java.ftl index a98ad02..e7bc608 100644 --- a/starter-generator/src/main/resources/template/rest/EntityController.java.ftl +++ b/starter-generator/src/main/resources/template/rest/EntityController.java.ftl @@ -197,7 +197,7 @@ public class ${controllerClass} { LOG.log(Level.FINE, "REST request to get ${EntityClass} : {}", ${pkName}); ${instanceType} ${instanceName} = ${entityRepository}.find(${pkName}); return Optional.ofNullable(${instanceName}) - .map(result -> Response.status(Response.Status.OK).entity(${instanceName}).build()) + .map(res -> Response.status(Response.Status.OK).entity(${instanceName}).build()) .orElse(Response.status(Response.Status.NOT_FOUND).build()); } diff --git a/starter-ui/src/main/java/fish/payara/starter/ERDiagramEnhanceChat.java b/starter-ui/src/main/java/fish/payara/starter/ERDiagramEnhanceChat.java index 2463b37..1082d14 100644 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramEnhanceChat.java +++ b/starter-ui/src/main/java/fish/payara/starter/ERDiagramEnhanceChat.java @@ -1,3 +1,41 @@ +/* + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.starter; import dev.langchain4j.service.SystemMessage; 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 9941672..65138ad 100644 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java +++ b/starter-ui/src/main/java/fish/payara/starter/ERDiagramPlainChat.java @@ -1,3 +1,41 @@ +/* + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.starter; import dev.langchain4j.service.SystemMessage; @@ -251,5 +289,95 @@ Attributes may also have a key or comment defined. Keys can be PK, FK or UK, for Respond only in Mermaid erDiagram format and the response adheres to valid Mermaid syntax. """) String updateERDiagram(@UserMessage String erDiagram, @UserMessage String userRequestToModifyTheDiagram); + + @SystemMessage(""" + 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 + 2(a) Each must have one primary key PK attribute. + 2(b) 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. + 2(c) Ensure that the related bootstrap icon is always added for each entity. + 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": [ + { + "name": "userId", + "primaryKey": true, + "type": "String" + }, + { + "name": "username", + "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": [ + { + "name": "commentId", + "primaryKey": true, + "type": "String" + }, + { + "name": "content", + "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. + """) + String addFronEndDetailsToERDiagram(@UserMessage String erDiagramJson); } 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 77a7fcf..8cda1d6 100644 --- a/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java +++ b/starter-ui/src/main/java/fish/payara/starter/ERDiagramResource.java @@ -1,10 +1,43 @@ /* - * 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 + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. */ package fish.payara.starter; -//import fish.payara.ai.GptService; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; 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 aa48bbe..774fa81 100644 --- a/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java +++ b/starter-ui/src/main/java/fish/payara/starter/LangChainChatService.java @@ -5,8 +5,6 @@ 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 { @@ -44,100 +42,7 @@ public String updateERDiagramSuggestion(String userRequest, String 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); + return erDiagramChat.addFronEndDetailsToERDiagram(erDiagram); } } diff --git a/starter-ui/src/main/java/fish/payara/starter/OpenAIFactory.java b/starter-ui/src/main/java/fish/payara/starter/OpenAIFactory.java index fa1ea54..f3e6502 100644 --- a/starter-ui/src/main/java/fish/payara/starter/OpenAIFactory.java +++ b/starter-ui/src/main/java/fish/payara/starter/OpenAIFactory.java @@ -1,3 +1,41 @@ +/* + * + * Copyright (c) 2024 Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.starter; import java.time.Duration; @@ -14,40 +52,40 @@ @ApplicationScoped public class OpenAIFactory { - @Inject - @ConfigProperty(name = "OPEN_API_KEY") - String apiKey; - @Inject - @ConfigProperty(name = "gpt.model") - String gptModel; - - - @Inject - @ConfigProperty(name = "model.temperature") - Double temperature; - @Inject - @ConfigProperty(name = "openai.timeout") - int apiTimeout; - - @Produces - @Singleton - public OpenAiService produceService() { - return new OpenAiService(apiKey, - Duration.ofSeconds(apiTimeout)); - } - - @Produces - @Singleton - public OpenAiChatModel produceModel() { - return OpenAiChatModel.builder() - .apiKey(apiKey) - // .responseFormat("json_object") - .modelName(gptModel) - .temperature(temperature) - .timeout(ofSeconds(60)) - .logRequests(true) - .logResponses(true) - .build(); - } + @Inject + @ConfigProperty(name = "OPEN_API_KEY") + private String apiKey; + + @Inject + @ConfigProperty(name = "gpt.model") + private String gptModel; + + @Inject + @ConfigProperty(name = "model.temperature") + private Double temperature; + + @Inject + @ConfigProperty(name = "openai.timeout") + private int apiTimeout; + + @Produces + @Singleton + public OpenAiService produceService() { + return new OpenAiService(apiKey, + Duration.ofSeconds(apiTimeout)); + } + + @Produces + @Singleton + public OpenAiChatModel produceModel() { + return OpenAiChatModel.builder() + .apiKey(apiKey) + .modelName(gptModel) + .temperature(temperature) + .timeout(ofSeconds(apiTimeout)) + .logRequests(true) + .logResponses(true) + .build(); + } } 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 76e925b..1262a2d 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 @@ -75,6 +75,7 @@ public class ApplicationConfiguration { public static final String AUTH = "auth"; public static final String ER_DIAGRAM = "erDiagram"; public static final String ER_DIAGRAM_NAME = "erDiagramName"; + public static final String REST_SUBPACKAGE = "restSubpackage"; 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 74b44e1..425d884 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 @@ -60,6 +60,7 @@ import static fish.payara.starter.resources.ApplicationConfiguration.PAYARA_VERSION; import static fish.payara.starter.resources.ApplicationConfiguration.PLATFORM; import static fish.payara.starter.resources.ApplicationConfiguration.PROFILE; +import static fish.payara.starter.resources.ApplicationConfiguration.REST_SUBPACKAGE; import static fish.payara.starter.resources.ApplicationConfiguration.VERSION; import jakarta.annotation.Resource; import jakarta.enterprise.concurrent.ManagedExecutorDefinition; @@ -70,7 +71,6 @@ 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; @@ -84,7 +84,6 @@ import org.apache.maven.cli.MavenCli; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; -import jakarta.json.bind.JsonbException; /** * @@ -137,7 +136,7 @@ public Future generate(ApplicationConfiguration appProperties) { if (erModel != null && !erModel.getEntities().isEmpty()) { Jsonb jsonb = JsonbBuilder.create(); String jsonString = jsonb.toJson(erModel); - LOGGER.info("Generating web components info from AI."); + LOGGER.info("Generating web components info from AI \n" + jsonString); String outputJson = langChainChatService.addFronEndDetailsToERDiagram(jsonString); if (outputJson != null && !outputJson.isEmpty()) { String updatedJson = outputJson.strip().replaceAll("^```json|```$", ""); @@ -208,6 +207,7 @@ private Properties buildMavenProperties(ApplicationConfiguration appProperties) properties.put(MP_FAULT_TOLERANCE, appProperties.isMpFaultTolerance()); properties.put(MP_METRICS, appProperties.isMpMetrics()); properties.put(AUTH, appProperties.getAuth()); + properties.put(REST_SUBPACKAGE, appProperties.getRestSubpackage()); return properties; } @@ -219,7 +219,7 @@ private void invokeMavenArchetype(String archetypeGroupId, String archetypeArtif options.addAll(Arrays.asList(new String[]{MAVEN_ARCHETYPE_CMD, "-DinteractiveMode=false", "-DaskForDefaultPropertyValues=false", "-DarchetypeGroupId=" + archetypeGroupId, "-DarchetypeArtifactId=" + archetypeArtifactId, "-DarchetypeVersion=" + archetypeVersion})); - properties.forEach((k, v) -> options.add("-D" + k + "=" + v)); + properties.forEach((k, v) -> options.add("-D" + k + "=" + (v.toString().contains(" ") ? "'" + v + "'" : v))); LOGGER.log(Level.INFO, "Executing Maven Archetype {0} ", new Object[]{options.toString()}); 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 deleted file mode 100644 index 0147d45..0000000 --- a/starter-ui/src/main/java/fish/payara/starter/resources/LoggerOutputStream.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 8fa58f9..38c7fa6 100644 --- a/starter-ui/src/main/resources/META-INF/microprofile-config.properties +++ b/starter-ui/src/main/resources/META-INF/microprofile-config.properties @@ -1,4 +1,4 @@ -openai.timeout=45 +openai.timeout=180 gpt.model=gpt-4o-mini gpt.image.mode=dall-e-3 model.temperature=0.7 diff --git a/starter-ui/src/main/webapp/index.html b/starter-ui/src/main/webapp/index.html index 7666ec7..99468ad 100644 --- a/starter-ui/src/main/webapp/index.html +++ b/starter-ui/src/main/webapp/index.html @@ -799,7 +799,6 @@

Live Preview

]; var erDiagramContent = {}; - var erDiagram; var erDiagramChat = []; var erDiagramChatIndex = 0; @@ -983,7 +982,6 @@

Live Preview

async function populateErDiagrams() { const previewSelect = document.getElementById('mermaidErDiagramListPreview'); const erDiagramSelect = document.getElementById('mermaidErDiagramList'); - const erDiagram = document.getElementById('erDiagram'); previewSelect.addEventListener('change', function () { erDiagramSelect.value = this.value; @@ -1003,7 +1001,7 @@

Live Preview

} if(erDiagramContent[selectedDiagramId]) { editor.setValue(erDiagramContent[selectedDiagramId]); - erDiagram.innerHTML = erDiagramContent[selectedDiagramId]; + document.getElementById('erDiagram').innerHTML = erDiagramContent[selectedDiagramId]; $('#mermaidErDiagramListPreview').val(selectedDiagramId); erDiagramChat = []; @@ -1042,7 +1040,7 @@

Live Preview

} } editor.setValue(erDiagramContent[selectedDiagramId]); - erDiagram.innerHTML = erDiagramContent[selectedDiagramId]; + document.getElementById('erDiagram').innerHTML = erDiagramContent[selectedDiagramId]; erDiagramChat = []; erDiagramChat.push({"erd": content}); @@ -1055,7 +1053,7 @@

Live Preview

prependOptionToMermaidErDiagramList(selectedDiagramId); if (content) { editor.setValue(content); - erDiagram.innerHTML = content; + document.getElementById('erDiagram').innerHTML = content; erDiagramChat = []; erDiagramChat.push({"erd": content}); @@ -1108,7 +1106,7 @@

Live Preview

} } editor.setValue(erDiagramContent[selectedDiagramId]); - erDiagram.innerHTML = erDiagramContent[selectedDiagramId]; + document.getElementById('erDiagram').innerHTML = erDiagramContent[selectedDiagramId]; const selectElement = document.getElementById('mermaidErDiagramList'); for (let i = 0; i < selectElement.options.length; i++) { @@ -1127,8 +1125,8 @@

Live Preview

function updateMermaidDiagram() { console.info('Updating diagram with new content.'); - erDiagram = editor.getValue(); - mermaid.render('graphDiv', erDiagram).then(({ svg, bindFunctions }) => { + document.getElementById('erDiagram').innerHTML = editor.getValue(); + mermaid.render('graphDiv', editor.getValue()).then(({ svg, bindFunctions }) => { var element = document.querySelector('#graphDiv'); document.querySelector('div.livePreview').innerHTML = svg; bindFunctions?.(element); @@ -1144,7 +1142,7 @@

Live Preview

function closeModal() { document.getElementById("previewModal").style.display = "none"; - erDiagram.innerHTML = editor.getValue(); + document.getElementById('erDiagram').innerHTML = editor.getValue(); } var modal = document.getElementById("previewModal"); var span = document.getElementsByClassName("close")[0]; @@ -1155,13 +1153,13 @@

Live Preview

// When the user clicks on (x), close the modal span.onclick = function () { modal.style.display = "none"; - erDiagram.innerHTML = editor.getValue(); + document.getElementById('erDiagram').innerHTML = editor.getValue(); }; // When the user clicks anywhere outside of the modal, close it window.onclick = function (event) { if (event.target == modal) { modal.style.display = "none"; - erDiagram.innerHTML = editor.getValue(); + document.getElementById('erDiagram').innerHTML = editor.getValue(); } };