From 04c14f29bf67b5877c272f2ed73ad2d38f13bc63 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sat, 15 Jan 2022 17:43:25 +0100 Subject: [PATCH] Add option for generator to ignore tables, views or enums. --- cmd/jet/main.go | 224 ++++++++++++++++++++++--------- tests/mysql/generator_test.go | 41 +++++- tests/postgres/generator_test.go | 53 ++++++++ tests/sqlite/generator_test.go | 33 +++++ tests/testdata | 2 +- 5 files changed, 284 insertions(+), 69 deletions(-) diff --git a/cmd/jet/main.go b/cmd/jet/main.go index fbbccaa..8368f70 100644 --- a/cmd/jet/main.go +++ b/cmd/jet/main.go @@ -3,7 +3,14 @@ package main import ( "flag" "fmt" + "github.com/go-jet/jet/v2/generator/metadata" sqlitegen "github.com/go-jet/jet/v2/generator/sqlite" + "github.com/go-jet/jet/v2/generator/template" + "github.com/go-jet/jet/v2/internal/jet" + "github.com/go-jet/jet/v2/internal/utils" + "github.com/go-jet/jet/v2/mysql" + postgres2 "github.com/go-jet/jet/v2/postgres" + "github.com/go-jet/jet/v2/sqlite" "os" "strings" @@ -27,34 +34,17 @@ var ( dbName string schemaName string + ignoreTables string + ignoreViews string + ignoreEnums string + destDir string ) func init() { flag.StringVar(&source, "source", "", "Database system name (PostgreSQL, MySQL, MariaDB or SQLite)") - flag.StringVar(&dsn, "dsn", "", "Data source name connection string (Example: postgresql://user@localhost:5432/otherdb?sslmode=trust)") - flag.StringVar(&host, "host", "", "Database host path (Example: localhost)") - flag.IntVar(&port, "port", 0, "Database port") - flag.StringVar(&user, "user", "", "Database user") - flag.StringVar(&password, "password", "", "The user’s password") - flag.StringVar(¶ms, "params", "", "Additional connection string parameters(optional)") - flag.StringVar(&dbName, "dbname", "", "Database name") - flag.StringVar(&schemaName, "schema", "public", `Database schema name. (default "public") (ignored for MySQL and MariaDB)`) - flag.StringVar(&sslmode, "sslmode", "disable", `Whether or not to use SSL(optional)(default "disable") (ignored for MySQL and MariaDB)`) - - flag.StringVar(&destDir, "path", "", "Destination dir for files generated.") -} - -func main() { - - flag.Usage = func() { - _, _ = fmt.Fprint(os.Stdout, ` -Jet generator 2.6.0 - -Usage: - -dsn string - Data source name. Unified format for connecting to database. + flag.StringVar(&dsn, "dsn", "", `Data source name. Unified format for connecting to database. PostgreSQL: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING Example: postgresql://user:pass@localhost:5432/dbname @@ -63,65 +53,70 @@ Usage: mysql://jet:jet@tcp(localhost:3306)/dvds SQLite: https://www.sqlite.org/c3ref/open.html#urifilenameexamples Example: - file://path/to/database/file - -source string - Database system name (PostgreSQL, MySQL, MariaDB or SQLite) - -host string - Database host path (Example: localhost) - -port int - Database port - -user string - Database user - -password string - The user’s password - -dbname string - Database name - -params string - Additional connection string parameters(optional) - -schema string - Database schema name. (default "public") (ignored for MySQL, MariaDB and SQLite) - -sslmode string - Whether or not to use SSL(optional) (default "disable") (ignored for MySQL, MariaDB and SQLite) - -path string - Destination dir for files generated. + file://path/to/database/file`) + flag.StringVar(&host, "host", "", "Database host path. Used only if dsn is not set. (Example: localhost)") + flag.IntVar(&port, "port", 0, "Database port. Used only if dsn is not set.") + flag.StringVar(&user, "user", "", "Database user. Used only if dsn is not set.") + flag.StringVar(&password, "password", "", "The user’s password. Used only if dsn is not set.") + flag.StringVar(&dbName, "dbname", "", "Database name. Used only if dsn is not set.") + flag.StringVar(&schemaName, "schema", "public", `Database schema name. Used only if dsn is not set. (default "public") (ignored for MySQL and MariaDB)`) + flag.StringVar(¶ms, "params", "", "Additional connection string parameters(optional). Used only if dsn is not set.") + flag.StringVar(&sslmode, "sslmode", "disable", `Whether or not to use SSL. Used only if dsn is not set. (optional)(default "disable") (ignored for MySQL and MariaDB)`) + flag.StringVar(&ignoreTables, "ignore-tables", "", `Comma-separated list of tables to ignore`) + flag.StringVar(&ignoreViews, "ignore-views", "", `Comma-separated list of views to ignore`) + flag.StringVar(&ignoreEnums, "ignore-enums", "", `Comma-separated list of enums to ignore`) -Example commands: + flag.StringVar(&destDir, "path", "", "Destination dir for files generated.") +} + +func main() { + + flag.Usage = func() { + fmt.Println("Jet generator 2.7.0") + fmt.Println() + fmt.Println("Usage:") + + order := []string{ + "source", "dsn", "host", "port", "user", "password", "dbname", "schema", "params", "sslmode", + "path", + "ignore-tables", "ignore-views", "ignore-enums", + } + for _, name := range order { + flagEntry := flag.CommandLine.Lookup(name) + fmt.Printf(" -%s\n", flagEntry.Name) + fmt.Printf("\t%s\n", flagEntry.Usage) + } + + fmt.Println() + fmt.Println(`Example command: - $ jet -source=PostgreSQL -dbname=jetdb -host=localhost -port=5432 -user=jet -password=jet -schema=dvds -path=./gen $ jet -dsn=postgresql://jet:jet@localhost:5432/jetdb -schema=dvds -path=./gen $ jet -source=postgres -dsn="user=jet password=jet host=localhost port=5432 dbname=jetdb" -schema=dvds -path=./gen + $ jet -source=mysql -host=localhost -port=3306 -user=jet -password=jet -dbname=jetdb -path=./gen $ jet -source=sqlite -dsn="file://path/to/sqlite/database/file" -schema=dvds -path=./gen -`) + `) } flag.Parse() - if dsn == "" { - // validations for separated connection flags. - if source == "" || host == "" || port == 0 || user == "" || dbName == "" { - printErrorAndExit("ERROR: required flag(s) missing") - } - } else { - if source == "" { - // try to get source from schema - source = detectSchema(dsn) - } - - // validations when dsn != "" - if source == "" { - printErrorAndExit("ERROR: required -source flag missing.") - } + if dsn == "" && (source == "" || host == "" || port == 0 || user == "" || dbName == "") { + printErrorAndExit("ERROR: required flag(s) missing") } + source := getSource() + ignoreTablesList := parseList(ignoreTables) + ignoreViewsList := parseList(ignoreViews) + ignoreEnumsList := parseList(ignoreEnums) + var err error - switch strings.ToLower(strings.TrimSpace(source)) { + switch source { case "postgresql", "postgres": if dsn != "" { err = postgresgen.GenerateDSN(dsn, schemaName, destDir) break } - genData := postgresgen.DBConnection{ + dbConn := postgresgen.DBConnection{ Host: host, Port: port, User: user, @@ -133,7 +128,11 @@ Example commands: SchemaName: schemaName, } - err = postgresgen.Generate(destDir, genData) + err = postgresgen.Generate( + destDir, + dbConn, + genTemplate(postgres2.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList), + ) case "mysql", "mysqlx", "mariadb": if dsn != "" { @@ -149,12 +148,24 @@ Example commands: DBName: dbName, } - err = mysqlgen.Generate(destDir, dbConn) + err = mysqlgen.Generate( + destDir, + dbConn, + genTemplate(mysql.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList), + ) case "sqlite": if dsn == "" { printErrorAndExit("ERROR: required -dsn flag missing.") } - err = sqlitegen.GenerateDSN(dsn, destDir) + err = sqlitegen.GenerateDSN( + dsn, + destDir, + genTemplate(sqlite.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList), + ) + + case "": + printErrorAndExit("ERROR: required -source or -dns flag missing.") + default: printErrorAndExit("ERROR: unknown data source " + source + ". Only postgres, mysql, mariadb and sqlite are supported.") } @@ -167,10 +178,19 @@ Example commands: func printErrorAndExit(error string) { fmt.Println("\n", error) + fmt.Println() flag.Usage() os.Exit(-2) } +func getSource() string { + if source != "" { + return strings.TrimSpace(strings.ToLower(source)) + } + + return detectSchema(dsn) +} + func detectSchema(dsn string) string { match := strings.SplitN(dsn, "://", 2) if len(match) < 2 { // not found @@ -183,5 +203,75 @@ func detectSchema(dsn string) string { return "sqlite" } - return match[0] + return strings.ToLower(match[0]) +} + +func parseList(list string) []string { + ret := strings.Split(list, ",") + + for i := 0; i < len(ret); i++ { + ret[i] = strings.ToLower(strings.TrimSpace(ret[i])) + } + + return ret +} + +func genTemplate(dialect jet.Dialect, ignoreTables []string, ignoreViews []string, ignoreEnums []string) template.Template { + + shouldSkipTable := func(table metadata.Table) bool { + return utils.StringSliceContains(ignoreTables, strings.ToLower(table.Name)) + } + + shouldSkipView := func(view metadata.Table) bool { + return utils.StringSliceContains(ignoreViews, strings.ToLower(view.Name)) + } + + shouldSkipEnum := func(enum metadata.Enum) bool { + return utils.StringSliceContains(ignoreEnums, strings.ToLower(enum.Name)) + } + + return template.Default(dialect). + UseSchema(func(schemaMetaData metadata.Schema) template.Schema { + return template.DefaultSchema(schemaMetaData). + UseModel(template.DefaultModel(). + UseTable(func(table metadata.Table) template.TableModel { + if shouldSkipTable(table) { + return template.TableModel{Skip: true} + } + return template.DefaultTableModel(table) + }). + UseView(func(view metadata.Table) template.ViewModel { + if shouldSkipView(view) { + return template.ViewModel{Skip: true} + } + return template.DefaultViewModel(view) + }). + UseEnum(func(enum metadata.Enum) template.EnumModel { + if shouldSkipEnum(enum) { + return template.EnumModel{Skip: true} + } + return template.DefaultEnumModel(enum) + }), + ). + UseSQLBuilder(template.DefaultSQLBuilder(). + UseTable(func(table metadata.Table) template.TableSQLBuilder { + if shouldSkipTable(table) { + return template.TableSQLBuilder{Skip: true} + } + return template.DefaultTableSQLBuilder(table) + }). + UseView(func(table metadata.Table) template.ViewSQLBuilder { + if shouldSkipView(table) { + return template.ViewSQLBuilder{Skip: true} + } + return template.DefaultViewSQLBuilder(table) + }). + UseEnum(func(enum metadata.Enum) template.EnumSQLBuilder { + if shouldSkipEnum(enum) { + return template.EnumSQLBuilder{Skip: true} + } + return template.DefaultEnumSQLBuilder(enum) + }), + ) + }) } diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index 61f364b..a414df3 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -85,9 +85,48 @@ func TestCmdGenerator(t *testing.T) { err = cmd.Run() require.NoError(t, err) +} - err = os.RemoveAll(genTestDirRoot) +func TestIgnoreTablesViewsEnums(t *testing.T) { + cmd := exec.Command("jet", + "-source=MySQL", + "-dbname=dvds", + "-host="+dbconfig.MySqLHost, + "-port="+strconv.Itoa(dbconfig.MySQLPort), + "-user="+dbconfig.MySQLUser, + "-password="+dbconfig.MySQLPassword, + "-ignore-tables=actor,ADDRESS,Category, city ,country,staff,store,rental", + "-ignore-views=actor_info,CUSTomER_LIST, film_list", + "-ignore-enums=film_list_rating,film_rating", + "-path="+genTestDir3) + + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + err := cmd.Run() require.NoError(t, err) + + tableSQLBuilderFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/table") + require.NoError(t, err) + testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "customer.go", "film.go", "film_actor.go", + "film_category.go", "film_text.go", "inventory.go", "language.go", "payment.go") + + viewSQLBuilderFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/view") + require.NoError(t, err) + testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "nicer_but_slower_film_list.go", + "sales_by_film_category.go", "sales_by_store.go", "staff_list.go") + + enumFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/enum") + require.NoError(t, err) + testutils.AssertFileNamesEqual(t, enumFiles, "nicer_but_slower_film_list_rating.go") + + modelFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/model") + require.NoError(t, err) + + testutils.AssertFileNamesEqual(t, modelFiles, + "customer.go", "film.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", + "payment.go", "nicer_but_slower_film_list_rating.go", "nicer_but_slower_film_list.go", "sales_by_film_category.go", + "sales_by_store.go", "staff_list.go") } func assertGeneratedFiles(t *testing.T) { diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index fe159aa..852745b 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -92,6 +92,59 @@ func TestCmdGenerator(t *testing.T) { require.NoError(t, err) } +func TestGeneratorIgnoreTables(t *testing.T) { + err := os.RemoveAll(genTestDir2) + require.NoError(t, err) + + cmd := exec.Command("jet", + "-source=PostgreSQL", + "-host=localhost", + "-port="+strconv.Itoa(dbconfig.PgPort), + "-user=jet", + "-password=jet", + "-dbname=jetdb", + "-schema=dvds", + "-ignore-tables=actor,ADDRESS,country, Film , cITY,", + "-ignore-views=Actor_info, FILM_LIST ,staff_list", + "-ignore-enums=mpaa_rating", + "-path="+genTestDir2) + + fmt.Println(cmd.Args) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + err = cmd.Run() + require.NoError(t, err) + + // Table SQL Builder files + tableSQLBuilderFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/table") + require.NoError(t, err) + + testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "category.go", + "customer.go", "film_actor.go", "film_category.go", "inventory.go", "language.go", + "payment.go", "rental.go", "staff.go", "store.go") + + // View SQL Builder files + viewSQLBuilderFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/view") + require.NoError(t, err) + + testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "nicer_but_slower_film_list.go", + "sales_by_film_category.go", "customer_list.go", "sales_by_store.go") + + // Enums SQL Builder files + _, err = ioutil.ReadDir("./.gentestdata2/jetdb/dvds/enum") + require.Error(t, err, "open ./.gentestdata2/jetdb/dvds/enum: no such file or directory") + + modelFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/model") + require.NoError(t, err) + + testutils.AssertFileNamesEqual(t, modelFiles, "category.go", + "customer.go", "film_actor.go", "film_category.go", "inventory.go", "language.go", + "payment.go", "rental.go", "staff.go", "store.go", + "nicer_but_slower_film_list.go", "sales_by_film_category.go", + "customer_list.go", "sales_by_store.go") +} + func TestGenerator(t *testing.T) { for i := 0; i < 3; i++ { diff --git a/tests/sqlite/generator_test.go b/tests/sqlite/generator_test.go index ac7ab5d..b8280fd 100644 --- a/tests/sqlite/generator_test.go +++ b/tests/sqlite/generator_test.go @@ -72,6 +72,39 @@ func TestCmdGenerator(t *testing.T) { require.NoError(t, err) } +func TestCmdGeneratorIgnoreTablesViewsEnums(t *testing.T) { + cmd := exec.Command("jet", + "-source=SQLite", + "-dsn=file://"+testDatabaseFilePath, + "-ignore-tables=actor,Address,CATEGORY , city ,film,rental,store", + "-ignore-views=customer_list, film_list,STAFF_LIst", + "-path="+genDestDir) + + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + err := cmd.Run() + require.NoError(t, err) + + tableSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/table") + require.NoError(t, err) + testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "country.go", + "customer.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", + "payment.go", "staff.go") + + viewSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/view") + require.NoError(t, err) + testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "sales_by_film_category.go", + "sales_by_store.go") + + modelFiles, err := ioutil.ReadDir(genDestDir + "/model") + require.NoError(t, err) + + testutils.AssertFileNamesEqual(t, modelFiles, "country.go", + "customer.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", + "payment.go", "staff.go", "sales_by_film_category.go", "sales_by_store.go") +} + func assertGeneratedFiles(t *testing.T) { // Table SQL Builder files tableSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/table") diff --git a/tests/testdata b/tests/testdata index f832949..aa46ddd 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit f8329498fd778157a1aac8d471d09ff39cdd5de1 +Subproject commit aa46ddd9aad89455f73b898a46e7410e36d03272