-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Change runtime wiring redefinition checks to be strict by default #3824
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
Changes from all commits
09f6a88
d28e95a
bc45e3d
e0b2c27
fe1ac76
a086041
a14fc33
2042ea4
ee9570a
8d5436a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -193,26 +193,23 @@ class RuntimeWiringTest extends Specification { | |
| newWiring.fieldVisibility == fieldVisibility | ||
| } | ||
|
|
||
| def "strict mode can stop certain redefinitions"() { | ||
| def "strict mode, on by default, can stop certain redefinitions"() { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updating tests and adding new ones to lock in the change in default behaviour |
||
| DataFetcher DF1 = env -> "x" | ||
| DataFetcher DF2 = env -> "x" | ||
| TypeResolver TR1 = env -> null | ||
| EnumValuesProvider EVP1 = name -> null | ||
|
|
||
| when: | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .strictMode() | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove these lines, as the default is strictness on |
||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("bar", DF1)) | ||
|
|
||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) // Cannot redefine the same field's datafetcher | ||
|
|
||
| then: | ||
| def e1 = thrown(StrictModeWiringException) | ||
| e1.message == "The type Foo has already been defined" | ||
| e1.message == "The field foo on type Foo has already been defined" | ||
|
|
||
| when: | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .strictMode() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
|
|
||
|
|
@@ -222,7 +219,6 @@ class RuntimeWiringTest extends Specification { | |
|
|
||
| when: | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .strictMode() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
| then: | ||
|
|
@@ -231,15 +227,13 @@ class RuntimeWiringTest extends Specification { | |
|
|
||
| when: | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .strictMode() | ||
| .scalar(Scalars.GraphQLString) | ||
| then: | ||
| def e4 = thrown(StrictModeWiringException) | ||
| e4.message == "The scalar String is already defined" | ||
|
|
||
| when: | ||
| TypeRuntimeWiring.newTypeWiring("Foo") | ||
| .strictMode() | ||
| .defaultDataFetcher(DF1) | ||
| .defaultDataFetcher(DF2) | ||
|
|
||
|
|
@@ -248,34 +242,148 @@ class RuntimeWiringTest extends Specification { | |
| e5.message == "The type Foo has already has a default data fetcher defined" | ||
| } | ||
|
|
||
| def "overwrite default data fetchers if they are null"() { | ||
|
|
||
| def "strict mode, on by default, permits a type to be defined more than once as long as elements are not overlapping"() { | ||
| DataFetcher DF1 = env -> "x" | ||
| DataFetcher DF2 = env -> "x" | ||
| DataFetcher DEFAULT_DF = env -> null | ||
| DataFetcher DEFAULT_DF2 = env -> null | ||
| TypeResolver TR1 = env -> null | ||
| EnumValuesProvider EVP1 = name -> null | ||
|
|
||
| when: | ||
| def runtimeWiring = RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF)) | ||
| // Permit type wiring to be defined more than once, if child DataFetchers are for distinct fields | ||
| def runtimeWiring1 = RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("bar", DF2)) | ||
| .build() | ||
|
|
||
| then: | ||
| runtimeWiring.getDefaultDataFetcherForType("Foo") == DEFAULT_DF | ||
| noExceptionThrown() | ||
| runtimeWiring1.getDataFetchers().get("Foo").get("foo") == DF1 | ||
| runtimeWiring1.getDataFetchers().get("Foo").get("bar") == DF2 | ||
|
|
||
| when: | ||
| // Only one type wiring is allowed per type, do not allow redefinition | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
|
|
||
| then: | ||
| def e2 = thrown(StrictModeWiringException) | ||
| e2.message == "The type Foo already has a type resolver defined" | ||
|
|
||
| when: | ||
| // Only one enum values provider is allowed per type, do not allow redefinition | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
|
|
||
| then: | ||
| def e3 = thrown(StrictModeWiringException) | ||
| e3.message == "The type Foo already has a enum provider defined" | ||
|
|
||
| when: | ||
| // Only one scalar wiring is allowed per scalar | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .scalar(Scalars.GraphQLString) | ||
| then: | ||
| def e4 = thrown(StrictModeWiringException) | ||
| e4.message == "The scalar String is already defined" | ||
|
|
||
| when: | ||
| // Only one default data fetcher is allowed, do not allow redefinition | ||
| TypeRuntimeWiring.newTypeWiring("Foo") | ||
| .defaultDataFetcher(DF1) | ||
| .defaultDataFetcher(DF2) | ||
|
|
||
| then: | ||
| def e5 = thrown(StrictModeWiringException) | ||
| e5.message == "The type Foo has already has a default data fetcher defined" | ||
| } | ||
|
|
||
| def "strict mode, if set to off, won't stop certain redefinitions"() { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests to lock in the strict mode off case |
||
| DataFetcher DF1 = env -> "x" | ||
| DataFetcher DF2 = env -> "x" | ||
| TypeResolver TR1 = env -> null | ||
| EnumValuesProvider EVP1 = name -> null | ||
|
|
||
| when: | ||
| runtimeWiring = RuntimeWiring.newRuntimeWiring() | ||
| def runtimeWiring1 = RuntimeWiring.newRuntimeWiring() | ||
| .strictMode(false) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF2)) | ||
| .build() | ||
|
|
||
| then: | ||
| noExceptionThrown() | ||
| runtimeWiring1.getDataFetchers().get("Foo").get("foo") == DF2 | ||
|
|
||
| when: | ||
| def runtimeWiring2 = RuntimeWiring.newRuntimeWiring() | ||
| .strictMode(false) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").typeResolver(TR1)) | ||
| .build() | ||
|
|
||
| then: | ||
| noExceptionThrown() | ||
| runtimeWiring2.typeResolvers.get("Foo") == TR1 | ||
|
|
||
| when: | ||
| def runtimeWiring3 = RuntimeWiring.newRuntimeWiring() | ||
| .strictMode(false) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").enumValues(EVP1)) | ||
| .build() | ||
|
|
||
| then: | ||
| noExceptionThrown() | ||
| runtimeWiring3.getEnumValuesProviders().get("Foo") == EVP1 | ||
|
|
||
| when: | ||
| def runtimeWiring4 = RuntimeWiring.newRuntimeWiring() | ||
| .strictMode(false) | ||
| .scalar(Scalars.GraphQLString) | ||
| .build() | ||
|
|
||
| then: | ||
| noExceptionThrown() | ||
| runtimeWiring4.scalars.get("String") == Scalars.GraphQLString | ||
|
|
||
| when: | ||
| def typeRuntimeWiring = TypeRuntimeWiring.newTypeWiring("Foo") | ||
| .strictMode(false) | ||
| .defaultDataFetcher(DF1) | ||
| .defaultDataFetcher(DF2) | ||
| .build() | ||
|
|
||
| then: | ||
| noExceptionThrown() | ||
| typeRuntimeWiring.defaultDataFetcher == DF2 | ||
| } | ||
|
|
||
| def "when strict mode on, do not allow default data fetcher redefinition"() { | ||
| DataFetcher DF1 = env -> "w" | ||
| DataFetcher DEFAULT_DF = env -> "x" | ||
| DataFetcher DEFAULT_DF2 = env -> "y" | ||
|
|
||
| // Having a datafetcher and a default for the type is ok | ||
| when: | ||
| def runtimeWiring1 = RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("bar", DF2)) | ||
| // we can specifically overwrite it later | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF2)) | ||
| .build() | ||
|
|
||
| then: | ||
| runtimeWiring.getDefaultDataFetcherForType("Foo") == DEFAULT_DF2 | ||
| runtimeWiring1.getDefaultDataFetcherForType("Foo") == DEFAULT_DF | ||
|
|
||
| // Do not permit redefinition of the default datafetcher | ||
| when: | ||
| RuntimeWiring.newRuntimeWiring() | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF)) | ||
| .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF2)) | ||
| .build() | ||
|
|
||
| then: | ||
| def error = thrown(StrictModeWiringException) | ||
| error.message == "The type Foo already has a default data fetcher defined" | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Covering a new edge case, where the default datafetcher is redefined at the runtime wiring level We already had a test for when it is redefined inside the type wiring |
||
| } | ||
| } | ||
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.
An edge case that wasn't previously covered: if a type wiring's default datafetcher was redefined at the runtime wiring level (and not the type wiring level), previously didn't raise an error
See test below to lock in the new behaviour