Skip to content

Commit

Permalink
Merge pull request #27 from ZenWave360/next
Browse files Browse the repository at this point in the history
adds support for rich 'aggregate' modeling
  • Loading branch information
ivangsa authored Mar 30, 2024
2 parents c91adaf + 5860d32 commit 537f7c6
Show file tree
Hide file tree
Showing 60 changed files with 1,255 additions and 347 deletions.
2 changes: 1 addition & 1 deletion e2e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.github.zenwave360.zenwave-sdk</groupId>
<artifactId>zenwave-sdk</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<artifactId>e2e</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
<configuration>
<generatorName>jsonschema2pojo</generatorName>
<configOptions>
<modelPackage>io.zenwave360.example.core.domain.events</modelPackage>
<modelPackage>io.zenwave360.example.core.outbound.events.dtos</modelPackage>
<!-- <jsonschema2pojo.includeTypeInfo>true</jsonschema2pojo.includeTypeInfo>-->
<jsonschema2pojo.isUseJakartaValidation>true</jsonschema2pojo.isUseJakartaValidation>
<jsonschema2pojo.useLongIntegers>true</jsonschema2pojo.useLongIntegers>
Expand All @@ -214,7 +214,7 @@
<role>provider</role>
<style>imperative</style>
<!-- <transactionalOutbox>mongodb</transactionalOutbox>-->
<modelPackage>io.zenwave360.example.core.domain.events</modelPackage>
<modelPackage>io.zenwave360.example.core.outbound.events.dtos</modelPackage>
<producerApiPackage>io.zenwave360.example.core.outbound.events</producerApiPackage>
<consumerApiPackage>io.zenwave360.example.adapters.commands</consumerApiPackage>
</configOptions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,25 @@ apis {
}
}

// == Aggregates =============================

/**
* Delivery Aggregate
*/
aggregate DeliveryAggregate (Delivery) {

createDelivery(DeliveryInput) withEvents DeliveryStatusUpdated

onOrderStatusUpdated(OrderStatusUpdated) withEvents DeliveryStatusUpdated

updateDeliveryStatus(DeliveryStatusInput) withEvents DeliveryStatusUpdated

}


// == Entities =============================

@aggregate
//@aggregate
entity Delivery {
orderId String required
customer Customer {
Expand Down Expand Up @@ -120,7 +136,7 @@ input DeliveryStatusInput {
}

@rest("/delivery")
service DeliveryService for (Delivery) {
service DeliveryService for (DeliveryAggregate) {

@asyncapi({api: OrdersAsyncAPI, channel: "OrderCreatedChannel"})
createDelivery(DeliveryInput) Delivery withEvents DeliveryStatusUpdated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
<inputSpec>${project.basedir}/src/main/resources/apis/asyncapi.yml</inputSpec>
<generatorName>jsonschema2pojo</generatorName>
<configOptions>
<modelPackage>${basePackage}.core.domain.events</modelPackage>
<modelPackage>${basePackage}.core.outbound.events.dtos</modelPackage>
<jsonschema2pojo.isUseJakartaValidation>true</jsonschema2pojo.isUseJakartaValidation>
<jsonschema2pojo.useLongIntegers>true</jsonschema2pojo.useLongIntegers>
</configOptions>
Expand All @@ -97,7 +97,7 @@
<inputSpec>${project.basedir}/src/main/resources/apis/asyncapi.yml</inputSpec>
<role>provider</role>
<style>imperative</style>
<modelPackage>${basePackage}.core.domain.events</modelPackage>
<modelPackage>${basePackage}.core.outbound.events.dtos</modelPackage>
<producerApiPackage>${basePackage}.core.outbound.events</producerApiPackage>
<consumerApiPackage>${basePackage}.adapters.commands</consumerApiPackage>
</configOptions>
Expand Down
2 changes: 1 addition & 1 deletion plugins/asyncapi-jsonschema2pojo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.github.zenwave360.zenwave-sdk</groupId>
<artifactId>plugins-parent</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<groupId>io.github.zenwave360.zenwave-sdk.plugins</groupId>
Expand Down
2 changes: 1 addition & 1 deletion plugins/asyncapi-spring-cloud-streams3/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.github.zenwave360.zenwave-sdk</groupId>
<artifactId>plugins-parent</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<groupId>io.github.zenwave360.zenwave-sdk.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ public class EventsProducerInMemoryContext {
{{#each services as |service|}}
private {{service.apiClassName}}Captor {{asInstanceName service.apiClassName}}Captor = new {{service.apiClassName}}Captor();

public <T extends I{{service.apiClassName}}> T {{asInstanceName service.apiClassName}}() {
return (T) {{asInstanceName service.apiClassName}}Captor;
public {{service.apiClassName}}Captor {{asInstanceName service.apiClassName}}() {
return {{asInstanceName service.apiClassName}}Captor;
}
{{/each}}
}
2 changes: 1 addition & 1 deletion plugins/backend-application-default/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.github.zenwave360.zenwave-sdk</groupId>
<artifactId>plugins-parent</artifactId>
<version>1.5.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<name>${project.groupId}:${project.artifactId}</name>
<groupId>io.github.zenwave360.zenwave-sdk.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.zenwave360.sdk.utils.NamingUtils;
import io.zenwave360.sdk.zdl.ZDLFindUtils;
import io.zenwave360.sdk.zdl.ZDLHttpUtils;
import io.zenwave360.sdk.zdl.ZDLJavaSignatureUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import com.github.jknack.handlebars.Options;
Expand All @@ -26,6 +25,50 @@ public class BackendApplicationDefaultHelpers {
this.generator = generator;
}

public String logMethodCall(Map method, Options options) {
var zdl = options.get("zdl");
var methodName = (String) method.get("name");
var parameters = ZDLJavaSignatureUtils.methodParametersCallSignature(method, (Map) zdl, generator.inputDTOSuffix);
var parameterPlaceHolders = Arrays.stream(parameters.split(", ")).map(p -> "{}").collect(Collectors.joining(" "));
if(parameters.isEmpty()) {
return String.format("log.debug(\"Request %s\");", methodName);
}
return String.format("log.debug(\"Request %s: %s\", %s);", methodName, parameterPlaceHolders, parameters);
}

public Map findAggregateCommandsForMethod(Map method, Options options) {
var zdl = options.get("zdl");
var aggregatesCommandsForMethod = ZDLFindUtils.findAggregateCommandsForMethod((Map) zdl, method);
if(aggregatesCommandsForMethod.isEmpty()) {
return Map.of("templateFile", "aggregates-void-methodBody");
}
if(aggregatesCommandsForMethod.size() == 1) {
var aggregate = JSONPath.get(aggregatesCommandsForMethod, "$[0].aggregate");
var entity = JSONPath.get(aggregatesCommandsForMethod, "$[0].entity");
var command = JSONPath.get(aggregatesCommandsForMethod, "$[0].command");
var crudMethod = JSONPath.get(aggregatesCommandsForMethod, "$[0].crudMethod");
if(aggregate != null && command != null) {
return Map.of("templateFile", "aggregates-commands-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
if(aggregate != null && crudMethod != null) {
return Map.of("templateFile", "aggregates-crud-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
if(entity != null && crudMethod != null) {
return Map.of("templateFile", "entities-crud-methodBody", "entity", entity, "crudMethod", crudMethod);
}
if(entity != null) {
return Map.of("templateFile", "entities-methodBody", "entity", entity);
}
}
return Map.of("templateFile", "aggregates-commands-methodBody", "aggregatesCommandsForMethod", aggregatesCommandsForMethod);
}
public boolean isWriteMethod(Map method, Options options) {
var methodName = (String) method.get("name");
var hasId = method.get("paramId") != null;
return hasId || methodName.startsWith("create") || methodName.startsWith("update") || methodName.startsWith("delete");
}

@Deprecated
public boolean isCrudMethod(String crudMethodPrefix, Options options) {
var entity = (Map<String, Object>) options.hash("entity");
var entityName = (String) entity.get("name");
Expand All @@ -42,12 +85,40 @@ public boolean isCrudMethod(String crudMethodPrefix, Options options) {
return isCrudMethod;
}

public List<Map> serviceAggregates(Map service, Options options) {
var zdl = options.get("zdl");
var aggregateNames = JSONPath.get(service, "aggregates", Collections.emptyList());
return aggregateNames.stream()
.map(aggregateName -> JSONPath.get(zdl, "$.allEntitiesAndEnums." + aggregateName, Map.of()))
.filter(aggregate -> "aggregates".equals(aggregate.get("type")))
.collect(Collectors.toList());
}

public boolean includeDomainEvents(Object service, Options options) {
var zdl = options.get("zdl");
return !JSONPath.get(zdl, "$.aggregates[*].commands[*].withEvents", List.of()).isEmpty();
}

public List<Map> aggregateEvents(Map<String, Object> aggregate, Options options) {
var zdl = options.get("zdl");
return ZDLFindUtils.aggregateEvents(aggregate).stream().map(event -> (Map) JSONPath.get(zdl, "$.events." + event)).toList();
}

public Collection<String> findAggregateInputs(Map aggregate, Options options) {
return new HashSet<>(JSONPath.get(aggregate, "$.commands[*].parameter", List.of()));
}

public String findEntityAggregate(String entityName, Options options) {
var zdl = options.get("zdl");
var aggregateNames = JSONPath.get(zdl, "$.aggregates[*][?(@.aggregateRoot == '" + entityName + "')].name", List.of());
return aggregateNames.isEmpty()? null : (String) aggregateNames.get(0);
}

public Collection<String> findServiceInputs(Map service, Options options) {
var zdl = options.get("zdl");
var aggregateName = (String) aggregate.get("name");
var inputDTOSuffix = (String) options.get("inputDTOSuffix");
Set<String> inputs = new HashSet<String>();
inputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].parameter"));
inputs.addAll(JSONPath.get(service, "$.methods[*].parameter"));
// inputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].returnType"));
// inputs.add(aggregateName + inputDTOSuffix);
inputs = inputs.stream().filter(Objects::nonNull).collect(Collectors.toSet());
Expand All @@ -59,12 +130,14 @@ public Collection<String> findAggregateInputs(Map aggregate, Options options) {
return inputs;
}

public Collection<String> findAggregateOutputs(Map aggregate, Options options) {
public Collection<String> findServiceOutputs(Map service, Options options) {
var zdl = options.get("zdl");
var aggregateName = (String) aggregate.get("name");
var serviceAggregates = (List<String>) service.get("aggregates");
var aggregatesAndEntities = new HashSet<>(serviceAggregates);
serviceAggregates.stream().map(aggregate -> (String) JSONPath.get(zdl, "$.aggregates." + aggregate + ".aggregateRoot")).filter(Objects::nonNull).forEach(aggregatesAndEntities::add);
Set<String> outputs = new HashSet<String>();
outputs.addAll(JSONPath.get(zdl, "$.services[*][?('" + aggregateName + "' in @.aggregates)].methods[*].returnType"));
outputs = outputs.stream().filter(input -> input != null && !aggregateName.equals(input)).collect(Collectors.toSet());
outputs.addAll(JSONPath.get(service, "$.methods[*].returnType"));
outputs = outputs.stream().filter(input -> input != null && !aggregatesAndEntities.contains(input)).collect(Collectors.toSet());
return outputs;
}

Expand Down Expand Up @@ -100,6 +173,15 @@ public List<Map<String, Object>> methodsWithEvents(Map<String, Object> zdl, Opti
return ZDLFindUtils.methodsWithEvents(zdl); // TODO review usages
}

public Collection<Map> domainEventsWithAsyncapiAnnotation(Map<String, Object> zdl, Options options) {
var domainEventNames = JSONPath.get(zdl, "$.aggregates[*].commands[*].withEvents", List.of()).stream()
.flatMap(e -> e instanceof List? ((List<?>) e).stream() : Stream.of(e))
.collect(Collectors.toSet());
return domainEventNames.stream().map(eventName -> (Map) JSONPath.get(zdl, "$.events." + eventName))
.filter(event -> JSONPath.get(event, "options.asyncapi") != null)
.toList();
}

public Collection<Map<String, Object>> listOfPairEventEntity(Map<String, Object> zdl, Options options) {
var result = new HashMap<String, Object>();
var methods = ZDLFindUtils.methodsWithEvents(zdl);
Expand Down Expand Up @@ -182,20 +264,22 @@ public String wrapWithMapper(Map<String, Object> entity, Options options) {
return "";
}
var returnTypeIsArray = (Boolean) method.getOrDefault("returnTypeIsArray", false);
var isReturnTypeOptional = (Boolean) method.getOrDefault("returnTypeIsOptional", false);
var instanceName = returnTypeIsArray? entity.get("instanceNamePlural") : entity.get("instanceName");
var serviceInstanceName = NamingUtils.asInstanceName((String) method.get("serviceName"));
if (Objects.equals(entity.get("name"), returnType.get("name"))) {
if(JSONPath.get(method, "options.paginated", false)) {
return "page";
}
// if(JSONPath.get(method, "options.paginated", false)) {
// return (String) instanceName;
// }
return (String) instanceName;
} else {
if(returnTypeIsArray) {
if(JSONPath.get(method, "options.paginated", false)) {
return String.format("%sMapper.as%sPage(%s)", instanceName, returnType.get("className"), "page");
return String.format("%sMapper.as%sPage(%s)", serviceInstanceName, returnType.get("className"), instanceName);
}
return String.format("%sMapper.as%sList(%s)", instanceName, returnType.get("className"), instanceName);
return String.format("%sMapper.as%sList(%s)", serviceInstanceName, returnType.get("className"), instanceName);
} else {
return String.format("%sMapper.as%s(%s)", instanceName, returnType.get("className"), instanceName);
return String.format("%sMapper.as%s(%s)", serviceInstanceName, returnType.get("className"), instanceName);
}
}
}
Expand All @@ -220,7 +304,14 @@ public String javaType(Map field, Options options) {

public Object findEntity(String entityName, Options options) {
var zdl = options.get("zdl");
return JSONPath.get(zdl, "entities." + entityName);
var entity = JSONPath.get(zdl, "$.allEntitiesAndEnums." + entityName);
if("entities".equals(JSONPath.get(entity, "type"))) {
return entity;
}
if("aggregates".equals(JSONPath.get(entity, "type"))) {
return findEntity(JSONPath.get(entity, "$.aggregateRoot"), options);
}
return null;
}

public String inputDTOSuffix(Object entity, Options options) {
Expand Down Expand Up @@ -305,14 +396,14 @@ public String validationPatternJava(String pattern, Options options) {
return pattern.replace("\\", "").replace("\\", "\\\\");
}

public Object skipEntityRepository(Object context, Options options) {
Map entity = (Map) context;
return generator.skipEntityRepository.apply(Map.of("entity", entity));
public Object skipEntityRepository(Map entity, Options options) {
var zdl = options.get("zdl");
return generator.skipEntityRepository.apply(Map.of("zdl", zdl, "entity", entity));
};

public Object skipEntityId(Object context, Options options) {
Map entity = (Map) context;
return generator.skipEntityId.apply(Map.of("entity", entity));
public Object skipEntityId(Map entity, Options options) {
var zdl = options.get("zdl");
return generator.skipEntityId.apply(Map.of("zdl", zdl, "entity", entity));
};

public Object addExtends(Object entity, Options options) {
Expand Down
Loading

0 comments on commit 537f7c6

Please sign in to comment.