-
-
Notifications
You must be signed in to change notification settings - Fork 916
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
Do not register unused graph types #1887
Conversation
To be explicit, what I hit was that going from 2.x to 3.x -- I had my own UriType, the base library added it's own UriType (both having name Uri/UriType, I can't recall). Scanning finds both with the same name and fires exception. In this case UriType was 'used', but it's shape differed. I'll try to file a repro of this tomorrow as well. |
Relates to #1423 . These changes should be carefully reviewed prior to merging. |
I skimmed through the edits. Yet it seems to me that this issue should proceed differently. I'll be back with an answer later. |
Also see graphql/graphql-spec#222 and graphql/graphql-spec#648 |
Originally posted by @sungam3r in https://github.com/graphql-dotnet/graphql-dotnet/issue_comments/705484550 That's EXACTLY what this PR does. Only the 5 basic scalars are registered with a simple schema. And just like any graph type, additional scalars referenced by the graph are automatically added to the schema. The only difference here is that we have special code to prevent the extra provided scalars (like There might be another way to go about it, but I think this is the right answer. We should not include these extra scalars in the schema by default. Nor should we remove them from the library. And there's no reason to force users to register them with their DI provider. Finally, it's not a breaking change -- in the sense that all existing code will work perfectly with this update. |
Well, actually, I think that moving all custom scalars into a separate package is the right way.
The reason is to make the library free of implementation assumptions and not create conflicts with custom scalars. Custom scalars are a good example of when you can move a piece of code into a separate package without negative consequences. It will be breaking change for clients, yes. I think this is a long-awaited technical debt. The emergence of individual packages is a normal evolutionary process. |
I'm going to simplify this PR a bit, and get rid of the other changes... |
Let's assume for the moment that in 4.0 we have custom scalars in a separate nuget package. Is there any reason not to include this change in 3.1? I think we should. I have removed the API changes, so please re-review. |
Give me 1-2 days. |
It will take me longer. I had to deal with other bugfixes. |
Nice workaround. This is an understandable little change. However, I would like to simplify a few things. I'll be back with another PR. |
@@ -184,6 +189,27 @@ public IEnumerable<IGraphType> All() | |||
} | |||
} | |||
|
|||
private void AddType(Type builtInGraphType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest perhaps a more distinct name like AddBuiltInType
? Too closely named to the other AddType
methods I think.
I like this direction. 👍🏼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! - done. 👍
Let me know if you have any other comments. It's a pretty simple enhancement, and will, I believe, work great all the way 'round.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already intervened in this PR and changed a lot. This method will no longer exist. I will update the PR and describe the changes in the next hour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolving conflicts 😞
I copied from the Since these new methods and fields are all private, we can refactor at will later also. |
Will update PR soon. |
6c49d33
to
b63ad3d
Compare
@@ -99,7 +99,7 @@ public override bool Equals(object obj) | |||
} | |||
|
|||
/// <summary> | |||
/// This sucks, find a better way | |||
/// TODO: This sucks, find a better way |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood this remark, but I don’t know how best to proceed. Maybe later.
What was done:
I think that #1423 is no longer needed. |
One consideration against static dictionaries. Types can contain metadata. Then a conflict can arise if the user adds different metadata into the built-in types. |
Switched to instance fields. This will return some allocations, but still there will be fewer of them than before this PR. |
public class GraphTypesLookup | ||
{ | ||
private readonly IDictionary<string, IGraphType> _types = new Dictionary<string, IGraphType>(); | ||
// Introspection types http://spec.graphql.org/June2018/#sec-Schema-Introspection | ||
private readonly Dictionary<Type, IGraphType> _introspectionTypes = new IGraphType[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these types are always added to the schema. Each type is created only once within schema instance. Before these changes types were created several times. It is also true for built-in scalars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
// Add introspection types. Note that introspection types rely on the | ||
// CamelCaseNameConverter, as some fields are defined in pascal case - e.g. Field(x => x.Name) | ||
_context = new TypeCollectionContext( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeCollectionContext
is just like a proxy to resolve/add methods pair. It can be shared across all introspection types whilst AddType
calls.
} | ||
if (namedType.IsGenericType) | ||
else if (namedType.IsGenericType && (namedType.ImplementsGenericType(typeof(EdgeType<>)) || namedType.ImplementsGenericType(typeof(ConnectionType<,>)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps these types should be taken out of here. I've been thinking about this for a long time. Not in thin PR though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better if upon finding a generic type (e.g. EdgeType<MyModel>
), it uses reflection to retrieve the generic version of the type (e.g. EdgeType<>
) and then checks _builtInCustomScalars
for the generic type. If matched, it will use Activator.CreateInstance
to create an instance. Fine as-is though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to make a new issue and link it to #1039
{ | ||
type = _builtInScalars.Values.FirstOrDefault(t => t.Name == reference.TypeName); | ||
if (type != null) | ||
this[type.Name] = type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example - discovering ID
built-in type during schema first for
type Post {
id: ID!
title: String!
}
type Query {
post(id: ID!): Post
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't use schema-first so I'll trust you here :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this also need to check the _builtInCustomScalars
? Seems like that would be the case everywhere, it would need to check both wouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Fixed.
Good. We may want to allow customization of this behavior in a future PR. (Not now.) |
This PR looks good to me. |
(I can't actually approve the PR since I created it) |
417582f
to
de54005
Compare
@@ -430,7 +430,7 @@ public void can_execute_complex_schema() | |||
} | |||
type Blog { | |||
title: String! | |||
post(id: ID!): Post | |||
post(id: ID!, unused: Long!): Post |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of custom built-in scalars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, I approve my own changes 😄 .
This is the last issue in 3.1 milestone. I am very glad that this work has been done. I've wanted to get rid of unused scalars for a very long time, but I've been putting it off all the time. Thanks for the review. Merging. |
This PR prevents
GraphTypesLookup
from registering custom .Net scalar graph types (e.g.DateTimeOffsetGraphType
) that are not used by the schema. See #1885Also allows custom
Schema
implementations necessary hooks to edit the list of built-in scalar graph types during schema initialization. Not actually necessary to solve #1885 since theUriGraphType
would never have been registered if it was not used, so there would be no conflict.This currently targets version 3.1. Should it target 4.0? The binary is backwards compatible, but generated schemas will not include unused types, so introspection results will differ (even though the missing types are unused by the schema).