@@ -103,8 +103,7 @@ 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
106+ | InputCustom customDef -> fun value variables -> customDef.CoerceInput ( InlineConstant value) variables
108107
109108 | InputObject objDef ->
110109 let objtype = objDef.Type
@@ -128,8 +127,7 @@ let rec internal compileByType
128127 | Some field ->
129128 let isParameterSkippable = ReflectionHelper.isParameterSkippable param
130129 match field.TypeDef with
131- | Nullable _ when field.IsSkippable <> isParameterSkippable ->
132- skippableMismatchParameters.Add param.Name |> ignore
130+ | Nullable _ when field.IsSkippable <> isParameterSkippable -> skippableMismatchParameters.Add param.Name |> ignore
133131 | Nullable _ when
134132 not ( isParameterSkippable)
135133 && ReflectionHelper.isPrameterMandatory param
@@ -143,7 +141,8 @@ let rec internal compileByType
143141 else
144142 inputDef.Type, param.ParameterType
145143 if ReflectionHelper.isAssignableWithUnwrap inputType paramType then
146- allParameters.Add ( struct ( ValueSome field, param)) |> ignore
144+ allParameters.Add ( struct ( ValueSome field, param))
145+ |> ignore
147146 else
148147 // TODO: Consider improving by specifying type mismatches
149148 typeMismatchParameters.Add param.Name |> ignore
@@ -223,25 +222,28 @@ let rec internal compileByType
223222 |> Seq.map ( fun struct ( field , param ) ->
224223 match field with
225224 | ValueSome field -> result {
226- match Map.tryFind field.Name props with
227- | None when field.IsSkippable -> return Activator.CreateInstance param.ParameterType
228- | None -> return wrapOptionalNone param.ParameterType field.TypeDef.Type
229- | Some prop ->
230- let! value =
231- field.ExecuteInput prop variables
232- |> attachErrorExtensionsIfScalar inputSource inputObjectPath originalInputDef field
233- if field.IsSkippable then
234- let innerType = param.ParameterType.GenericTypeArguments[ 0 ]
235- if not ( ReflectionHelper.isTypeOptional innerType ) &&
236- ( value = null || ( innerType.IsValueType && value = Activator.CreateInstance innerType) )
237- then
238- return Activator.CreateInstance param.ParameterType
239- else
240- let ``include`` , _ = ReflectionHelper.ofSkippable param.ParameterType
241- return normalizeOptional innerType value |> `` include ``
225+ match Map.tryFind field.Name props with
226+ | None when field.IsSkippable -> return Activator.CreateInstance param.ParameterType
227+ | None -> return wrapOptionalNone param.ParameterType field.TypeDef.Type
228+ | Some prop ->
229+ let! value =
230+ field.ExecuteInput prop variables
231+ |> attachErrorExtensionsIfScalar inputSource inputObjectPath originalInputDef field
232+ if field.IsSkippable then
233+ let innerType = param.ParameterType.GenericTypeArguments[ 0 ]
234+ if
235+ not ( ReflectionHelper.isTypeOptional innerType)
236+ && ( value = null
237+ || ( innerType.IsValueType
238+ && value = Activator.CreateInstance innerType ))
239+ then
240+ return Activator.CreateInstance param.ParameterType
242241 else
243- return normalizeOptional param.ParameterType value
244- }
242+ let ``include`` , _ = ReflectionHelper.ofSkippable param.ParameterType
243+ return normalizeOptional innerType value |> `` include ``
244+ else
245+ return normalizeOptional param.ParameterType value
246+ }
245247 | ValueNone -> Ok <| wrapOptionalNone param.ParameterType typeof< obj>)
246248 |> Seq.toList
247249
@@ -413,18 +415,26 @@ type CoerceVariableContext = {
413415 Input : JsonElement
414416}
415417
416- let rec internal coerceVariableValue ( ctx : CoerceVariableContext )
417- : Result < obj , IGQLError list > =
418+ type CoerceVariableInputContext = {
419+ InputObjectPath : FieldPath
420+ OriginalObjectDef : InputDef
421+ ObjectDef : InputObjectDef
422+ VarDef : VarDef
423+ Input : JsonElement
424+ }
425+
426+ let rec internal coerceVariableValue ( ctx : CoerceVariableContext ) : Result < obj , IGQLError list > =
418427
419428 let {
420- IsNullable = isNullable
421- InputObjectPath = inputObjectPath
422- ObjectFieldErrorDetails = objectFieldErrorDetails
423- OriginalTypeDef = originalTypeDef
424- TypeDef = typeDef
425- VarDef = varDef
426- Input = input
427- } = ctx
429+ IsNullable = isNullable
430+ InputObjectPath = inputObjectPath
431+ ObjectFieldErrorDetails = objectFieldErrorDetails
432+ OriginalTypeDef = originalTypeDef
433+ TypeDef = typeDef
434+ VarDef = varDef
435+ Input = input
436+ } =
437+ ctx
428438
429439 let createVariableCoercionError message =
430440 Error [
@@ -564,7 +574,14 @@ let rec internal coerceVariableValue (ctx : CoerceVariableContext)
564574 else
565575 return List.foldBack cons items nil
566576 }
567- | InputObject objdef -> coerceVariableInputObject inputObjectPath ( originalTypeDef, objdef) varDef input
577+ | InputObject objdef ->
578+ coerceVariableInputObject {
579+ InputObjectPath = inputObjectPath
580+ OriginalObjectDef = originalTypeDef
581+ ObjectDef = objdef
582+ VarDef = varDef
583+ Input = input
584+ }
568585 | Enum enumdef ->
569586 match input with
570587 | _ when input.ValueKind = JsonValueKind.Null && isNullable -> Ok null
@@ -579,19 +596,40 @@ let rec internal coerceVariableValue (ctx : CoerceVariableContext)
579596 | Some option -> Ok option.Value
580597 | None -> createVariableCoercionError $" A value '%s {value}' is not defined in Enum '%s {enumdef.Name}'."
581598 | _ -> createVariableCoercionError $" Enum values must be strings but got '%O {input.ValueKind}'."
599+ | InputCustom custDef ->
600+ if input.ValueKind = JsonValueKind.Null then
601+ createNullError originalTypeDef
602+ else
603+ match custDef.CoerceInput ( InputParameterValue.Variable input) ImmutableDictionary.Empty with
604+ | Ok null when isNullable -> Ok null
605+ // TODO: Capture position in the JSON document
606+ | Ok null -> createNullError originalTypeDef
607+ | Ok value when not isNullable ->
608+ let ``type`` = value.GetType ()
609+ if
610+ `` type `` .IsValueType
611+ && `` type `` .FullName.StartsWith ReflectionHelper.ValueOptionTypeName
612+ && value = Activator.CreateInstance `` type ``
613+ then
614+ createNullError originalTypeDef
615+ else
616+ Ok value
617+ | result ->
618+ result
619+ |> Result.mapError ( List.map ( mapInputError varDef inputObjectPath objectFieldErrorDetails))
582620 | _ -> failwith $" Variable '$%s {varDef.Name}': Only Scalars, Nullables, Lists, and InputObjects are valid type definitions."
583621
584- and private coerceVariableInputObject inputObjectPath ( originalObjDef , objDef ) ( varDef : VarDef ) ( input : JsonElement ) =
585- match input .ValueKind with
622+ and private coerceVariableInputObject ( ctx : CoerceVariableInputContext ) =
623+ match ctx.Input .ValueKind with
586624 | JsonValueKind.Object -> result {
587625 let mappedResult =
588- objDef .Fields
626+ ctx.ObjectDef .Fields
589627 |> Seq.vchoose ( fun field ->
590628 let inline coerce value =
591- let inputObjectPath ' = ( box field.Name) :: inputObjectPath
629+ let inputObjectPath ' = ( box field.Name) :: ctx.InputObjectPath
592630 let objectFieldErrorDetails =
593631 ValueSome
594- <| { ObjectDef = originalObjDef ; FieldDef = ValueSome field }
632+ <| { ObjectDef = ctx.OriginalObjectDef ; FieldDef = ValueSome field }
595633 let fieldTypeDef = field.TypeDef
596634 let value =
597635 let ctx = {
@@ -600,12 +638,12 @@ and private coerceVariableInputObject inputObjectPath (originalObjDef, objDef) (
600638 ObjectFieldErrorDetails = objectFieldErrorDetails
601639 OriginalTypeDef = fieldTypeDef
602640 TypeDef = fieldTypeDef
603- VarDef = varDef
641+ VarDef = ctx.VarDef
604642 Input = value
605643 }
606644 coerceVariableValue ctx
607645 KeyValuePair ( field.Name, value)
608- match input .TryGetProperty field.Name with
646+ match ctx.Input .TryGetProperty field.Name with
609647 | true , value -> coerce value |> ValueSome
610648 | false , _ when field.IsSkippable -> ValueNone
611649 | false , _ ->
@@ -619,20 +657,20 @@ and private coerceVariableInputObject inputObjectPath (originalObjDef, objDef) (
619657 // TODO: Improve without creating a dictionary
620658 // This also causes incorrect error messages and extensions to be generated
621659 let variables =
622- seq { KeyValuePair ( varDef .Name, mapped :> obj ) }
660+ seq { KeyValuePair ( ctx.VarDef .Name, mapped :> obj ) }
623661 |> ImmutableDictionary.CreateRange
624662
625- return ! objDef. ExecuteInput ( VariableName varDef .Name) variables
663+ return ! ctx.ObjectDef. ExecuteInput ( VariableName ctx.VarDef .Name) variables
626664 }
627665 | JsonValueKind.Null -> Ok null
628666 | valueKind ->
629667 Error [
630668 {
631- InputSource = Variable varDef
632- Message = $" A variable '$%s {varDef .Name}' expected to be '%O {JsonValueKind.Object}' but got '%O {valueKind}'."
669+ InputSource = Variable ctx.VarDef
670+ Message = $" A variable '$%s {ctx.VarDef .Name}' expected to be '%O {JsonValueKind.Object}' but got '%O {valueKind}'."
633671 ErrorKind = InputCoercion
634- Path = inputObjectPath
635- FieldErrorDetails = ValueSome { ObjectDef = originalObjDef ; FieldDef = ValueNone }
672+ Path = ctx.InputObjectPath
673+ FieldErrorDetails = ValueSome { ObjectDef = ctx.OriginalObjectDef ; FieldDef = ValueNone }
636674 }
637675 :> IGQLError
638676 ]
0 commit comments