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 4: getter methods #57

Merged
merged 4 commits into from
Aug 25, 2021

Conversation

benjaminjkraft
Copy link
Collaborator

Summary:

Right now, if you make a query like { myInterface { field } }, you
have to type-switch on all the possible implementations of myInterface
to get at field. Now, we generate getter-methods (e.g. GetField),
to make that access easier. Of course this only applies to shared
fields (which for now are the only ones, but once we support fragments
will no longer be).

This also includes a small change to the way we generate type-names for
interfaces: we no longer include the name of the concrete type in the
interface we propagate forward, so we generate
MyInterfaceMyFieldMyType, not MyInterfaceMyImplMyFieldMyType, in the
case where you have an interface MyInterface implemented by MyImpl
(and maybe other types) with field myField: MyType. This is necessary
so the getter method returns a well-defined type, and also probably
convenient for calling code. It will have to get a little bit more
complicated once we support fragments, where you could have two
implementing types with identically-named fields of different types, but
I think it'll be easiest to figure out how to deal with that when
implementing fragments.

While I was in the area, I added to the interface doc-comment a list of
the implementations. (In GraphQL, we're guaranteed to know them all
assuming our schema is up to date.)

Issue: #8

Test plan:

make check

@@ -116,11 +137,25 @@ func TestInterfaceListField(t *testing.T) {

require.Len(t, resp.Beings, 3)

// We should get the following three beings:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note to self: post-rebase, need to apply these changes to TestInterfaceListPointerField.

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.

Looks great! The detailed docstrings are a nice touch.

// how to refactor.
for _, selection := range selectionSet {
field, ok := selection.(*ast.Field)
if !ok { // fragment/interface, not a shared field
Copy link
Contributor

Choose a reason for hiding this comment

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

As we discovered with the validator edge case, a union doesn't have shared fields, but it may be possible to treat the fields as shared if all the union members implement the same interface corresponding to a fragment. Anyway, this will be tackled in the upcoming fragment changes (if we want to tackle it at all).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, yeah that's an interesting situation, and maybe a place where abstract-in-abstract spreads are more useful. Anyway, this is gonna get rewritten as you say.

@benjaminjkraft benjaminjkraft changed the base branch from benkraft.interfaces-3 to main August 25, 2021 18:59
Right now, if you make a query like `{ myInterface { field } }`, you
have to type-switch on all the possible implementations of `myInterface`
to get at `field`.  Now, we generate getter-methods (e.g. `GetField`),
to make that access easier.  Of course this only applies to shared
fields (which for now are the only ones, but once we support fragments
will no longer be).

This also includes a small change to the way we generate type-names for
interfaces: we no longer include the name of the concrete type in the
interface we propagate forward, so we generate
`MyInterfaceMyFieldMyType`, not `MyInterfaceMyImplMyFieldMyType`, in the
case where you have an interface `MyInterface` implemented by `MyImpl`
(and maybe other types) with field `myField: MyType`.  This is necessary
so the getter method returns a well-defined type, and also probably
convenient for calling code.  It will have to get a little bit more
complicated once we support fragments, where you could have two
implementing types with identically-named fields of different types, but
I think it'll be easiest to figure out how to deal with that when
implementing fragments.

While I was in the area, I added to the interface doc-comment a list of
the implementations.  (In GraphQL, we're guaranteed to know them all
assuming our schema is up to date.)

Issue: #8

Test plan:
make check

Reviewers: marksandstrom, adam, miguel
@benjaminjkraft benjaminjkraft merged commit 8f9d1cf into main Aug 25, 2021
@benjaminjkraft benjaminjkraft deleted the benkraft.interfaces-4 branch August 25, 2021 19:07
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