Skip to content

Commit 5fe44de

Browse files
authored
feat: add nodes field to relay connections (#471)
This adds a `nodes` field to relay connections. With this added it allows GraphQL queries like this: ``` location(id: $id) { id name devices { nodes { id name } } } ``` Which makes it easier to loop through results instead of having to loop through the `edges` and then `node` to get the actual list of nodes. This is similar to how GitHub and other public GraphQL APIs provide things through the relay connection. This is entirely based off of ent/contrib#602 and can be removed once we are on a release that includes that PR. Signed-off-by: Nicole Hubbard <[email protected]>
1 parent 4eb87cd commit 5fe44de

File tree

4 files changed

+730
-1
lines changed

4 files changed

+730
-1
lines changed

entx/generator.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ func WithFederation() ExtensionOption {
4444
}
4545
}
4646

47+
// WithConnectionNodes adds the templates for adding nodes to relay connections
48+
func WithConnectionNodes() ExtensionOption {
49+
return func(ex *Extension) error {
50+
ex.templates = append(ex.templates, PaginationTemplate)
51+
ex.gqlSchemaHooks = append(ex.gqlSchemaHooks, addNodesToConnections)
52+
53+
return nil
54+
}
55+
}
56+
4757
// WithJSONScalar adds the JSON scalar definition
4858
func WithJSONScalar() ExtensionOption {
4959
return func(ex *Extension) error {

entx/gql_hooks.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ package entx
1616

1717
import (
1818
"errors"
19+
"slices"
20+
"strings"
1921

2022
"entgo.io/ent/entc"
2123
"entgo.io/ent/entc/gen"
@@ -88,6 +90,36 @@ var (
8890
}
8991
return nil
9092
}
93+
94+
addNodesToConnections = func(_ *gen.Graph, s *ast.Schema) error {
95+
for _, t := range s.Types {
96+
if !strings.HasSuffix(t.Name, "Connection") {
97+
continue
98+
}
99+
100+
existingFields := []string{"edges", "pageInfo", "totalCount"}
101+
missingField := false
102+
for _, f := range t.Fields {
103+
if !slices.Contains(existingFields, f.Name) {
104+
missingField = true
105+
}
106+
}
107+
if missingField {
108+
continue
109+
}
110+
111+
// If we are here then this type is a connection with only the fields we expect so add nodes
112+
nodeType := strings.TrimSuffix(t.Name, "Connection")
113+
114+
t.Fields = append(t.Fields, &ast.FieldDefinition{
115+
Name: "nodes",
116+
Type: ast.ListType(ast.NamedType(nodeType, nil), nil),
117+
Description: "A list of nodes.",
118+
})
119+
}
120+
121+
return nil
122+
}
91123
)
92124

93125
// import string mutations from entc

entx/template.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"strings"
2020
"text/template"
2121

22+
"entgo.io/contrib/entgql"
2223
"entgo.io/ent/entc/gen"
2324
"github.com/mitchellh/mapstructure"
2425
)
@@ -30,6 +31,9 @@ var (
3031
// EventHooksTemplate adds support for generating event hooks
3132
EventHooksTemplate = parseT("template/event_hooks.tmpl")
3233

34+
// PaginationTemplate adds support for adding the nodes field to relay connections
35+
PaginationTemplate = parseT("template/pagination.tmpl")
36+
3337
// TemplateFuncs contains the extra template functions used by entx.
3438
TemplateFuncs = template.FuncMap{
3539
"contains": strings.Contains,
@@ -41,8 +45,14 @@ var (
4145
)
4246

4347
func parseT(path string) *gen.Template {
48+
funcMap := entgql.TemplateFuncs
49+
50+
for k, v := range TemplateFuncs {
51+
funcMap[k] = v
52+
}
53+
4454
return gen.MustParse(gen.NewTemplate(path).
45-
Funcs(TemplateFuncs).
55+
Funcs(funcMap).
4656
ParseFS(_templates, path))
4757
}
4858

0 commit comments

Comments
 (0)