Skip to content

Commit

Permalink
Add support for interfaces, part 2: list-of-interface (#54)
Browse files Browse the repository at this point in the history
## Summary:
In this commit I remove one of the limitations of our support for
interfaces, from #52, by adding support for list-of-interface fields.
This was surprisingly complex!  The issue is that, as before, it's the
containing type that has to do all the glue work -- and it's that glue
work that is complicated by list-of-interface fields.

All in all, it's not that much new code, and by far the hard part is
just 20 lines in the UnmarshalJSON template (which come with almost
twice as many lines of comments to explain them).  It may be easiest to
start by reading some of the generated code, and then read the template.

I also added support for such fields with `pointer: true` specified,
such that the type is `[][]...[]*MyInterface`, although I don't know why
you would want that.  This does *not* allow e.g. `*[]*[][]*MyInterface`;
that would require a way to specify it (see #16) but also add some extra
complexity (as we'd have to actually walk the type-unwrap chain
properly, instead of just counting the number of slices and whether
there's a pointer).

Issue: #8

## Test plan:
make check


Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, aberkan, csilvers, MiguelCastillo

Required Reviewers: 

Approved by: dnerdy

Checks: ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13), ⌛ Lint, ⌛ Lint, ⌛ Test (1.17), ⌛ Test (1.16), ⌛ Test (1.15), ⌛ Test (1.14), ⌛ Test (1.13)

Pull request URL: #54
  • Loading branch information
benjaminjkraft authored Aug 25, 2021
1 parent 4c38cb7 commit 1e87553
Show file tree
Hide file tree
Showing 22 changed files with 1,308 additions and 75 deletions.
4 changes: 0 additions & 4 deletions generate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ func TestGenerate(t *testing.T) {
t.Run("Build", func(t *testing.T) {
if testing.Short() {
t.Skip("skipping build due to -short")
} else if sourceFilename == "InterfaceNesting.graphql" ||
sourceFilename == "InterfaceListField.graphql" {
t.Skip("TODO: enable after fixing " +
"https://github.com/Khan/genqlient/issues/8")
} else if sourceFilename == "Omitempty.graphql" {
t.Skip("TODO: enable after fixing " +
"https://github.com/Khan/genqlient/issues/43")
Expand Down
24 changes: 23 additions & 1 deletion generate/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io"
"path/filepath"
"runtime"
"strings"
"text/template"
)

Expand All @@ -12,13 +13,34 @@ var (
thisDir = filepath.Dir(thisFilename)
)

func repeat(n int, s string) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString(s)
}
return builder.String()
}

func intRange(n int) []int {
ret := make([]int, n)
for i := 0; i < n; i++ {
ret[i] = i
}
return ret
}

func sub(x, y int) int { return x - y }

// execute executes the given template with the funcs from this generator.
func (g *generator) execute(tmplRelFilename string, w io.Writer, data interface{}) error {
tmpl := g.templateCache[tmplRelFilename]
if tmpl == nil {
absFilename := filepath.Join(thisDir, tmplRelFilename)
funcMap := template.FuncMap{
"ref": g.ref,
"ref": g.ref,
"repeat": repeat,
"intRange": intRange,
"sub": sub,
}
var err error
tmpl, err = template.New(tmplRelFilename).Funcs(funcMap).ParseFiles(absFilename)
Expand Down
12 changes: 11 additions & 1 deletion generate/testdata/queries/InterfaceListField.graphql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
query InterfaceNoFragmentsQuery {
query InterfaceListField {
root {
id
name
Expand All @@ -8,4 +8,14 @@ query InterfaceNoFragmentsQuery {
name
}
}
# @genqlient(pointer: true)
withPointer: root {
id
name
children {
__typename
id
name
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
query InterfaceListOfListOfListsField {
listOfListsOfListsOfContent { __typename id name }
# @genqlient(pointer: true)
withPointer: listOfListsOfListsOfContent { __typename id name }
}
2 changes: 2 additions & 0 deletions generate/testdata/queries/InterfaceNoFragments.graphql
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
query InterfaceNoFragmentsQuery {
root { id name } # (make sure sibling fields work)
randomItem { __typename id name }
# @genqlient(pointer: true)
withPointer: randomItem { __typename id name }
}
1 change: 1 addition & 0 deletions generate/testdata/queries/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type Query {
getJunk: Junk
getComplexJunk: ComplexJunk
listOfListsOfLists: [[[String!]!]!]!
listOfListsOfListsOfContent: [[[Content!]!]!]!
}

type Mutation {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"operations": [
{
"operationName": "InterfaceNoFragmentsQuery",
"query": "\nquery InterfaceNoFragmentsQuery {\n\troot {\n\t\tid\n\t\tname\n\t\tchildren {\n\t\t\t__typename\n\t\t\tid\n\t\t\tname\n\t\t}\n\t}\n}\n",
"operationName": "InterfaceListField",
"query": "\nquery InterfaceListField {\n\troot {\n\t\tid\n\t\tname\n\t\tchildren {\n\t\t\t__typename\n\t\t\tid\n\t\t\tname\n\t\t}\n\t}\n\twithPointer: root {\n\t\tid\n\t\tname\n\t\tchildren {\n\t\t\t__typename\n\t\t\tid\n\t\t\tname\n\t\t}\n\t}\n}\n",
"sourceLocation": "testdata/queries/InterfaceListField.graphql"
}
]
Expand Down
Loading

0 comments on commit 1e87553

Please sign in to comment.