Support to see if user set field explicitly to `null` on input object
See original GitHub issueContext
I am trying to create a mutation to allow partial updates an object with many nested fields.
fun updatePersonInfo(request: UpdatePersonInfo): Profile?
nested fields are all optional, so the user only needs to supply fields they want to update in the request.
Fields absent from request will be not be changed.
Fields present in the request will be set to the newly values defined in request, including fields set to null
.
Example –
a mutation to set familyName to “natasha-new-family-name” and middleName to null
would look like:
mutation myMutation {
updatePersonInfo(
userId: 123,
personInfo {
“name”: {
“familyName”: “natasha-new-family-name”,
“middleName”: null
}
}
)
}
a mutation to only update family name, leaving middleName unchanged, would look like:
mutation myMutation {
updatePersonInfo(
userId: 123,
personInfo {
“name”: {
“familyName”: “natasha-new-family-name”
}
}
)
}
Problem I am unable to cleanly use graphql-kotlin to see if a user has set a field to null in the mutation input object (indicating desire to delete a field) because there is no way currently to differentiate if a field was set to null by the user, or if it was simply never defined (absent in request).
This is also nature of kotlin itself, undefined fields default to null
as the value. However, in graphql there is a difference between undefined and set to null
that I am hoping to leverage. https://graphql.github.io/graphql-spec/June2018/#sec-Input-Objects (see: Input Coercion)
Proposed Solution A wrapper class built-in to graphql-kotlin such that a field can have three values to represent it (undefined, null, value). This wrapper would not show up in the schema definition of the object, but would be used to represent if the object it wraps was present in the request or not. If it was present, it’s value is also defined.
Alternatively, leverage java optional wrapper to do this, where the optional wrapper would also not show up in the schema type.
Alternatives Since I do not want the wrapper shown in the schema, my current solution is to parse through the available graphql-java DataFetchingEnvironment instead of using the input object itself (input object still defined for user input) to see if fields are present in the request or explicitly set to null (indicates desire to delete).
As a temporary solution, I wanted to create this wrapper myself, however it would show up in schema. Hoping a built-in optional wrapper supported by graphql-kotlin could help this…
Additional context A similar question has already been asked in this thread: https://github.com/ExpediaGroup/graphql-kotlin/issues/50#issuecomment-483432882 but I feel the root of the problem was never addressed/answered in the original thread.
Issue Analytics
- State:
- Created 3 years ago
- Comments:10 (4 by maintainers)
While it is possible to achieve this with the current hooks it is somewhat problematic, i.e. you could use
willGenerateGraphQLType
to process the wrapper class but since hooks don’t have access to the generator you would need to manually build underlying GraphQL type (or reference built in scalars). Once schema is generated you would still need to create custom data fetcher to properly unmarshall the incoming object into the wrapper class.I’m thinking for
4.0.0
we probably could introduce some sealed class to represent defined/undefined state (introduce the unwrapping logic in input object generation and update default data fetcher to properly handle those as well).@dariuszkuc, @smyrick I was able to get this to work by adding a hook into generateGraphQLType in a local build of graphql-kotlin. The hook was basically the equivalent of willResolveMonad. Leveraging that hook and Jackson’s support for Optional worked in @natashashams and I project. generateGraphQLType is probably no the ideal place for this but it helped me prototype it quickly. It would be great to add a hook that allowed you to unwrap types, even if a built in type will be provided for undefined. These cases would require users to write their own deserializers but it’s a very generic feature that could help as a stepping stone to related wrapper functionality. It would be especially helpful if someone is looking to map to a type that is more natural to deeper layers of their service.