diff --git a/go.mod b/go.mod index d10449fe4e..18da48b618 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/riza-io/grpc-go v0.2.0 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 + github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/sync v0.3.0 google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 @@ -29,6 +30,8 @@ require ( require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect ) diff --git a/go.sum b/go.sum index c5115d4d60..3aa6805e22 100644 --- a/go.sum +++ b/go.sum @@ -183,6 +183,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= diff --git a/internal/config/v_one.json b/internal/config/v_one.json new file mode 100644 index 0000000000..4ca86a0252 --- /dev/null +++ b/internal/config/v_one.json @@ -0,0 +1,334 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "const": "1" + }, + "project": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "cloud": { + "type": "object", + "properties": { + "organization": { + "type": "string" + }, + "project": { + "type": "string" + }, + "hostname": { + "type": "string" + } + } + }, + "packages": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "engine" + ], + "properties": { + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "schema": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "queries": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "database": { + "type": "object", + "properties": { + "uri": { + "type": "string" + } + } + }, + "strict_function_checks": { + "type": "boolean" + }, + "strict_order_by": { + "type": "boolean" + }, + "emit_interface": { + "type": "boolean" + }, + "emit_json_tags": { + "type": "boolean" + }, + "json_tags_id_uppercase": { + "type": "boolean" + }, + "emit_db_tags": { + "type": "boolean" + }, + "emit_prepared_queries": { + "type": "boolean" + }, + "emit_exact_table_names": { + "type": "boolean" + }, + "emit_empty_slices": { + "type": "boolean" + }, + "emit_exported_queries": { + "type": "boolean" + }, + "emit_result_struct_pointers": { + "type": "boolean" + }, + "emit_params_struct_pointers": { + "type": "boolean" + }, + "emit_methods_with_db_argument": { + "type": "boolean" + }, + "emit_pointers_for_null_types": { + "type": "boolean" + }, + "emit_enum_valid_method": { + "type": "boolean" + }, + "emit_all_enum_values": { + "type": "boolean" + }, + "json_tags_case_style": { + "type": "string" + }, + "package": { + "type": "string" + }, + "out": { + "type": "string" + }, + "overrides": { + "type": "array", + "items": { + "type": "object", + "properties": { + "go_type": { + "oneOf": [ + { + "type": "object", + "properties": { + "import": { + "type": "string" + }, + "package": { + "type": "string" + }, + "type": { + "type": "string" + }, + "pointer": { + "type": "boolean" + }, + "slice": { + "type": "boolean" + }, + "spec": { + "type": "string" + }, + "builtin": { + "type": "boolean" + } + } + }, + { + "type": "string" + } + ] + }, + "go_struct_tag": { + "type": "string" + }, + "db_type": { + "type": "string" + }, + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "nullable": { + "type": "boolean" + }, + "unsigned": { + "type": "boolean" + }, + "column": { + "type": "string" + } + } + } + }, + "sql_package": { + "type": "string" + }, + "sql_driver": { + "type": "string" + }, + "output_batch_file_name": { + "type": "string" + }, + "output_db_file_name": { + "type": "string" + }, + "output_models_file_name": { + "type": "string" + }, + "output_querier_file_name": { + "type": "string" + }, + "output_files_suffix": { + "type": "string" + }, + "inflection_exclude_table_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "query_parameter_limit": { + "type": "integer" + }, + "omit_unused_structs": { + "type": "boolean" + }, + "rules": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "overrides": { + "type": "array", + "items": { + "type": "object", + "properties": { + "go_type": { + "oneOf": [ + { + "type": "object", + "properties": { + "import": { + "type": "string" + }, + "package": { + "type": "string" + }, + "type": { + "type": "string" + }, + "pointer": { + "type": "boolean" + }, + "slice": { + "type": "boolean" + }, + "spec": { + "type": "string" + }, + "builtin": { + "type": "boolean" + } + } + }, + { + "type": "string" + } + ] + }, + "go_struct_tag": { + "type": "string" + }, + "db_type": { + "type": "string" + }, + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "nullable": { + "type": "boolean" + }, + "unsigned": { + "type": "boolean" + }, + "column": { + "type": "string" + } + } + } + } + }, + "rename": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rule": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/internal/config/v_two.json b/internal/config/v_two.json new file mode 100644 index 0000000000..572d0bfe5e --- /dev/null +++ b/internal/config/v_two.json @@ -0,0 +1,433 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "const": "2" + }, + "project": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "cloud": { + "type": "object", + "properties": { + "organization": { + "type": "string" + }, + "project": { + "type": "string" + }, + "hostname": { + "type": "string" + } + } + }, + "sql": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "engine" + ], + "properties": { + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "schema": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "queries": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "database": { + "type": "object", + "properties": { + "uri": { + "type": "string" + } + } + }, + "strict_function_checks": { + "type": "boolean" + }, + "strict_order_by": { + "type": "boolean" + }, + "gen": { + "type": "object", + "properties": { + "go": { + "type": "object", + "properties": { + "emit_interface": { + "type": "boolean" + }, + "emit_json_tags": { + "type": "boolean" + }, + "json_tags_id_uppercase": { + "type": "boolean" + }, + "emit_db_tags": { + "type": "boolean" + }, + "emit_prepared_queries": { + "type": "boolean" + }, + "emit_exact_table_names": { + "type": "boolean" + }, + "emit_empty_slices": { + "type": "boolean" + }, + "emit_exported_queries": { + "type": "boolean" + }, + "emit_result_struct_pointers": { + "type": "boolean" + }, + "emit_params_struct_pointers": { + "type": "boolean" + }, + "emit_methods_with_db_argument": { + "type": "boolean" + }, + "emit_pointers_for_null_types": { + "type": "boolean" + }, + "emit_enum_valid_method": { + "type": "boolean" + }, + "emit_all_enum_values": { + "type": "boolean" + }, + "json_tags_case_style": { + "type": "string" + }, + "package": { + "type": "string" + }, + "out": { + "type": "string" + }, + "overrides": { + "type": "array", + "items": { + "type": "object", + "properties": { + "go_type": { + "oneOf": [ + { + "type": "object", + "properties": { + "import": { + "type": "string" + }, + "package": { + "type": "string" + }, + "type": { + "type": "string" + }, + "pointer": { + "type": "boolean" + }, + "slice": { + "type": "boolean" + }, + "spec": { + "type": "string" + }, + "builtin": { + "type": "boolean" + } + } + }, + { + "type": "string" + } + ] + }, + "go_struct_tag": { + "type": "string" + }, + "db_type": { + "type": "string" + }, + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "nullable": { + "type": "boolean" + }, + "unsigned": { + "type": "boolean" + }, + "column": { + "type": "string" + } + } + } + } + }, + "rename": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "sql_package": { + "type": "string" + }, + "sql_driver": { + "type": "string" + }, + "output_batch_file_name": { + "type": "string" + }, + "output_db_file_name": { + "type": "string" + }, + "output_models_file_name": { + "type": "string" + }, + "output_querier_file_name": { + "type": "string" + }, + "output_files_suffix": { + "type": "string" + }, + "inflection_exclude_table_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "query_parameter_limit": { + "type": "integer" + }, + "omit_unused_structs": { + "type": "boolean" + } + }, + "json": { + "type": "object", + "properties": { + "out": { + "type": "string" + }, + "indent": { + "type": "string" + }, + "filename": { + "type": "string" + } + } + } + } + }, + "codegen": { + "type": "array", + "items": { + "type": "object", + "properties": { + "out": { + "type": "string" + }, + "plugin": { + "type": "string" + }, + "options": { + "type": "object" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "overrides": { + "type": "object", + "properties": { + "go": { + "type": "object", + "properties": { + "overrides": { + "type": "array", + "items": { + "type": "object", + "properties": { + "go_type": { + "oneOf": [ + { + "type": "object", + "properties": { + "import": { + "type": "string" + }, + "package": { + "type": "string" + }, + "type": { + "type": "string" + }, + "pointer": { + "type": "boolean" + }, + "slice": { + "type": "boolean" + }, + "spec": { + "type": "string" + }, + "builtin": { + "type": "boolean" + } + } + }, + { + "type": "string" + } + ] + }, + "go_struct_tag": { + "type": "string" + }, + "db_type": { + "type": "string" + }, + "engine": { + "enum": [ + "postgresql", + "mysql", + "sqlite" + ] + }, + "nullable": { + "type": "boolean" + }, + "unsigned": { + "type": "boolean" + }, + "column": { + "type": "string" + } + } + } + }, + "rename": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + } + } + } + } + }, + "plugins": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "env": { + "type": "array", + "items": { + "type": "string" + } + }, + "process": { + "type": "object", + "properties": { + "cmd": { + "type": "string" + } + } + }, + "wasm": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "sha256": { + "type": "string" + } + } + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rule": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/internal/endtoend/json_schema_test.go b/internal/endtoend/json_schema_test.go new file mode 100644 index 0000000000..64893e2379 --- /dev/null +++ b/internal/endtoend/json_schema_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "io/fs" + "os" + "path/filepath" + "testing" + + "github.com/xeipuuv/gojsonschema" +) + +type conf struct { + Version string `json:"version"` +} + +func loadSchema(t *testing.T, path string) *gojsonschema.Schema { + t.Helper() + + schemaBytes, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + + loader := gojsonschema.NewStringLoader(string(schemaBytes)) + schema, err := gojsonschema.NewSchema(loader) + if err != nil { + t.Fatalf("invalid schema: %s", err) + } + return schema +} + +func TestJsonSchema(t *testing.T) { + t.Parallel() + + schemaOne := loadSchema(t, filepath.Join("..", "config", "v_one.json")) + schemaTwo := loadSchema(t, filepath.Join("..", "config", "v_two.json")) + + srcs := []string{ + filepath.Join("..", "..", "examples"), + filepath.Join("testdata"), + } + + for _, dir := range srcs { + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if filepath.Base(path) != "sqlc.json" { + return nil + } + t.Run(path, func(t *testing.T) { + t.Parallel() + contents, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var c conf + if err := json.Unmarshal(contents, &c); err != nil { + t.Fatal(err) + } + l := gojsonschema.NewStringLoader(string(contents)) + switch c.Version { + case "1": + if _, err := schemaOne.Validate(l); err != nil { + t.Fatal(err) + } + case "2": + if _, err := schemaTwo.Validate(l); err != nil { + t.Fatal(err) + } + default: + t.Fatalf("unknown schema version: %s", c.Version) + } + }) + return nil + }) + if err != nil { + t.Error(err) + } + } +} diff --git a/internal/endtoend/testdata/notifylisten/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/notifylisten/postgresql/pgx/v5/sqlc.json index f7344cf1ba..6645ccbd1b 100644 --- a/internal/endtoend/testdata/notifylisten/postgresql/pgx/v5/sqlc.json +++ b/internal/endtoend/testdata/notifylisten/postgresql/pgx/v5/sqlc.json @@ -7,7 +7,7 @@ "sql_package": "pgx/v5", "name": "querytest", "schema": "query.sql", - "queries": "query.sql", + "queries": "query.sql" } ] } diff --git a/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v4/sqlc.json b/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v4/sqlc.json index 238b937a36..80f4577b9d 100644 --- a/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v4/sqlc.json +++ b/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v4/sqlc.json @@ -15,7 +15,7 @@ "import": "github.com/gofrs/uuid", "package": "uuid", "type": "UUID" - }, + } }, { "column": "*.*_id", @@ -23,7 +23,7 @@ "import": "github.com/gofrs/uuid", "package": "fuid", "type": "UUID" - }, + } }, { "column": "foo.age", @@ -32,7 +32,7 @@ "import": "database/sql", "package": "orm", "type": "NullInt32" - }, + } }, { "column": "foo.balance", @@ -40,7 +40,7 @@ "go_type": { "import": "github.com/volatiletech/null/v8", "type": "Float32" - }, + } }, { "column": "foo.bio", @@ -48,7 +48,7 @@ "go_type": { "import": "gopkg.in/guregu/null.v4", "type": "String" - }, + } }, { "column": "foo.about", diff --git a/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v5/sqlc.json index fa238bdae8..5baf32800b 100644 --- a/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v5/sqlc.json +++ b/internal/endtoend/testdata/overrides_go_types/postgresql/pgx/v5/sqlc.json @@ -15,7 +15,7 @@ "import": "github.com/gofrs/uuid", "package": "uuid", "type": "UUID" - }, + } }, { "column": "*.*_id", @@ -23,7 +23,7 @@ "import": "github.com/gofrs/uuid", "package": "fuid", "type": "UUID" - }, + } }, { "column": "foo.age", @@ -32,7 +32,7 @@ "import": "database/sql", "package": "orm", "type": "NullInt32" - }, + } }, { "column": "foo.balance", @@ -40,7 +40,7 @@ "go_type": { "import": "github.com/volatiletech/null/v8", "type": "Float32" - }, + } }, { "column": "foo.bio", @@ -48,7 +48,7 @@ "go_type": { "import": "gopkg.in/guregu/null.v4", "type": "String" - }, + } }, { "column": "foo.about", diff --git a/internal/endtoend/testdata/overrides_go_types/postgresql/stdlib/sqlc.json b/internal/endtoend/testdata/overrides_go_types/postgresql/stdlib/sqlc.json index 8d3b0c2223..c97894fd2a 100644 --- a/internal/endtoend/testdata/overrides_go_types/postgresql/stdlib/sqlc.json +++ b/internal/endtoend/testdata/overrides_go_types/postgresql/stdlib/sqlc.json @@ -13,7 +13,7 @@ "go_type": { "import": "github.com/gofrs/uuid", "type": "UUID" - }, + } }, { "column": "foo.other_id", @@ -21,7 +21,7 @@ "import": "github.com/gofrs/uuid", "package": "fuid", "type": "UUID" - }, + } }, { "column": "foo.age", @@ -30,7 +30,7 @@ "import": "database/sql", "package": "orm", "type": "NullInt32" - }, + } }, { "column": "foo.balance", @@ -38,7 +38,7 @@ "go_type": { "import": "github.com/volatiletech/null/v8", "type": "Float32" - }, + } }, { "column": "foo.bio", @@ -46,7 +46,7 @@ "go_type": { "import": "gopkg.in/guregu/null.v4", "type": "String" - }, + } }, { "column": "foo.about", diff --git a/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v4/sqlc.json b/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v4/sqlc.json index 4d8ec80dfb..9403bd0279 100644 --- a/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v4/sqlc.json +++ b/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v4/sqlc.json @@ -7,7 +7,7 @@ "sql_package": "pgx/v4", "name": "querytest", "schema": "query.sql", - "queries": "query.sql", + "queries": "query.sql" } ] } diff --git a/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v5/sqlc.json index f7344cf1ba..6645ccbd1b 100644 --- a/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v5/sqlc.json +++ b/internal/endtoend/testdata/refreshmatview/postgresql/pgx/v5/sqlc.json @@ -7,7 +7,7 @@ "sql_package": "pgx/v5", "name": "querytest", "schema": "query.sql", - "queries": "query.sql", + "queries": "query.sql" } ] } diff --git a/internal/endtoend/testdata/rename/v2/stdlib/sqlc.json b/internal/endtoend/testdata/rename/v2/stdlib/sqlc.json index 38190bc1de..6c62c414d7 100644 --- a/internal/endtoend/testdata/rename/v2/stdlib/sqlc.json +++ b/internal/endtoend/testdata/rename/v2/stdlib/sqlc.json @@ -19,5 +19,5 @@ } } } - ], + ] }