Skip to content

Commit

Permalink
Merge pull request #9 from ZenWave360/feature/zdl-1.2
Browse files Browse the repository at this point in the history
adds new 'aggregate' section
  • Loading branch information
ivangsa authored Mar 26, 2024
2 parents ace7711 + 238b6a6 commit 4d4643c
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 22 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>io.github.zenwave360.zenwave-sdk</groupId>
<artifactId>zdl-jvm</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>1.2.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down
24 changes: 17 additions & 7 deletions src/main/antlr4/io.github.zenwave360.zdl.antlr/Zdl.g4
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ MANY_TO_ONE: 'ManyToOne';
ONE_TO_MANY: 'OneToMany';
ONE_TO_ONE: 'OneToOne';
SERVICE: 'service';
AGGREGATE: 'aggregate';
PARAM_ID: 'id';
FOR: 'for';
TO: 'to';
Expand Down Expand Up @@ -115,15 +116,15 @@ PATTERN_REGEX: '/' .*? '/' ; // TODO: improve regex
ERRCHAR: . -> channel(HIDDEN);
// Rules
zdl: global_javadoc? legacy_constants config? apis? (policies | entity | enum | input | output | event | relationships | service | service_legacy)* EOF;
zdl: global_javadoc? legacy_constants config? apis? (policies | aggregate | entity | enum | input | output | event | relationships | service | service_legacy)* EOF;
global_javadoc: JAVADOC;
javadoc: JAVADOC;
suffix_javadoc: JAVADOC;
legacy_constants: LEGACY_CONSTANT*;
// values
keyword: ID | CONFIG | APIS | PLUGINS | DISABLED | ASYNCAPI | OPENAPI | ENTITY | INPUT | OUTPUT | EVENT | RELATIONSHIP | SERVICE | PARAM_ID | FOR | TO | WITH_EVENTS | WITH | REQUIRED | UNIQUE | MIN | MAX | MINLENGTH | MAXLENGTH | PATTERN;
keyword: ID | CONFIG | APIS | PLUGINS | DISABLED | ASYNCAPI | OPENAPI | ENTITY | AGGREGATE | INPUT | OUTPUT | EVENT | RELATIONSHIP | SERVICE | PARAM_ID | FOR | TO | WITH_EVENTS | WITH | REQUIRED | UNIQUE | MIN | MAX | MINLENGTH | MAXLENGTH | PATTERN;
complex_value: value | array | object;
value: simple | object;
Expand Down Expand Up @@ -227,20 +228,29 @@ relationship_field_name: keyword;
relationship_description_field: ID;
relationship_field_required: REQUIRED;
// aggregates
aggregate: javadoc? annotations AGGREGATE aggregate_name LPAREN aggregate_root RPAREN LBRACE aggregate_command* RBRACE;
aggregate_name: ID;
aggregate_root: ID;
aggregate_command: javadoc? annotations aggregate_command_name LPAREN aggregate_command_parameter? RPAREN with_events? suffix_javadoc?;
aggregate_command_name: ID;
aggregate_command_parameter: ID;
// services
service: javadoc? annotations SERVICE service_name FOR LPAREN service_aggregates RPAREN LBRACE service_method* RBRACE;
service_name: ID;
service_aggregates: ID (COMMA ID)*;
service_method: javadoc? annotations service_method_name LPAREN service_method_parameter_id? COMMA? service_method_parameter? RPAREN service_method_return? service_method_with_events? suffix_javadoc?;
service_method: javadoc? annotations service_method_name LPAREN service_method_parameter_id? COMMA? service_method_parameter? RPAREN service_method_return? with_events? suffix_javadoc?;
service_method_name: ID;
service_method_parameter_id: PARAM_ID;
service_method_parameter: ID;
service_method_return: ID | ID ARRAY | ID OPTIONAL;
service_method_with_events: WITH_EVENTS (service_method_events)*;
service_method_events: service_method_event | service_method_events_or;
service_method_event: ID;
service_method_events_or: LBRACK service_method_event (OR service_method_event)* RBRACK;
with_events: WITH_EVENTS (with_events_events)*;
with_events_events: with_events_event| with_events_events_or;
with_events_event: ID;
with_events_events_or: LBRACK with_events_event (OR with_events_event)* RBRACK;
service_legacy: SERVICE service_aggregates WITH ID;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/github/zenwave360/zdl/ZdlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

public class ZdlParser {

public static final List<String> STANDARD_FIELD_TYPES = List.of("String", "Integer", "Long", "int", "long", "BigDecimal", "Float", "float", "Double", "double", "Enum", "Boolean", "boolean", "LocalDate", "ZonedDateTime", "Instant", "Duration", "UUID", "byte", "byte[]", "Blob", "AnyBlob", "ImageBlob", "TextBlob");
public static final List<String> STANDARD_FIELD_TYPES = List.of("String", "Integer", "Long", "int", "long", "BigDecimal", "Float", "float", "Double", "double", "Enum", "Boolean", "boolean", "LocalDate", "LocalDateTime", "ZonedDateTime", "Instant", "Duration", "UUID", "byte", "byte[]", "Blob", "AnyBlob", "ImageBlob", "TextBlob");

public static final List<String> STANDARD_VALIDATIONS = List.of("required", "unique", "min", "max", "minlength", "maxlength", "pattern");

Expand Down
77 changes: 67 additions & 10 deletions src/main/java/io/github/zenwave360/zdl/antlr/ZdlListenerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,62 @@ public void exitService_legacy(io.github.zenwave360.zdl.antlr.ZdlParser.Service_
currentStack.pop();
}

@Override
public void enterAggregate(ZdlParser.AggregateContext ctx) {
var aggregateName = getText(ctx.aggregate_name());
var javadoc = javadoc(ctx.javadoc());
var aggregateRoot = getText(ctx.aggregate_root());
currentStack.push(new FluentMap()
.with("name", aggregateName)
.with("type", "aggregates")
.with("className", camelCase(aggregateName))
.with("javadoc", javadoc)
.with("aggregateRoot", aggregateRoot)
.with("commands", new FluentMap())
);
model.appendTo("aggregates", aggregateName, currentStack.peek());

var name = currentStack.peek().get("name");
var location = "aggregates." + name;
model.setLocation(location, getLocations(ctx));
model.setLocation(location + ".name", getLocations(ctx.aggregate_name()));
model.setLocation(location + ".aggregateRoot", getLocations(ctx.aggregate_root()));
}

@Override
public void exitAggregate(io.github.zenwave360.zdl.antlr.ZdlParser.AggregateContext ctx) {
currentStack.pop();
}

@Override
public void enterAggregate_command(io.github.zenwave360.zdl.antlr.ZdlParser.Aggregate_commandContext ctx) {
var aggregateName = getText(((io.github.zenwave360.zdl.antlr.ZdlParser.AggregateContext) ctx.getParent()).aggregate_name());
var commandName = getText(ctx.aggregate_command_name());
var location = "aggregates." + aggregateName + ".commands." + commandName;
var parameter = ctx.aggregate_command_parameter() != null? ctx.aggregate_command_parameter().getText() : null;
var withEvents = getServiceMethodEvents(location, ctx.with_events());
var javadoc = javadoc(first(ctx.javadoc(), ctx.suffix_javadoc()));

var method = new FluentMap()
.with("name", commandName)
.with("aggregateName", aggregateName)
.with("parameter", parameter)
.with("withEvents", withEvents)
.with("javadoc", javadoc)
;
currentStack.peek().appendTo("commands", commandName, method);
currentStack.push(method);

model.setLocation(location, getLocations(ctx));
model.setLocation(location + ".name", getLocations(ctx.aggregate_command_name()));
model.setLocation(location + ".parameter", getLocations(ctx.aggregate_command_parameter()));
}

@Override
public void exitAggregate_command(ZdlParser.Aggregate_commandContext ctx) {
currentStack.pop();
}

@Override
public void enterService(io.github.zenwave360.zdl.antlr.ZdlParser.ServiceContext ctx) {
var serviceName = getText(ctx.service_name());
Expand Down Expand Up @@ -471,7 +527,7 @@ public void enterService_method(io.github.zenwave360.zdl.antlr.ZdlParser.Service
var returnType = ctx.service_method_return() != null? ctx.service_method_return().ID().getText() : null;
var returnTypeIsArray = ctx.service_method_return() != null? ctx.service_method_return().ARRAY() != null : null;
var returnTypeIsOptional = ctx.service_method_return() != null? ctx.service_method_return().OPTIONAL() != null : null;
var withEvents = getServiceMethodEvents(location, ctx.service_method_with_events());
var withEvents = getServiceMethodEvents(location, ctx.with_events());
var javadoc = javadoc(first(ctx.javadoc(), ctx.suffix_javadoc()));

var method = new FluentMap()
Expand Down Expand Up @@ -499,23 +555,23 @@ public void exitService_method(io.github.zenwave360.zdl.antlr.ZdlParser.Service_
currentStack.pop();
}

private List<Object> getServiceMethodEvents(String location, io.github.zenwave360.zdl.antlr.ZdlParser.Service_method_with_eventsContext ctx) {
private List<Object> getServiceMethodEvents(String location, io.github.zenwave360.zdl.antlr.ZdlParser.With_eventsContext ctx) {
model.setLocation(location + ".withEvents", getLocations(ctx));
var events = new ArrayList<>();
if (ctx != null) {
AtomicInteger i = new AtomicInteger(0);
ctx.service_method_events().forEach(event -> {
if (event.service_method_event() != null) {
var eventName = getText(event.service_method_event());
ctx.with_events_events().forEach(event -> {
if (event.with_events_event() != null) {
var eventName = getText(event.with_events_event());
events.add(eventName);
model.setLocation(location + ".withEvents." + i.get(), getLocations(event.service_method_event()));
model.setLocation(location + ".withEvents." + eventName, getLocations(event.service_method_event()));
model.setLocation(location + ".withEvents." + i.get(), getLocations(event.with_events_event()));
model.setLocation(location + ".withEvents." + eventName, getLocations(event.with_events_event()));
}
if (event.service_method_events_or() != null) {
var orEvents = event.service_method_events_or().service_method_event().stream().map(ParseTree::getText).collect(Collectors.toList());
if (event.with_events_events_or() != null) {
var orEvents = event.with_events_events_or().with_events_event().stream().map(ParseTree::getText).collect(Collectors.toList());
events.add(orEvents);
int j = 0;
for (var eventContext: event.service_method_events_or().service_method_event()) {
for (var eventContext: event.with_events_events_or().with_events_event()) {
model.setLocation(location + ".withEvents." + i.get() + "." + j, getLocations(eventContext));
model.setLocation(location + ".withEvents." + getText(eventContext), getLocations(eventContext));
j++;
Expand All @@ -534,6 +590,7 @@ public void enterEvent(io.github.zenwave360.zdl.antlr.ZdlParser.EventContext ctx
var kebabCase = kebabCase(name);
currentStack.push(new FluentMap()
.with("name", name)
.with("className", camelCase(name))
.with("type", "events")
.with("kebabCase", kebabCase)
.with("javadoc", javadoc)
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/github/zenwave360/zdl/antlr/ZdlModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class ZdlModel extends FluentMap {
public ZdlModel() {
put("config", new FluentMap());
put("apis", new FluentMap());
put("aggregates", new FluentMap());
put("entities", new FluentMap());
put("enums", new FluentMap());
put("relationships", new FluentMap());
Expand All @@ -19,6 +20,10 @@ public ZdlModel() {
put("problems", new ArrayList<>());
}

public FluentMap getAggregates() {
return (FluentMap) get("aggregates");
}

public FluentMap getEntities() {
return (FluentMap) get("entities");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
public class ZdlModelPostProcessor {

public static ZdlModel postProcess(ZdlModel model) {
var aggregates = model.getAggregates();
var entities = model.getEntities();
var inputs = model.getInputs();
var outputs = model.getOutputs();
Expand Down Expand Up @@ -41,6 +42,9 @@ public static ZdlModel postProcess(ZdlModel model) {
}

var allEntitiesAndEnums = new HashMap<>();
if(aggregates != null) {
allEntitiesAndEnums.putAll(aggregates);
}
if(entities != null) {
allEntitiesAndEnums.putAll(entities);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public ZdlModel validate(ZdlModel model) {
validateEntitiesFields(model, "inputs");
validateEntitiesFields(model, "outputs");
validateEntitiesFields(model, "events");
validateAggregates(model);
validateServices(model);
validateRelationships(model);
return model;
Expand Down Expand Up @@ -107,6 +108,44 @@ else if ("events".equals(entityType)) {
}
}

private List<Map> validateAggregates(ZdlModel model) {
var services = JSONPath.get(model, "$.aggregates", Map.<String, Object>of());
for (Map.Entry<String, Object> service : services.entrySet()) {
var aggregateRoot = (String) JSONPath.get(service.getValue(), "$.aggregateRoot");
if(aggregateRoot == null || !isEntity(model, aggregateRoot)) {
model.addProblem(path("aggregates", service.getKey(), "aggregateRoot"), aggregateRoot,"%s is not an entity");
}


var methods = JSONPath.get(service.getValue(), "$.commands[*]", List.<Map>of());
for (Map method : methods) {
var methodName = (String) JSONPath.get(method, "$.name");
var parameter = (String) JSONPath.get(method, "$.parameter");
if(parameter != null && !isEntity(model, parameter) && !isInput(model, parameter)) {
model.addProblem(path("aggregates", service.getKey(), "commands", methodName, "parameter"), parameter, "%s is not an entity or input");
}
List<Object> withEvents = (List) method.getOrDefault("withEvents", List.of());
for (int i = 0; i < withEvents.size(); i++) {
var event = withEvents.get(i);
if (event instanceof List) {
for (int j = 0; j < ((List<?>) event).size(); j++) {
var innerEvent = (String) ((List<?>) event).get(j);
if(!isEvent(model, innerEvent)) {
model.addProblem(path("aggregates", service.getKey(), "commands", methodName, "withEvents", i+"", j+""), innerEvent, "%s is not an event");
}
}
} else {
if(!isEvent(model, (String) event)) {
model.addProblem(path("aggregates", service.getKey(), "commands", methodName, "withEvents", i+""), (String) event, "%s is not an event");
}
}
}
}
}

return null;
}

private List<Map> validateServices(ZdlModel model) {
var services = JSONPath.get(model, "$.services", Map.<String, Object>of());
for (Map.Entry<String, Object> service : services.entrySet()) {
Expand Down Expand Up @@ -183,7 +222,8 @@ private boolean isEntityOrEnum(ZdlModel model, String entityName) {
}

private boolean isAggregate(ZdlModel model, String entityName) {
return JSONPath.get(model, "$.entities." + entityName + ".options.aggregate", false);
return JSONPath.get(model, "$.aggregates." + entityName) != null
|| JSONPath.get(model, "$.entities." + entityName + ".options.aggregate", false);
}

private List<String> methodEventsFlatList(Map<String, Object> method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public void parseZdl_Problems() throws Exception {

ZdlModel model = parseZdl("src/test/resources/problems.zdl");
var problems = get(model, "$.problems", List.of());
assertEquals(13, problems.size());
assertEquals(15, problems.size());
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model));
}

Expand All @@ -160,7 +160,7 @@ public void parseZdl_Problems_ExtraTypes() throws Exception {

ZdlModel model = parseZdl("src/test/resources/problems.zdl", List.of("OrderStatusX"));
var problems = get(model, "$.problems", List.of());
assertEquals(11, problems.size());
assertEquals(13, problems.size());
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model));
}

Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/complete.zdl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ apis {

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

aggregate CustomerOrderAggregate(CustomerOrder) {
customerOrderCommand(CustomerOrderInput) withEvents OrderEvent
}

@aggregate
entity CustomerOrder {
orderTime Instant = "Instant.now()" required /** orderTime javadoc */
Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/problems.zdl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ apis {

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

aggregate CustomerOrderAggregate(CustomerOrderX) {
commandName(CustomerOrderInputXX)
}

@aggregate
entity CustomerOrder {
orderTime Instant = "Instant.now()" required /** orderTime javadoc */
Expand Down

0 comments on commit 4d4643c

Please sign in to comment.