Skip to content

Commit a3738b3

Browse files
committed
Implemented InputCustom type for internal type definitions
1 parent a3ec328 commit a3738b3

File tree

5 files changed

+137
-17
lines changed

5 files changed

+137
-17
lines changed

src/FSharp.Data.GraphQL.Server.Middleware/MiddlewareDefinitions.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ type internal ObjectListFilterMiddleware<'ObjectType, 'ListType>(reportToMetadat
9393
|> Seq.map (fun x ->
9494
match x.Name, x.Value with
9595
| "filter", (VariableName variableName) -> Ok (ValueSome (ctx.Variables[variableName] :?> ObjectListFilter))
96-
| "filter", inlineConstant -> ObjectListFilterType.CoerceInput (InlineConstant inlineConstant) |> Result.map ValueOption.ofObj
96+
| "filter", inlineConstant -> ObjectListFilterType.CoerceInput (InlineConstant inlineConstant) ctx.Variables |> Result.map ValueOption.ofObj
9797
| _ -> Ok ValueNone)
9898
|> Seq.toList
9999
match filterResults |> splitSeqErrorsList with

src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,14 @@ let rec private jsonElementToInputValue (element : JsonElement) =
182182
| _ -> raise (NotSupportedException "Unsupported JSON element type")
183183

184184
/// Defines an object list filter for use as an argument for filter list of object fields.
185-
let ObjectListFilterType : ScalarDefinition<ObjectListFilter> = {
185+
let ObjectListFilterType : InputCustomDefinition<ObjectListFilter> = {
186186
Name = "ObjectListFilter"
187187
Description =
188188
Some
189189
"The `Filter` scalar type represents a filter on one or more fields of an object in an object list. The filter is represented by a JSON object where the fields are the complemented by specific suffixes to represent a query."
190190
CoerceInput =
191-
(function
191+
(fun input variables ->
192+
match input with
192193
| InlineConstant c ->
193194
coerceObjectListFilterInput c
194195
|> Result.map ValueOption.toObj
@@ -197,5 +198,4 @@ let ObjectListFilterType : ScalarDefinition<ObjectListFilter> = {
197198
|> jsonElementToInputValue
198199
|> coerceObjectListFilterInput
199200
|> Result.map ValueOption.toObj)
200-
CoerceOutput = coerceObjectListFilterValue
201201
}

src/FSharp.Data.GraphQL.Server/Execution.fs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,16 @@ let internal coerceVariables (variables: VarDef list) (vars: ImmutableDictionary
599599
fun (acc : Result<ImmutableDictionary<string, obj>.Builder, IGQLError list>) struct(varDef, jsonElement) -> validation {
600600
let! value =
601601
let varTypeDef = varDef.TypeDef
602-
coerceVariableValue false [] ValueNone (varTypeDef, varTypeDef) varDef jsonElement
602+
let ctx = {
603+
IsNullable = false
604+
InputObjectPath = []
605+
ObjectFieldErrorDetails = ValueNone
606+
OriginalTypeDef = varTypeDef
607+
TypeDef = varTypeDef
608+
VarDef = varDef
609+
Input = jsonElement
610+
}
611+
coerceVariableValue ctx
603612
|> Result.mapError (
604613
List.map (fun err ->
605614
match err with

src/FSharp.Data.GraphQL.Server/Values.fs

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ let rec internal compileByType
103103

104104
| Scalar scalardef -> variableOrElse (InlineConstant >> scalardef.CoerceInput)
105105

106+
| InputCustom customDef ->
107+
fun value variables -> customDef.CoerceInput (InlineConstant value) variables
108+
106109
| InputObject objDef ->
107110
let objtype = objDef.Type
108111
let ctor = ReflectionHelper.matchConstructor objtype (objDef.Fields |> Array.map (fun x -> x.Name))
@@ -400,15 +403,29 @@ let rec internal compileByType
400403
Debug.Fail "Unexpected InputDef"
401404
failwithf "Unexpected value of inputDef: %O" inputDef
402405

403-
let rec internal coerceVariableValue
404-
isNullable
405-
inputObjectPath
406-
(objectFieldErrorDetails : ObjectFieldErrorDetails voption)
407-
(originalTypeDef, typeDef)
408-
(varDef : VarDef)
409-
(input : JsonElement)
406+
type CoerceVariableContext = {
407+
IsNullable : bool
408+
InputObjectPath : FieldPath
409+
ObjectFieldErrorDetails : ObjectFieldErrorDetails voption
410+
OriginalTypeDef : InputDef
411+
TypeDef : InputDef
412+
VarDef : VarDef
413+
Input : JsonElement
414+
}
415+
416+
let rec internal coerceVariableValue (ctx : CoerceVariableContext)
410417
: Result<obj, IGQLError list> =
411418

419+
let {
420+
IsNullable = isNullable
421+
InputObjectPath = inputObjectPath
422+
ObjectFieldErrorDetails = objectFieldErrorDetails
423+
OriginalTypeDef = originalTypeDef
424+
TypeDef = typeDef
425+
VarDef = varDef
426+
Input = input
427+
} = ctx
428+
412429
let createVariableCoercionError message =
413430
Error [
414431
{
@@ -463,12 +480,26 @@ let rec internal coerceVariableValue
463480
if input.ValueKind = JsonValueKind.Null then
464481
Ok null
465482
else
466-
coerceVariableValue true inputObjectPath ValueNone (typeDef, innerdef :> InputDef) varDef input
483+
let ctx' = {
484+
ctx with
485+
IsNullable = true
486+
ObjectFieldErrorDetails = ValueNone
487+
OriginalTypeDef = typeDef
488+
TypeDef = innerdef :> InputDef
489+
}
490+
coerceVariableValue ctx'
467491
| Nullable (Input innerdef) ->
468492
if input.ValueKind = JsonValueKind.Null then
469493
Ok null
470494
else
471-
coerceVariableValue true inputObjectPath ValueNone (typeDef, innerdef) varDef input
495+
let ctx' = {
496+
ctx with
497+
IsNullable = true
498+
ObjectFieldErrorDetails = ValueNone
499+
OriginalTypeDef = typeDef
500+
TypeDef = innerdef
501+
}
502+
coerceVariableValue ctx'
472503
| List (Input innerDef) ->
473504
let cons, nil = ReflectionHelper.listOfType innerDef.Type
474505

@@ -487,7 +518,15 @@ let rec internal coerceVariableValue
487518
let! items =
488519
input.EnumerateArray ()
489520
|> Seq.mapi (fun i elem ->
490-
coerceVariableValue areItemsNullable ((box i) :: inputObjectPath) ValueNone (originalTypeDef, innerDef) varDef elem)
521+
let ctx' = {
522+
ctx with
523+
IsNullable = areItemsNullable
524+
InputObjectPath = (box i) :: inputObjectPath
525+
ObjectFieldErrorDetails = ValueNone
526+
TypeDef = innerDef
527+
Input = elem
528+
}
529+
coerceVariableValue ctx')
491530
|> Seq.toList
492531
|> splitSeqErrorsList
493532
if areItemsNullable then
@@ -501,7 +540,14 @@ let rec internal coerceVariableValue
501540
}
502541
else
503542
result {
504-
let! single = coerceVariableValue areItemsNullable inputObjectPath ValueNone (innerDef, innerDef) varDef input
543+
let ctx' = {
544+
ctx with
545+
IsNullable = areItemsNullable
546+
ObjectFieldErrorDetails = ValueNone
547+
OriginalTypeDef = innerDef
548+
TypeDef = innerDef
549+
}
550+
let! single = coerceVariableValue ctx'
505551

506552
if areItemsNullable then
507553
let some, none, _ = ReflectionHelper.optionOfType innerDef.Type.GenericTypeArguments[0]
@@ -548,7 +594,16 @@ and private coerceVariableInputObject inputObjectPath (originalObjDef, objDef) (
548594
<| { ObjectDef = originalObjDef; FieldDef = ValueSome field }
549595
let fieldTypeDef = field.TypeDef
550596
let value =
551-
coerceVariableValue false inputObjectPath' objectFieldErrorDetails (fieldTypeDef, fieldTypeDef) varDef value
597+
let ctx = {
598+
IsNullable = false
599+
InputObjectPath = inputObjectPath'
600+
ObjectFieldErrorDetails = objectFieldErrorDetails
601+
OriginalTypeDef = fieldTypeDef
602+
TypeDef = fieldTypeDef
603+
VarDef = varDef
604+
Input = value
605+
}
606+
coerceVariableValue ctx
552607
KeyValuePair (field.Name, value)
553608
match input.TryGetProperty field.Name with
554609
| true, value -> coerce value |> ValueSome

src/FSharp.Data.GraphQL.Shared/TypeSystem.fs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,56 @@ and [<CustomEquality; NoComparison>] InputFieldDefinition<'In> = {
17571757

17581758
override x.ToString () = x.Name + ": " + x.TypeDef.ToString ()
17591759

1760+
and internal InputCustomDef =
1761+
interface
1762+
/// Name of the input field / argument.
1763+
abstract Name : string
1764+
/// Optional input field / argument description.
1765+
abstract Description : string option
1766+
/// A function used to retrieve a .NET object from provided GraphQL query or JsonElement variable.
1767+
abstract CoerceInput : InputParameterValue -> IReadOnlyDictionary<string, obj> -> Result<obj, IGQLError list>
1768+
inherit TypeDef
1769+
inherit NamedDef
1770+
inherit InputDef
1771+
inherit LeafDef
1772+
end
1773+
1774+
and InputCustomDefinition<'Val> = internal {
1775+
Name : string
1776+
Description : string option
1777+
CoerceInput : InputParameterValue -> IReadOnlyDictionary<string, obj> -> Result<'Val, IGQLError list>
1778+
} with
1779+
interface TypeDef with
1780+
member _.Type = typeof<'Val>
1781+
1782+
member x.MakeNullable () =
1783+
let nullable : NullableDefinition<_> = { OfType = x }
1784+
upcast nullable
1785+
1786+
member x.MakeList () =
1787+
let list : ListOfDefinition<_, _> = { OfType = x }
1788+
upcast list
1789+
1790+
interface InputDef
1791+
interface InputDef<'Val>
1792+
interface LeafDef
1793+
1794+
interface InputCustomDef with
1795+
member x.Name = x.Name
1796+
member x.Description = x.Description
1797+
member x.CoerceInput input variables = x.CoerceInput input variables |> Result.map box
1798+
1799+
interface NamedDef with
1800+
member x.Name = x.Name
1801+
1802+
override x.Equals y =
1803+
match y with
1804+
| :? InputCustomDefinition<'Val> as f -> x.Name = f.Name
1805+
| _ -> false
1806+
1807+
override x.GetHashCode () = x.Name.GetHashCode ()
1808+
override x.ToString () = x.Name + "!"
1809+
17601810
and Tag = System.IComparable
17611811

17621812
and TagsResolver = ResolveFieldContext -> Tag seq
@@ -2404,6 +2454,12 @@ module Patterns =
24042454
| :? InputObjectDef as x -> ValueSome x
24052455
| _ -> ValueNone
24062456

2457+
/// Active pattern to match GraphQL type defintion with input object.
2458+
let internal (|InputCustom|_|) (tdef : TypeDef) =
2459+
match tdef with
2460+
| :? InputCustomDef as x -> ValueSome x
2461+
| _ -> ValueNone
2462+
24072463
/// Active patter to match GraphQL subscription object definitions
24082464
24092465
let (|SubscriptionObject|_|) (tdef : TypeDef) =

0 commit comments

Comments
 (0)