Skip to content

Commit

Permalink
experimental - allow payloadless variant constructors in input unions
Browse files Browse the repository at this point in the history
  • Loading branch information
zth committed Oct 9, 2024
1 parent c9faa3c commit b7a831b
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 37 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## main

- _experimental_ Allow payloadless variant cases in input unions.

## 0.13.2

- Fix bug when input objects and input unions were mixed.
Expand Down
16 changes: 16 additions & 0 deletions src/ml/GenerateSchema.ml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ let rec findGraphQLType ~(env : SharedTypes.QueryEnv.t)
match c.args with
| InlineRecord _ -> Some c.cname.txt
| _ -> None);
emptyPayloads =
cases
|> List.filter_map (fun (c : SharedTypes.Constructor.t) ->
match c.args with
| Args [] -> Some c.cname.txt
| _ -> None);
})
| Some (InterfaceResolver {interfaceId}), {kind = Variant _} ->
Some
Expand Down Expand Up @@ -896,6 +902,16 @@ and variantCasesToInputUnionValues ~env ~debug ~schemaState ~full ~ownerName
}
in
Some member
| Args [] ->
Some
{
typ = EmptyPayload;
fieldName = uncapitalizeFirstChar case.cname.txt;
loc = case.cname.loc;
description =
case.attributes |> ProcessAttributes.findDocAttribute;
constructorName = case.cname.txt;
}
| Args [(typ, _)] -> (
(* TODO: Validate more that only input types are present. *)
match
Expand Down
1 change: 1 addition & 0 deletions src/ml/GenerateSchemaSDL.ml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let rec graphqlTypeToString ?(nullable = false) (t : graphqlType) =
let nullableSuffix = if nullable = false then "!" else "" in
match t with
| Scalar scalar -> scalarToString scalar ^ nullableSuffix
| EmptyPayload -> graphqlTypeToString ~nullable:true (Scalar Boolean)
| Nullable inner | RescriptNullable inner ->
graphqlTypeToString ~nullable:true inner
| List inner ->
Expand Down
7 changes: 6 additions & 1 deletion src/ml/GenerateSchemaTypePrinters.ml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ let rec printGraphQLType ?(nullable = false) (returnType : graphqlType) =
(printGraphQLType inner) nullablePostfix
| RescriptNullable inner | Nullable inner ->
printGraphQLType ~nullable:true inner
| EmptyPayload -> printGraphQLType ~nullable:true (Scalar Boolean)
| Scalar scalar ->
let scalarStr =
match scalar with
Expand Down Expand Up @@ -402,7 +403,7 @@ let printSchemaJsFile schemaState processSchema =
(* Add the input union unwrapper. TODO: Explain more
*)
addWithNewLine
{|let inputUnionUnwrapper: ('src, array<string>) => 'return = %raw(`function inputUnionUnwrapper(src, inlineRecordTypenames) {
{|let inputUnionUnwrapper: ('src, array<string>, array<string>) => 'return = %raw(`function inputUnionUnwrapper(src, inlineRecordTypenames, emptyPayloadTypenames) {
if (src == null) return null;

let targetKey = null;
Expand All @@ -421,6 +422,10 @@ let printSchemaJsFile schemaState processSchema =
if (inlineRecordTypenames.includes(tagName)) {
return Object.assign({ TAG: tagName }, targetValue);
}

if (emptyPayloadTypenames.includes(tagName)) {
return tagName;
}

return {
TAG: tagName,
Expand Down
3 changes: 3 additions & 0 deletions src/ml/GenerateSchemaTypes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ type graphqlType =
| Nullable of graphqlType
| RescriptNullable of graphqlType
| Scalar of scalar
| EmptyPayload
(** Used to represent empty payloads, like constructor-less unions. *)
| InjectContext
| InjectInfo
| InjectInterfaceTypename of string (** ID of interface *)
Expand All @@ -14,6 +16,7 @@ type graphqlType =
id: string;
displayName: string;
inlineRecords: string list;
emptyPayloads: string list;
}
| GraphQLEnum of {id: string; displayName: string}
| GraphQLUnion of {id: string; displayName: string}
Expand Down
8 changes: 6 additions & 2 deletions src/ml/GenerateSchemaUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -501,14 +501,18 @@ let rec generateConverter lastValue (graphqlType : graphqlType) =
Printf.sprintf
"%s->applyConversionToInputObject(input_%s_conversionInstructions)"
lastValue displayName
| GraphQLInputUnion {displayName; inlineRecords} ->
| GraphQLInputUnion {displayName; inlineRecords; emptyPayloads} ->
(* TODO: Precompute/persist? *)
Printf.sprintf
"%s->applyConversionToInputObject(inputUnion_%s_conversionInstructions)->inputUnionUnwrapper([%s])"
"%s->applyConversionToInputObject(inputUnion_%s_conversionInstructions)->inputUnionUnwrapper([%s], \
[%s])"
lastValue displayName
(inlineRecords
|> List.map (fun s -> "\"" ^ s ^ "\"")
|> String.concat ", ")
(emptyPayloads
|> List.map (fun s -> "\"" ^ s ^ "\"")
|> String.concat ", ")
| _ -> lastValue

let printInputObjectAssets (inputObject : gqlInputObjectType) =
Expand Down
5 changes: 5 additions & 0 deletions tests/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ input UpdatableNullableInt {
updateValue: Int
}

input UnionWithEmptyMember {
empty: Boolean
string: String
}

input UpdatableInt {
leaveUnchanged: Boolean
updateValue: Int
Expand Down
20 changes: 10 additions & 10 deletions tests/src/Thing.res
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
type updatableOptions = LeaveUnchanged(bool)
type updatableOptions = LeaveUnchanged

type updatableOptionsNullable = UnsetValue(bool) | ...updatableOptions
type updatableOptionsNullable = UnsetValue | ...updatableOptions

@gql.inputUnion
type updatableNullableString = UpdateValue(string) | ...updatableOptionsNullable
Expand Down Expand Up @@ -66,26 +66,26 @@ let updateThing = (_: Mutation.mutation, ~thingId: ResGraph.id, ~input: updateTh
...currentThing,
name: switch input.name {
| UpdateValue(name) => name
| LeaveUnchanged(_) => currentThing.name
| LeaveUnchanged => currentThing.name
},
age: switch input.age {
| UpdateValue(age) => age
| LeaveUnchanged(_) => currentThing.age
| LeaveUnchanged => currentThing.age
},
favoriteColor: switch input.favoriteColor {
| UpdateValue(favoriteColor) => Some(favoriteColor)
| LeaveUnchanged(_) => currentThing.favoriteColor
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.favoriteColor
| UnsetValue => None
},
isAdmin: switch input.isAdmin {
| UpdateValue(isAdmin) => Some(isAdmin)
| LeaveUnchanged(_) => currentThing.isAdmin
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.isAdmin
| UnsetValue => None
},
height: switch input.height {
| UpdateValue(height) => Some(height)
| LeaveUnchanged(_) => currentThing.height
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.height
| UnsetValue => None
},
}

Expand Down
47 changes: 40 additions & 7 deletions tests/src/__generated__/ResGraphSchema.res

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tests/src/__generated__/schema.graphql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 13 additions & 10 deletions tests/tests/Schema.res
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,9 @@ let countdown = (_: subscription) => {
iterator
}

type updatableOptions = LeaveUnchanged(bool)
type updatableOptions = LeaveUnchanged

type updatableOptionsNullable = UnsetValue(bool) | ...updatableOptions
type updatableOptionsNullable = UnsetValue | ...updatableOptions

@gql.inputUnion
type updatableNullableString = UpdateValue(string) | ...updatableOptionsNullable
Expand Down Expand Up @@ -497,28 +497,31 @@ let updateThing = (_: mutation, ~thingId: ResGraph.id, ~input: updateThingInput)
...currentThing,
name: switch input.name {
| UpdateValue(name) => name
| LeaveUnchanged(_) => currentThing.name
| LeaveUnchanged => currentThing.name
},
age: switch input.age {
| UpdateValue(age) => age
| LeaveUnchanged(_) => currentThing.age
| LeaveUnchanged => currentThing.age
},
favoriteColor: switch input.favoriteColor {
| UpdateValue(favoriteColor) => Some(favoriteColor)
| LeaveUnchanged(_) => currentThing.favoriteColor
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.favoriteColor
| UnsetValue => None
},
isAdmin: switch input.isAdmin {
| UpdateValue(isAdmin) => Some(isAdmin)
| LeaveUnchanged(_) => currentThing.isAdmin
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.isAdmin
| UnsetValue => None
},
height: switch input.height {
| UpdateValue(height) => Some(height)
| LeaveUnchanged(_) => currentThing.height
| UnsetValue(_) => None
| LeaveUnchanged => currentThing.height
| UnsetValue => None
},
}

Some(newThing)
}

@gql.inputUnion
type unionWithEmptyMember = String(string) | Empty
Loading

0 comments on commit b7a831b

Please sign in to comment.