Skip to content

Commit b999047

Browse files
committed
feat(avro-schema-compiler): now compatible from v1.8.0 up to v1.12.0
#60
1 parent a979156 commit b999047

File tree

9 files changed

+245
-81
lines changed

9 files changed

+245
-81
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
generate-summary: true
4040
jacoco-csv-file: >
4141
./plugins/asyncapi-spring-cloud-streams3/target/site/jacoco/jacoco.csv
42+
./plugins/avro-schema-compiler/target/site/jacoco/jacoco.csv
4243
./plugins/java-to-jdl/target/site/jacoco/jacoco.csv
4344
./plugins/java-to-asyncapi/target/site/jacoco/jacoco.csv
4445
./plugins/backend-application-default/target/site/jacoco/jacoco.csv

.github/workflows/publish-maven-central.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
generate-summary: true
5252
jacoco-csv-file: >
5353
./plugins/asyncapi-spring-cloud-streams3/target/site/jacoco/jacoco.csv
54+
./plugins/avro-schema-compiler/target/site/jacoco/jacoco.csv
5455
./plugins/java-to-jdl/target/site/jacoco/jacoco.csv
5556
./plugins/java-to-asyncapi/target/site/jacoco/jacoco.csv
5657
./plugins/backend-application-default/target/site/jacoco/jacoco.csv

.github/workflows/publish-maven-snapshots.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
generate-summary: true
4545
jacoco-csv-file: >
4646
./plugins/asyncapi-spring-cloud-streams3/target/site/jacoco/jacoco.csv
47+
./plugins/avro-schema-compiler/target/site/jacoco/jacoco.csv
4748
./plugins/java-to-jdl/target/site/jacoco/jacoco.csv
4849
./plugins/java-to-asyncapi/target/site/jacoco/jacoco.csv
4950
./plugins/backend-application-default/target/site/jacoco/jacoco.csv

plugins/avro-schema-compiler/src/main/java/io/zenwave360/sdk/plugins/AvroSchemaGenerator.java

Lines changed: 140 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.zenwave360.sdk.plugins;
22

3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
35
import io.zenwave360.sdk.doc.DocumentedOption;
46
import io.zenwave360.sdk.generators.AbstractAsyncapiGenerator;
57
import io.zenwave360.sdk.utils.AntStyleMatcher;
8+
import io.zenwave360.sdk.utils.JSONPath;
69
import io.zenwave360.sdk.zdl.GeneratedProjectFiles;
710
import org.apache.avro.LogicalTypes;
811
import org.apache.avro.Schema;
@@ -154,31 +157,124 @@ private String asJsonArray(Collection<File> avscFiles) throws IOException {
154157
}
155158
}
156159

157-
return "[" + String.join(",", allSchemas) + "]";
160+
var jsonArrayString = "[" + String.join(",", allSchemas) + "]";
161+
return sortSchemas(jsonArrayString);
162+
}
163+
164+
public String sortSchemas(String jsonArrayString) throws JsonProcessingException {
165+
if(isAvroVersionLater("1.12.0")) {
166+
return jsonArrayString;
167+
}
168+
log.info("Avro version detected {} < 1.12.0. Sorting schemas...", AVRO_VERSION);
169+
var objectMapper = new ObjectMapper();
170+
171+
List<Map<String, Object>> schemas = objectMapper.readValue(jsonArrayString, List.class);
172+
var schemasToSort = JSONPath.get(schemas, "$.[?(@.type == 'record' || @.type == 'enum')]", List.<Map<String, Object>>of());
173+
174+
log.debug("Sorting schemas: {}", JSONPath.get(schemasToSort, "$.[*].name", List.of()));
175+
schemasToSort.sort(createDependencyComparator());
176+
log.debug("Sorted schemas: {}", JSONPath.get(schemasToSort, "$.[*].name", List.of()));
177+
178+
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schemasToSort);
179+
}
180+
181+
private Comparator<Map<String, Object>> createDependencyComparator() {
182+
return (map1, map2) -> {
183+
String name1 = (String) map1.get("name");
184+
String name2 = (String) map2.get("name");
185+
String type1 = (String) map1.get("type");
186+
String type2 = (String) map2.get("type");
187+
188+
List<String> dependencies1 = extractDependencies(map1);
189+
List<String> dependencies2 = extractDependencies(map2);
190+
191+
boolean map1IsEnum = "enum".equals(type1);
192+
boolean map2IsEnum = "enum".equals(type2);
193+
boolean map1HasNoDeps = dependencies1.isEmpty();
194+
boolean map2HasNoDeps = dependencies2.isEmpty();
195+
196+
// Enums and maps with no dependencies go first
197+
if ((map1IsEnum || map1HasNoDeps) && !(map2IsEnum || map2HasNoDeps)) {
198+
return -1; // map1 comes before map2
199+
} else if (!(map1IsEnum || map1HasNoDeps) && (map2IsEnum || map2HasNoDeps)) {
200+
return 1; // map1 comes after map2
201+
}
202+
203+
boolean map1DependsOnMap2 = dependencies1.contains(name2);
204+
boolean map2DependsOnMap1 = dependencies2.contains(name1);
205+
206+
if (map1DependsOnMap2 && !map2DependsOnMap1) {
207+
return 1; // map1 comes after map2
208+
} else if (!map1DependsOnMap2 && map2DependsOnMap1) {
209+
return -1; // map1 comes before map2
210+
}
211+
return 0; // no dependency relationship
212+
};
213+
}
214+
215+
private List<String> extractDependencies(Map<String, Object> schema) {
216+
List<Object> fieldTypes = JSONPath.get(schema, "$.fields[*].type", List.of());
217+
List<String> dependencies = new ArrayList<>();
218+
219+
for (Object fieldType : fieldTypes) {
220+
if (fieldType instanceof String) {
221+
dependencies.add((String) fieldType);
222+
} else if (fieldType instanceof Map) {
223+
Map<String, Object> typeMap = (Map<String, Object>) fieldType;
224+
String items = JSONPath.get(typeMap, "$.items", null);
225+
if (items != null) {
226+
dependencies.add(items);
227+
}
228+
}
229+
}
230+
231+
return dependencies;
158232
}
159233

160234
protected void setCompilerProperties(SpecificCompiler compiler, AvroCompilerProperties properties) {
161235
compiler.setTemplateDir(properties.templateDirectory);
162236
compiler.setStringType(GenericData.StringType.valueOf(properties.stringType));
163237
compiler.setFieldVisibility(SpecificCompiler.FieldVisibility.valueOf(properties.fieldVisibility.toUpperCase()));
164-
compiler.setCreateOptionalGetters(properties.createOptionalGetters);
165-
compiler.setGettersReturnOptional(properties.gettersReturnOptional);
166-
compiler.setOptionalGettersForNullableFieldsOnly(properties.optionalGettersForNullableFieldsOnly);
167238
compiler.setCreateSetters(properties.createSetters);
168-
// compiler.setCreateNullSafeAnnotations(properties.createNullSafeAnnotations);
169-
// compiler.setNullSafeAnnotationNullable(properties.nullSafeAnnotationNullable);
170-
// compiler.setNullSafeAnnotationNotNull(properties.nullSafeAnnotationNotNull);
171-
compiler.setEnableDecimalLogicalType(properties.enableDecimalLogicalType);
172239
compiler.setOutputCharacterEncoding(properties.outputCharacterEncoding);
173-
compiler.setAdditionalVelocityTools(properties.instantiateAdditionalVelocityTools());
174-
// compiler.setRecordSpecificClass(properties.recordSpecificClass);
175-
// compiler.setErrorSpecificClass(properties.errorSpecificClass);
176240

241+
if (isAvroVersionLater("1.8.0")) {
242+
setCompilerProperties_v1_8_0(compiler, properties);
243+
}
244+
if (isAvroVersionLater("1.8.2")) {
245+
setCompilerProperties_v1_8_2(compiler, properties);
246+
}
247+
if (isAvroVersionLater("1.9.0")) {
248+
setCompilerProperties_v1_9_0(compiler, properties);
249+
}
250+
if (isAvroVersionLater("1.11.0")) {
251+
setCompilerProperties_v1_11_0(compiler, properties);
252+
}
253+
if (isAvroVersionLater("1.12.0")) {
254+
setCompilerProperties_v1_12_0(compiler, properties);
255+
}
256+
}
257+
258+
protected void setCompilerProperties_v1_8_0(SpecificCompiler compiler, AvroCompilerProperties properties) {
259+
compiler.setEnableDecimalLogicalType(properties.enableDecimalLogicalType);
260+
}
261+
262+
protected void setCompilerProperties_v1_8_2(SpecificCompiler compiler, AvroCompilerProperties properties) {
263+
compiler.setCreateOptionalGetters(properties.createOptionalGetters);
264+
compiler.setGettersReturnOptional(properties.gettersReturnOptional);
177265
if (properties.customConversions != null) {
178266
for (var conversionClass : properties.customConversions) {
179267
compiler.addCustomConversion(conversionClass);
180268
}
181269
}
270+
}
271+
272+
protected void setCompilerProperties_v1_9_0(SpecificCompiler compiler, AvroCompilerProperties properties) {
273+
compiler.setOptionalGettersForNullableFieldsOnly(properties.optionalGettersForNullableFieldsOnly);
274+
compiler.setAdditionalVelocityTools(properties.instantiateAdditionalVelocityTools());
275+
}
276+
277+
protected void setCompilerProperties_v1_11_0(SpecificCompiler compiler, AvroCompilerProperties properties) {
182278
if (properties.customLogicalTypeFactories != null) {
183279
for (var logicalTypeFactoryClass : properties.customLogicalTypeFactories) {
184280
try {
@@ -189,8 +285,41 @@ protected void setCompilerProperties(SpecificCompiler compiler, AvroCompilerProp
189285
throw new RuntimeException("Failed to instantiate logical type factory " + logicalTypeFactoryClass, e);
190286
}
191287
}
288+
}
289+
}
192290

291+
protected void setCompilerProperties_v1_12_0(SpecificCompiler compiler, AvroCompilerProperties properties) {
292+
compiler.setCreateNullSafeAnnotations(properties.createNullSafeAnnotations);
293+
compiler.setRecordSpecificClass(properties.recordSpecificClass);
294+
compiler.setErrorSpecificClass(properties.errorSpecificClass);
295+
}
296+
297+
protected void setCompilerProperties_v1_12_1(SpecificCompiler compiler, AvroCompilerProperties properties) {
298+
// compiler.setNullSafeAnnotationNullable(properties.nullSafeAnnotationNullable);
299+
// compiler.setNullSafeAnnotationNotNull(properties.nullSafeAnnotationNotNull);
300+
}
301+
302+
private static final String AVRO_VERSION = _getAvroVersion();
303+
private static String _getAvroVersion() {
304+
Package avroPackage = Schema.class.getPackage();
305+
if (avroPackage != null && avroPackage.getImplementationVersion() != null) {
306+
return avroPackage.getImplementationVersion();
193307
}
308+
return "0.0.0";
194309
}
195310

311+
private boolean isAvroVersionLater(String version) {
312+
String[] currentVersionParts = AVRO_VERSION.split("\\.");
313+
String[] targetVersionParts = version.split("\\.");
314+
315+
int currentMajor = Integer.parseInt(currentVersionParts[0]);
316+
int currentMinor = Integer.parseInt(currentVersionParts[1]);
317+
int targetMajor = Integer.parseInt(targetVersionParts[0]);
318+
int targetMinor = Integer.parseInt(targetVersionParts[1]);
319+
320+
return currentMajor > targetMajor ||
321+
(currentMajor == targetMajor && currentMinor >= targetMinor);
322+
}
323+
324+
196325
}

plugins/avro-schema-compiler/src/test/java/io/zenwave360/sdk/plugins/AvroSchemaGeneratorTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public LogicalType fromSchema(Schema schema) {
8383
return null;
8484
}
8585

86-
@Override
86+
// @Override
8787
public String getTypeName() {
8888
return this.getClass().getName();
8989
}
@@ -96,7 +96,7 @@ public LogicalType fromSchema(Schema schema) {
9696
return null;
9797
}
9898

99-
@Override
99+
// @Override
100100
public String getTypeName() {
101101
return this.getClass().getName();
102102
}
Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,100 @@
11
[
22
{
3-
"type" : "record",
4-
"name" : "Address",
5-
"namespace" : "io.zenwave360.example.core.outbound.events.dtos",
6-
"fields" : [ {
7-
"name" : "street",
8-
"type" : "string"
9-
}, {
10-
"name" : "city",
11-
"type" : "string"
12-
} ]
3+
"type": "record",
4+
"name": "Address",
5+
"namespace": "io.zenwave360.example.core.outbound.events.dtos",
6+
"fields": [
7+
{
8+
"name": "street",
9+
"type": "string"
10+
},
11+
{
12+
"name": "city",
13+
"type": "string"
14+
}
15+
]
1316
},
1417
{
15-
"type" : "record",
16-
"name" : "PaymentMethod",
17-
"namespace" : "io.zenwave360.example.core.outbound.events.dtos",
18-
"fields" : [ {
19-
"name" : "id",
20-
"type" : "long"
21-
}, {
22-
"name" : "type",
23-
"type" : "PaymentMethodType"
24-
}, {
25-
"name" : "cardNumber",
26-
"type" : "string"
27-
} ]
18+
"type": "record",
19+
"name": "PaymentMethod",
20+
"namespace": "io.zenwave360.example.core.outbound.events.dtos",
21+
"fields": [
22+
{
23+
"name": "id",
24+
"type": "long"
25+
},
26+
{
27+
"name": "type",
28+
"type": "PaymentMethodType"
29+
},
30+
{
31+
"name": "cardNumber",
32+
"type": "string"
33+
}
34+
]
2835
},
2936
{
30-
"type" : "record",
31-
"name" : "CustomerEvent",
32-
"namespace" : "io.zenwave360.example.core.outbound.events.dtos",
33-
"fields" : [ {
34-
"name" : "name",
35-
"type" : "string",
36-
"doc" : "Customer name"
37-
}, {
38-
"name" : "email",
39-
"type" : "string",
40-
"doc" : ""
41-
}, {
42-
"name" : "addresses",
43-
"type" : {
44-
"type" : "array",
45-
"items" : "Address",
46-
"java-class" : "java.util.List"
37+
"type": "record",
38+
"name": "CustomerEvent",
39+
"namespace": "io.zenwave360.example.core.outbound.events.dtos",
40+
"fields": [
41+
{
42+
"name": "name",
43+
"type": "string",
44+
"doc": "Customer name"
45+
},
46+
{
47+
"name": "email",
48+
"type": "string",
49+
"doc": ""
50+
},
51+
{
52+
"name": "addresses",
53+
"type": {
54+
"type": "array",
55+
"items": "Address",
56+
"java-class": "java.util.List"
57+
}
58+
},
59+
{
60+
"name": "id",
61+
"type": "long"
62+
},
63+
{
64+
"name": "version",
65+
"type": [
66+
"null",
67+
"int"
68+
]
69+
},
70+
{
71+
"name": "paymentMethods",
72+
"type": {
73+
"type": "array",
74+
"items": "PaymentMethod",
75+
"java-class": "java.util.List"
76+
}
4777
}
48-
}, {
49-
"name" : "id",
50-
"type" : "long"
51-
}, {
52-
"name" : "version",
53-
"type" : [ "null", "int" ]
54-
}, {
55-
"name" : "paymentMethods",
56-
"type" : {
57-
"type" : "array",
58-
"items" : "PaymentMethod",
59-
"java-class" : "java.util.List"
60-
}
61-
} ]
78+
]
6279
},
6380
{
64-
"type" : "record",
65-
"name" : "CustomerDeletedEvent",
66-
"namespace" : "io.zenwave360.example.core.outbound.events.dtos",
67-
"fields" : [ {
68-
"name" : "id",
69-
"type" : "long"
70-
} ]
81+
"type": "record",
82+
"name": "CustomerDeletedEvent",
83+
"namespace": "io.zenwave360.example.core.outbound.events.dtos",
84+
"fields": [
85+
{
86+
"name": "id",
87+
"type": "long"
88+
}
89+
]
7190
},
7291
{
73-
"type" : "enum",
74-
"name" : "PaymentMethodType",
75-
"namespace" : "io.zenwave360.example.core.outbound.events.dtos",
76-
"symbols" : [ "VISA", "MASTERCARD" ]
92+
"type": "enum",
93+
"name": "PaymentMethodType",
94+
"namespace": "io.zenwave360.example.core.outbound.events.dtos",
95+
"symbols": [
96+
"VISA",
97+
"MASTERCARD"
98+
]
7799
}
78100
]

0 commit comments

Comments
 (0)