Skip to content

Commit

Permalink
Moved all GraphQL exceptions to the Server project
Browse files Browse the repository at this point in the history
  • Loading branch information
xperiandri committed Oct 19, 2023
1 parent 47bd7cf commit dd2d83c
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Condition="$(IsNuget) != ''" Include="FSharp.Data.GraphQL.Shared" VersionOverride="$(Version)" />
<ProjectReference Condition="$(IsNuget) == ''" Include="..\FSharp.Data.GraphQL.Shared\FSharp.Data.GraphQL.Shared.fsproj" />
<PackageReference Condition="$(IsNuget) != ''" Include="FSharp.Data.GraphQL.Server" VersionOverride="$(Version)" />
<ProjectReference Condition="$(IsNuget) == ''" Include="..\FSharp.Data.GraphQL.Server\FSharp.Data.GraphQL.Server.fsproj" />
</ItemGroup>

</Project>
6 changes: 5 additions & 1 deletion src/FSharp.Data.GraphQL.Server/ErrorMessages.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// The MIT License (MIT)

module FSharp.Data.GraphQL.ErrorMessagess
module FSharp.Data.GraphQL.ErrorMessages

open System

let variableNotFound variableName = $"A variable '$%s{variableName}' was not provided"

let expectedEnumerableValue indetifier ``type`` = $"Expected to have enumerable value in field '%s{indetifier}' but got '%O{(``type``:Type)}'"
32 changes: 29 additions & 3 deletions src/FSharp.Data.GraphQL.Server/Exceptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,37 @@ namespace FSharp.Data.GraphQL

open System
open System.Collections.Immutable
open System.Collections.Generic
open System.Runtime.InteropServices

[<AbstractClass>]
type GraphQLException =
inherit Exception

new () = { inherit Exception () }
new (msg) = { inherit Exception (msg) }
new (info : Runtime.Serialization.SerializationInfo, context : Runtime.Serialization.StreamingContext) = { inherit Exception (info, context) }

[<AbstractClass>]
type GQLMessageExceptionBase (errorKind, msg, [<Optional>] extensions) =
inherit GraphQLException (msg)
interface IGQLError with
member _.Message = msg
interface IGQLErrorExtensions with
member _.Extensions =
match extensions with
| null -> Dictionary<string, obj> 1
| _ -> extensions
|> GQLProblemDetails.SetErrorKind errorKind
|> ValueSome

type GQLMessageException (msg) =
inherit GQLMessageExceptionBase (Execution, msg)

type InvalidInputTypeException (msg, unmatchedOptionalFields) =
inherit Exception(msg)
inherit GQLMessageExceptionBase (InputCoercion, msg)

member _.UnmatchedOptionalFields : string ImmutableHashSet = unmatchedOptionalFields

type MalformedGQLQueryException(msg) =
inherit GraphQLException(msg)
type MalformedGQLQueryException (msg) =
inherit GQLMessageExceptionBase (Validation, msg)
18 changes: 9 additions & 9 deletions src/FSharp.Data.GraphQL.Server/Execution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,12 @@ let private raiseErrors errs = AsyncVal.wrap <| Error errs
/// to a list of <see href="GQLProblemDetails">GQLProblemDetails</see>.
let private resolverError path ctx e = ctx.Schema.ParseError path e |> List.map (GQLProblemDetails.OfFieldExecutionError (path |> List.rev))
// Helper functions for generating more specific <see href="GQLProblemDetails">GQLProblemDetails</see>.
let private nullResolverError name path ctx = resolverError path ctx (GraphQLException <| sprintf "Non-Null field %s resolved as a null!" name)
let private coercionError value tyName path ctx = resolverError path ctx (GraphQLException <| sprintf "Value '%O' could not be coerced to scalar %s" value tyName)
let private interfaceImplError ifaceName tyName path ctx = resolverError path ctx (GraphQLException <| sprintf "GraphQL Interface '%s' is not implemented by the type '%s'" ifaceName tyName)
let private unionImplError unionName tyName path ctx = resolverError path ctx (GraphQLException (sprintf "GraphQL Union '%s' is not implemented by the type '%s'" unionName tyName))
let private deferredNullableError name tyName path ctx = resolverError path ctx (GraphQLException (sprintf "Deferred field %s of type '%s' must be nullable" name tyName))
let private streamListError name tyName path ctx = resolverError path ctx (GraphQLException (sprintf "Streamed field %s of type '%s' must be list" name tyName))
let private nullResolverError name path ctx = resolverError path ctx (GQLMessageException <| sprintf "Non-Null field %s resolved as a null!" name)
let private coercionError value tyName path ctx = resolverError path ctx (GQLMessageException <| sprintf "Value '%O' could not be coerced to scalar %s" value tyName)
let private interfaceImplError ifaceName tyName path ctx = resolverError path ctx (GQLMessageException <| sprintf "GraphQL Interface '%s' is not implemented by the type '%s'" ifaceName tyName)
let private unionImplError unionName tyName path ctx = resolverError path ctx (GQLMessageException (sprintf "GraphQL Union '%s' is not implemented by the type '%s'" unionName tyName))
let private deferredNullableError name tyName path ctx = resolverError path ctx (GQLMessageException (sprintf "Deferred field %s of type '%s' must be nullable" name tyName))
let private streamListError name tyName path ctx = resolverError path ctx (GQLMessageException (sprintf "Streamed field %s of type '%s' must be list" name tyName))

let private resolved name v : AsyncVal<ResolverResult<KeyValuePair<string, obj>>> = AsyncVal.wrap <| Ok(KeyValuePair(name, box v), None, [])

Expand Down Expand Up @@ -353,7 +353,7 @@ let rec private direct (returnDef : OutputDef) (ctx : ResolveFieldContext) (path
|> Array.mapi resolveItem
|> collectFields Parallel
|> AsyncVal.map(ResolverResult.mapValue(fun items -> KeyValuePair(name, items |> Array.map(fun d -> d.Value) |> box)))
| _ -> raise <| GraphQLException (sprintf "Expected to have enumerable value in field '%s' but got '%O'" ctx.ExecutionInfo.Identifier (value.GetType()))
| _ -> raise <| GQLMessageException (ErrorMessages.expectedEnumerableValue ctx.ExecutionInfo.Identifier (value.GetType()))

| Nullable (Output innerDef) ->
let innerCtx = { ctx with ExecutionInfo = { ctx.ExecutionInfo with IsNullable = true; ReturnDef = innerDef } }
Expand Down Expand Up @@ -445,7 +445,7 @@ and private streamed (options : BufferedStreamOptions) (innerDef : OutputDef) (c
|> Observable.ofAsyncValSeq
|> buffer
AsyncVal.wrap <| Ok(KeyValuePair(info.Identifier, box [||]), Some stream, [])
| _ -> raise <| GraphQLException (sprintf "Expected to have enumerable value in field '%s' but got '%O'" ctx.ExecutionInfo.Identifier (value.GetType()))
| _ -> raise <| GQLMessageException (ErrorMessages.expectedEnumerableValue ctx.ExecutionInfo.Identifier (value.GetType()))

and private live (ctx : ResolveFieldContext) (path : FieldPath) (parent : obj) (value : obj) =
let info = ctx.ExecutionInfo
Expand Down Expand Up @@ -549,7 +549,7 @@ let internal compileSubscriptionField (subfield: SubscriptionFieldDef) =
match subfield.Resolve with
| Resolve.BoxedFilterExpr(_, _, _, filter) -> fun ctx a b -> filter ctx a b |> AsyncVal.wrap |> AsyncVal.toAsync
| Resolve.BoxedAsyncFilterExpr(_, _, _, filter) -> filter
| _ -> raise <| GraphQLException ("Invalid filter expression for subscription field!")
| _ -> raise <| GQLMessageException ("Invalid filter expression for subscription field!")

let internal compileField (fieldDef: FieldDef) : ExecuteField =
match fieldDef.Resolve with
Expand Down
4 changes: 2 additions & 2 deletions src/FSharp.Data.GraphQL.Server/Executor.fs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type Executor<'Root>(schema: ISchema<'Root>, middlewares : IExecutorMiddleware s
runMiddlewares (fun x -> x.PostCompileSchema) (upcast schema) ignore
match Validation.Types.validateTypeMap schema.TypeMap with
| Success -> ()
| ValidationError errors -> raise (GraphQLException (System.String.Join("\n", errors)))
| ValidationError errors -> raise (GQLMessageException (System.String.Join("\n", errors)))

let eval (executionPlan: ExecutionPlan, data: 'Root option, variables: ImmutableDictionary<string, JsonElement>): Async<GQLExecutionResult> =
let documentId = executionPlan.DocumentId
Expand Down Expand Up @@ -124,7 +124,7 @@ type Executor<'Root>(schema: ISchema<'Root>, middlewares : IExecutorMiddleware s
let! res = runMiddlewares (fun x -> x.ExecuteOperationAsync) executionCtx executeOperation |> AsyncVal.toAsync
return prepareOutput res
with
| :? GraphQLException as ex -> return prepareOutput(GQLExecutionResult.Error (documentId, ex, executionPlan.Metadata))
| :? GQLMessageException as ex -> return prepareOutput(GQLExecutionResult.Error (documentId, ex, executionPlan.Metadata))
| ex -> return prepareOutput (GQLExecutionResult.Error(documentId, ex.ToString(), executionPlan.Metadata)) // TODO: Handle better
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Compile Include="ErrorMessages.fs" />
<Compile Include="ErrorsProcessing.fs" />
<Compile Include="Exceptions.fs" />
<Compile Include="TypeSystem.fs" />
<Compile Include="Values.fs" />
<Compile Include="Planning.fs" />
<Compile Include="ObservableExtensions.fs" />
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Data.GraphQL.Server/Schema.fs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ type SchemaConfig =
ParseError =
fun path ex ->
match ex with
| :? GraphQLException as ex -> [ex]
| :? GQLMessageException as ex -> [ex]
| ex -> [{ new IGQLError with member _.Message = ex.Message }]
SubscriptionProvider = SchemaConfig.DefaultSubscriptionProvider()
LiveFieldSubscriptionProvider = SchemaConfig.DefaultLiveFieldSubscriptionProvider()
Expand Down
17 changes: 17 additions & 0 deletions src/FSharp.Data.GraphQL.Server/TypeSystem.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// The MIT License (MIT)
// Copyright (c) 2016 Bazinga Technologies Inc
[<AutoOpen>]
module FSharp.Data.GraphQL.Types.ResolveFieldContextExtensions

open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Extensions

type ResolveFieldContext with

/// Returns an argument by provided name. If argument was not found a GraphQL exception will be thrown.
/// <exception cref="GraphQLException">When argument with the name not found in the Args.</exception>
member x.Arg(name : string) : 't =
match Map.tryFind name x.Args with
| Some found -> downcast found
| None -> raise (GQLMessageException $"Argument '%s{name}' was not provided within context of a field '%s{x.ExecutionInfo.Identifier}'. Check if it was supplied within GraphQL query.")

17 changes: 0 additions & 17 deletions src/FSharp.Data.GraphQL.Shared/Exceptions.fs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
<Compile Include="Helpers\Reflection.fs" />
<Compile Include="Helpers\MemoryCache.fs" />
<Compile Include="Errors.fs" />
<Compile Include="Exceptions.fs" />
<Compile Include="ValidationTypes.fs" />
<Compile Include="AsyncVal.fs" />
<Compile Include="Ast.fs" />
Expand Down
2 changes: 0 additions & 2 deletions src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,6 @@ module SchemaDefinitions =
DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT ||| DirectiveLocation.FRAGMENT_DEFINITION
Args = [||] }

let internal matchParameters (methodInfo : MethodInfo) (ctx : ResolveFieldContext) =
methodInfo.GetParameters() |> Array.map (fun param -> ctx.Arg<obj>(param.Name))
let inline internal strip (fn : 'In -> 'Out) : obj -> obj = fun i -> upcast fn (i :?> 'In)

/// Common space for all definition helper methods.
Expand Down
7 changes: 0 additions & 7 deletions src/FSharp.Data.GraphQL.Shared/TypeSystem.fs
Original file line number Diff line number Diff line change
Expand Up @@ -893,13 +893,6 @@ and ResolveFieldContext =
| Some o -> Some(o :?> 't) // TODO: Use Convert.ChangeType
| None -> None

/// Returns an argument by provided name. If argument was not found a GraphQL exception will be thrown.
/// <exception cref="GraphQLException">When argument with the name not found in the Args.</exception>
member x.Arg(name : string) : 't =
match Map.tryFind name x.Args with
| Some found -> downcast found
| None -> raise (GraphQLException $"Argument '%s{name}' was not provided within context of a field '%s{x.ExecutionInfo.Identifier}'. Check if it was supplied within GraphQL query.")

/// Function type for the compiled field executor.
and ExecuteField = ResolveFieldContext -> obj -> AsyncVal<obj>

Expand Down

0 comments on commit dd2d83c

Please sign in to comment.