Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate events using jsonschema2pojo and mustache template #58

Merged
merged 27 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9865d62
Changes to generate class file using template
Jun 26, 2023
886070a
update CDEventsGenerator file
Jun 26, 2023
3653b32
update CDEventsGenerator file
Jun 26, 2023
7366ba2
Update phase for CDEventsGenerator main class
Jun 26, 2023
fcb0514
adding missing file to repo
Jun 26, 2023
8dc440a
Update src/main/java/dev/cdevents/CDEventsGenerator.java
rjalander Jun 27, 2023
8bf6783
Update src/main/java/dev/cdevents/CDEventsGenerator.java
rjalander Jun 27, 2023
8715628
update with review comments and some linter issues
Jun 27, 2023
8e837e6
fix linter issues
Jun 27, 2023
fb70e74
updating linter.yml
Jun 27, 2023
a594c13
fix linter issues with MagicNumber
Jun 27, 2023
04151e7
fix linter issues with MagicNumber
Jun 27, 2023
4e1c5ed
lint exclude list in array path
Jun 27, 2023
68f666b
update FILTER_REGEX_EXCLUDE pattern
Jun 27, 2023
30c6654
update MaginNumners with constants
Jun 27, 2023
05e0dd3
ignoreFieldDeclaration for MagicNumber check-style
Jun 27, 2023
d78dfeb
cleanup existing/manual events to keep only gererated events
Jun 29, 2023
a2e9beb
Refacoring the code to use maven submodules for generator and sdk
Jul 12, 2023
81a7864
fix minor lint issue
Jul 12, 2023
ea383aa
updating release files as per comment
Jul 13, 2023
77f84e4
Changes to generate class file using template
Jun 26, 2023
52992b5
Merge branch 'gen_sdk_v0_1_2' of github.com:Nordix/sdk-java into gen_…
Jul 13, 2023
9c10633
removing plugin definition from sdk/pom
Jul 14, 2023
8b4ec23
removing test dependencies
Jul 14, 2023
6571579
Merge branch 'main' into gen_sdk_v0_1_2
rjalander Jul 28, 2023
4080307
Update parent pom artifactId
Aug 8, 2023
7aa2e7e
Update sdk and generator pom artifact version
Aug 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/linters/sun_checks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@

<!-- Checks for imports -->
<!-- See https://checkstyle.org/config_imports.html -->
<module name="AvoidStarImport"/>
<module name="AvoidStarImport">
<property name="allowClassImports" value="true"/>
</module>
<module name="IllegalImport"/>
<!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
Expand Down Expand Up @@ -171,7 +173,9 @@
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber"/>
<module name="MagicNumber">
<property name="ignoreFieldDeclaration" value="true"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="MultipleVariableDeclarations"/>
<module name="SimplifyBooleanExpression"/>
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
- name: Lint Code Base
uses: github/super-linter/slim@v4
env:
FILTER_REGEX_INCLUDE: .*src/main/.*
FILTER_REGEX_INCLUDE: .*sdk/src/main/.*|.*generator/src/main/.*
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# mvnw is provided as is from the Maven Wrapper project. Can't change it
FILTER_REGEX_EXCLUDE: .*mvnw
FILTER_REGEX_EXCLUDE: .*mvnw|.*sdk/src/main/java/dev/cdevents/models/.*
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:

- name: Release
run: |
./mvnw -ntp -B --file pom.xml -Prelease
./mvnw -ntp -B --file pom.xml -Prelease -pl :cdevents-sdk-java-parent
env:
JRELEASER_GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
Expand Down
162 changes: 162 additions & 0 deletions generator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>dev.cdevents</groupId>
<artifactId>cdevents-sdk-java-parent</artifactId>
<version>0.1.2-SNAPSHOT</version>
</parent>

<artifactId>cdevents-sdk-java-generator</artifactId>

<name>cdevents-sdk-java-generator</name>
<description>Source code generator for CDEvents Java SDK</description>
<url>https://github.com/cdevents</url>

<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
<maven.source.skip>true</maven.source.skip>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<sdk.project.dir>${project.basedir}/../sdk</sdk.project.dir>
</properties>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.6</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>

<!-- Test dependencies -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generator Maven module needs no test dependencies. These can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I will create a Unit testing for generator code in the coming PR. Is that fine to keep this dependency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great that unit tests will be added, maybe you can add the dependency along with the tests?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. It's a good practice to add only the minimum reqs for a given unit of work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, Removed

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj-core.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.jsonschema2pojo</groupId>
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<outputDirectory>${sdk.project.dir}/src/main/java</outputDirectory>
<includeHashcodeAndEquals>false</includeHashcodeAndEquals>
<includeToString>false</includeToString>
<addCompileSourceRoot>false</addCompileSourceRoot>
</configuration>
<executions>
<execution>
<id>generate-artifact-packaged-from-schema</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<sourcePaths>
<sourcePath>${sdk.project.dir}/src/main/resources/schema/artifact-packaged-event.json
</sourcePath>
</sourcePaths>
<targetPackage>dev.cdevents.models.artifact.packaged</targetPackage>
</configuration>
</execution>
<execution>
<id>generate-artifact-published-from-schema</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<sourcePaths>
<sourcePath>${sdk.project.dir}/src/main/resources/schema/artifact-published-event.json
</sourcePath>
</sourcePaths>
<targetPackage>dev.cdevents.models.artifact.published</targetPackage>
</configuration>
</execution>
<execution>
<id>generate-pipeline-run-finished-from-schema</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<sourcePaths>
<sourcePath>
${sdk.project.dir}/src/main/resources/schema/pipeline-run-finished-event.json
</sourcePath>
</sourcePaths>
<targetPackage>dev.cdevents.models.pipelinerun.finished</targetPackage>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>run-CDEventsGenerator-main-class</id>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>dev.cdevents.generator.CDEventsGenerator</mainClass>
<arguments>
<argument>${project.basedir}</argument>
<argument>${sdk.project.dir}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
165 changes: 165 additions & 0 deletions generator/src/main/java/dev/cdevents/generator/CDEventsGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package dev.cdevents.generator;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public final class CDEventsGenerator {

private CDEventsGenerator() {
}

private static ObjectMapper objectMapper = new ObjectMapper();
private static Logger log = LoggerFactory.getLogger(CDEventsGenerator.class);

private static final int SUBJECT_INDEX = 2;
private static final int PREDICATE_INDEX = 3;
private static final int VERSION_INDEX = 4;
private static final int SUBSTRING_PIPELINE_INDEX = 8;

private static final String TARGET_PACKAGE = "src/main/java/dev/cdevents/events";

/**
* Event JsonSchema files location.
*/
private static final String RESOURCES_DIR = "src/main/resources/";
/**
* Mustache generic event template file.
*/
private static final String EVENT_TEMPLATE_MUSTACHE = RESOURCES_DIR + "template/event-template.mustache";

/**
* Main method to generate CDEvents from Json schema files.
* @param args [0] - base directory for the cdevents-java-sdk-generator module
* [1] - base directory for the cdevents-java-sdk module
*/
public static void main(String[] args) {
if (args == null || args.length < 2) {
throw new IllegalArgumentException("Insufficient arguments passed to CDEventsGenerator");
}
String generatorBaseDir = args[0];
String sdkBaseDir = args[1];
String targetPackageDir = sdkBaseDir + File.separator + "src/main/java/dev/cdevents/events";
File folder = new File(sdkBaseDir + File.separator + RESOURCES_DIR + "schema");
System.out.println(folder.toPath().toAbsolutePath());
if (folder.isDirectory()) {
File[] files = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".json"));
if (files != null) {
//Create Mustache factory and compile event-template.mustache template
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile(generatorBaseDir + File.separator + EVENT_TEMPLATE_MUSTACHE);

//Generate a class file for each Json schema file using a mustache template

for (File file : files) {
SchemaData schemaData = buildCDEventDataFromJsonSchema(file);
generateClassFileFromSchemaData(mustache, schemaData, targetPackageDir);
}
}
}
}

private static void generateClassFileFromSchemaData(Mustache mustache, SchemaData schemaData, String targetPackageDir) {
String classFileName = StringUtils.join(new String[]{schemaData.getCapitalizedSubject(), schemaData.getCapitalizedPredicate(), "CDEvent", ".java"});
File classFile = new File(targetPackageDir, classFileName);
try {
FileWriter fileWriter = new FileWriter(classFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
mustache.execute(bufferedWriter, schemaData).flush();
fileWriter.close();
} catch (IOException e) {
log.error("Exception occurred while generating class file from Json schema {}", e.getMessage());
throw new IllegalStateException("Exception occurred while generating class file from Json schema ", e);
}
log.info("Rendered event-template has been written to file - {}", classFile.getAbsolutePath());
}

private static SchemaData buildCDEventDataFromJsonSchema(File file) {
SchemaData schemaData = new SchemaData();

log.info("Processing event JsonSchema file: {}", file.getAbsolutePath());
try {
JsonNode rootNode = objectMapper.readTree(file);
JsonNode contextNode = rootNode.get("properties").get("context").get("properties");

String eventType = contextNode.get("type").get("enum").get(0).asText();
log.info("eventType: {}", eventType);
String[] type = eventType.split("\\.");
String subject = type[SUBJECT_INDEX];
String predicate = type[PREDICATE_INDEX];
String capitalizedSubject = StringUtils.capitalize(subject);
if (subject.equals("pipelinerun")) {
capitalizedSubject = capitalizedSubject.substring(0, SUBSTRING_PIPELINE_INDEX)
+ StringUtils.capitalize(subject.substring(SUBSTRING_PIPELINE_INDEX));
}
String capitalizedPredicate = StringUtils.capitalize(predicate);
String version = type[VERSION_INDEX];

//set the Schema JsonNode required values to schemaData
schemaData.setSubject(subject);
schemaData.setPredicate(predicate);
schemaData.setCapitalizedSubject(capitalizedSubject);
schemaData.setCapitalizedPredicate(capitalizedPredicate);
schemaData.setSchemaFileName(file.getName());
schemaData.setUpperCaseSubject(subject.toUpperCase());
schemaData.setVersion(version);

JsonNode subjectNode = rootNode.get("properties").get("subject").get("properties");
JsonNode subjectContentNode = subjectNode.get("content").get("properties");
updateSubjectContentProperties(schemaData, subjectContentNode);
} catch (IOException e) {
log.error("Exception occurred while building schema data from Json schema {}", e.getMessage());
throw new IllegalStateException("Exception occurred while building schema data from Json schema ", e);
}
return schemaData;
}

private static void updateSubjectContentProperties(SchemaData schemaData, JsonNode subjectContentNode) {
Iterator<Map.Entry<String, JsonNode>> contentProps = subjectContentNode.fields();
List<SchemaData.ContentField> contentFields = new ArrayList<>();
List<SchemaData.ContentObjectField> contentObjectFields = new ArrayList<>();
while (contentProps.hasNext()) {
Map.Entry<String, JsonNode> contentMap = contentProps.next();
String contentField = contentMap.getKey();
String capitalizedContentField = StringUtils.capitalize(contentField);
JsonNode contentNode = contentMap.getValue();
if (!contentNode.get("type").asText().equals("object")) {
contentFields.add(new SchemaData.ContentField(contentField, capitalizedContentField, "String"));
} else {
schemaData.setObjectName(contentField);
schemaData.setCapitalizedObjectName(capitalizedContentField);
JsonNode contentObjectNode = contentNode.get("properties");
Iterator<String> contentObjectProps = contentObjectNode.fieldNames();
while (contentObjectProps.hasNext()) {
String contentObjField = contentObjectProps.next();
String capitalizedContentObjField = StringUtils.capitalize(contentObjField);
contentObjectFields.add(new SchemaData.ContentObjectField(contentObjField,
capitalizedContentObjField, contentField, capitalizedContentField, "String"));
}
}
}
schemaData.setContentFields(contentFields);
schemaData.setContentObjectFields(contentObjectFields);
}

private static String getFieldsDataType(String fieldName) {
if (fieldName.equalsIgnoreCase("url")) {
return "URI";
} else {
return "String";
}
}
}
Loading