-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[generator] Replace GraphQLContext annotation with interface (#610)
* BREAKING CHANGE: Change GraphQLContext from annotation to interface Instead of using an annotation and having to remember to include it in function arguments, we can create a marker interface for the GraphQLContext which library users can implement on a class to indicate that this class is the GraphQLContext. This moves the declaration from the arguments to the class itself which I think makes it more clear. This is obviously a large breaking change to fundamental part of our library. It requires heavy documentation changes and review to take place before the 2.0 release. Please review and provided feedback on the interface package path as well * Change GraphQLContextFactory to return the interface * Combine interface and default implementation into single file * Revert deletion of annotation class Undo the deletion of the old annotation and instead make these changes a minor feature addition * Update context docs * Update docs wording * typos * typo * typos, minor wording changes * typos, minor wording changes * Remove context annotation * Update the docs * GraphQLContextFactory must accept a GraphQLContext type * Change generic type of ContextWebFilter Instead of having a generic of a GraphQLContextFactory, we can make the generic for the type required of the context factory * Simplify kFunction unit tests Co-authored-by: Shane Myrick <[email protected]> Co-authored-by: Robert Del Favero <[email protected]>
- Loading branch information
1 parent
9a54d5d
commit 6311df8
Showing
23 changed files
with
278 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,124 +1,125 @@ | ||
--- | ||
id: arguments | ||
title: Arguments | ||
--- | ||
|
||
Method arguments are automatically exposed as part of the arguments to the corresponding GraphQL fields. | ||
|
||
```kotlin | ||
class SimpleQuery{ | ||
|
||
@GraphQLDescription("performs some operation") | ||
fun doSomething(@GraphQLDescription("super important value") value: Int): Boolean = true | ||
} | ||
``` | ||
|
||
The above Kotlin code will generate following GraphQL schema: | ||
|
||
```graphql | ||
type Query { | ||
"""performs some operation""" | ||
doSomething( | ||
"""super important value""" | ||
value: Int! | ||
): Boolean! | ||
} | ||
``` | ||
|
||
This behavior is true for all arguments except for the GraphQL context objects. See section below for detailed | ||
information about `@GraphQLContext`. | ||
|
||
### Input Types | ||
|
||
Query and mutation function arguments are automatically converted to corresponding GraphQL input fields. GraphQL makes a | ||
distinction between input and output types and requires unique names for all the types. Since we can use the same | ||
objects for input and output in our Kotlin functions, `graphql-kotlin-schema-generator` will automatically append | ||
`Input` suffix to the query input objects. | ||
|
||
```kotlin | ||
class WidgetMutation { | ||
|
||
@GraphQLDescription("modifies passed in widget so it doesn't have null value") | ||
fun processWidget(@GraphQLDescription("widget to be modified") widget: Widget): Widget { | ||
if (null == widget.value) { | ||
widget.value = 42 | ||
} | ||
return widget | ||
} | ||
} | ||
|
||
@GraphQLDescription("A useful widget") | ||
data class Widget( | ||
@GraphQLDescription("The widget's value that can be null") | ||
var value: Int? = nul | ||
) { | ||
@GraphQLDescription("returns original value multiplied by target OR null if original value was null") | ||
fun multiplyValueBy(multiplier: Int) = value?.times(multiplier) | ||
} | ||
``` | ||
|
||
Will generate | ||
|
||
```graphql | ||
type Mutation { | ||
"""modifies passed in widget so it doesn't have null value""" | ||
processWidget( | ||
"""widget to be modified""" | ||
widget: WidgetInput! | ||
): Widget! | ||
} | ||
|
||
"""A useful widget""" | ||
type Widget { | ||
|
||
"""The widget's value that can be null""" | ||
value: Int | ||
|
||
""" | ||
returns original value multiplied by target OR null if original value was null | ||
""" | ||
multiplyValueBy(multiplier: Int!): Int | ||
} | ||
|
||
"""A useful widget""" | ||
input WidgetInput { | ||
|
||
"""The widget's value that can be null""" | ||
value: Int | ||
} | ||
|
||
``` | ||
|
||
Please note that only fields are exposed in the input objects. Functions will only be available on the GraphQL output | ||
types. | ||
|
||
If you know a type will only be used for input types you can call your class `CustomTypeInput`. The library will not | ||
append `Input` if the class name already ends with `Input` but that means you can not use this type as output because | ||
the schema would have two types with the same name and will be invalid. | ||
|
||
### Optional input fields | ||
|
||
Kotlin requires variables/values to be initialized upon their declaration either from the user input OR by providing | ||
defaults (even if they are marked as nullable). Therefore in order for GraphQL input field to be optional it needs to be | ||
nullable and also specify default Kotlin value. | ||
|
||
```kotlin | ||
@GraphQLDescription("query with optional input") | ||
fun doSomethingWithOptionalInput( | ||
@GraphQLDescription("this field is required") requiredValue: Int, | ||
@GraphQLDescription("this field is optional") optionalValue: Int?) | ||
= "required value=$requiredValue, optional value=$optionalValue" | ||
``` | ||
|
||
NOTE: Non nullable input fields will always require users to specify the value regardless whether default Kotlin value | ||
is provided or not. | ||
|
||
NOTE: Even though you could specify a default value in Kotlin `optionalValue: Int? = null`, this will not be used since | ||
if no value is provided to the schema `graphql-java` passes null as the value so the Kotlin default value will never be | ||
used, like in this argument `optionalList: List<Int>? = emptyList()`, the value will be null if not passed a value by | ||
the client. | ||
|
||
### Default values | ||
|
||
Default argument values are currently not supported. See issue | ||
[#53](https://github.com/ExpediaGroup/graphql-kotlin/issues/53) for more details. | ||
--- | ||
id: arguments | ||
title: Arguments | ||
--- | ||
|
||
Method arguments are automatically exposed as part of the arguments to the corresponding GraphQL fields. | ||
|
||
```kotlin | ||
class SimpleQuery{ | ||
|
||
@GraphQLDescription("performs some operation") | ||
fun doSomething(@GraphQLDescription("super important value") value: Int): Boolean = true | ||
} | ||
``` | ||
|
||
The above Kotlin code will generate following GraphQL schema: | ||
|
||
```graphql | ||
type Query { | ||
"""performs some operation""" | ||
doSomething( | ||
"""super important value""" | ||
value: Int! | ||
): Boolean! | ||
} | ||
``` | ||
|
||
This behavior is true for all arguments except for the special classes for the [GraphQLContext](../execution/contextual-data) and the [DataFetchingEnvironment](../execution/data-fetching-environment) | ||
|
||
### Input Types | ||
|
||
Query and mutation function arguments are automatically converted to corresponding GraphQL input fields. GraphQL makes a | ||
distinction between input and output types and requires unique names for all the types. Since we can use the same | ||
objects for input and output in our Kotlin functions, `graphql-kotlin-schema-generator` will automatically append | ||
an `Input` suffix to the query input objects. | ||
|
||
For example, the following code: | ||
|
||
```kotlin | ||
class WidgetMutation { | ||
|
||
@GraphQLDescription("modifies passed in widget so it doesn't have null value") | ||
fun processWidget(@GraphQLDescription("widget to be modified") widget: Widget): Widget { | ||
if (null == widget.value) { | ||
widget.value = 42 | ||
} | ||
return widget | ||
} | ||
} | ||
|
||
@GraphQLDescription("A useful widget") | ||
data class Widget( | ||
@GraphQLDescription("The widget's value that can be null") | ||
var value: Int? = nul | ||
) { | ||
@GraphQLDescription("returns original value multiplied by target OR null if original value was null") | ||
fun multiplyValueBy(multiplier: Int) = value?.times(multiplier) | ||
} | ||
``` | ||
|
||
Will generate the following schema: | ||
|
||
```graphql | ||
type Mutation { | ||
"""modifies passed in widget so it doesn't have null value""" | ||
processWidget( | ||
"""widget to be modified""" | ||
widget: WidgetInput! | ||
): Widget! | ||
} | ||
|
||
"""A useful widget""" | ||
type Widget { | ||
|
||
"""The widget's value that can be null""" | ||
value: Int | ||
|
||
""" | ||
returns original value multiplied by target OR null if original value was null | ||
""" | ||
multiplyValueBy(multiplier: Int!): Int | ||
} | ||
|
||
"""A useful widget""" | ||
input WidgetInput { | ||
|
||
"""The widget's value that can be null""" | ||
value: Int | ||
} | ||
|
||
``` | ||
|
||
Please note that only fields are exposed in the input objects. Functions will only be available on the GraphQL output | ||
types. | ||
|
||
If you know a type will only be used for input types you can call your class something like `CustomTypeInput`. The library will not | ||
append `Input` if the class name already ends with `Input` but that means you can not use this type as output because | ||
the schema would have two types with the same name and that would be invalid. | ||
|
||
### Optional input fields | ||
|
||
Kotlin requires variables/values to be initialized upon their declaration either from the user input OR by providing | ||
defaults (even if they are marked as nullable). Therefore in order for a GraphQL input field to be optional it needs to be | ||
nullable and also specify a default Kotlin value. | ||
|
||
```kotlin | ||
@GraphQLDescription("query with optional input") | ||
fun doSomethingWithOptionalInput( | ||
@GraphQLDescription("this field is required") requiredValue: Int, | ||
@GraphQLDescription("this field is optional") optionalValue: Int?) | ||
= "required value=$requiredValue, optional value=$optionalValue" | ||
``` | ||
|
||
NOTE: Non nullable input fields will always require users to specify the value regardless of whether a default Kotlin value | ||
is provided or not. | ||
|
||
NOTE: Even though you could specify a default value in Kotlin `optionalValue: Int? = null`, this will not be used. This is because | ||
if no value is provided to the schema, `graphql-java` passes null as the value. The Kotlin default value will never be | ||
used. For example, with argument `optionalList: List<Int>? = emptyList()`, the value will be null if not passed a value by | ||
the client. | ||
|
||
### Default values | ||
|
||
Default argument values are currently not supported. See issue | ||
[#53](https://github.com/ExpediaGroup/graphql-kotlin/issues/53) for more details. |
Oops, something went wrong.