Skip to content

Commit

Permalink
Merge pull request #8 from axonivy-market/dialog
Browse files Browse the repository at this point in the history
Dialog
  • Loading branch information
ivy-rew authored Oct 25, 2023
2 parents cf2fdee + 50e6cb8 commit 50aa02f
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 57 deletions.
4 changes: 3 additions & 1 deletion excel-importer-product/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ Imports Excel sheets and transforms it into a full featured web application.
In the project, where the Excel data should be managed:

1. Create a persistence unit under `/config/persistence.xml`
2. Add the property, to allow schema changes `hibernate.hbm2ddl.auto=update`
2. Add the properties
1. to allow schema changes `hibernate.hbm2ddl.auto=create`
2. to use classic sequence `hibernate.id.new_generator_mappings=false`
3. Set the Data source to a valid database. If there is none, set it up under `/config/databases.yaml`
1 change: 1 addition & 0 deletions excel-importer-test/config/persistence.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show-sql" value="true"/>
<property name="hibernate.id.new_generator_mappings" value="false"/>
</properties>
</persistence-unit>
</persistence>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -23,7 +24,11 @@
import ch.ivyteam.ivy.environment.IvyTest;
import ch.ivyteam.ivy.process.model.Process;
import ch.ivyteam.ivy.process.model.element.activity.Script;
import ch.ivyteam.ivy.process.model.element.event.start.dialog.html.HtmlDialogEventStart;
import ch.ivyteam.ivy.process.model.element.event.start.dialog.html.HtmlDialogMethodStart;
import ch.ivyteam.ivy.process.model.element.value.Mappings;
import ch.ivyteam.ivy.scripting.dataclass.IDataClass;
import ch.ivyteam.ivy.scripting.dataclass.IDataClassField;
import ch.ivyteam.ivy.scripting.dataclass.IEntityClass;

@IvyTest
Expand All @@ -48,20 +53,11 @@ void createEntityDialog(@TempDir Path dir) throws IOException, CoreException {
String unit = "testing";
dialog = new DialogCreator().createDialog(customer, unit);

Process process = dialog.getProcess(null).getModel();
Script loader = process.search().type(Script.class).findOne();
assertThat(loader.getCode()).contains(customer.getName());
var delete = process.search().type(HtmlDialogMethodStart.class).findOne();
String removal = delete.getOutput().getCode();
assertThat(removal)
.contains("testing.remove(");

var view = read(dialog.getViewFile());
assertThat(view).contains("p:dataTable");
assertThat(view)
.as("visualizes properties of the entity")
.contains("firstname")
.doesNotContain("<!-- [entity.fields] -->");
assertData(dialog.getDataClass(null));
assertProcess(customer, dialog.getProcess(null).getModel());
assertView(read(dialog.getViewFile()));
var udRoot = (IFolder) dialog.getResource();
assertDetailView(read(udRoot.getFile("EntityDetail.xhtml")));

} finally {
customer.getResource().delete(true, new NullProgressMonitor());
Expand All @@ -71,6 +67,52 @@ void createEntityDialog(@TempDir Path dir) throws IOException, CoreException {
}
}

private void assertData(IDataClass dataClass) {
assertThat(dataClass.getFields()).extracting(IDataClassField::getName)
.containsOnly("entries", "edit");
}

private void assertProcess(IEntityClass customer, Process process) {
Script loader = process.search().type(Script.class).findOne();
assertThat(loader.getCode()).contains(customer.getName());

var delete = process.search().type(HtmlDialogMethodStart.class).name("delete(customer)").findOne();
String removal = delete.getOutput().getCode();
assertThat(removal)
.contains("testing.remove(");

var edit = process.search().type(HtmlDialogMethodStart.class).name("edit(customer)").findOne();
Mappings mappings = edit.getOutput().getMappings();
assertThat(mappings.asList())
.hasSize(1);

var save = process.search().type(HtmlDialogEventStart.class).name("save").findOne();
assertThat(save.getOutput().getCode())
.contains("ivy.persistence.testing.merge(out.edit)");

var add = process.search().type(HtmlDialogEventStart.class).name("add").findOne();
assertThat(add.getOutput().getMappings().asList().get(0).getRightSide())
.contains("new "+customer.getName()+"()");
}

private void assertView(String view) {
assertThat(view).contains("p:dataTable");
assertThat(view)
.as("visualizes properties of the entity")
.contains("firstname")
.doesNotContain("<!-- [entity.fields] -->");
}

private void assertDetailView(String view) {
assertThat(view)
.as("visualizes properties of the entity")
.contains("firstname")
.doesNotContain("<!-- [entity.fields] -->");
assertThat(view)
.as("navigation to list must be adapted by the template renderer")
.doesNotContain("action=\"EntityList\"");
}

private static String read(IFile viewFile) throws IOException, CoreException {
try(InputStream in = viewFile.getContents()) {
var bos = new java.io.ByteArrayOutputStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void readToEntity(@TempDir Path dir) throws IOException {
var entity = reader.getEntity(path);
assertThat(entity).isNotNull();
}

@Test
void readGermanized(@TempDir Path dir) throws Exception {
Path path = dir.resolve("Arzneimittel.xlsx");
Expand All @@ -44,6 +44,14 @@ void readGermanized(@TempDir Path dir) throws Exception {
.doesNotContain("(")
.doesNotContain("ä");
}
assertThat(entity.getField("anzahlInneresBehltnis").getComment())
.as("preserve real column names")
.isEqualTo("Anzahl Inneres Behältnis");

assertThat(entity.getField("zulassungsinhaberName").getType())
.isEqualTo(String.class.getName());
assertThat(entity.getField("pNRZulassungsinhaber").getType())
.isEqualTo(Double.class.getName());
}

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ void loadArznei(@TempDir Path dir) throws IOException, CoreException {
TstRes.loadTo(path, "ArzneimittelLight.xlsx");

Workbook wb = ExcelLoader.load(path);
Sheet customerSheet = wb.getSheetAt(0);
Sheet medSheet = wb.getSheetAt(0);

IEntityClass customer = reader.toEntity(customerSheet, "meds");
IEntityClass meds = reader.toEntity(medSheet, "meds");
try {
customer.save(new NullProgressMonitor());
Class<?> entity = loader.createTable(customer);
meds.save(new NullProgressMonitor());
Class<?> entity = loader.createTable(meds);
assertThat(unit.findAll(entity)).isEmpty();
loader.load(customerSheet, customer);
loader.load(medSheet, meds);
List<?> records = unit.findAll(entity);
assertThat(records).hasSizeGreaterThanOrEqualTo(2);
} finally {
customer.getResource().delete(true, new NullProgressMonitor());
meds.getResource().delete(true, new NullProgressMonitor());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;

import ch.ivyteam.ivy.IvyConstants;
Expand Down Expand Up @@ -41,13 +43,14 @@ public IUserDialog createDialog(IEntityClass entity, String unit) {
var target = dialogStartFor(entity);

VariableDesc entries = new VariableDesc("entries", new QualifiedType(List.class.getName(), List.of(new QualifiedType(entity.getName()))));
VariableDesc edit = new VariableDesc("edit", new QualifiedType(entity.getName()));

prepareTemplate(project, "frame-10");
String dialogId = target.getId().getRawId();
var params = new DialogCreationParameters.Builder(project, dialogId)
.viewTechId(IvyConstants.VIEW_TECHONOLOGY_JSF)
.signature(target.getStartMethod())
.dataClassFields(List.of(entries))
.dataClassFields(List.of(entries, edit))
.toCreationParams();
var userDialog = local.createProjectUserDialog(params, null);

Expand All @@ -56,10 +59,27 @@ public IUserDialog createDialog(IEntityClass entity, String unit) {
processRdm.save();

extendView(userDialog.getViewFile(), entity);
detailView(userDialog, entity);

return userDialog;
}

private void detailView(IUserDialog userDialog, IEntityClass entity) {
String template = readTemplate("EntityDetail.xhtml");

String rendered = renderFields(entity, template, this::renderDetail);
rendered = rendered.replaceAll("action=\"EntityList\"",
"action=\"%s\"".formatted(entity.getSimpleName()+"Manager"));

var dir = (IFolder) userDialog.getResource();
var detailView = dir.getFile("EntityDetail.xhtml");
try(InputStream bis = new ByteArrayInputStream(rendered.getBytes())) {
detailView.create(bis, true, null);
} catch (Exception ex) {
throw new RuntimeException("Failed to write detail view "+detailView, ex);
}
}

private void prepareTemplate(IProject project, String template) {
try {
var view = ch.ivyteam.ivy.dialog.ui.ViewTechnologyDesignerUiRegistry.getInstance().getViewTechnology(IvyConstants.VIEW_TECHONOLOGY_JSF);
Expand All @@ -73,34 +93,54 @@ private void prepareTemplate(IProject project, String template) {
}

private void extendView(IFile viewFile, IEntityClass entity) {
try(InputStream is = DialogCreator.class.getResourceAsStream("/com/axonivy/util/excel/importer/EntityManager/EntityManager.xhtml")) {
String template = readTemplate("EntityManager.xhtml");
String rendered = renderFields(entity, template, this::renderColumn);
write(viewFile, rendered);
}

private static String readTemplate(String resource) {
try(InputStream is = DialogCreator.class.getResourceAsStream("/com/axonivy/util/excel/importer/EntityManager/"+resource)) {
var bos = new ByteArrayOutputStream();
is.transferTo(bos);
var template = new String(bos.toByteArray());
return template;
} catch (Exception ex) {
throw new RuntimeException("Failed to read template "+resource);
}
}

String rendered = renderFields(entity, template);

var bis = new ByteArrayInputStream(rendered.getBytes());
viewFile.setContents(bis, 0, null);
private static void write(IFile view, String content) {
try(var bis = new ByteArrayInputStream(content.getBytes())){
view.setContents(bis, 0, null);
} catch (Exception ex) {
throw new RuntimeException("Failed to extend view for "+viewFile, ex);
throw new RuntimeException("Failed to extend view for "+view, ex);
}
}

private String renderFields(IEntityClass entity, String template) {
private String renderFields(IEntityClass entity, String template, Function<IEntityClassField, String> renderer) {
String fieldXhtml = entity.getFields().stream()
.filter(fld -> !fld.getName().equals("id"))
.map(this::htmlview)
.map(renderer)
.collect(Collectors.joining("\n"));
return template.replace("<!-- [entity.fields] -->", fieldXhtml);
}

private String htmlview(IEntityClassField field) {
private String renderColumn(IEntityClassField field) {
String fieldXhtml = """
<p:column headerText="%s">
<h:outputText value="#{entity.%s}"/>
</p:column>
""".formatted(field.getName(), field.getName());
""".formatted(field.getComment(), field.getName());
return fieldXhtml;
}

private String renderDetail(IEntityClassField field) {
String fieldXhtml = """
<p:outputLabel for="FIELD" value="LABEL" />
<p:inputText id="FIELD" value="#{data.edit.FIELD}"></p:inputText>
"""
.replaceAll("FIELD", field.getName())
.replaceAll("LABEL", field.getComment());
return fieldXhtml;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import java.util.List;

import ch.ivyteam.ivy.process.model.NodeElement;
import ch.ivyteam.ivy.process.model.Process;
import ch.ivyteam.ivy.process.model.diagram.shape.DiagramShape;
import ch.ivyteam.ivy.process.model.diagram.value.Position;
import ch.ivyteam.ivy.process.model.diagram.value.PositionDelta;
import ch.ivyteam.ivy.process.model.element.activity.Script;
import ch.ivyteam.ivy.process.model.element.event.start.dialog.html.HtmlDialogEventStart;
import ch.ivyteam.ivy.process.model.element.event.start.dialog.html.HtmlDialogMethodStart;
import ch.ivyteam.ivy.process.model.element.event.start.dialog.html.HtmlDialogStart;
import ch.ivyteam.ivy.process.model.element.event.start.value.CallSignature;
import ch.ivyteam.ivy.process.model.element.value.Mappings;
import ch.ivyteam.ivy.process.model.value.MappingCode;
import ch.ivyteam.ivy.process.model.value.scripting.QualifiedType;
import ch.ivyteam.ivy.process.model.value.scripting.VariableDesc;
Expand All @@ -22,6 +25,9 @@ public class DialogProcess {
private final IEntityClass entity;
private final String unit;

private final int x = 96;
private int y = 248;

public DialogProcess(Process process, IEntityClass entity, String unit) {
this.process = process;
this.entity = entity;
Expand All @@ -31,6 +37,9 @@ public DialogProcess(Process process, IEntityClass entity, String unit) {
public void extendProcess() {
addDbLoaderScript();
addDeleteAction();
addEditAction();
addCreateAction();
addSaveAction();
}

private void addDbLoaderScript() {
Expand All @@ -53,10 +62,7 @@ private void addDbLoaderScript() {
}

private void addDeleteAction() {
int x = 96;
int y = 248;
var delete = process.add().element(HtmlDialogMethodStart.class);
delete.getShape().moveTo(new Position(x, y));
var delete = addMethod();
delete.setName("delete(" + entity.getSimpleName() + ")");
var param = new VariableDesc("entity", new QualifiedType(entity.getName()));
delete.setSignature(new CallSignature("delete").setInputParameters(List.of(param)));
Expand All @@ -75,4 +81,51 @@ private void addDeleteAction() {
delete.setOutput(new MappingCode(code));
}

private void addEditAction() {
var edit = addMethod();
edit.setName("edit(" + entity.getSimpleName() + ")");
var param = new VariableDesc("entity", new QualifiedType(entity.getName()));
edit.setSignature(new CallSignature("edit").setInputParameters(List.of(param)));

edit.setOutput(new MappingCode(Mappings.single("out.edit", "param.entity")));
}

private void addCreateAction() {
var add = addEvent();
add.setName("add");
add.setOutput(new MappingCode(Mappings.single("out.edit", "new "+entity.getName()+"()")));
}

private void addSaveAction() {
var save = addEvent();
save.setName("save");
String doSave = """
if (!out.edit.#id is initialized) {
out.edit = ivy.persistence.UNIT.persist(out.edit) as ENTITY;
out.entries.add(out.edit);
} else {
ivy.persistence.UNIT.merge(out.edit);
}
out.edit = null;
"""
.replaceAll("UNIT", unit)
.replaceAll("ENTITY", entity.getName());
save.setOutput(new MappingCode(doSave));
}

private HtmlDialogMethodStart addMethod() {
return addAction(HtmlDialogMethodStart.class);
}

private HtmlDialogEventStart addEvent() {
return addAction(HtmlDialogEventStart.class);
}

private <T extends NodeElement> T addAction(Class<T> type) {
var action = process.add().element(type);
action.getShape().moveTo(new Position(x, y));
y += 80;
return action;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public IEntityClass toEntity(Sheet sheet, String dataName) {

withIdField(entity);
ExcelReader.parseColumns(sheet).stream().forEachOrdered(col -> {
entity.addField(fieldName(col.name()), col.type().getName());
var field = entity.addField(fieldName(col.name()), col.type().getName());
field.setComment(col.name());
});
return entity;
}
Expand Down
Loading

0 comments on commit 50aa02f

Please sign in to comment.