Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for interfaces, part 2: list-of-interface #54

Merged
merged 3 commits into from
Aug 25, 2021

Conversation

benjaminjkraft
Copy link
Collaborator

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

Copy link
Contributor

@dnerdy dnerdy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I mostly reviewed the generated code and not the template. As mentioned inline, only supporting a pointer for the inner-most type seems right. Also, it would be nice to have an integration test that exercises pointer handling.

@@ -111,6 +111,7 @@ type Query {
getJunk: Junk
getComplexJunk: ComplexJunk
listOfListsOfLists: [[[String!]!]!]!
listOfListsOfListsOfContent: [[[Content!]!]!]!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listOfListsOfListsOfContent: [[[Content]!]!]! is a type where one might want to use a pointer (though types like this usually seem to usually be accidental). Pointers to slices shouldn't be necessary, so it seems correct to only support pointers on the most-inner type.

Copy link
Collaborator Author

@benjaminjkraft benjaminjkraft Aug 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are valid uses for required-list-of-optional-item, if you want the input in correspondence to the output. But I agree I can only see a reason to support it at the innermost point. Anyways, this is all mentioned at #16; my attitude is basically "if someone finds a legitimate reason to want it, we can implement it" :-) .

}

// InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle includes the requested fields of the GraphQL type Article.
type InterfaceListOfListOfListsFieldListOfListsOfListsOfContentArticle struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whew. Hopefully no one would actually need to use a type named like this anywhere.

Unwrap() goType

// Count the number of times Unwrap() will unwrap a slice type. For
// example, given []*[]**[]MyStruct, return 3.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this type isn't supported in the current design; it seems like it would be nice to use a valid example (even though the method could in principle support this type as well).

This gets a little complicated because we may have a slice field.
So what we do is basically, for each field:

target := &v.MyField // *[][]...[]MyType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nit here about this not being a supported type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct as-is -- target has type *[][]...[]MyType (or *[][]...[]*MyType, because it's a pointer to the field-type, again because that simplifies the templating at the cost of making the generated code look a bit strange.

@@ -99,6 +99,36 @@ func TestInterfaceNoFragments(t *testing.T) {
assert.Nil(t, resp.Being)
}

func TestInterfaceListField(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add an integration test that uses @genqlient(pointer: true)? I think such a test is the only place the generated code would be exercises (and where we'd suss out any issues). Is that right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My theory was that any issues there would show up at build-time. But it's probably better not to be lazy :-) .

Copy link
Collaborator Author

@benjaminjkraft benjaminjkraft Aug 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, you were right, the code is actually incorrect (crashes with a nil pointer dereference)! Thanks for the prod :-) .

@benjaminjkraft benjaminjkraft changed the base branch from benkraft.interfaces-1 to main August 25, 2021 18:51
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

Reviewers: marksandstrom, miguel, csilvers, adam
I was like "I don't need no stinkin' integration test, the compiler
would catch any problems", but Mark asked for one anyway, and it turns
out it caught a bug!

Also some other minor fixes to documentation, from review comments.
@benjaminjkraft benjaminjkraft merged commit 1e87553 into main Aug 25, 2021
@benjaminjkraft benjaminjkraft deleted the benkraft.interfaces-2 branch August 25, 2021 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants