Skip to content

Provide more information to TypeResolver#369

Closed
kaqqao wants to merge 1 commit into
graphql-java:masterfrom
kaqqao:122TypeResolver
Closed

Provide more information to TypeResolver#369
kaqqao wants to merge 1 commit into
graphql-java:masterfrom
kaqqao:122TypeResolver

Conversation

@kaqqao

@kaqqao kaqqao commented Apr 10, 2017

Copy link
Copy Markdown
Contributor

closes #122

(I closed my original PR #343 because I managed to get myself in a Git situation that was easier for me to resolve by recreating the branch, thus opening a new PR)

I've reimplemented the changes to the ExecutionStrategy in a way that allows easier future modifications, and made overloads and deprecations for backward-compatibility.

Implementation notes:

  • I've only introduced the ...Parameters classes where I needed them for this change, not everywhere where they should arguably go. Tell me If you want me refactor more places.
  • I made the ...Parameters constructors private to avoid implementors relying on them and ending up in this same situation. I wanted them package private so that they can be used internally, but BatchedExecutionStrategy is in a different package so there wasn't much point.
  • I added Javadoc to the @Deprecated methods only to link to the new options, but since this is not a full Javadoc, a lot of lint warnings are generated during the build. Would you rather have the incomplete docs removed or maybe provide the text you'd like in the Javadoc, or would you like me to add my own?

Now, the question comes back to the TypeResolver:

  • should I re-introduce the Java 8 dependent default for backwards compatibility
  • should I leave as a breaking change, since it seems more breaking changes have been planned
  • should I implement it in a different, backwards-compatible way (described below)?

One idea I have is to introduce a new interface, e.g. TypeDecider, that would provide the new functionality: GraphQLObjectType getType(TypeResolutionEnvironment env), and then change the builders that accept TypeResolvers in a fashion similar to this:

//wrap the given TypeResolver in a simple TypeDecider that just delegates to it
public Builder typeResolver(TypeResolver typeResolver) {
    this.typeDecider = new SimpleTypeDecider(typeResolver);
    return this;
 }

//add a new method that accepts TypeDecider directly
public Builder typeDecider(TypeDecider typeDecider) {
    this.typeDecider = typeDecider;
    return this;
 }

With both approaches, backwards-compatibility would be, I think, complete.

I've already implemented the TypeDecider approach, so if you like that option I can push it for review.

Allow richer resolution logic
@andimarek andimarek added this to the 3.0.0 milestone Apr 10, 2017
@andimarek

andimarek commented Apr 10, 2017

Copy link
Copy Markdown
Member

@kaqqao Please add the default implementation again: We are on Java 8 now and if that helps not breaking stuff, we will take it.

Because changing such a central interface we should avoid almost always, I prefer adding a better alternative side by side.

Thanks!

@kaqqao

kaqqao commented Apr 10, 2017

Copy link
Copy Markdown
Contributor Author

@andimarek The only issue with adding default to the new TypeResolver method and deprecating the current is that it forces implementors to still implement the deprecated one:

public interface TypeResolver {

    default GraphQLObjectType getType(TypeResolutionEnvironment env) { //backward compatible :)
        return getType(env.getObject());
    }

    @Deprecated
    GraphQLObjectType getType(Object object); //still forces implementation :(
}

Not sure I got you on the last bit, do you mean you'd like me to add the TypeDecider interface I've described (can change name if you wish) in addition to default or you think default is enough`?

@bbakerman

Copy link
Copy Markdown
Member

Sorry to give you a bum steer here. We also have another PR in the works that uses the "parameters pattern". re: #353

I want to get this in place before this PR as the naming of the parameters is preferred.

@kaqqao

kaqqao commented Apr 10, 2017

Copy link
Copy Markdown
Contributor Author

OK, I'll rework this one then after #353 is merged. Do drop a line if you an opinion on default/TypeDecider thing.

@bbakerman

Copy link
Copy Markdown
Member

I think @andimarek wants you to use the default keywords because we can now that we are on Java 8. So yes people will have to implement the old as always but they can opt into the "default" implementation

@andimarek

Copy link
Copy Markdown
Member

@kaqqao @bbakerman I would try to avoid such a big breaking change: TypeResolver is probably implemented quite often.
Maybe we could subclass TypeResolver and therefore detect if somebody is using the new resolve method or the old? Not sure this is a great idea though.

@kaqqao

kaqqao commented Apr 10, 2017

Copy link
Copy Markdown
Contributor Author

@andimarek @bbakerman Here's a suggestion mixing the approaches mentioned so far:

public interface TypeResolver {

    GraphQLObjectType getType(Object object);
    
    default GraphQLObjectType getType(TypeResolutionEnvironment env) {
        return getType(env.getObject());
    }
}

public interface AdvancedTypeResolver extends TypeResolver { //could maybe use a better name

    @Override
    GraphQLObjectType getType(TypeResolutionEnvironment env);

    @Override
    default GraphQLObjectType getType(Object object) {
        //Could instead delegate to the method above, but I think an exception is better as library code never actually calls this method
        throw new UnsupportedOperationException("AdvancedTypeResolver does not support simple type resolution");
    }
}

While a bit convoluted, this solves the conundrum rather effectively.
All existing TypeResolvers will keep working as usual and the new code can choose to use AdvancedTypeResolver instead, implementing only the new method. Library code will only ever call the new method in all cases (already true in this PR). None of the builders that accept TypeResolver or any other code need to change, unlike in my original TypeDecider approach.

Didn't want to push it like this before I hear your comments.

@andimarek

Copy link
Copy Markdown
Member

@kaqqao Thanks ... this goes in the direction I would prefer. But do we need the new method in TypeResolver with the default Implementation? This seems confusing to be honest.

@kaqqao

kaqqao commented Apr 11, 2017

Copy link
Copy Markdown
Contributor Author

@andimarek It is confusing, I agree. I added it to remove the need for any other changes. If I remove it, I have 2 options:

  • Do instanceof check inside resolveType:
typeResolver instanceof AdvancedTypeResolver ? ((AdvancedTypeResolver) typeResolver).getType(env) : typeResolver.getType(env.getObject);
  • Make GraphQLInterfaceType and GraphQLUnionType always wrap the given TypeResolver into AdvancedTypeResolver (the same approach I took for my TypeDecider idea)

Do you think one of these is the way to go?

return result;
}

/**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We already broke this SPI in 3.0 with the earlier "parameters" change. You dont need to to this.

@bbakerman

Copy link
Copy Markdown
Member

@kaqqao - since we changed the parameter calling structure quite a lot this has large conflicts.

But I forked your fork and made a new commit which is mostly your code but with fix ups

Its here

#395

@bbakerman

Copy link
Copy Markdown
Member

I think I will close this one and use the other one (which is essentially yours) as the way forward

@kaqqao

kaqqao commented Apr 28, 2017

Copy link
Copy Markdown
Contributor Author

Awesome! Do you need me to do anything at this point?

@bbakerman

Copy link
Copy Markdown
Member

Now merged

@kaqqao kaqqao deleted the 122TypeResolver branch May 1, 2017 07:37
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.

TypeResolver often needs more input to resolve (Relay Node) type

3 participants