From d0908e9ecf082a9cacb6610cd7095e4eaf543ed7 Mon Sep 17 00:00:00 2001 From: Divi Date: Sun, 15 Sep 2024 18:13:32 +0530 Subject: [PATCH] fix selecting relationship fields in quries (#19) * fix selecting relationship fields in quries * lint * update go version in workflow * remove nolint * use local lint config * fix path * fix path * nolint --- .github/workflows/lint.yaml | 3 +- .golangci.yaml | 52 ++++++++++++++++++ .golangci.yml | 3 -- README.md | 12 ++--- client.go | 8 +-- .../{eywa_fields.go => eywa_generated.go} | 7 +-- cmd/eywagen/eywatest/eywa_test.go | 37 +++++++++++++ cmd/eywagen/main.go | 15 +++--- eywa.go | 8 +-- gql_types.go | 54 +++++++++---------- query_arg.go | 2 +- query_vars.go | 1 - unsafe/actions_test.go | 1 - unsafe/eywa_test.go | 2 +- 14 files changed, 146 insertions(+), 59 deletions(-) create mode 100644 .golangci.yaml delete mode 100644 .golangci.yml rename cmd/eywagen/eywatest/{eywa_fields.go => eywa_generated.go} (93%) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2a8a6b8..2a6c5ac 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -13,9 +13,10 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: "1.22.2" + go-version: "1.23.1" - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: version: latest + args: -c .golangci.yaml --out-format=colored-line-number diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..eb5adfd --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,52 @@ +# This file was adapted from https://github.com/dagger/dagger/blob/main/.golangci.yml +# to get a decent set of defaults. + +linters: + disable-all: true + enable: + - bodyclose + - dogsled + - dupl + - copyloopvar + - gocritic + - gocyclo + - gofmt + - goimports + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - nolintlint + - prealloc + - revive + - rowserrcheck + - staticcheck + - stylecheck + - typecheck + - unconvert + - unused + - whitespace + +issues: + exclude-dirs: + - cmd/eywagen/eywatest + +linters-settings: + revive: + rules: + # This rule is annoying. Often you want to name the + # parameters for clarity because it conforms to an + # interface. + - name: unused-parameter + severity: warning + disabled: true + +output: + formats: + - format: colored-line-number + print-issued-lines: true + print-linter-name: true + sort-results: true diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 6335aeb..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,3 +0,0 @@ -issues: - exclude-dirs: - - cmd/eywagen/eywatest diff --git a/README.md b/README.md index b365f02..1f8fe01 100644 --- a/README.md +++ b/README.md @@ -73,17 +73,17 @@ resp, err := GetUnsafe[User]().Where( ).Limit(5).Select("id", "age").Exec(client) ``` -## `fieldgen` and death to raw string literals +## `eywagen` and death to raw string literals In the examples above, you may have noticed that the `Select` method takes raw strings as field names. This is prone to typos. `eywa` has a codegen tool for generating constants and functions for selecting fields. -Install fieldgen +Install eywagen ```bash -go install github.com/imperfect-fourth/eywa/cmd/fieldgen +go install github.com/imperfect-fourth/eywa/cmd/eywagen ``` Add `go:generate` comments to your code. ```go -//go:generate fieldgen -types User -output user_fields.go +//go:generate eywagen -types User -output user_fields.go type User struct { ... } @@ -114,10 +114,10 @@ resp, err := Get[User]().Where( ).Exec(client) ``` -If a model has a relationship with another model, `fieldgen` will generate a +If a model has a relationship with another model, `eywagen` will generate a function to select fields for that relationship. Eg. ```go -//go:generate fieldgen -types User,Order -output model_fields.go +//go:generate eywagen -types User,Order -output model_fields.go type User struct { ... Orders []Order `json:"orders"` diff --git a/client.go b/client.go index bf0af0b..183ad1b 100644 --- a/client.go +++ b/client.go @@ -15,7 +15,7 @@ type Client struct { } type ClientOpts struct { - HttpClient *http.Client + HTTPClient *http.Client Headers map[string]string } @@ -28,11 +28,11 @@ func NewClient(gqlEndpoint string, opt *ClientOpts) *Client { } if opt != nil { - if opt.HttpClient != nil { - c.httpClient = opt.HttpClient + if opt.HTTPClient != nil { + c.httpClient = opt.HTTPClient } - if opt.Headers != nil && len(opt.Headers) > 0 { + if len(opt.Headers) > 0 { c.headers = opt.Headers } } diff --git a/cmd/eywagen/eywatest/eywa_fields.go b/cmd/eywagen/eywatest/eywa_generated.go similarity index 93% rename from cmd/eywagen/eywatest/eywa_fields.go rename to cmd/eywagen/eywatest/eywa_generated.go index 216c595..61274ac 100644 --- a/cmd/eywagen/eywatest/eywa_fields.go +++ b/cmd/eywagen/eywatest/eywa_generated.go @@ -84,15 +84,16 @@ func testTable_customVar[T interface{eywa.JSONValue | eywa.JSONBValue;eywa.Typed } } -func testTable_testTable2(subField eywa.ModelFieldName[testTable2], subFields ...eywa.ModelFieldName[testTable2]) string { - buf := bytes.NewBuffer([]byte("testTable2 {")) +func testTable_testTable2(subField eywa.ModelFieldName[testTable2], subFields ...eywa.ModelFieldName[testTable2]) eywa.ModelFieldName[testTable] { + buf := bytes.NewBuffer([]byte((new(testTable2)).ModelName())) + buf.WriteString(" {") buf.WriteString(string(subField)) for _, f := range subFields { buf.WriteString("\n") buf.WriteString(string(f)) } buf.WriteString("}") - return buf.String() + return eywa.ModelFieldName[testTable](buf.String()) } const testTable_JsonBCol eywa.ModelFieldName[testTable] = "jsonb_col" diff --git a/cmd/eywagen/eywatest/eywa_test.go b/cmd/eywagen/eywatest/eywa_test.go index a6c7c91..991ef11 100644 --- a/cmd/eywagen/eywatest/eywa_test.go +++ b/cmd/eywagen/eywatest/eywa_test.go @@ -39,6 +39,43 @@ name } } +func TestRelationshipSelectQuery(t *testing.T) { + age := 10 + q := eywa.Get[testTable]().Limit(2).Offset(1).DistinctOn(testTable_Name).OrderBy( + eywa.Desc[testTable](testTable_Name), + ).Where( + eywa.Or( + eywa.Eq[testTable](testTable_NameField("abcd")), + eywa.Eq[testTable](testTable_AgeField(&age)), + ), + ).Select( + testTable_Name, + testTable_testTable2( + testTable2_ID, + ), + ) + + expected := `query get_test_table { +test_table(limit: 2, offset: 1, distinct_on: name, where: {_or: [{name: {_eq: "abcd"}}, {age: {_eq: 10}}]}, order_by: {name: desc}) { +test_table2 {id} +name +} +}` + if assert.Equal(t, expected, q.Query()) { + accessKey := os.Getenv("TEST_HGE_ACCESS_KEY") + c := eywa.NewClient("https://aware-cowbird-80.hasura.app/v1/graphql", &eywa.ClientOpts{ + Headers: map[string]string{ + "x-hasura-access-key": accessKey, + }, + }) + + resp, err := q.Exec(c) + + assert.NoError(t, err) + assert.Equal(t, []testTable{{Name: "abcd"}, {Name: "abc"}}, resp) + } +} + func TestUpdateQuery(t *testing.T) { q := eywa.Update[testTable]().Where( eywa.Eq[testTable](testTable_IDField(3)), diff --git a/cmd/eywagen/main.go b/cmd/eywagen/main.go index 3f2dadf..f93aea9 100644 --- a/cmd/eywagen/main.go +++ b/cmd/eywagen/main.go @@ -6,7 +6,6 @@ import ( "fmt" "go/types" "os" - "regexp" re "regexp" "strings" @@ -54,15 +53,16 @@ func %sVar[T interface{%s;eywa.TypedValue}](val %s) eywa.ModelField[%s] { ` modelRelationshipNameFunc = ` -func %s(subField eywa.ModelFieldName[%s], subFields ...eywa.ModelFieldName[%s]) string { - buf := bytes.NewBuffer([]byte("%s {")) +func %s(subField eywa.ModelFieldName[%s], subFields ...eywa.ModelFieldName[%s]) eywa.ModelFieldName[%s] { + buf := bytes.NewBuffer([]byte((new(%s)).ModelName())) + buf.WriteString(" {") buf.WriteString(string(subField)) for _, f := range subFields { buf.WriteString("\n") buf.WriteString(string(f)) } buf.WriteString("}") - return buf.String() + return eywa.ModelFieldName[%s](buf.String()) } ` ) @@ -192,7 +192,9 @@ func parseType(typeName string, pkg *types.Package, contents *fileContent) { fmt.Sprintf("%s_%s", typeName, field.Name()), fieldTypeName, fieldTypeName, + typeName, fieldName, + typeName, )) recurseParse = append(recurseParse, fieldTypeName) } else { @@ -279,7 +281,6 @@ func parseType(typeName string, pkg *types.Package, contents *fileContent) { for _, t := range recurseParse { parseType(t, pkg, contents) } - } func writeToFile(filename string, contents *fileContent) error { @@ -313,8 +314,8 @@ func loadPackage() (*types.Package, error) { } func parseFieldTypeName(name, rootPkgPath string) (sourcePkgPath, typeName string) { - re, _ := regexp.Compile(`^(\*)?(.*/(.*))\.(.*)$`) - matches := re.FindStringSubmatch(name) + rgx := re.MustCompile(`^(\*)?(.*/(.*))\.(.*)$`) + matches := rgx.FindStringSubmatch(name) if len(matches) == 0 { return "", name } diff --git a/eywa.go b/eywa.go index a6c02b4..f50fc75 100644 --- a/eywa.go +++ b/eywa.go @@ -116,16 +116,16 @@ func (fs fieldArr[M, MF]) MarshalGQL() string { if i > 0 { buf.WriteString(", ") } - buf.WriteString(string(f.GetName())) + buf.WriteString(f.GetName()) buf.WriteString(": ") buf.WriteString(f.GetValue()) } return buf.String() } -//func RawField[M Model](s string, v interface{}) Field[M] { -// return Field[M]{s, v} -//} +// func RawField[M Model](s string, v interface{}) Field[M] { +// return Field[M]{s, v} +// } type Queryable interface { Query() string diff --git a/gql_types.go b/gql_types.go index 601bcf7..f130696 100644 --- a/gql_types.go +++ b/gql_types.go @@ -1,33 +1,33 @@ package eywa -//type Type interface { -// Type() string -//} +// type Type interface { +// Type() string +// } // -//type Boolean interface { -// ~bool -//} -//type NullableBoolean interface { -// ~*bool -//} -//type Int interface { -// ~int -//} -//type NullableInt interface { -// ~*int -//} -//type Float interface { -// ~float32 | ~float64 -//} -//type NullableFloat interface { -// ~*float32 | ~*float64 -//} -//type String interface { -// ~string -//} -//type NullableBoolean interface { -// ~*string -//} +// type Boolean interface { +// ~bool +// } +// type NullableBoolean interface { +// ~*bool +// } +// type Int interface { +// ~int +// } +// type NullableInt interface { +// ~*int +// } +// type Float interface { +// ~float32 | ~float64 +// } +// type NullableFloat interface { +// ~*float32 | ~*float64 +// } +// type String interface { +// ~string +// } +// type NullableBoolean interface { +// ~*string +// } type TypedValue interface { Type() string diff --git a/query_arg.go b/query_arg.go index 24aa104..4602ff5 100644 --- a/query_arg.go +++ b/query_arg.go @@ -91,7 +91,7 @@ func (s set[M, F]) queryArgName() string { return "_set" } func (s set[M, F]) MarshalGQL() string { - if s.fieldArr == nil || len(s.fieldArr) == 0 { + if len(s.fieldArr) == 0 { return "" } return fmt.Sprintf("%s: {%s}", s.queryArgName(), s.fieldArr.MarshalGQL()) diff --git a/query_vars.go b/query_vars.go index 043cd78..797d46d 100644 --- a/query_vars.go +++ b/query_vars.go @@ -29,7 +29,6 @@ func (vs queryVarArr) MarshalGQL() string { } buf.WriteString(")") return buf.String() - } func QueryVar(name string, value TypedValue) queryVar { diff --git a/unsafe/actions_test.go b/unsafe/actions_test.go index 106b3b4..423eef0 100644 --- a/unsafe/actions_test.go +++ b/unsafe/actions_test.go @@ -1,4 +1,3 @@ -// nolint package unsafe import ( diff --git a/unsafe/eywa_test.go b/unsafe/eywa_test.go index 0689ceb..4a38ae2 100644 --- a/unsafe/eywa_test.go +++ b/unsafe/eywa_test.go @@ -1,4 +1,4 @@ -// nolint +//nolint:all package unsafe import (