diff --git a/engine/lifecycle.go b/engine/lifecycle.go index 9fdd4062..9081c1ff 100644 --- a/engine/lifecycle.go +++ b/engine/lifecycle.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "log" "os" "os/exec" "path" @@ -202,18 +201,7 @@ func (e *QueryEngine) GetEncodedDatasources() (string, error) { } for i := range datasources { - if env := datasources[i].URL.FromEnvVar; env != "" { - url := os.Getenv(env) - if url == "" { - log.Printf("WARNING: env var %s which was defined in the Prisma schema is not set", env) - continue - // return "", fmt.Errorf("env var %s which was defined in the Prisma schema is not set", env) - } - overrides = append(overrides, DatasourceOverride{ - Name: datasources[i].Name.String(), - URL: url, - }) - } else { + if val := datasources[i].URL.Value; val != "" { overrides = append(overrides, DatasourceOverride{ Name: datasources[i].Name.String(), URL: e.datasourceURL, diff --git a/generator/templates/query.gotpl b/generator/templates/query.gotpl index f20dee33..64da0fd6 100644 --- a/generator/templates/query.gotpl +++ b/generator/templates/query.gotpl @@ -54,7 +54,7 @@ {{/* composite keys for FindUnique */}} {{ range $unique := $model.CompoundKeys }} - func ({{ $nsQuery }}) {{ $unique.Name }}( + func ({{ $nsQuery }}) {{ $unique.Name.GoCase }}( {{- range $f := $unique.Fields }} _{{- $f.GoLowerCase }} {{ $model.Name.GoCase }}WithPrisma{{ $f.GoCase }}WhereParam, {{ end -}} diff --git a/generator/types/types.go b/generator/types/types.go index 285948c7..1daf1cb9 100644 --- a/generator/types/types.go +++ b/generator/types/types.go @@ -16,12 +16,12 @@ func (s String) String() string { // GoCase transforms strings into Go-style casing, meaning uppercase including Go casing edge cases. func (s String) GoCase() string { - return gocase.To(strcase.ToCamel(string(s))) + return gocase.ToUpper(string(s)) } // GoLowerCase transforms strings into Go-style lowercase casing. It is like GoCase but used for private fields. func (s String) GoLowerCase() string { - return gocase.To(strcase.ToLowerCamel(string(s))) + return gocase.ToLower(string(s)) } // CamelCase transforms strings into camelCase casing. It is often used for json mappings. @@ -39,7 +39,7 @@ func (s String) Tag(isRequired bool) string { // PrismaGoCase transforms `relevance` into `Relevance_` func (s String) PrismaGoCase() string { - return strcase.ToCamel(string(s)) + "_" + return strcase.ToUpperCamel(string(s)) + "_" } // PrismaInternalCase transforms `relevance` into `_relevance` @@ -75,17 +75,17 @@ func (t Type) Value() string { return v } - return gocase.To(strcase.ToCamel(str)) + return gocase.ToUpper(strcase.ToUpperCamel(str)) } // GoCase transforms strings into Go-style lowercase casing. It is like GoCase but used for private fields. func (t Type) GoCase() string { - return gocase.To(strcase.ToCamel(string(t))) + return gocase.ToUpper(string(t)) } // GoLowerCase transforms strings into Go-style lowercase casing. It is like GoCase but used for private fields. func (t Type) GoLowerCase() string { - return gocase.To(strcase.ToLowerCamel(string(t))) + return gocase.ToLower(string(t)) } // CamelCase transforms strings into camelCase casing. It is often used for json mappings. diff --git a/generator/types/types_test.go b/generator/types/types_test.go new file mode 100644 index 00000000..1bfb01ba --- /dev/null +++ b/generator/types/types_test.go @@ -0,0 +1,29 @@ +package types + +import ( + "fmt" + "testing" +) + +func TestString_GoCase(t *testing.T) { + tests := []struct { + have String + want string + }{{ + have: "", + want: "", + }, { + have: "anotherIDStuffSomethingID", + want: "AnotherIDStuffSomethingID", + }, { + have: "anotherIdStuffSomethingId", + want: "AnotherIDStuffSomethingID", + }} + for _, tt := range tests { + t.Run(fmt.Sprintf("%s -> %s", tt.have, tt.want), func(t *testing.T) { + if got := tt.have.GoCase(); got != tt.want { + t.Errorf("GoCase() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/helpers/gocase/gocase.go b/helpers/gocase/gocase.go index 91a8e4b7..d89547fe 100644 --- a/helpers/gocase/gocase.go +++ b/helpers/gocase/gocase.go @@ -34,15 +34,22 @@ import ( "fmt" "regexp" "strings" + + "github.com/steebchen/prisma-client-go/helpers/strcase" ) -// To returns a string converted to Go case. -func To(s string) string { - return defaultConverter.To(s) +// ToLower returns a string converted to Go upper case. +func ToLower(s string) string { + return defaultConverter.To(s, false) +} + +// ToUpper returns a string converted to Go lower case. +func ToUpper(s string) string { + return defaultConverter.To(s, true) } // To returns a string converted to Go case with converter. -func (c *Converter) To(s string) string { +func (c *Converter) To(s string, upper bool) string { for _, i := range c.initialisms { // not end re1 := regexp.MustCompile(fmt.Sprintf("%s([^a-z])", i.capUpper())) @@ -52,7 +59,60 @@ func (c *Converter) To(s string) string { re2 := regexp.MustCompile(fmt.Sprintf("%s$", i.capUpper())) s = re2.ReplaceAllString(s, i.allUpper()) } - return s + + if len(s) == 0 { + return s + } + + var isAllCap = true + for _, i := range s { + if i < 'A' || i > 'Z' { + isAllCap = false + } + } + + if strings.Contains(s, "_") || isAllCap { + if upper { + // for snake case + s = strcase.ToUpperCamel(s) + } else { + s = strcase.ToLowerCamel(s) + } + } else { + if upper { + s = strings.ToUpper(s[:1]) + s[1:] + } else { + // TODO!!! + s = strings.ToLower(s[:1]) + s[1:] + } + } + + // run again for new uppercase words + for _, i := range c.initialisms { + // not end + re1 := regexp.MustCompile(fmt.Sprintf("%s([^a-z])", i.capUpper())) + s = re1.ReplaceAllString(s, i.allUpper()+"$1") + + // end + re2 := regexp.MustCompile(fmt.Sprintf("%s$", i.capUpper())) + s = re2.ReplaceAllString(s, i.allUpper()) + } + + // fix casing after numbers + n := strings.Builder{} + n.Grow(len(s)) + prevIsNumber := false + for _, v := range []byte(s) { + vIsLow := v >= 'a' && v <= 'z' + if prevIsNumber && vIsLow { + v += 'A' + v -= 'a' + } + prevIsNumber = v >= '0' && v <= '9' + n.WriteByte(v) + } + + return n.String() } // Revert returns a string converted from Go case to normal case. diff --git a/helpers/gocase/gocase_test.go b/helpers/gocase/gocase_test.go index 80622371..1e90e092 100644 --- a/helpers/gocase/gocase_test.go +++ b/helpers/gocase/gocase_test.go @@ -1,6 +1,7 @@ package gocase_test import ( + "fmt" "strings" "testing" "unicode/utf8" @@ -8,34 +9,101 @@ import ( "github.com/steebchen/prisma-client-go/helpers/gocase" ) -func TestConverter_To(t *testing.T) { +func TestConverter_ToLower(t *testing.T) { t.Parallel() dc, _ := gocase.New() cc, _ := gocase.New(gocase.WithInitialisms("JSON", "CSV")) cases := []struct { - conv *gocase.Converter - s, want string + conv *gocase.Converter + have, want string }{ - {conv: dc, s: "", want: ""}, - {conv: dc, s: "jsonFile", want: "jsonFile"}, - {conv: dc, s: "IpAddress", want: "IPAddress"}, - {conv: dc, s: "defaultDnsServer", want: "defaultDNSServer"}, - {conv: dc, s: "somethingHttpApiId", want: "somethingHTTPAPIID"}, - {conv: dc, s: "somethingUuid", want: "somethingUUID"}, - {conv: dc, s: "somethingSip", want: "somethingSIP"}, - {conv: dc, s: "Urid", want: "Urid"}, - {conv: cc, s: "JsonFile", want: "JSONFile"}, - {conv: cc, s: "CsvFile", want: "CSVFile"}, - {conv: cc, s: "IpAddress", want: "IpAddress"}, + {conv: dc, have: "", want: ""}, + {conv: dc, have: "CONSTANT", want: "constant"}, + {conv: dc, have: "id", want: "id"}, + {conv: dc, have: "ID", want: "id"}, + {conv: dc, have: "jsonFile", want: "jsonFile"}, + // {conv: dc, have: "IpAddress", want: "ipAddress"}, + {conv: dc, have: "ip_address", want: "ipAddress"}, + {conv: dc, have: "defaultDnsServer", want: "defaultDNSServer"}, + {conv: dc, have: "somethingHttpApiId", want: "somethingHTTPAPIID"}, + {conv: dc, have: "somethingUuid", want: "somethingUUID"}, + {conv: dc, have: "somethingSip", want: "somethingSIP"}, + {conv: dc, have: "Urid", want: "urid"}, + {conv: dc, have: "stuffLast7D", want: "stuffLast7D"}, + {conv: dc, have: "stuffLast7d", want: "stuffLast7D"}, + {conv: dc, have: "StuffLast7d", want: "stuffLast7D"}, + {conv: dc, have: "StuffLast7dAnd", want: "stuffLast7DAnd"}, + {conv: dc, have: "StuffLast7DAnd", want: "stuffLast7DAnd"}, + {conv: dc, have: "anotherIDStuffSomethingID", want: "anotherIDStuffSomethingID"}, + {conv: dc, have: "anotherIdStuffSomethingId", want: "anotherIDStuffSomethingID"}, + {conv: dc, have: "anotherIdStuffSomethingId", want: "anotherIDStuffSomethingID"}, + {conv: dc, have: "another_id_stuff_something_id", want: "anotherIDStuffSomethingID"}, + + // {conv: cc, have: "JsonFile", want: "jsonFile"}, + // {conv: cc, have: "CsvFile", want: "csvFile"}, + {conv: cc, have: "IpAddress", want: "ipAddress"}, } for _, c := range cases { - r := c.conv.To(c.s) - if r != c.want { - t.Errorf("value doesn't match: %s (want %s)", r, c.want) - } + cc := c + t.Run(fmt.Sprintf("%s -> %s", cc.have, cc.want), func(t *testing.T) { + r := cc.conv.To(cc.have, false) + if r != cc.want { + t.Errorf("value doesn't match: have %s, is %s, want %s", cc.have, r, cc.want) + } + }) + } +} + +func TestConverter_ToUpper(t *testing.T) { + t.Parallel() + + dc, _ := gocase.New() + cc, _ := gocase.New(gocase.WithInitialisms("JSON", "CSV")) + + cases := []struct { + conv *gocase.Converter + have, want string + }{ + {conv: dc, have: "", want: ""}, + {conv: dc, have: "CONSTANT", want: "Constant"}, + {conv: dc, have: "id", want: "ID"}, + {conv: dc, have: "Id", want: "ID"}, + {conv: dc, have: "IdSomething", want: "IDSomething"}, + {conv: dc, have: "IDSomething", want: "IDSomething"}, + {conv: dc, have: "jsonFile", want: "JSONFile"}, + {conv: dc, have: "IpAddress", want: "IPAddress"}, + {conv: dc, have: "ip_address", want: "IPAddress"}, + {conv: dc, have: "defaultDnsServer", want: "DefaultDNSServer"}, + {conv: dc, have: "somethingHttpApiId", want: "SomethingHTTPAPIID"}, + {conv: dc, have: "somethingUuid", want: "SomethingUUID"}, + {conv: dc, have: "somethingSip", want: "SomethingSIP"}, + {conv: dc, have: "stuffLast7D", want: "StuffLast7D"}, + {conv: dc, have: "stuffLast7d", want: "StuffLast7D"}, + {conv: dc, have: "StuffLast7d", want: "StuffLast7D"}, + {conv: dc, have: "StuffLast7dAnd", want: "StuffLast7DAnd"}, + {conv: dc, have: "StuffLast7DAnd", want: "StuffLast7DAnd"}, + {conv: dc, have: "Urid", want: "Urid"}, + {conv: dc, have: "anotherIDStuffSomethingID", want: "AnotherIDStuffSomethingID"}, + {conv: dc, have: "anotherIdStuffSomethingId", want: "AnotherIDStuffSomethingID"}, + {conv: dc, have: "anotherIdStuffSomethingId", want: "AnotherIDStuffSomethingID"}, + {conv: dc, have: "another_id_stuff_something_id", want: "AnotherIDStuffSomethingID"}, + + {conv: cc, have: "JsonFile", want: "JSONFile"}, + {conv: cc, have: "CsvFile", want: "CSVFile"}, + {conv: cc, have: "IpAddress", want: "IpAddress"}, + } + + for _, c := range cases { + cc := c + t.Run(fmt.Sprintf("%s -> %s", cc.have, cc.want), func(t *testing.T) { + r := cc.conv.To(cc.have, true) + if r != cc.want { + t.Errorf("value doesn't match: have %s, is %s, want %s", cc.have, r, cc.want) + } + }) } } @@ -67,7 +135,7 @@ func TestConverter_Revert(t *testing.T) { } } -// FuzzReverse runs a Fuzzing test to check if the strings +// FuzzReverseUpper runs a Fuzzing test to check if the strings // before and after `To` and `Revert` match. // Note that there may be cases where the strings before and after // the `To` and `Revert` do not match for certain inputs. @@ -75,13 +143,34 @@ func TestConverter_Revert(t *testing.T) { // ```cmd // go test -fuzz=Fuzz // ``` -func FuzzReverse(f *testing.F) { - testcases := []string{"jsonFile", "IpAddress", "defaultDnsServer"} +func FuzzReverseUpper(f *testing.F) { + testcases := []string{"JsonFile", "IpAddress", "DefaultDnsServer"} + for _, tc := range testcases { + f.Add(tc) + } + f.Fuzz(func(t *testing.T, orig string) { + to := gocase.ToUpper(orig) + rev := gocase.Revert(to) + if !ignoreInput(orig) && orig != rev { + t.Errorf("before: %q, after: %q", orig, rev) + } + if utf8.ValidString(orig) && !utf8.ValidString(rev) { + t.Errorf("To or Revert produced invalid UTF-8 string %q", rev) + } + }) +} + +// FuzzReverseLower runs a Fuzzing test to check if the strings +// before and after `To` and `Revert` match. +// Note that there may be cases where the strings before and after +// the `To` and `Revert` do not match for certain inputs. +func FuzzReverseLower(f *testing.F) { + testcases := []string{"jsonFile", "ipAddress", "defaultDnsServer"} for _, tc := range testcases { f.Add(tc) } f.Fuzz(func(t *testing.T, orig string) { - to := gocase.To(orig) + to := gocase.ToLower(orig) rev := gocase.Revert(to) if !ignoreInput(orig) && orig != rev { t.Errorf("before: %q, after: %q", orig, rev) diff --git a/helpers/strcase/camel.go b/helpers/strcase/camel.go index 5224c912..be029b5d 100644 --- a/helpers/strcase/camel.go +++ b/helpers/strcase/camel.go @@ -74,8 +74,8 @@ func toCamelInitCase(s string, initCase bool) string { return n.String() } -// ToCamel converts a string to CamelCase -func ToCamel(s string) string { +// ToUpperCamel converts a string to CamelCase +func ToUpperCamel(s string) string { return toCamelInitCase(s, true) } diff --git a/helpers/strcase/camel_test.go b/helpers/strcase/camel_test.go index 7bb759ec..636588d1 100644 --- a/helpers/strcase/camel_test.go +++ b/helpers/strcase/camel_test.go @@ -46,7 +46,7 @@ func toCamel(tb testing.TB) { for _, i := range cases { in := i[0] out := i[1] - result := ToCamel(in) + result := ToUpperCamel(in) if result != out { tb.Errorf("%q (%q != %q)", in, result, out) } diff --git a/test/features/composite/default_test.go b/test/features/composite/default_test.go index acf4b2eb..b4dc5a70 100644 --- a/test/features/composite/default_test.go +++ b/test/features/composite/default_test.go @@ -13,12 +13,17 @@ type Func func(t *testing.T, client *PrismaClient, ctx cx) func TestComposite(t *testing.T) { // no-op compile time test - User.SomethingIDAnotherIDStuff( User.SomethingID.Equals(""), User.AnotherIDStuff.Equals(""), ) + // custom name test + User.AnotherIDStuffSomethingID( + User.AnotherIDStuff.Equals(""), + User.SomethingID.Equals(""), + ) + tests := []struct { name string before []string diff --git a/test/features/composite/schema.prisma b/test/features/composite/schema.prisma index 221cc543..08077873 100644 --- a/test/features/composite/schema.prisma +++ b/test/features/composite/schema.prisma @@ -21,4 +21,5 @@ model User { @@id([firstName, lastName]) @@unique([firstName, middleName, lastName]) @@unique([somethingId, anotherIdStuff]) + @@unique([anotherIdStuff, somethingId], name: "anotherIDStuffSomethingID") } diff --git a/test/features/enums/enums_test.go b/test/features/enums/enums_test.go index c1b2829a..fd433ea0 100644 --- a/test/features/enums/enums_test.go +++ b/test/features/enums/enums_test.go @@ -23,7 +23,7 @@ func TestEnums(t *testing.T) { _ = StuffLast30D _ = StuffSlack _ = StuffLast7DAnd - _ = StuffLast30Dand + _ = StuffLast30DAnd _ = StuffID tests := []struct { diff --git a/test/integration/main.go b/test/integration/main.go index fa71824d..d4463db3 100644 --- a/test/integration/main.go +++ b/test/integration/main.go @@ -1,9 +1,10 @@ +//go:generate go run github.com/steebchen/prisma-client-go generate --schema schemax.prisma + package main import ( "context" "fmt" - "integration/db" )