Skip to content

Commit

Permalink
adds support for @imports and improves nested fields and relationship…
Browse files Browse the repository at this point in the history
…s validations.
  • Loading branch information
ivangsa committed Jul 9, 2024
1 parent 87f3298 commit 3cbc000
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 17 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<version>0.8.12</version>
<configuration>
<excludes>
<exclude>**/Inflector.*</exclude>
Expand Down
16 changes: 12 additions & 4 deletions src/main/antlr4/io.github.zenwave360.zdl.antlr/Zdl.g4
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ARRAY: '[]';
OPTIONAL: '?';

// Keywords
IMPORT: 'import';
CONFIG: 'config';
APIS: 'apis';
PLUGINS: 'plugins';
Expand Down Expand Up @@ -116,18 +117,22 @@ PATTERN_REGEX: '/' .*? '/' ; // TODO: improve regex
ERRCHAR: . -> channel(HIDDEN);
// Rules
zdl: global_javadoc? legacy_constants config? apis? (policies | aggregate | entity | enum | input | output | event | relationships | service | service_legacy)* EOF;
zdl: imports global_javadoc? legacy_constants config? apis? (policies | aggregate | entity | enum | input | output | event | relationships | service | service_legacy)* EOF;
imports: ('@import' LPAREN import_value RPAREN)*;
import_value: string;
global_javadoc: JAVADOC;
javadoc: JAVADOC;
suffix_javadoc: JAVADOC;
legacy_constants: LEGACY_CONSTANT*;
// values
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;
keyword: ID | IMPORT | 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;
string: keyword | SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING;
simple: keyword | SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING | INT | NUMBER | TRUE | FALSE | NULL;
pair: keyword COLON value;
object: LBRACE pair (COMMA pair)* RBRACE;
Expand Down Expand Up @@ -192,7 +197,7 @@ field_validations: field_validation_name (LPAREN field_validation_value RPAREN)?
field_validation_name: REQUIRED | UNIQUE | MIN | MAX | MINLENGTH | MAXLENGTH | PATTERN;
field_validation_value: INT | ID | PATTERN_REGEX;
nested_field_validations: nested_field_validation_name (LPAREN nested_field_validation_value RPAREN)?;
nested_field_validation_name: REQUIRED | UNIQUE;
nested_field_validation_name: REQUIRED | UNIQUE | MIN | MAX;
nested_field_validation_value: INT | ID | PATTERN_REGEX;
// enums
Expand Down Expand Up @@ -222,11 +227,14 @@ relationship_type: MANY_TO_MANY | MANY_TO_ONE| ONE_TO_MANY | ONE_TO_ONE;
relationship: relationship_from TO relationship_to;
relationship_from: javadoc? annotations relationship_definition;
relationship_to: javadoc? annotations relationship_definition;
relationship_definition: relationship_entity_name (LBRACE relationship_field_name (LPAREN relationship_description_field RPAREN)? relationship_field_required? RBRACE)?;
relationship_definition: relationship_entity_name (LBRACE relationship_field_name (LPAREN relationship_description_field RPAREN)? relationship_field_validations RBRACE)?;
relationship_entity_name: ID;
relationship_field_name: keyword;
relationship_description_field: ID;
relationship_field_validations: relationship_field_required? relationship_field_min? relationship_field_max?;
relationship_field_required: REQUIRED;
relationship_field_min: MIN(INT);
relationship_field_max: MAX(INT);
// aggregates
aggregate: javadoc? annotations AGGREGATE aggregate_name LPAREN aggregate_root RPAREN LBRACE aggregate_command* RBRACE;
Expand Down
67 changes: 57 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 @@ -61,6 +61,13 @@ public void enterLegacy_constants(io.github.zenwave360.zdl.antlr.ZdlParser.Legac
ctx.LEGACY_CONSTANT().stream().map(TerminalNode::getText).map(c -> c.split(" *= *")).forEach(c -> model.appendTo("constants", c[0], c[1]));
}

@Override
public void enterImports(ZdlParser.ImportsContext ctx) {
for (ZdlParser.Import_valueContext importValue : ctx.import_value()) {
model.appendToList("imports", getValueText(importValue.string()));
}
}

@Override
public void enterConfig_option(io.github.zenwave360.zdl.antlr.ZdlParser.Config_optionContext ctx) {
var name = ctx.field_name().getText();
Expand Down Expand Up @@ -106,7 +113,7 @@ public void exitApi(io.github.zenwave360.zdl.antlr.ZdlParser.ApiContext ctx) {
}

@Override
public void enterPlugin(ZdlParser.PluginContext ctx) {
public void enterPlugin(io.github.zenwave360.zdl.antlr.ZdlParser.PluginContext ctx) {
var name = getText(ctx.plugin_name());
var javadoc = javadoc(ctx.javadoc());
var disabled = ctx.plugin_disabled().DISABLED() != null;
Expand Down Expand Up @@ -135,21 +142,21 @@ public void enterPlugin(ZdlParser.PluginContext ctx) {
}

@Override
public void enterPlugin_config_option(ZdlParser.Plugin_config_optionContext ctx) {
public void enterPlugin_config_option(io.github.zenwave360.zdl.antlr.ZdlParser.Plugin_config_optionContext ctx) {
var name = getText(ctx.field_name());
var value = getComplexValue(ctx.complex_value());
currentStack.peek().appendTo("config", name, value);
}

@Override
public void enterPlugin_config_cli_option(ZdlParser.Plugin_config_cli_optionContext ctx) {
public void enterPlugin_config_cli_option(io.github.zenwave360.zdl.antlr.ZdlParser.Plugin_config_cli_optionContext ctx) {
var keyword = getText(ctx.keyword());
var value = getText(ctx.simple());
currentStack.peek().appendTo("cliOptions", keyword, value);
}

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

Expand Down Expand Up @@ -283,7 +290,7 @@ public void enterNested_field(io.github.zenwave360.zdl.antlr.ZdlParser.Nested_fi
String entityJavadoc = javadoc(parent.javadoc());
String tableName = getText(parent.entity_table_name());
var validations = processNestedFieldValidations(ctx.nested_field_validations());
((Map)parentField).put("validations", validations);
((FluentMap) parentField).appendTo("validations", validations);
currentStack.push(processEntity(entityName, entityJavadoc, tableName).with("type", currentCollection.split("\\.")[0]));
currentStack.peek().appendTo("options", "embedded", true);
var parenFieldOptions = JSONPath.get(parentField, "options", Map.of());
Expand Down Expand Up @@ -361,14 +368,21 @@ public void enterRelationship(io.github.zenwave360.zdl.antlr.ZdlParser.Relations
var fromField = getText(ctx.relationship_from().relationship_definition().relationship_field_name());
var commentInFrom = javadoc(ctx.relationship_from().javadoc());
var fromOptions = relationshipOptions(ctx.relationship_from().annotations().option());
var isInjectedFieldInFromRequired = ctx.relationship_from().relationship_definition().relationship_field_required() != null;
var isInjectedFieldInFromRequired = isRequired(ctx.relationship_from().relationship_definition());
var injectedFieldInFromDescription = getText(ctx.relationship_from().relationship_definition().relationship_description_field());
var relationshipValidations = relationshipValidations(ctx.relationship_from().relationship_definition());
model.setLocation(location + ".from.entity", getLocations(ctx.relationship_from().relationship_definition().relationship_entity_name()));
model.setLocation(location + ".from.field", getLocations(ctx.relationship_from().relationship_definition().relationship_field_name()));
if (ctx.relationship_from().relationship_definition().relationship_field_validations() != null) {
model.setLocation(location + ".from.validations", getLocations(ctx.relationship_from().relationship_definition().relationship_field_validations()));
model.setLocation(location + ".from.validations.min", getLocations(ctx.relationship_from().relationship_definition().relationship_field_validations().relationship_field_min()));
model.setLocation(location + ".from.validations.max", getLocations(ctx.relationship_from().relationship_definition().relationship_field_validations().relationship_field_max()));
}
relationship.with("from", from)
.with("commentInFrom", commentInFrom)
.with("injectedFieldInFrom", fromField)
.with("fromOptions", fromOptions)
.with("fromValidations", relationshipValidations)
.with("injectedFieldInFromDescription", injectedFieldInFromDescription)
.with("isInjectedFieldInFromRequired", isInjectedFieldInFromRequired);
}
Expand All @@ -378,29 +392,62 @@ public void enterRelationship(io.github.zenwave360.zdl.antlr.ZdlParser.Relations
var toField = getText(ctx.relationship_to().relationship_definition().relationship_field_name());
var commentInTo = javadoc(ctx.relationship_to().javadoc());
var toOptions = relationshipOptions(ctx.relationship_to().annotations().option());
var isInjectedFieldInToRequired = ctx.relationship_to().relationship_definition().relationship_field_required() != null;
var isInjectedFieldInToRequired = isRequired(ctx.relationship_to().relationship_definition());
var injectedFieldInToDescription = getText(ctx.relationship_to().relationship_definition().relationship_description_field());
var relationshipValidations = relationshipValidations(ctx.relationship_to().relationship_definition());
model.setLocation(location + ".to.entity", getLocations(ctx.relationship_to().relationship_definition().relationship_entity_name()));
model.setLocation(location + ".to.field", getLocations(ctx.relationship_to().relationship_definition().relationship_field_name()));
if (ctx.relationship_to().relationship_definition().relationship_field_validations() != null) {
model.setLocation(location + ".to.validations", getLocations(ctx.relationship_to().relationship_definition().relationship_field_validations()));
model.setLocation(location + ".to.validations.min", getLocations(ctx.relationship_to().relationship_definition().relationship_field_validations().relationship_field_min()));
model.setLocation(location + ".to.validations.max", getLocations(ctx.relationship_to().relationship_definition().relationship_field_validations().relationship_field_max()));
}
relationship.with("to", to)
.with("commentInTo", commentInTo)
.with("injectedFieldInTo", toField)
.with("toOptions", toOptions)
.with("toValidations", relationshipValidations)
.with("injectedFieldInToDescription", injectedFieldInToDescription)
.with("isInjectedFieldInToRequired", isInjectedFieldInToRequired);
}

model.getRelationships().appendTo(relationshipType, relationshipName, relationship);
}

private boolean isRequired(io.github.zenwave360.zdl.antlr.ZdlParser.Relationship_definitionContext relationshipDefinitionContext) {
return relationshipDefinitionContext.relationship_field_validations() != null
&& relationshipDefinitionContext.relationship_field_validations().relationship_field_required() != null;
}

private Object relationshipValidations(io.github.zenwave360.zdl.antlr.ZdlParser.Relationship_definitionContext relationshipDefinitionContext) {
var validations = new FluentMap();
if (relationshipDefinitionContext.relationship_field_validations() != null) {
if (relationshipDefinitionContext.relationship_field_validations().relationship_field_required() != null) {
var name = "required";
validations.with(name, Map.of("name", name, "value", true));
}
if (relationshipDefinitionContext.relationship_field_validations().relationship_field_min() != null) {
var name = "min";
var value = getText(relationshipDefinitionContext.relationship_field_validations().relationship_field_min());
validations.with(name, Map.of("name", name, "value", value));
}
if (relationshipDefinitionContext.relationship_field_validations().relationship_field_max() != null) {
var name = "max";
var value = getText(relationshipDefinitionContext.relationship_field_validations().relationship_field_min());
validations.with(name, Map.of("name", name, "value", value));
}
}
return validations;
}

private String removeJavadoc(String text) {
final String regex = "(/\\*\\*.+?\\*/)";
text = text.replace("\r\n", "");
text = text.replace("\n", "");
return text.replaceAll(regex, "");
}

private String relationshipDescription(ZdlParser.Relationship_definitionContext ctx) {
private String relationshipDescription(io.github.zenwave360.zdl.antlr.ZdlParser.Relationship_definitionContext ctx) {
var description = "";
if(ctx != null) {
description = description + getText(ctx.relationship_entity_name());
Expand Down Expand Up @@ -440,7 +487,7 @@ public void exitService_legacy(io.github.zenwave360.zdl.antlr.ZdlParser.Service_
}

@Override
public void enterAggregate(ZdlParser.AggregateContext ctx) {
public void enterAggregate(io.github.zenwave360.zdl.antlr.ZdlParser.AggregateContext ctx) {
var aggregateName = getText(ctx.aggregate_name());
var javadoc = javadoc(ctx.javadoc());
var aggregateRoot = getText(ctx.aggregate_root());
Expand Down Expand Up @@ -491,7 +538,7 @@ public void enterAggregate_command(io.github.zenwave360.zdl.antlr.ZdlParser.Aggr
}

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

Expand Down
17 changes: 17 additions & 0 deletions src/main/java/io/github/zenwave360/zdl/antlr/ZdlListenerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ static Object getValueText(io.github.zenwave360.zdl.antlr.ZdlParser.ValueContext
return getText(ctx);
}

static Object getValueText(io.github.zenwave360.zdl.antlr.ZdlParser.StringContext ctx) {
if(ctx == null) {
return null;
}
if(ctx.keyword() != null) {
return ctx.keyword().getText();
}
if(ctx.SINGLE_QUOTED_STRING() != null) {
return unquote(ctx.SINGLE_QUOTED_STRING().getText(), "'");
}
if(ctx.DOUBLE_QUOTED_STRING() != null) {
return unquote(ctx.DOUBLE_QUOTED_STRING().getText(), "\"");
}
return getText(ctx);
}


static Object getValueText(io.github.zenwave360.zdl.antlr.ZdlParser.SimpleContext ctx) {
if(ctx == null) {
return null;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/github/zenwave360/zdl/antlr/ZdlModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

public class ZdlModel extends FluentMap {
public ZdlModel() {
put("imports", new ArrayList<>());
put("config", new FluentMap());
put("apis", new FluentMap());
put("aggregates", new FluentMap());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public void parseZdl_CompleteZdl() throws Exception {
ZdlModel model = parseZdl("src/test/resources/complete.zdl");
assertEquals("ZenWave Online Food Delivery - Orders Module.", get(model, "$.javadoc"));

assertEquals("com.example:artifact:RELEASE", get(model, "$.imports[0]"));

// CONFIG
assertEquals("io.zenwave360.example.orders", get(model, "$.config.basePackage"));
assertEquals("mongodb", get(model, "$.config.persistence"));
Expand Down Expand Up @@ -90,6 +92,8 @@ public void parseZdl_CompleteZdl() throws Exception {
assertEquals("OrderItem", get(model, "$.entities.CustomerOrder.fields.orderItems.type"));
assertNull(get(model, "$.entities.CustomerOrder.fields.orderItems.initialValue"));
assertNull(get(model, "$.entities.CustomerOrder.fields.orderItems.validations.required"));
assertEquals("1", get(model, "$.entities.CustomerOrder.fields.orderItems.validations.min.value"));
assertEquals("200", get(model, "$.entities.CustomerOrder.fields.orderItems.validations.max.value"));
assertEquals(false, get(model, "$.entities.CustomerOrder.fields.orderItems.isEnum"));
assertEquals(true, get(model, "$.entities.CustomerOrder.fields.orderItems.isEntity"));
assertEquals(true, get(model, "$.entities.CustomerOrder.fields.orderItems.isArray"));
Expand Down
6 changes: 4 additions & 2 deletions src/test/resources/complete.zdl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import("com.example:artifact:RELEASE")

/**
* ZenWave Online Food Delivery - Orders Module.
*/
Expand Down Expand Up @@ -108,13 +110,13 @@ entity CustomerOrder {
/**
* orderItems javadoc
*/
orderItems OrderItem[] {
orderItems OrderItem[] min(1) max(100) {
menuItemId String required
name String required
description String
price BigDecimal required
quantity Integer required
}
} max(200)
}

@aggregate
Expand Down

0 comments on commit 3cbc000

Please sign in to comment.