From c37339466f6bb2990e03d355fb99c134d798eb35 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Sat, 26 Mar 2022 18:28:47 +0300 Subject: [PATCH 1/2] entgql: move the order-fields logic to template function --- entgql/annotation.go | 16 ++-- entgql/internal/todo/ent/gql_edge.go | 3 +- entgql/internal/todopulid/ent/gql_edge.go | 3 +- entgql/internal/todouuid/ent/gql_edge.go | 3 +- entgql/schema.go | 10 +-- entgql/template.go | 91 ++++++++++++----------- entgql/template/edge.tmpl | 3 +- entgql/template/pagination.tmpl | 12 +-- go.mod | 3 +- go.sum | 7 +- 10 files changed, 69 insertions(+), 82 deletions(-) diff --git a/entgql/annotation.go b/entgql/annotation.go index 91a724359..242bc9d27 100644 --- a/entgql/annotation.go +++ b/entgql/annotation.go @@ -164,7 +164,7 @@ func (a Annotation) Merge(other schema.Annotation) schema.Annotation { return a } -// Decode unmarshal annotation +// Decode unmarshalls the annotation. func (a *Annotation) Decode(annotation interface{}) error { buf, err := json.Marshal(annotation) if err != nil { @@ -173,15 +173,13 @@ func (a *Annotation) Decode(annotation interface{}) error { return json.Unmarshal(buf, a) } -// decodeAnnotation decodes the annotation from the schema. -func decodeAnnotation(annotations gen.Annotations) (*Annotation, error) { +// annotation extracts the entgql.Annotation or returns its empty value. +func annotation(ants gen.Annotations) (*Annotation, error) { ant := &Annotation{} - if annotations == nil || annotations[ant.Name()] == nil { - return ant, nil - } - - if err := ant.Decode(annotations[ant.Name()]); err != nil { - return nil, err + if ants != nil || ants[ant.Name()] != nil { + if err := ant.Decode(ants[ant.Name()]); err != nil { + return nil, err + } } return ant, nil } diff --git a/entgql/internal/todo/ent/gql_edge.go b/entgql/internal/todo/ent/gql_edge.go index aea383c45..db4aa6e19 100644 --- a/entgql/internal/todo/ent/gql_edge.go +++ b/entgql/internal/todo/ent/gql_edge.go @@ -24,8 +24,7 @@ import ( func (c *Category) Todos( ctx context.Context, after *Cursor, first *int, - before *Cursor, last *int, orderBy *TodoOrder, - opts ...TodoPaginateOption, + before *Cursor, last *int, orderBy *TodoOrder, opts ...TodoPaginateOption, ) (*TodoConnection, error) { query := c.QueryTodos() if err := validateFirstLast(first, last); err != nil { diff --git a/entgql/internal/todopulid/ent/gql_edge.go b/entgql/internal/todopulid/ent/gql_edge.go index aea383c45..db4aa6e19 100644 --- a/entgql/internal/todopulid/ent/gql_edge.go +++ b/entgql/internal/todopulid/ent/gql_edge.go @@ -24,8 +24,7 @@ import ( func (c *Category) Todos( ctx context.Context, after *Cursor, first *int, - before *Cursor, last *int, orderBy *TodoOrder, - opts ...TodoPaginateOption, + before *Cursor, last *int, orderBy *TodoOrder, opts ...TodoPaginateOption, ) (*TodoConnection, error) { query := c.QueryTodos() if err := validateFirstLast(first, last); err != nil { diff --git a/entgql/internal/todouuid/ent/gql_edge.go b/entgql/internal/todouuid/ent/gql_edge.go index aea383c45..db4aa6e19 100644 --- a/entgql/internal/todouuid/ent/gql_edge.go +++ b/entgql/internal/todouuid/ent/gql_edge.go @@ -24,8 +24,7 @@ import ( func (c *Category) Todos( ctx context.Context, after *Cursor, first *int, - before *Cursor, last *int, orderBy *TodoOrder, - opts ...TodoPaginateOption, + before *Cursor, last *int, orderBy *TodoOrder, opts ...TodoPaginateOption, ) (*TodoConnection, error) { query := c.QueryTodos() if err := validateFirstLast(first, last); err != nil { diff --git a/entgql/schema.go b/entgql/schema.go index e83fcd5b7..855a92592 100644 --- a/entgql/schema.go +++ b/entgql/schema.go @@ -130,7 +130,7 @@ func (e *schemaGenerator) buildTypes() (map[string]*ast.Definition, error) { defaultInterfaces = append(defaultInterfaces, "Node") } for _, node := range e.nodes { - ant, err := decodeAnnotation(node.Annotations) + ant, err := annotation(node.Annotations) if err != nil { return nil, err } @@ -160,7 +160,7 @@ func (e *schemaGenerator) buildTypes() (map[string]*ast.Definition, error) { var enumOrderByValues ast.EnumValueList for _, f := range node.Fields { - ant, err := decodeAnnotation(f.Annotations) + ant, err := annotation(f.Annotations) if err != nil { return nil, err } @@ -307,7 +307,7 @@ func (e *schemaGenerator) buildTypeFields(t *gen.Type) (ast.FieldList, error) { } func (e *schemaGenerator) typeField(f *gen.Field, isID bool) ([]*ast.FieldDefinition, error) { - ant, err := decodeAnnotation(f.Annotations) + ant, err := annotation(f.Annotations) if err != nil { return nil, err } @@ -387,7 +387,7 @@ func (e *schemaGenerator) genModels() (map[string]string, error) { models[RelayCursor] = e.entGoType(RelayCursor) } for _, node := range e.nodes { - ant, err := decodeAnnotation(node.Annotations) + ant, err := annotation(node.Annotations) if err != nil { return nil, err } @@ -403,7 +403,7 @@ func (e *schemaGenerator) genModels() (map[string]string, error) { var hasOrderBy bool for _, field := range node.Fields { - ant, err := decodeAnnotation(field.Annotations) + ant, err := annotation(field.Annotations) if err != nil { return nil, err } diff --git a/entgql/template.go b/entgql/template.go index c1b358ca1..8548474af 100644 --- a/entgql/template.go +++ b/entgql/template.go @@ -68,6 +68,7 @@ var ( "fieldCollections": fieldCollections, "filterEdges": filterEdges, "filterFields": filterFields, + "orderFields": orderFields, "filterNodes": filterNodes, "findIDType": findIDType, "nodePaginationNames": nodePaginationNames, @@ -110,11 +111,8 @@ func fieldCollections(edges []*gen.Edge) (map[string]fieldCollection, error) { Name: e.Type.Name, Mapping: []string{e.Name}, } - ant := &Annotation{} - if e.Annotations == nil || e.Annotations[ant.Name()] == nil { - continue - } - if err := ant.Decode(e.Annotations[ant.Name()]); err != nil { + ant, err := annotation(e.Annotations) + if err != nil { return nil, err } if ant.Unbind { @@ -135,67 +133,72 @@ func fieldCollections(edges []*gen.Edge) (map[string]fieldCollection, error) { // filterNodes filters out nodes that should not be included in the GraphQL schema. func filterNodes(nodes []*gen.Type) ([]*gen.Type, error) { - var filteredNodes []*gen.Type + filteredNodes := make([]*gen.Type, 0, len(nodes)) for _, n := range nodes { - ant := &Annotation{} - if n.Annotations != nil && n.Annotations[ant.Name()] != nil { - if err := ant.Decode(n.Annotations[ant.Name()]); err != nil { - return nil, err - } - if ant.Skip { - continue - } + ant, err := annotation(n.Annotations) + if err != nil { + return nil, err + } + if !ant.Skip { + filteredNodes = append(filteredNodes, n) } - filteredNodes = append(filteredNodes, n) } return filteredNodes, nil } // filterEdges filters out edges that should not be included in the GraphQL schema. func filterEdges(edges []*gen.Edge) ([]*gen.Edge, error) { - var filteredEdges []*gen.Edge + filteredEdges := make([]*gen.Edge, 0, len(edges)) for _, e := range edges { - ant := &Annotation{} - if e.Annotations != nil && e.Annotations[ant.Name()] != nil { - if err := ant.Decode(e.Annotations[ant.Name()]); err != nil { - return nil, err - } - if ant.Skip { - continue - } + antE, err := annotation(e.Annotations) + if err != nil { + return nil, err } - // Check if type is skipped - if e.Type.Annotations != nil && e.Type.Annotations[ant.Name()] != nil { - if err := ant.Decode(e.Type.Annotations[ant.Name()]); err != nil { - return nil, err - } - if ant.Skip { - continue - } + antT, err := annotation(e.Type.Annotations) + if err != nil { + return nil, err + } + if !antE.Skip && !antT.Skip { + filteredEdges = append(filteredEdges, e) } - filteredEdges = append(filteredEdges, e) } return filteredEdges, nil } // filterFields filters out fields that should not be included in the GraphQL schema. func filterFields(fields []*gen.Field) ([]*gen.Field, error) { - var filteredFields []*gen.Field + filteredFields := make([]*gen.Field, 0, len(fields)) for _, f := range fields { - ant := &Annotation{} - if f.Annotations != nil && f.Annotations[ant.Name()] != nil { - if err := ant.Decode(f.Annotations[ant.Name()]); err != nil { - return nil, err - } - if ant.Skip { - continue - } + ant, err := annotation(f.Annotations) + if err != nil { + return nil, err + } + if !ant.Skip { + filteredFields = append(filteredFields, f) } - filteredFields = append(filteredFields, f) } return filteredFields, nil } +// orderFields returns the fields of the given node with the `OrderField` annotation. +func orderFields(n *gen.Type) ([]*gen.Field, error) { + var ordered []*gen.Field + for _, f := range n.Fields { + ant, err := annotation(f.Annotations) + if err != nil { + return nil, err + } + if ant.OrderField == "" { + continue + } + if !f.Type.Comparable() { + return nil, fmt.Errorf("entgql: ordered field %s.%s must be comparable", n.Name, f.Name) + } + ordered = append(ordered, f) + } + return ordered, nil +} + // PaginationNames holds the names of the pagination fields. type PaginationNames struct { Connection string @@ -208,7 +211,7 @@ type PaginationNames struct { // nodePaginationNames returns the names of the pagination types for the node. func nodePaginationNames(t *gen.Type) (*PaginationNames, error) { node := t.Name - ant, err := decodeAnnotation(t.Annotations) + ant, err := annotation(t.Annotations) if err != nil { return nil, err } diff --git a/entgql/template/edge.tmpl b/entgql/template/edge.tmpl index 4163a21d8..bb6c7a00a 100644 --- a/entgql/template/edge.tmpl +++ b/entgql/template/edge.tmpl @@ -45,8 +45,7 @@ import "context" func ({{ $r }} *{{ $n.Name }}) {{ $e.StructField }}( ctx context.Context, after *Cursor, first *int, - before *Cursor, last *int, orderBy *{{ $order }}, - opts ...{{ $opt }}, + before *Cursor, last *int, {{ if orderFields $e.Type }}orderBy *{{ $order }},{{ end }} opts ...{{ $opt }}, ) (*{{ $conn }}, error) { query := {{ $r }}.Query{{ $e.StructField }}() {{ with extend $n "Node" $e.Type "Query" "query" -}} diff --git a/entgql/template/pagination.tmpl b/entgql/template/pagination.tmpl index 7dc7be05c..50161bfdb 100644 --- a/entgql/template/pagination.tmpl +++ b/entgql/template/pagination.tmpl @@ -217,17 +217,7 @@ const ( ) {{ range $node := $gqlNodes -}} -{{ $orderFields := list -}} -{{- range $f := append (filterFields $node.Fields) $node.ID }} - {{- if $annotation := $f.Annotations.EntGQL }} - {{- if $annotation.OrderField }} - {{- if not $f.Type.Comparable }} - {{ fail (printf "annotated field %s.%s must be comparable" $node.Name $f.Name) }} - {{- end }} - {{ $orderFields = append $orderFields $f }} - {{- end }} - {{- end }} -{{- end }} +{{ $orderFields := orderFields $node }} {{ $names := nodePaginationNames $node -}} {{ $name := $names.Node -}} diff --git a/go.mod b/go.mod index 529777e07..a1c7bd32d 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,6 @@ require ( require ( ariga.io/atlas v0.3.8-0.20220314111236-b2171e04c5b2 // indirect - github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect github.com/agnivade/levenshtein v1.1.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect @@ -38,9 +37,9 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/go-faster/errors v0.5.0 // indirect github.com/go-faster/jx v0.25.0 // indirect - github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-yaml v1.9.4 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/addlicense v1.0.0 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect diff --git a/go.sum b/go.sum index 13ccbe8be..3ce36ecb0 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,6 @@ github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -29,6 +28,8 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA= +github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -73,8 +74,6 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/goccy/go-yaml v1.9.4 h1:S0GCYjwHKVI6IHqio7QWNKNThUl6NLzFd/g8Z65Axw8= @@ -98,6 +97,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/addlicense v1.0.0 h1:cqvo5suPWlsk6r6o42Fs2K66xYCl2tnhVPUYoP3EnO4= +github.com/google/addlicense v1.0.0/go.mod h1:Sm/DHu7Jk+T5miFHHehdIjbi4M5+dJDRS3Cq0rncIxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= From 8d7bc5abf945a4b4ebf6b6b467bde4c7ceeeef89 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki <7413593+a8m@users.noreply.github.com> Date: Sun, 27 Mar 2022 10:46:42 +0300 Subject: [PATCH 2/2] Update entgql/annotation.go Co-authored-by: Giau. Tran Minh --- entgql/annotation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entgql/annotation.go b/entgql/annotation.go index 242bc9d27..710f9480a 100644 --- a/entgql/annotation.go +++ b/entgql/annotation.go @@ -176,7 +176,7 @@ func (a *Annotation) Decode(annotation interface{}) error { // annotation extracts the entgql.Annotation or returns its empty value. func annotation(ants gen.Annotations) (*Annotation, error) { ant := &Annotation{} - if ants != nil || ants[ant.Name()] != nil { + if ants != nil && ants[ant.Name()] != nil { if err := ant.Decode(ants[ant.Name()]); err != nil { return nil, err }