Skip to content

Commit

Permalink
adds AbstractZDLProjectGenerator and "multimodule" backend support
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangsa committed Nov 12, 2023
1 parent 31ff27f commit e42b7c0
Show file tree
Hide file tree
Showing 13 changed files with 465 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import io.zenwave360.sdk.templating.*;
import io.zenwave360.sdk.utils.JSONPath;

public class BackendApplicationDefaultGenerator extends AbstractZDLGenerator {
public class BackendApplicationDefaultGeneratorBack extends AbstractZDLGenerator {

public String sourceProperty = "zdl";

Expand Down Expand Up @@ -43,15 +43,15 @@ public class BackendApplicationDefaultGenerator extends AbstractZDLGenerator {
@DocumentedOption(description = "Suffix for elasticsearch document entities (default: Document)")
public String searchDTOSuffix = "Document";

public BackendApplicationDefaultGenerator withSourceProperty(String sourceProperty) {
public BackendApplicationDefaultGeneratorBack withSourceProperty(String sourceProperty) {
this.sourceProperty = sourceProperty;
return this;
}

private HandlebarsEngine handlebarsEngine = new HandlebarsEngine();
{
handlebarsEngine.getHandlebars().registerHelpers(new BackendApplicationDefaultHelpers(this));
handlebarsEngine.getHandlebars().registerHelpers(new BackendApplicationDefaultJpaHelpers(this));
// handlebarsEngine.getHandlebars().registerHelpers(new BackendApplicationDefaultHelpers(this));
// handlebarsEngine.getHandlebars().registerHelpers(new BackendApplicationDefaultJpaHelpers(this));
}

private String templatesFolder = "io/zenwave360/sdk/plugins/BackendApplicationDefaultGenerator/";
Expand All @@ -76,7 +76,7 @@ boolean is(Map<String, Object> model, String... annotations) {
Object[] enumTemplate = {"src/main/java", "core/domain/common/Enum.java", "core/domain/{{enum.name}}.java", JAVA};
Object[] enumDtoTemplate = {"src/main/java", "core/inbound/dtos/Enum.java", "core/inbound/dtos/{{enum.name}}.java", JAVA};
protected List<Object[]> templatesByEntity = List.of(
new Object[] {"src/main/java", "core/domain/vo/Entity.java", "core/domain/{{entity.name}}.java", JAVA, skipVO},
new Object[] {"src/main/java", "core/domain/{{persistence}}/Entity.java", "core/domain/{{entity.name}}.java", JAVA, skipVO},
new Object[] {"src/main/java", "core/domain/{{persistence}}/Entity.java", "core/domain/{{entity.name}}.java", JAVA, skipEntity},
new Object[] {"src/main/java", "core/outbound/{{persistence}}/{{style}}/EntityRepository.java", "core/outbound/{{persistence}}/{{entity.className}}Repository.java", JAVA, skipEntityRepository, true},
new Object[] {"src/main/java", "core/inbound/dtos/EntityCriteria.java", "core/inbound/dtos/{{criteriaClassName entity }}.java", JAVA, skipSearchCriteria},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

public class BackendApplicationDefaultHelpers {

private final BackendApplicationDefaultGenerator generator;
private final BackendDefaultApplicationGenerator generator;

BackendApplicationDefaultHelpers(BackendApplicationDefaultGenerator generator) {
BackendApplicationDefaultHelpers(BackendDefaultApplicationGenerator generator) {
this.generator = generator;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

public class BackendApplicationDefaultJpaHelpers {

private final BackendApplicationDefaultGenerator generator;
private final BackendDefaultApplicationGenerator generator;

BackendApplicationDefaultJpaHelpers(BackendApplicationDefaultGenerator generator) {
BackendApplicationDefaultJpaHelpers(BackendDefaultApplicationGenerator generator) {
this.generator = generator;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.zenwave360.sdk.parsers.ZDLParser;
import io.zenwave360.sdk.processors.ZDLProcessor;
import io.zenwave360.sdk.writers.TemplateFileWriter;
import io.zenwave360.sdk.writers.TemplateStdoutWriter;

/**
* This is the long description
Expand All @@ -15,7 +16,15 @@ public class BackendApplicationDefaultPlugin extends Plugin {

public BackendApplicationDefaultPlugin() {
super();
withChain(ZDLParser.class, ZDLProcessor.class, BackendApplicationDefaultGenerator.class, JavaFormatter.class, TemplateFileWriter.class);
withChain(ZDLParser.class, ZDLProcessor.class, BackendDefaultApplicationGenerator.class, JavaFormatter.class, TemplateFileWriter.class);
}

@Override
public <T extends Plugin> T processOptions() {
if (getOptions().containsKey("multiModule") && "true".equals(getOptions().get("multiModule").toString())) {
replaceInChain(BackendDefaultApplicationGenerator.class, BackendMultiModuleApplicationGenerator.class);
}
return (T) this;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package io.zenwave360.sdk.plugins;

import static io.zenwave360.sdk.templating.OutputFormatType.JAVA;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.zenwave360.sdk.doc.DocumentedOption;
import io.zenwave360.sdk.generators.AbstractZDLProjectGenerator;
import io.zenwave360.sdk.generators.ZDLProjectTemplates;
import io.zenwave360.sdk.options.DatabaseType;
import io.zenwave360.sdk.options.PersistenceType;
import io.zenwave360.sdk.options.ProgrammingStyle;
import io.zenwave360.sdk.utils.JSONPath;

/**
* Generates a backend application with the following structure:
<pre>
📦 basePackage
📦 adapters
└─ web
| └─ RestControllers (spring mvc)
└─ events
└─ *EventListeners (spring-cloud-streams)
📦 core
└─ 📦 domain
| └─ (entities and aggregates)
└─ 📦 inbound
| ├─ dtos/
| └─ ServiceInterface (inbound service interface)
├─ 📦 outbound
| ├─ mongodb
| | └─ *RepositoryInterface (spring-data interface)
| └─ jpa
| └─ *RepositoryInterface (spring-data interface)
├─ 📦 implementation
| ├─ mappers/
| └─ ServiceImplementation (inbound service implementation)
📦 infrastructure
├─ mongodb
| └─ CustomRepositoryImpl (spring-data custom implementation)
└─ jpa
└─ CustomRepositoryImpl (spring-data custom implementation)
</pre>
*/
public class BackendDefaultApplicationGenerator extends AbstractZDLProjectGenerator {

public String configPackage = "{{basePackage}}.config";
public String entitiesPackage = "{{basePackage}}.core.domain";
public String inboundPackage = "{{basePackage}}.core.inbound";
public String inboundDtosPackage = "{{basePackage}}.core.inbound.dtos";
public String outboundPackage = "{{basePackage}}.core.outbound";
public String coreImplementationPackage = "{{basePackage}}.core.implementation";
public String infrastructurePackage = "{{basePackage}}.infrastructure";
public String adaptersPackage = "{{basePackage}}.adapters";


@DocumentedOption(description = "Entities to generate code for")
public List<String> entities = new ArrayList<>();

@DocumentedOption(description = "Persistence")
public PersistenceType persistence = PersistenceType.mongodb;

@DocumentedOption(description = "SQL database flavor")
public DatabaseType databaseType = DatabaseType.postgresql;

@DocumentedOption(description = "Programming Style")
public ProgrammingStyle style = ProgrammingStyle.imperative;

@DocumentedOption(description = "Use @Getter and @Setter annotations from Lombok")
public boolean useLombok = false;

@DocumentedOption(description = "If not empty, it will generate (and use) an `input` DTO for each entity used as command parameter")
public String inputDTOSuffix = "";

@DocumentedOption(description = "Suffix for search criteria DTOs (default: Criteria)")
public String criteriaDTOSuffix = "Criteria";

@DocumentedOption(description = "Suffix for elasticsearch document entities (default: Document)")
public String searchDTOSuffix = "Document";

{
getTemplateEngine().getHandlebars().registerHelpers(new BackendApplicationDefaultHelpers(this));
getTemplateEngine().getHandlebars().registerHelpers(new BackendApplicationDefaultJpaHelpers(this));
}

protected boolean is(Map<String, Object> model, String... annotations) {
String annotationsFilter = Arrays.stream(annotations).map(a -> "@." + a).collect(Collectors.joining(" || "));
return !((List) JSONPath.get(model, "$.entity.options[?(" + annotationsFilter + ")]")).isEmpty();
}

protected Function<Map<String, Object>, Boolean> skipEntityRepository = (model) -> !is(model, "aggregate");
protected Function<Map<String, Object>, Boolean> skipEntityId = (model) -> is(model, "embedded", "vo", "input", "isSuperClass");
protected Function<Map<String, Object>, Boolean> skipEntity = (model) -> is(model, "vo", "input");
protected Function<Map<String, Object>, Boolean> skipEntityInput = (model) -> inputDTOSuffix == null || inputDTOSuffix.isEmpty();
protected Function<Map<String, Object>, Boolean> skipSearchCriteria = (model) -> is(model, "vo", "input") || !is(model, "searchCriteria");
protected Function<Map<String, Object>, Boolean> skipElasticSearch = (model) -> is(model, "vo", "input") || !is(model, "search");

@Override
protected ZDLProjectTemplates configureProjectTemplates() {
var ts = new ZDLProjectTemplates("io/zenwave360/sdk/plugins/BackendApplicationDefaultGenerator");

ts.addTemplate(ts.entityTemplates, "src/main/java","core/domain/{{persistence}}/Entity.java",
"{{asPackageFolder entitiesPackage}}/{{entity.name}}.java", JAVA, skipEntity, false);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/outbound/{{persistence}}/{{style}}/EntityRepository.java",
"{{asPackageFolder outboundPackage}}/{{persistence}}/{{entity.className}}Repository.java", JAVA, skipEntityRepository, true);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/inbound/dtos/EntityCriteria.java",
"{{asPackageFolder inboundDtosPackage}}/{{criteriaClassName entity }}.java", JAVA, skipSearchCriteria, false);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/inbound/dtos/EntityInput.java",
"{{asPackageFolder inboundDtosPackage}}/{{entity.className}}{{inputDTOSuffix entity}}.java", JAVA, skipEntityInput, false);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/implementation/mappers/EntityMapper.java",
"{{asPackageFolder coreImplementationPackage}}/mappers/{{entity.className}}Mapper.java", JAVA, skipEntity, true);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/domain/search/EntityDocument.java",
"{{asPackageFolder entitiesPackage}}/search/{{entity.className}}{{searchDTOSuffix}}.java", JAVA, skipElasticSearch, false);
ts.addTemplate(ts.entityTemplates, "src/main/java","core/outbound/search/EntitySearchRepository.java",
"{{asPackageFolder outboundPackage}}/search/{{entity.className}}SearchRepository.java", JAVA, skipElasticSearch, true);
ts.addTemplate(ts.entityTemplates, "src/test/java","infrastructure/{{persistence}}/{{style}}/BaseRepositoryIntegrationTest.java",
"{{asPackageFolder infrastructurePackage}}/{{persistence}}/BaseRepositoryIntegrationTest.java", JAVA, skipEntityRepository, true);
ts.addTemplate(ts.entityTemplates, "src/test/java","infrastructure/{{persistence}}/{{style}}/EntityRepositoryIntegrationTest.java",
"{{asPackageFolder infrastructurePackage}}/{{persistence}}/{{entity.className}}RepositoryIntegrationTest.java", JAVA, skipEntityRepository, true);
ts.addTemplate(ts.entityTemplates, "src/test/java","infrastructure/{{persistence}}/{{style}}/inmemory/InMemory{{capitalizeFirst persistence}}Repository.java",
"{{asPackageFolder infrastructurePackage}}/{{persistence}}/inmemory/InMemory{{capitalizeFirst persistence}}Repository.java", JAVA, skipEntityRepository, true);
ts.addTemplate(ts.entityTemplates, "src/test/java","infrastructure/{{persistence}}/{{style}}/inmemory/EntityRepositoryInMemory.java",
"{{asPackageFolder infrastructurePackage}}/{{persistence}}/inmemory/{{entity.className}}RepositoryInMemory.java", JAVA, skipEntityRepository, true);
ts.addTemplate(ts.entityTemplates, "src/test/java","infrastructure/{{persistence}}/{{style}}/inmemory/InMemory{{capitalizeFirst persistence}}Repository.java",
"{{asPackageFolder infrastructurePackage}}/{{persistence}}/inmemory/InMemory{{capitalizeFirst persistence}}Repository.java", JAVA, skipEntityRepository, true);

ts.addTemplate(ts.enumTemplates, "src/main/java", "core/domain/common/Enum.java",
"{{asPackageFolder entitiesPackage}}/{{enum.name}}.java", JAVA, null, false);
ts.addTemplate(ts.inputEnumTemplates, "src/main/java", "core/inbound/dtos/Enum.java",
"{{asPackageFolder inboundDtosPackage}}/{{enum.name}}.java", JAVA, null, false);

ts.addTemplate(ts.inputTemplates, "src/main/java", "core/inbound/dtos/InputOrOutput.java",
"{{asPackageFolder inboundDtosPackage}}/{{entity.className}}.java", JAVA, null, false);
ts.addTemplate(ts.outputTemplates, "src/main/java", "core/inbound/dtos/InputOrOutput.java",
"{{asPackageFolder inboundDtosPackage}}/{{entity.className}}.java", JAVA, null, false);

ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/inbound/Service.java",
"{{asPackageFolder inboundPackage}}/{{service.name}}.java", JAVA, null, false);
ts.addTemplate(ts.serviceTemplates, "src/main/java", "core/implementation/{{persistence}}/{{style}}/ServiceImpl.java",
"{{asPackageFolder coreImplementationPackage}}/{{service.name}}Impl.java", JAVA, null, true);
ts.addTemplate(ts.serviceTemplates, "src/test/java", "core/implementation/{{persistence}}/{{style}}/ServiceTest.java",
"{{asPackageFolder coreImplementationPackage}}/{{service.name}}Test.java", JAVA, null, true);

ts.addTemplate(ts.allServicesTemplates, "src/test/java", "config/InMemoryTestsConfig.java",
"{{asPackageFolder configPackage}}/InMemoryTestsConfig.java", JAVA, null, false);
ts.addTemplate(ts.allServicesTemplates, "src/test/java", "config/InMemoryTestsManualContext.java",
"{{asPackageFolder configPackage}}/InMemoryTestsManualContext.java", JAVA, null, false);

ts.addTemplate(ts.singleTemplates, "src/main/java", "core/inbound/dtos/package-info.java",
"{{asPackageFolder inboundDtosPackage}}/package-info.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/main/java", "infrastructure/package-info.java",
"{{asPackageFolder infrastructurePackage}}/package-info.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "ArchitectureTest.java",
"{{asPackageFolder basePackage}}/ArchitectureTest.java", JAVA, null, true);

return ts;
}
protected boolean isGenerateEntity(Map entity) {
boolean skip = JSONPath.get(entity, "options.skip", false);
String entityName = (String) entity.get("name");
return !skip && (entities.isEmpty() || entities.contains(entityName));
}

@Override
public Map<String, Object> asConfigurationMap() {
var config = super.asConfigurationMap();
config.put("idJavaType", this.persistence == PersistenceType.jpa ? "Long" : "String");
// config.put("webFlavor", style == ProgrammingStyle.imperative ? WebFlavorType.mvc : WebFlavorType.webflux);
return config;
}

}
Loading

0 comments on commit e42b7c0

Please sign in to comment.