From 1deb2a440b4dc556ab0e914398341adc829fc19e Mon Sep 17 00:00:00 2001 From: Thomas Andres Date: Tue, 8 Oct 2024 08:55:16 +0200 Subject: [PATCH 1/5] add adamGenerateJooqMetamodel --- .gitignore | 4 + gradle-plugin/build.gradle | 2 + .../ergon/adam/gradleplugin/AdamPlugin.java | 44 +++-- .../tasks/GenerateJooqMetamodelTask.java | 112 +++++++++++ .../adam/gradleplugin/util/AdamDatabase.java | 180 ++++++++++++++++++ .../util/AdamTableDefinition.java | 101 ++++++++++ .../util/TableSuffixGeneratorStrategy.java | 23 +++ 7 files changed, 449 insertions(+), 17 deletions(-) create mode 100644 gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java create mode 100644 gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java create mode 100644 gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java create mode 100644 gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/TableSuffixGeneratorStrategy.java diff --git a/.gitignore b/.gitignore index c95c149..923f5bb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ gradle-plugin-test/gradle/ gradle-plugin-test/gradlew* gradle.properties local.properties +bin/ +.settings/ +.project +.classpath \ No newline at end of file diff --git a/gradle-plugin/build.gradle b/gradle-plugin/build.gradle index 97b2d5e..12409bf 100644 --- a/gradle-plugin/build.gradle +++ b/gradle-plugin/build.gradle @@ -13,6 +13,8 @@ sourceCompatibility = 21 dependencies { implementation gradleApi() implementation project(':core') + implementation project(':yml') + implementation group: 'org.jooq', name: 'jooq-codegen', version: '3.19.11' } apply from: "${rootProject.projectDir}/common.gradle" diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/AdamPlugin.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/AdamPlugin.java index 0987670..8739356 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/AdamPlugin.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/AdamPlugin.java @@ -1,35 +1,45 @@ package ch.ergon.adam.gradleplugin; -import ch.ergon.adam.gradleplugin.tasks.*; +import javax.annotation.Nonnull; + import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; -import javax.annotation.Nonnull; +import ch.ergon.adam.gradleplugin.tasks.CleanDbTask; +import ch.ergon.adam.gradleplugin.tasks.ExportGitHistoryTask; +import ch.ergon.adam.gradleplugin.tasks.ExportMigrationScriptsTask; +import ch.ergon.adam.gradleplugin.tasks.ExportTargetVersionTask; +import ch.ergon.adam.gradleplugin.tasks.GenerateJooqMetamodelTask; +import ch.ergon.adam.gradleplugin.tasks.MigrateDBTask; public class AdamPlugin implements Plugin { - public static final String ADAM_EXTENSION = "adam"; + public static final String ADAM_EXTENSION = "adam"; + + @Override + public void apply(@Nonnull Project project) { - @Override - public void apply(@Nonnull Project project) { + project.getExtensions().create(ADAM_EXTENSION, AdamExtension.class, project); - project.getExtensions().create(ADAM_EXTENSION, AdamExtension.class, project); + project.getTasks().create("adamExportMigrationScripts", ExportMigrationScriptsTask.class) + .setGroup(ADAM_EXTENSION); - project.getTasks().create("adamExportMigrationScripts", ExportMigrationScriptsTask.class).setGroup(ADAM_EXTENSION); + project.getTasks().create("adamExportGitHistory", ExportGitHistoryTask.class).setGroup(ADAM_EXTENSION); - project.getTasks().create("adamExportGitHistory", ExportGitHistoryTask.class).setGroup(ADAM_EXTENSION); + project.getTasks().create("adamExportTargetVersion", ExportTargetVersionTask.class).setGroup(ADAM_EXTENSION); - project.getTasks().create("adamExportTargetVersion", ExportTargetVersionTask.class).setGroup(ADAM_EXTENSION); + project.getTasks().create("adamGenerateJooqMetamodel", GenerateJooqMetamodelTask.class) + .setGroup(ADAM_EXTENSION); - Task adamExportTask = project.getTasks().create("adamExport", Task.class); - adamExportTask.setGroup(ADAM_EXTENSION); - adamExportTask.dependsOn("adamExportTargetVersion", "adamExportGitHistory", "adamExportMigrationScripts"); + Task adamExportTask = project.getTasks().create("adamExport", Task.class); + adamExportTask.setGroup(ADAM_EXTENSION); + adamExportTask.dependsOn("adamExportTargetVersion", "adamExportGitHistory", "adamExportMigrationScripts"); - Task migrateDbTask = project.getTasks().create("adamMigrateDb", MigrateDBTask.class); - migrateDbTask.setGroup(ADAM_EXTENSION); - migrateDbTask.dependsOn(adamExportTask); + Task migrateDbTask = project.getTasks().create("adamMigrateDb", MigrateDBTask.class); + migrateDbTask.setGroup(ADAM_EXTENSION); + migrateDbTask.dependsOn(adamExportTask); - project.getTasks().create("adamCleanDb", CleanDbTask.class).setGroup(ADAM_EXTENSION); - } + project.getTasks().create("adamCleanDb", CleanDbTask.class).setGroup(ADAM_EXTENSION); + } } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java new file mode 100644 index 0000000..48de614 --- /dev/null +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java @@ -0,0 +1,112 @@ +package ch.ergon.adam.gradleplugin.tasks; + +import java.io.ByteArrayInputStream; +import java.nio.file.Path; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskAction; +import org.jooq.codegen.GenerationTool; +import org.jooq.meta.jaxb.Configuration; +import org.jooq.meta.jaxb.Database; +import org.jooq.meta.jaxb.Generate; +import org.jooq.meta.jaxb.Generator; +import org.jooq.meta.jaxb.Property; +import org.jooq.meta.jaxb.Strategy; +import org.jooq.meta.jaxb.Target; + +import ch.ergon.adam.gradleplugin.util.AdamDatabase; +import ch.ergon.adam.gradleplugin.util.TableSuffixGeneratorStrategy; + +public class GenerateJooqMetamodelTask extends DefaultTask { + + @Input + @Optional + private String jooqConfig; + + // default ADAM? + @Input + @Optional + private String schemaName; + + @Input + private String packageName; + + @Input + private Path outputPath; + + @Input + private String ymlSource; + + @TaskAction + void generateJooqMetamodel() throws Exception { + Configuration configuration; + if (jooqConfig != null) { + configuration = GenerationTool.load(new ByteArrayInputStream(jooqConfig.getBytes("UTF-8"))); + } else { + configuration = buildConfiguration(); + } + GenerationTool.generate(configuration); + } + + private Configuration buildConfiguration() { + Configuration configuration; + configuration = new Configuration(); + + Generator generator = new Generator(); + Database database = new Database(); + database.setName(AdamDatabase.class.getName()); + database.getProperties().add(new Property().withKey(AdamDatabase.YML_SOURCE_PROPERTY).withValue(ymlSource)); + generator.setDatabase(database); + + Strategy strategy = new Strategy(); + strategy.setName(TableSuffixGeneratorStrategy.class.getName()); + generator.setStrategy(strategy); + + Generate generate = new Generate(); + generate.setGlobalObjectReferences(true); + generate.setJavaTimeTypes(true); + generator.setGenerate(generate); + + Target target = new Target(); + target.setPackageName(getPackageName()); + target.setDirectory(outputPath.toString()); + generator.setTarget(target); + configuration.setGenerator(generator); + return configuration; + } + + public String getJooqConfig() { + return jooqConfig; + } + + public void setJooqConfig(String jooqConfig) { + this.jooqConfig = jooqConfig; + } + + public String getSchemaName() { + return schemaName; + } + + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public Path getOutputPath() { + return outputPath; + } + + public void setOutputPath(Path outputPath) { + this.outputPath = outputPath; + } + +} diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java new file mode 100644 index 0000000..c29c2ef --- /dev/null +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java @@ -0,0 +1,180 @@ +package ch.ergon.adam.gradleplugin.util; + +import java.sql.SQLException; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import org.jooq.DSLContext; +import org.jooq.impl.DSL; +import org.jooq.meta.AbstractDatabase; +import org.jooq.meta.ArrayDefinition; +import org.jooq.meta.CatalogDefinition; +import org.jooq.meta.DefaultEnumDefinition; +import org.jooq.meta.DefaultRelations; +import org.jooq.meta.DomainDefinition; +import org.jooq.meta.EnumDefinition; +import org.jooq.meta.PackageDefinition; +import org.jooq.meta.RoutineDefinition; +import org.jooq.meta.SchemaDefinition; +import org.jooq.meta.SequenceDefinition; +import org.jooq.meta.TableDefinition; +import org.jooq.meta.UDTDefinition; +import org.jooq.meta.XMLSchemaCollectionDefinition; + +import ch.ergon.adam.core.db.schema.Field; +import ch.ergon.adam.core.db.schema.ForeignKey; +import ch.ergon.adam.core.db.schema.Index; +import ch.ergon.adam.core.db.schema.Schema; +import ch.ergon.adam.core.db.schema.Table; +import ch.ergon.adam.yml.YmlFactory; + +public class AdamDatabase extends AbstractDatabase { + public static final String YML_SOURCE_PROPERTY = "ymlSource"; + private Schema schema; + private SchemaDefinition schemaDefinition; + + @Override + protected DSLContext create0() { + // TODO: ok? + return DSL.using(getConnection()); + } + + @Override + protected void loadPrimaryKeys(DefaultRelations r) throws SQLException { + handleIndexes(i -> i.isPrimary(), i -> { + TableDefinition td = getTable(i.getTable()); + String name = i.getName(); + for (Field field : i.getFields()) { + r.addPrimaryKey(name, td, td.getColumn(field.getName())); + } + }); + } + + @Override + protected void loadUniqueKeys(DefaultRelations r) throws SQLException { + handleIndexes(i -> i.isUnique() && !i.isPrimary(), i -> { + TableDefinition td = getTable(i.getTable()); + String name = i.getName(); + for (Field field : i.getFields()) { + r.addUniqueKey(name, td, td.getColumn(field.getName())); + } + }); + } + + private void handleIndexes(Predicate filter, Consumer consumer) { + for (Table table : schema.getTables()) { + TableDefinition td = getTable(table); + if (td == null) { + // table is excluded from build + continue; + } + table.getIndexes().stream().filter(filter).forEach(consumer); + } + + } + + @Override + protected void loadForeignKeys(DefaultRelations r) throws SQLException { + for (Table table : schema.getTables()) { + TableDefinition td = getTable(table); + if (td == null) { + // table is excluded from build + continue; + } + for (ForeignKey fkey : table.getForeignKeys()) { + TableDefinition target = getTable(fkey.getTargetIndex().getTable()); + if (target != null) { + r.addForeignKey(fkey.getName(), td, td.getColumn(fkey.getField().getName()), + fkey.getTargetIndex().getName(), target); + } + } + } + } + + @Override + protected void loadCheckConstraints(DefaultRelations r) throws SQLException { + // not supported + } + + @Override + protected List getCatalogs0() throws SQLException { + ensureSchema(); + return List.of(new CatalogDefinition(this, "", "")); + } + + @Override + protected List getSchemata0() throws SQLException { + ensureSchema(); + return List.of(new SchemaDefinition(this, "", null)); + } + + @Override + protected List getSequences0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getTables0() throws SQLException { + return schema.getTables().stream().map(t -> (TableDefinition) new AdamTableDefinition(schemaDefinition, t)) + .toList(); + } + + @Override + protected List getRoutines0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getPackages0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getEnums0() throws SQLException { + return schema.getEnums().stream().map(e -> { + DefaultEnumDefinition definition = new DefaultEnumDefinition(schemaDefinition, e.getName(), null); + definition.addLiterals(e.getValues()); + return (EnumDefinition) definition; + }).toList(); + } + + @Override + protected List getDomains0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getXMLSchemaCollections0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getUDTs0() throws SQLException { + // not supported + return List.of(); + } + + @Override + protected List getArrays0() throws SQLException { + // not supported + return List.of(); + } + + private TableDefinition getTable(Table table) { + return getTable(schemaDefinition, table.getName()); + } + + private void ensureSchema() { + if (schema == null) { + String source = (String) getProperties().get(YML_SOURCE_PROPERTY); + schema = new YmlFactory().createSource(source).getSchema(); + schemaDefinition = new SchemaDefinition(this, "", null); + } + } +} diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java new file mode 100644 index 0000000..4d97f09 --- /dev/null +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java @@ -0,0 +1,101 @@ +package ch.ergon.adam.gradleplugin.util; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.jooq.Name; +import org.jooq.impl.SQLDataType; +import org.jooq.meta.AbstractTableDefinition; +import org.jooq.meta.ColumnDefinition; +import org.jooq.meta.DataTypeDefinition; +import org.jooq.meta.DefaultColumnDefinition; +import org.jooq.meta.DefaultDataTypeDefinition; +import org.jooq.meta.SchemaDefinition; + +import ch.ergon.adam.core.db.schema.DataType; +import ch.ergon.adam.core.db.schema.Field; +import ch.ergon.adam.core.db.schema.Table; + +public class AdamTableDefinition extends AbstractTableDefinition { + + private Table table; + + public AdamTableDefinition(SchemaDefinition schema, Table table) { + super(schema, table.getName(), null); + this.table = table; + } + + @Override + protected List getElements0() throws SQLException { + List result = new ArrayList(); + for (Field field : table.getFields()) { + result.add(new DefaultColumnDefinition(this, field.getName(), field.getIndex(), dataTypeDefinition(field), + isIdentity(field), null)); + } + return result; + } + + private DataTypeDefinition dataTypeDefinition(Field field) { + DataTypeDefinition dataType = new DefaultDataTypeDefinition(getDatabase(), getSchema(), + typeName(field.getDataType()), field.getLength(), field.getPrecision(), field.getScale(), + field.isNullable(), field.getDefaultValue(), (Name) null); + + return dataType; + } + + private String typeName(DataType type) { + var jooqType = switch (type) { + case BIGINT -> SQLDataType.BIGINT; + case BIGINTUNSIGNED -> SQLDataType.BIGINTUNSIGNED; + case BINARY -> SQLDataType.BINARY; + case BIT -> SQLDataType.BIT; + case BLOB -> SQLDataType.BLOB; + case BOOLEAN -> SQLDataType.BOOLEAN; + case CHAR -> SQLDataType.CHAR; + case CLOB -> SQLDataType.CLOB; + case DATE -> SQLDataType.DATE; + case DECIMAL -> SQLDataType.DECIMAL; + case DECIMAL_INTEGER -> SQLDataType.DECIMAL_INTEGER; + case DOUBLE -> SQLDataType.DOUBLE; + case ENUM -> SQLDataType.VARCHAR; // TODO ??? + case FLOAT -> SQLDataType.FLOAT; + case INTEGER -> SQLDataType.INTEGER; + case INTEGERUNSIGNED -> SQLDataType.INTEGERUNSIGNED; + case INTERVALDAYTOSECOND -> SQLDataType.INTERVALDAYTOSECOND; + case INTERVALYEARTOMONTH -> SQLDataType.INTERVALYEARTOMONTH; + case INTERVALYEARTOSECOND -> SQLDataType.INTERVAL; + case LOCALDATE -> SQLDataType.LOCALDATE; + case LOCALDATETIME -> SQLDataType.LOCALDATETIME; + case LOCALTIME -> SQLDataType.LOCALTIME; + case LONGNVARCHAR -> SQLDataType.LONGNVARCHAR; + case LONGVARBINARY -> SQLDataType.LONGVARBINARY; + case LONGVARCHAR -> SQLDataType.LONGVARCHAR; + case NCHAR -> SQLDataType.NCHAR; + case NCLOB -> SQLDataType.NCLOB; + case NUMERIC -> SQLDataType.NUMERIC; + case NVARCHAR -> SQLDataType.NVARCHAR; + case OFFSETDATETIME -> SQLDataType.OFFSETDATETIME; + case OFFSETTIME -> SQLDataType.OFFSETTIME; + case REAL -> SQLDataType.REAL; + case SMALLINT -> SQLDataType.SMALLINT; + case SMALLINTUNSIGNED -> SQLDataType.SMALLINTUNSIGNED; + case TIME -> SQLDataType.TIME; + case TIMESTAMP -> SQLDataType.TIMESTAMP; + case TIMESTAMPWITHTIMEZONE -> SQLDataType.TIMESTAMPWITHTIMEZONE; + case TIMEWITHTIMEZONE -> SQLDataType.TIMEWITHTIMEZONE; + case TINYINT -> SQLDataType.TINYINT; + case TINYINTUNSIGNED -> SQLDataType.TINYINTUNSIGNED; + case UUID -> SQLDataType.UUID; + case VARBINARY -> SQLDataType.VARBINARY; + case VARCHAR -> SQLDataType.VARCHAR; + }; + return jooqType.getTypeName(); + } + + private boolean isIdentity(Field field) { + // Identity columns??? + return false; + } + +} diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/TableSuffixGeneratorStrategy.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/TableSuffixGeneratorStrategy.java new file mode 100644 index 0000000..d249ebc --- /dev/null +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/TableSuffixGeneratorStrategy.java @@ -0,0 +1,23 @@ +package ch.ergon.adam.gradleplugin.util; + +import org.jooq.codegen.DefaultGeneratorStrategy; +import org.jooq.meta.Definition; +import org.jooq.meta.TableDefinition; + +/** + * Alters jOOQ's DefaultGeneratorStrategy to name tables with an explicit suffix, + * e.g. {@code PERSON} becomes {@code PersonTable}. + */ +public class TableSuffixGeneratorStrategy extends DefaultGeneratorStrategy { + + @Override + public String getJavaClassName(Definition definition, org.jooq.codegen.GeneratorStrategy.Mode mode) { + String defaultName = super.getJavaClassName(definition, mode); + if (mode == org.jooq.codegen.GeneratorStrategy.Mode.DEFAULT && definition instanceof TableDefinition) { + return defaultName + "Table"; + } else { + return defaultName; + } + } + +} From d59f8c8874751b024610cb7a67a395c16e6bfbb0 Mon Sep 17 00:00:00 2001 From: Thomas Andres Date: Tue, 8 Oct 2024 14:48:09 +0200 Subject: [PATCH 2/5] add integration tests for metamodel generator --- .../tasks/GenerateJooqMetamodelTask.java | 74 ++++--------------- .../adam/gradleplugin/util/AdamDatabase.java | 11 ++- .../util/AdamJooqMetamodelGenerator.java | 66 +++++++++++++++++ integration-test/build.gradle | 1 + .../plugin/GenerateJooqMetamodelTest.java | 39 ++++++++++ 5 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java create mode 100644 integration-test/src/test/java/ch/ergon/adam/integrationtest/plugin/GenerateJooqMetamodelTest.java diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java index 48de614..bbaa54b 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java @@ -1,23 +1,13 @@ package ch.ergon.adam.gradleplugin.tasks; -import java.io.ByteArrayInputStream; import java.nio.file.Path; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; -import org.jooq.codegen.GenerationTool; -import org.jooq.meta.jaxb.Configuration; -import org.jooq.meta.jaxb.Database; -import org.jooq.meta.jaxb.Generate; -import org.jooq.meta.jaxb.Generator; -import org.jooq.meta.jaxb.Property; -import org.jooq.meta.jaxb.Strategy; -import org.jooq.meta.jaxb.Target; - -import ch.ergon.adam.gradleplugin.util.AdamDatabase; -import ch.ergon.adam.gradleplugin.util.TableSuffixGeneratorStrategy; + +import ch.ergon.adam.gradleplugin.util.AdamJooqMetamodelGenerator; public class GenerateJooqMetamodelTask extends DefaultTask { @@ -25,11 +15,6 @@ public class GenerateJooqMetamodelTask extends DefaultTask { @Optional private String jooqConfig; - // default ADAM? - @Input - @Optional - private String schemaName; - @Input private String packageName; @@ -40,41 +25,10 @@ public class GenerateJooqMetamodelTask extends DefaultTask { private String ymlSource; @TaskAction - void generateJooqMetamodel() throws Exception { - Configuration configuration; - if (jooqConfig != null) { - configuration = GenerationTool.load(new ByteArrayInputStream(jooqConfig.getBytes("UTF-8"))); - } else { - configuration = buildConfiguration(); - } - GenerationTool.generate(configuration); - } - - private Configuration buildConfiguration() { - Configuration configuration; - configuration = new Configuration(); - - Generator generator = new Generator(); - Database database = new Database(); - database.setName(AdamDatabase.class.getName()); - database.getProperties().add(new Property().withKey(AdamDatabase.YML_SOURCE_PROPERTY).withValue(ymlSource)); - generator.setDatabase(database); - - Strategy strategy = new Strategy(); - strategy.setName(TableSuffixGeneratorStrategy.class.getName()); - generator.setStrategy(strategy); - - Generate generate = new Generate(); - generate.setGlobalObjectReferences(true); - generate.setJavaTimeTypes(true); - generator.setGenerate(generate); - - Target target = new Target(); - target.setPackageName(getPackageName()); - target.setDirectory(outputPath.toString()); - generator.setTarget(target); - configuration.setGenerator(generator); - return configuration; + public void generateJooqMetamodel() throws Exception { + AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator(packageName, outputPath, ymlSource, + jooqConfig); + generator.run(); } public String getJooqConfig() { @@ -85,14 +39,6 @@ public void setJooqConfig(String jooqConfig) { this.jooqConfig = jooqConfig; } - public String getSchemaName() { - return schemaName; - } - - public void setSchemaName(String schemaName) { - this.schemaName = schemaName; - } - public String getPackageName() { return packageName; } @@ -109,4 +55,12 @@ public void setOutputPath(Path outputPath) { this.outputPath = outputPath; } + public String getYmlSource() { + return ymlSource; + } + + public void setYmlSource(String ymlSource) { + this.ymlSource = ymlSource; + } + } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java index c29c2ef..d9c22bc 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java @@ -1,6 +1,7 @@ package ch.ergon.adam.gradleplugin.util; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; @@ -100,13 +101,13 @@ protected void loadCheckConstraints(DefaultRelations r) throws SQLException { @Override protected List getCatalogs0() throws SQLException { ensureSchema(); - return List.of(new CatalogDefinition(this, "", "")); + return mutableList(new CatalogDefinition(this, "", "")); } @Override protected List getSchemata0() throws SQLException { ensureSchema(); - return List.of(new SchemaDefinition(this, "", null)); + return mutableList(new SchemaDefinition(this, "", null)); } @Override @@ -170,6 +171,12 @@ private TableDefinition getTable(Table table) { return getTable(schemaDefinition, table.getName()); } + private List mutableList(T value) { + List list = new ArrayList<>(); + list.add(value); + return list; + } + private void ensureSchema() { if (schema == null) { String source = (String) getProperties().get(YML_SOURCE_PROPERTY); diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java new file mode 100644 index 0000000..9164d6d --- /dev/null +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java @@ -0,0 +1,66 @@ +package ch.ergon.adam.gradleplugin.util; + +import java.io.ByteArrayInputStream; +import java.nio.file.Path; + +import org.jooq.codegen.GenerationTool; +import org.jooq.meta.jaxb.Configuration; +import org.jooq.meta.jaxb.Database; +import org.jooq.meta.jaxb.Generate; +import org.jooq.meta.jaxb.Generator; +import org.jooq.meta.jaxb.Property; +import org.jooq.meta.jaxb.Strategy; +import org.jooq.meta.jaxb.Target; + +public class AdamJooqMetamodelGenerator { + + private String packageName; + private Path outputPath; + private String ymlSource; + private String jooqConfig; + + public AdamJooqMetamodelGenerator(String packageName, Path outputPath, String ymlSource, String jooqConfig) { + this.packageName = packageName; + this.outputPath = outputPath; + this.ymlSource = ymlSource; + this.jooqConfig = jooqConfig; + } + + public void run() throws Exception { + Configuration configuration; + if (jooqConfig != null) { + configuration = GenerationTool.load(new ByteArrayInputStream(jooqConfig.getBytes("UTF-8"))); + } else { + configuration = buildConfiguration(); + } + GenerationTool.generate(configuration); + } + + private Configuration buildConfiguration() { + Configuration configuration; + configuration = new Configuration(); + + Generator generator = new Generator(); + Database database = new Database(); + database.setName(AdamDatabase.class.getName()); + database.getProperties().add(new Property().withKey(AdamDatabase.YML_SOURCE_PROPERTY).withValue(ymlSource)); + generator.setDatabase(database); + + Strategy strategy = new Strategy(); + strategy.setName(TableSuffixGeneratorStrategy.class.getName()); + generator.setStrategy(strategy); + + Generate generate = new Generate(); + generate.setGlobalObjectReferences(true); + generate.setJavaTimeTypes(true); + generator.setGenerate(generate); + + Target target = new Target(); + target.setPackageName(packageName); + target.setDirectory(outputPath.toString()); + generator.setTarget(target); + configuration.setGenerator(generator); + return configuration; + } + +} diff --git a/integration-test/build.gradle b/integration-test/build.gradle index 81f58a0..fb380b7 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -30,6 +30,7 @@ dependencies { testImplementation project(':oracle') testImplementation project(':sqlite') testImplementation project(':integration-test-db') + testImplementation project(':gradle-plugin') testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.1' testImplementation group: 'org.hamcrest', name: 'hamcrest', version: '3.0' diff --git a/integration-test/src/test/java/ch/ergon/adam/integrationtest/plugin/GenerateJooqMetamodelTest.java b/integration-test/src/test/java/ch/ergon/adam/integrationtest/plugin/GenerateJooqMetamodelTest.java new file mode 100644 index 0000000..be0d19c --- /dev/null +++ b/integration-test/src/test/java/ch/ergon/adam/integrationtest/plugin/GenerateJooqMetamodelTest.java @@ -0,0 +1,39 @@ +package ch.ergon.adam.integrationtest.plugin; + +import static ch.ergon.adam.core.Adam.DEFAULT_SCHEMA_PACKAGE; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import ch.ergon.adam.gradleplugin.util.AdamJooqMetamodelGenerator; + +public class GenerateJooqMetamodelTest { + + private static Path tempFolder; + + @BeforeAll + public static void setupTempFolder() throws IOException { + tempFolder = Files.createTempDirectory("MetamodelTest"); + tempFolder.toFile().deleteOnExit(); + } + + @Test + public void testMetamodelGeneration() { + AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator("test", tempFolder, + "yml-classpath://" + DEFAULT_SCHEMA_PACKAGE, null); + try { + generator.run(); + Path generated = tempFolder.resolve("test").resolve("tables").resolve("TestTableTable.java"); + assertTrue(generated.toFile().exists(), "Metamodel for test table must be generated"); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } +} From 1f3f1eae449db15b2e792e4d89e074c89d23eb6c Mon Sep 17 00:00:00 2001 From: Thomas Andres Date: Tue, 8 Oct 2024 14:50:45 +0200 Subject: [PATCH 3/5] cleanup TODO --- .../java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java | 1 - .../ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java index d9c22bc..7f668fa 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java @@ -37,7 +37,6 @@ public class AdamDatabase extends AbstractDatabase { @Override protected DSLContext create0() { - // TODO: ok? return DSL.using(getConnection()); } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java index 4d97f09..1d10278 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamTableDefinition.java @@ -58,7 +58,7 @@ private String typeName(DataType type) { case DECIMAL -> SQLDataType.DECIMAL; case DECIMAL_INTEGER -> SQLDataType.DECIMAL_INTEGER; case DOUBLE -> SQLDataType.DOUBLE; - case ENUM -> SQLDataType.VARCHAR; // TODO ??? + case ENUM -> SQLDataType.VARCHAR; case FLOAT -> SQLDataType.FLOAT; case INTEGER -> SQLDataType.INTEGER; case INTEGERUNSIGNED -> SQLDataType.INTEGERUNSIGNED; @@ -94,8 +94,7 @@ private String typeName(DataType type) { } private boolean isIdentity(Field field) { - // Identity columns??? - return false; + return field.isSequence(); } } From 033406ba28f7b14525dbe53c6ed9c5f098350a66 Mon Sep 17 00:00:00 2001 From: Thomas Andres Date: Tue, 8 Oct 2024 15:20:02 +0200 Subject: [PATCH 4/5] use more general way to create source --- gradle-plugin/build.gradle | 1 - .../tasks/GenerateJooqMetamodelTask.java | 12 ++++++------ .../ergon/adam/gradleplugin/util/AdamDatabase.java | 8 ++++---- .../util/AdamJooqMetamodelGenerator.java | 8 ++++---- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/gradle-plugin/build.gradle b/gradle-plugin/build.gradle index 12409bf..f893926 100644 --- a/gradle-plugin/build.gradle +++ b/gradle-plugin/build.gradle @@ -13,7 +13,6 @@ sourceCompatibility = 21 dependencies { implementation gradleApi() implementation project(':core') - implementation project(':yml') implementation group: 'org.jooq', name: 'jooq-codegen', version: '3.19.11' } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java index bbaa54b..d47f31d 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java @@ -22,11 +22,11 @@ public class GenerateJooqMetamodelTask extends DefaultTask { private Path outputPath; @Input - private String ymlSource; + private String source; @TaskAction public void generateJooqMetamodel() throws Exception { - AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator(packageName, outputPath, ymlSource, + AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator(packageName, outputPath, getSource(), jooqConfig); generator.run(); } @@ -55,12 +55,12 @@ public void setOutputPath(Path outputPath) { this.outputPath = outputPath; } - public String getYmlSource() { - return ymlSource; + public String getSource() { + return source; } - public void setYmlSource(String ymlSource) { - this.ymlSource = ymlSource; + public void setSource(String source) { + this.source = source; } } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java index 7f668fa..d943ac9 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamDatabase.java @@ -23,15 +23,15 @@ import org.jooq.meta.UDTDefinition; import org.jooq.meta.XMLSchemaCollectionDefinition; +import ch.ergon.adam.core.db.SourceAndSinkFactory; import ch.ergon.adam.core.db.schema.Field; import ch.ergon.adam.core.db.schema.ForeignKey; import ch.ergon.adam.core.db.schema.Index; import ch.ergon.adam.core.db.schema.Schema; import ch.ergon.adam.core.db.schema.Table; -import ch.ergon.adam.yml.YmlFactory; public class AdamDatabase extends AbstractDatabase { - public static final String YML_SOURCE_PROPERTY = "ymlSource"; + public static final String SOURCE_PROPERTY = "source"; private Schema schema; private SchemaDefinition schemaDefinition; @@ -178,8 +178,8 @@ private List mutableList(T value) { private void ensureSchema() { if (schema == null) { - String source = (String) getProperties().get(YML_SOURCE_PROPERTY); - schema = new YmlFactory().createSource(source).getSchema(); + String source = (String) getProperties().get(SOURCE_PROPERTY); + schema = SourceAndSinkFactory.getInstance().getSource(source).getSchema(); schemaDefinition = new SchemaDefinition(this, "", null); } } diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java index 9164d6d..82409c7 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/util/AdamJooqMetamodelGenerator.java @@ -16,13 +16,13 @@ public class AdamJooqMetamodelGenerator { private String packageName; private Path outputPath; - private String ymlSource; + private String source; private String jooqConfig; - public AdamJooqMetamodelGenerator(String packageName, Path outputPath, String ymlSource, String jooqConfig) { + public AdamJooqMetamodelGenerator(String packageName, Path outputPath, String source, String jooqConfig) { this.packageName = packageName; this.outputPath = outputPath; - this.ymlSource = ymlSource; + this.source = source; this.jooqConfig = jooqConfig; } @@ -43,7 +43,7 @@ private Configuration buildConfiguration() { Generator generator = new Generator(); Database database = new Database(); database.setName(AdamDatabase.class.getName()); - database.getProperties().add(new Property().withKey(AdamDatabase.YML_SOURCE_PROPERTY).withValue(ymlSource)); + database.getProperties().add(new Property().withKey(AdamDatabase.SOURCE_PROPERTY).withValue(source)); generator.setDatabase(database); Strategy strategy = new Strategy(); From af835f3919bd53c46310561dd56c6ef449b108c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20B=C3=BChlmann?= Date: Tue, 8 Oct 2024 15:51:54 +0200 Subject: [PATCH 5/5] Fix defining output path --- .../gradleplugin/tasks/GenerateJooqMetamodelTask.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java index d47f31d..e7f0a37 100644 --- a/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java +++ b/gradle-plugin/src/main/java/ch/ergon/adam/gradleplugin/tasks/GenerateJooqMetamodelTask.java @@ -1,8 +1,10 @@ package ch.ergon.adam.gradleplugin.tasks; import java.nio.file.Path; +import java.nio.file.Paths; import org.gradle.api.DefaultTask; +import org.gradle.api.file.Directory; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; @@ -19,14 +21,15 @@ public class GenerateJooqMetamodelTask extends DefaultTask { private String packageName; @Input - private Path outputPath; + private String outputPath; @Input private String source; @TaskAction public void generateJooqMetamodel() throws Exception { - AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator(packageName, outputPath, getSource(), + Path output = getProject().getLayout().getProjectDirectory().dir(outputPath).getAsFile().toPath(); + AdamJooqMetamodelGenerator generator = new AdamJooqMetamodelGenerator(packageName, output, getSource(), jooqConfig); generator.run(); } @@ -47,11 +50,11 @@ public void setPackageName(String packageName) { this.packageName = packageName; } - public Path getOutputPath() { + public String getOutputPath() { return outputPath; } - public void setOutputPath(Path outputPath) { + public void setOutputPath(String outputPath) { this.outputPath = outputPath; }