From 32dfb9941ecbf637385de92c03c14f20586d91a1 Mon Sep 17 00:00:00 2001 From: Henrik Feldt Date: Wed, 4 Sep 2019 19:13:52 +0200 Subject: [PATCH 1/9] Complete the AST according to the June 2018 GraphQL spec edition - Add some code-docs so that developers using the AST can read about what they are doing - Add TypeSystemDefinition and TypeSystemExtension to the Definition DU - Move DirectiveLocation from TypeSystem to Ast - Update usages --- src/FSharp.Data.GraphQL.Server/Schema.fs | 1 + src/FSharp.Data.GraphQL.Shared/Ast.fs | 432 +++++++++++++----- .../Introspection.fs | 1 + src/FSharp.Data.GraphQL.Shared/Parser.fs | 2 +- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 25 +- .../AstValidationTests.fs | 1 + 6 files changed, 334 insertions(+), 128 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Server/Schema.fs b/src/FSharp.Data.GraphQL.Server/Schema.fs index efe495701..f38344fd5 100644 --- a/src/FSharp.Data.GraphQL.Server/Schema.fs +++ b/src/FSharp.Data.GraphQL.Server/Schema.fs @@ -5,6 +5,7 @@ namespace FSharp.Data.GraphQL open System.Collections.Generic open System.Reactive.Linq +open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Types.Patterns open FSharp.Data.GraphQL.Types.Introspection diff --git a/src/FSharp.Data.GraphQL.Shared/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index fc5ce8f3f..ce39c2e6f 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -1,46 +1,77 @@ /// The MIT License (MIT) /// Copyright (c) 2016 Bazinga Technologies Inc +/// Copyright (c) 2019 Logibit AB namespace FSharp.Data.GraphQL.Ast -//NOTE: For references, see https://facebook.github.io/graphql/ +open System -/// 2.2 Query Document -type Document = { - Definitions: Definition list +/// There are three types of operations that GraphQL models: +/// +/// - query – a read‐only fetch. +/// - mutation – a write followed by a fetch. +/// - subscription – a long‐lived request that fetches data in response to source events. +/// +/// Each operation is represented by an optional operation name and a selection set. +/// https://graphql.github.io/graphql-spec/June2018/#OperationType +type OperationType = + | Query + | Mutation + | Subscription + +/// 2.9 Input Values +type Value = + /// 2.9.1 Int Value + | IntValue of int64 + /// 2.9.2 Float Value + | FloatValue of double + /// 2.9.3 Boolean Value + | BooleanValue of bool + /// 2.9.4 String Value + | StringValue of string + /// 2.9.5 Null Value + | NullValue + /// 2.9.6 Enum Value + | EnumValue of string + /// 2.9.7 List Value + | ListValue of Value list + /// 2.9.8 Input Object Values + | ObjectValue of Map + /// 2.10 Variables + | Variable of string + +/// 2.6 Arguments +/// Fields are conceptually functions which return values, and occasionally accept arguments which alter their behavior. These arguments often map directly to function arguments within a GraphQL server’s implementation. +/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Arguments +type Argument = { + Name: string + Value: Value } -and Definition = - | OperationDefinition of OperationDefinition - | FragmentDefinition of FragmentDefinition - member x.Name = - match x with - | OperationDefinition op -> op.Name - | FragmentDefinition frag -> frag.Name - member x.Directives = - match x with - | OperationDefinition op -> op.Directives - | FragmentDefinition frag -> frag.Directives - member x.SelectionSet = - match x with - | OperationDefinition op -> op.SelectionSet - | FragmentDefinition frag -> frag.SelectionSet +/// 2.2.10 Directives +type Directive = + { + Name: string + Arguments: Argument list + } + member x.If = x.Arguments |> List.find (fun arg -> arg.Name = "if") -/// 2.2.1 Operations -and OperationDefinition = { - OperationType: OperationType +/// 2.2.6 Fragments +type FragmentSpread = { + Name: string + Directives: Directive list +} + +type FragmentDefinition = { Name: string option - VariableDefinitions: VariableDefinition list + /// 2.2.6.1 Type Conditions + TypeCondition: string option Directives: Directive list SelectionSet: Selection list } -and OperationType = - | Query - | Mutation - | Subscription /// 2.2.2 Selection Sets -and Selection = +and Selection = | Field of Field | FragmentSpread of FragmentSpread /// 2.2.6.2 Inline Fragments @@ -52,7 +83,7 @@ and Selection = | InlineFragment f -> f.Directives /// 2.2.3 Fields -and Field = +and Field = { /// 2.2.5 Field Alias Alias: string option @@ -61,60 +92,14 @@ and Field = Directives: Directive list SelectionSet: Selection list } - member x.AliasOrName = + member x.AliasOrName = match x.Alias with | Some alias -> alias | None -> x.Name -/// 2.2.4 Arguments -and Argument = { - Name: string - Value: Value -} - -/// 2.2.6 Fragments -and FragmentSpread = { - Name: string - Directives: Directive list -} - -and FragmentDefinition = { - Name: string option - /// 2.2.6.1 Type Conditions - TypeCondition: string option - Directives: Directive list - SelectionSet: Selection list -} - -/// 2.9 Input Values -and Value = - /// 2.9.1 Int Value - | IntValue of int64 - /// 2.9.2 Float Value - | FloatValue of double - /// 2.9.3 Boolean Value - | BooleanValue of bool - /// 2.9.4 String Value - | StringValue of string - /// 2.9.5 Null Value - | NullValue - /// 2.9.6 Enum Value - | EnumValue of string - /// 2.9.7 List Value - | ListValue of Value list - /// 2.9.8 Input Object Values - | ObjectValue of Map - /// 2.10 Variables - | Variable of string - -/// 2.2.8 Variables -and VariableDefinition = - { VariableName: string - Type: InputType - DefaultValue: Value option } /// 2.2.9 Input Types -and InputType = +type InputType = | NamedType of string | ListType of InputType | NonNullType of InputType @@ -125,69 +110,310 @@ and InputType = | NonNullType inner -> (str inner) + "!" str x -/// 2.2.10 Directives -and Directive = + + +/// 2.2.8 Variables +type VariableDefinition = + { VariableName: string + Type: InputType + DefaultValue: Value option } + +//NOTE: For references, see https://facebook.github.io/graphql/ +/// 2.3 Operations +type OperationDefinition = { + /// Defaults to `query`; "query shorthand" + OperationType: OperationType + Name: string option + /// May be empty + VariableDefinitions: VariableDefinition list + /// May be empty + Directives: Directive list + /// May not be empty + SelectionSet: Selection list +} + +type InputDefinition = string + +/// GraphQL field input definition. Can be used as fields for +/// input objects or as arguments for any ordinary field definition. +type InputFieldDefinition = { - Name: string - Arguments: Argument list + /// Name of the input field / argument. + Name : string + /// Optional input field / argument description. + Description : string option + /// GraphQL type definition of the input type. + TypeDef : InputDefinition + /// Optional default input value - used when no input was provided. + DefaultValue : Value option } - member x.If = x.Arguments |> List.find (fun arg -> arg.Name = "if") - -// Type System Definition -and OperationTypeDefinition = { - Type: string +/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Operations +type OperationTypeDefinition = { + NamedType: string Operation: OperationType } -and SchemaDefintion = { +/// A GraphQL service’s collective type system capabilities are referred to as that service’s “schema”. A schema is defined in terms of the types and directives it supports as well as the root operation types for each kind of operation: query, mutation, and subscription; this determines the place in the type system where those operations begin. +/// A GraphQL schema must itself be internally valid. This section describes the rules for this validation process where relevant. +/// https://graphql.github.io/graphql-spec/June2018/#SchemaDefinition +type SchemaDefinition = { + Directives: Directive list OperationTypes: OperationTypeDefinition } -and ObjectTypeDefinition = { +type InputValueDefinition = { + Description: string option Name: string - Interfaces: string [] - Fields: FieldDefinition [] + Type: InputType + DefaultValue: Value option } -and FieldDefinition = { +/// https://graphql.github.io/graphql-spec/June2018/#FieldDefinition +type FieldDefinition = { + Description: string option Name: string Arguments: InputValueDefinition [] Type: InputType + Directives: Directive list } -and InputValueDefinition = { +// TypeDefinitions: + +/// Scalar types represent primitive leaf values in a GraphQL type system. GraphQL responses take the form of a hierarchical tree; the leaves on these trees are GraphQL scalars. +/// https://graphql.github.io/graphql-spec/June2018/#ScalarTypeDefinition +type ScalarTypeDefinition = { + Description: string option Name: string - Type: InputType - DefaultValue: Value option + Directives: Directive list } -and InterfaceTypeDefinition = { +type ScalarTypeExtension = { Name: string + Directives: Directive list +} + +/// GraphQL queries are hierarchical and composed, describing a tree of information. While Scalar types describe the leaf values of these hierarchical queries, Objects describe the intermediate levels. +/// https://graphql.github.io/graphql-spec/June2018/#sec-Objects +type ObjectTypeDefinition = { + Description: string option + Name: string + /// May be empty + Interfaces: string [] + /// May be empty + Directives: Directive list Fields: FieldDefinition [] } -and UnionTypeDefinition = { +/// Object type extensions are used to represent a type which has been extended from some original type. For example, this might be used to represent local data, or by a GraphQL service which is itself an extension of another GraphQL service. +/// +/// Either: +/// - `Fields` is non-empty, and all others may be, xor +/// - `Directives` is non-empty, and all others may be, xor +/// - `Interfaces` is non-empty, and all others may be. +/// +/// https://graphql.github.io/graphql-spec/June2018/#ObjectTypeExtension +type ObjectTypeExtension = { + /// May be empty + Interfaces: string [] + /// May be empty + Directives: Directive list + /// May be empty + Fields: FieldDefinition [] +} + +/// GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects can then implement these interfaces which requires that the object type will define all fields defined by those interfaces. +/// https://graphql.github.io/graphql-spec/June2018/#InterfaceTypeDefinition +type InterfaceTypeDefinition = { + Description: string option Name: string - Types: string [] + Directives: Directive list + Fields: FieldDefinition [] +} + +type InterfaceTypeExtension = { + Name: string + Directives: Directive list + Fields: FieldDefinition [] } -and EnumTypeDefinition = { +type UnionMemberType = string + +/// GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed fields between those types. They also differ from interfaces in that Object types declare what interfaces they implement, but are not aware of what unions contain them. +/// https://graphql.github.io/graphql-spec/June2018/#UnionTypeDefinition +type UnionTypeDefinition = { + Description: string option + Name: string + Directives: Directive list + Types: UnionMemberType [] +} + +type UnionTypeExtension = { Name: string - Values: string [] + Directives: Directive list + Types: UnionMemberType [] } -and InputObjectTypeDefinition = { +/// Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the name of the represented value. +/// Most often a name like "BLUE" or "RED". +/// https://graphql.github.io/graphql-spec/June2018/#EnumValueDefinition +type EnumValueDefinition = { + Description: string option Name: string + Directives: Directive list +} + +/// GraphQL Enum types, like scalar types, also represent leaf values in a GraphQL type system. However Enum types describe the set of possible values. +/// https://graphql.github.io/graphql-spec/June2018/#EnumTypeDefinition +type EnumTypeDefinition = { + Description: string option + Name: string + Directives: Directive list + Values: EnumValueDefinition[] +} + +/// Enum type extensions are used to represent an enum type which has been extended from some original enum type. For example, this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another GraphQL service. +/// https://graphql.github.io/graphql-spec/June2018/#sec-Enum-Extensions +type EnumTypeExtension = { + Name: string + Directives: Directive list + Values: EnumValueDefinition[] +} + + +/// Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to represent more complex values. +/// https://graphql.github.io/graphql-spec/June2018/#InputObjectTypeDefinition +type InputObjectTypeDefinition = { + Description: string option + Name: string + Directives: Directive list + Fields: InputValueDefinition [] +} + +/// 3.10.1 https://graphql.github.io/graphql-spec/June2018/#sec-Input-Object-Extensions +/// Input object type extensions are used to represent an input object type which has been extended from some original input object type. For example, this might be used by a GraphQL service which is itself an extension of another GraphQL service. +type InputObjectTypeExtension = { + Name: string + Directives: Directive list Fields: InputValueDefinition [] } -and TypeDefinition = - | ScalarTypeDefinition of string +type TypeDefinition = + | ScalarTypeDefinition of ScalarTypeDefinition | ObjectTypeDefinition of ObjectTypeDefinition | InterfaceTypeDefinition of InterfaceTypeDefinition | UnionTypeDefinition of UnionTypeDefinition | EnumTypeDefinition of EnumTypeDefinition | InputObjectTypeDefinition of InputObjectTypeDefinition - \ No newline at end of file +/// Enum describing parts of the GraphQL query document AST, where +/// related directive is valid to be used. +[] +type DirectiveLocation = + | QUERY = 1 + | MUTATION = 2 + | SUBSCRIPTION = 4 + | FIELD = 8 + | FRAGMENT_DEFINITION = 16 + | FRAGMENT_SPREAD = 32 + | INLINE_FRAGMENT = 64 + | SCHEMA = 128 + | SCALAR = 256 + | OBJECT = 512 + | FIELD_DEFINITION = 1024 + | ARGUMENT_DEFINITION = 2048 + | INTERFACE = 4096 + | UNION = 8192 + | ENUM = 16384 + | ENUM_VALUE = 32768 + | INPUT_OBJECT = 65536 + | INPUT_FIELD_DEFINITION = 131072 + +/// 3.13 +/// DirectiveDefinition: +/// Description opt directive @ `Name` `ArgumentsDefinition` opt on `DirectiveLocations` +/// +/// https://graphql.github.io/graphql-spec/June2018/#DirectiveDefinition +/// https://graphql.github.io/graphql-spec/June2018/#sec-Type-System.Directives +type DirectiveDefinition = + { /// Directive's name - it's NOT '@' prefixed. + Name : string + /// Optional directive description. + Description : string option + /// Directive location - describes, which part's of the query AST + /// are valid places to include current directive to. + Locations : DirectiveLocation + /// Array of arguments defined within that directive. + Args : InputFieldDefinition [] + } + +type TypeSystemDefinition = + | SchemaDefinition of SchemaDefinition + | TypeDefinition of TypeDefinition + | DirectiveDefinition of DirectiveDefinition + +/// Schema extensions are used to represent a schema which has been extended from an original schema. For example, this might be used by a GraphQL service which adds additional operation types, or additional directives to an existing schema. +/// https://graphql.github.io/graphql-spec/June2018/#SchemaExtension +type SchemaExtension = + { + /// May be empty. Any directives provided must not already apply to the original Schema. + Directives: Directive list + /// May be empty + OperationTypes: OperationTypeDefinition list + } + +/// Type extensions are used to represent a GraphQL type which has been extended from some original type. For example, this might be used by a local service to represent additional fields a GraphQL client only accesses locally. +/// https://graphql.github.io/graphql-spec/June2018/#TypeExtension +type TypeExtension = + | ScalarTypeExtension of ScalarTypeExtension + | ObjectTypeExtension of ObjectTypeExtension + | InterfaceTypeExtension of InterfaceTypeExtension + | UnionTypeExtension of UnionTypeExtension + | EnumTypeExtension of EnumTypeExtension + | InputObjectTypeExtension of InputObjectTypeExtension + +/// Type system extensions are used to represent a GraphQL type system which has been extended from some original type system. For example, this might be used by a local service to represent data a GraphQL client only accesses locally, or by a GraphQL service which is itself an extension of another GraphQL service. +/// 3.1 https://graphql.github.io/graphql-spec/June2018/#sec-Type-System-Extensions +type TypeSystemExtension = + | SchemaExtension of SchemaExtension + | TypeExtension of TypeExtension + +type Definition = + | OperationDefinition of OperationDefinition + | FragmentDefinition of FragmentDefinition + | TypeSystemDefinition of TypeSystemDefinition + | TypeSystemExtension of TypeSystemExtension + + member x.Name = + match x with + | OperationDefinition op -> op.Name + | FragmentDefinition frag -> frag.Name + | TypeSystemDefinition _ -> None + | TypeSystemExtension _ -> None + + member x.Directives = + match x with + | OperationDefinition op -> op.Directives + | FragmentDefinition frag -> frag.Directives + | _ -> [] + + member x.SelectionSet = + match x with + | OperationDefinition op -> op.SelectionSet + | FragmentDefinition frag -> frag.SelectionSet + | _ -> [] + +/// 2.2 Query Document +/// A GraphQL Document describes a complete file or request string operated on by a GraphQL service or client. A document contains multiple definitions, either executable or representative of a GraphQL type system. +/// +/// Documents are only executable by a GraphQL service if they contain an OperationDefinition and otherwise only contain ExecutableDefinition. However documents which do not contain OperationDefinition or do contain TypeSystemDefinition or TypeSystemExtension may still be parsed and validated to allow client tools to represent many GraphQL uses which may appear across many individual files. +/// +/// If a Document contains only one operation, that operation may be unnamed or represented in the shorthand form, which omits both the query keyword and operation name. Otherwise, if a GraphQL Document contains multiple operations, each operation must be named. When submitting a Document with multiple operations to a GraphQL service, the name of the desired operation to be executed must also be provided. +/// +/// GraphQL services which only seek to provide GraphQL query execution may choose to only include ExecutableDefinition and omit the TypeSystemDefinition and TypeSystemExtension rules from Definition. +/// +/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Document +type Document = + { + Definitions: Definition list + } diff --git a/src/FSharp.Data.GraphQL.Shared/Introspection.fs b/src/FSharp.Data.GraphQL.Shared/Introspection.fs index a16f37bba..da407375c 100644 --- a/src/FSharp.Data.GraphQL.Shared/Introspection.fs +++ b/src/FSharp.Data.GraphQL.Shared/Introspection.fs @@ -4,6 +4,7 @@ module FSharp.Data.GraphQL.Introspection #nowarn "40" +open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Types.Introspection open FSharp.Data.GraphQL.Extensions diff --git a/src/FSharp.Data.GraphQL.Shared/Parser.fs b/src/FSharp.Data.GraphQL.Shared/Parser.fs index 0b274ded1..d83d498a6 100644 --- a/src/FSharp.Data.GraphQL.Shared/Parser.fs +++ b/src/FSharp.Data.GraphQL.Shared/Parser.fs @@ -307,7 +307,7 @@ module internal Internal = // 2.2 Document - // Definitionlist + // DefinitionList let documents = whitespaces >>. definitions .>> (skipMany ignored <|> eof) |>> (fun definitions -> { Document.Definitions = definitions }) diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index df3254451..b65d52a31 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -16,29 +16,6 @@ open FSharp.Quotations.Patterns open FSharp.Reflection open FSharp.Linq.RuntimeHelpers -/// Enum describing parts of the GraphQL query document AST, where -/// related directive is valid to be used. -[] -type DirectiveLocation = - | QUERY = 1 - | MUTATION = 2 - | SUBSCRIPTION = 4 - | FIELD = 8 - | FRAGMENT_DEFINITION = 16 - | FRAGMENT_SPREAD = 32 - | INLINE_FRAGMENT = 64 - | SCHEMA = 128 - | SCALAR = 256 - | OBJECT = 512 - | FIELD_DEFINITION = 1024 - | ARGUMENT_DEFINITION = 2048 - | INTERFACE = 4096 - | UNION = 8192 - | ENUM = 16384 - | ENUM_VALUE = 32768 - | INPUT_OBJECT = 65536 - | INPUT_FIELD_DEFINITION = 131072 - module Introspection = /// Type kind. GraphQL type system puts all types into one of eight categories. @@ -1742,7 +1719,7 @@ and [] SubscriptionObjectDefinition<'Val> = override x.ToString() = x.Name + "!" -/// GraphQL directive defintion. +/// 3.13 GraphQL directive definition. and DirectiveDef = { /// Directive's name - it's NOT '@' prefixed. Name : string diff --git a/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs b/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs index 10249da3b..70ec3777d 100644 --- a/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs @@ -3,6 +3,7 @@ module FSharp.Data.GraphQL.Tests.AstValidationTests open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Validation open FSharp.Data.GraphQL.Validation.Ast open Xunit From 4e3c2376158765eb3d47b704ca793ec725e8e903 Mon Sep 17 00:00:00 2001 From: Juri Date: Sun, 4 Sep 2022 01:31:59 +0300 Subject: [PATCH 2/9] Removed Paket and Fake-CLI, migrated to Packages.props, updated to .NET 6 and F# 6 --- .config/dotnet-tools.json | 30 +- .github/workflows/dotnetcore.yml | 6 +- .paket/Paket.Restore.targets | 494 ------------- Directory.Build.targets | 29 + FSharp.Data.GraphQL.sln | 27 +- Packages.props | 31 + build.cmd | 2 +- build.fsx | 118 ++- build.fsx.lock | 680 +----------------- build.sh | 2 +- global.json | 10 +- paket.dependencies | 41 -- paket.lock | 641 ----------------- ...rp.Data.GraphQL.Samples.StarWarsApi.fsproj | 22 +- samples/star-wars-api/HttpHandlers.fs | 29 +- ...harp.Data.GraphQL.Client.DesignTime.fsproj | 31 +- .../paket.references | 6 - .../AssemblyInfo.fs | 17 - .../FSharp.Data.GraphQL.Client.fsproj | 19 +- ....Data.GraphQL.Client.fsproj.paket.template | 24 - .../paket.references | 4 - .../AssemblyInfo.fs | 17 - ...harp.Data.GraphQL.Server.Middleware.fsproj | 24 +- ...QL.Server.Middleware.fsproj.paket.template | 20 - .../paket.references | 2 - .../AssemblyInfo.fs | 22 - .../FSharp.Data.GraphQL.Server.fsproj | 36 +- ....Data.GraphQL.Server.fsproj.paket.template | 23 - .../paket.references | 4 - .../AssemblyInfo.fs | 26 - .../FSharp.Data.GraphQL.Shared.fsproj | 33 +- ....Data.GraphQL.Shared.fsproj.paket.template | 21 - .../paket.references | 3 - .../FSharp.Data.GraphQL.Benchmarks.fsproj | 16 +- .../paket.references | 3 - ...ata.GraphQL.IntegrationTests.Server.fsproj | 18 +- .../Helpers.fs | 2 +- .../HttpHandlers.fs | 33 +- .../MultipartRequest.fs | 4 +- .../Schema.fs | 2 +- .../Startup.fs | 6 +- .../paket.references | 3 - ...Sharp.Data.GraphQL.IntegrationTests.fsproj | 33 +- .../paket.references | 7 - .../xunit.runner.json | 4 +- .../paket.references | 3 - tests/FSharp.Data.GraphQL.Tests/App.config | 6 - .../FSharp.Data.GraphQL.Tests/AssemblyInfo.fs | 22 - .../FSharp.Data.GraphQL.Tests.fsproj | 34 +- tests/FSharp.Data.GraphQL.Tests/Program.fs | 3 + .../paket.references | 7 - .../xunit.runner.json | 4 +- 52 files changed, 360 insertions(+), 2344 deletions(-) delete mode 100644 .paket/Paket.Restore.targets create mode 100644 Directory.Build.targets create mode 100644 Packages.props delete mode 100644 paket.dependencies delete mode 100644 paket.lock delete mode 100644 src/FSharp.Data.GraphQL.Client.DesignTime/paket.references delete mode 100644 src/FSharp.Data.GraphQL.Client/AssemblyInfo.fs delete mode 100644 src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj.paket.template delete mode 100644 src/FSharp.Data.GraphQL.Client/paket.references delete mode 100644 src/FSharp.Data.GraphQL.Server.Middleware/AssemblyInfo.fs delete mode 100644 src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj.paket.template delete mode 100644 src/FSharp.Data.GraphQL.Server.Middleware/paket.references delete mode 100644 src/FSharp.Data.GraphQL.Server/AssemblyInfo.fs delete mode 100644 src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj.paket.template delete mode 100644 src/FSharp.Data.GraphQL.Server/paket.references delete mode 100644 src/FSharp.Data.GraphQL.Shared/AssemblyInfo.fs delete mode 100644 src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj.paket.template delete mode 100644 src/FSharp.Data.GraphQL.Shared/paket.references delete mode 100644 tests/FSharp.Data.GraphQL.Benchmarks/paket.references delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/paket.references delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests/paket.references delete mode 100644 tests/FSharp.Data.GraphQL.Tests.Sql/paket.references delete mode 100644 tests/FSharp.Data.GraphQL.Tests/App.config delete mode 100644 tests/FSharp.Data.GraphQL.Tests/AssemblyInfo.fs delete mode 100644 tests/FSharp.Data.GraphQL.Tests/paket.references diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 41e28fcb3..e57098a44 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,24 +1,12 @@ { - "version": 1, - "isRoot": true, - "tools": { - "paket": { - "version": "5.257.0", - "commands": [ - "paket" - ] - }, - "fake-cli": { - "version": "5.20.4", - "commands": [ - "fake" - ] - }, - "fsharp.formatting.commandtool": { - "version": "11.1.0", - "commands": [ - "fsdocs" - ] - } + "version": 1, + "isRoot": true, + "tools": { + "fsharp.formatting.commandtool": { + "version": "11.1.0", + "commands": [ + "fsdocs" + ] } + } } \ No newline at end of file diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index c1c7f27e1..ce5e9e066 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - dotnet: [5.0.100] + dotnet: [6.0.400] runs-on: ${{ matrix.os }} steps: @@ -31,7 +31,5 @@ jobs: dotnet-version: ${{ matrix.dotnet }} - name: Install local tools run: dotnet tool restore - - name: Paket Restore - run: dotnet paket restore - name: Build and Test - run: dotnet fake run build.fsx \ No newline at end of file + run: dotnet fsi build.fsx \ No newline at end of file diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets deleted file mode 100644 index 8d37e28bc..000000000 --- a/.paket/Paket.Restore.targets +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - $(MSBuildVersion) - 15.0.0 - false - true - - true - $(MSBuildThisFileDirectory) - $(MSBuildThisFileDirectory)..\ - $(PaketRootPath)paket-files\paket.restore.cached - $(PaketRootPath)paket.lock - classic - proj - assembly - native - /Library/Frameworks/Mono.framework/Commands/mono - mono - - - $(PaketRootPath)paket.bootstrapper.exe - $(PaketToolsPath)paket.bootstrapper.exe - $([System.IO.Path]::GetDirectoryName("$(PaketBootStrapperExePath)"))\ - - "$(PaketBootStrapperExePath)" - $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" - - - - - true - true - - - True - - - False - - $(BaseIntermediateOutputPath.TrimEnd('\').TrimEnd('\/')) - - - - - - - - - $(PaketRootPath)paket - $(PaketToolsPath)paket - - - - - - $(PaketRootPath)paket.exe - $(PaketToolsPath)paket.exe - - - - - - <_DotnetToolsJson Condition="Exists('$(PaketRootPath)/.config/dotnet-tools.json')">$([System.IO.File]::ReadAllText("$(PaketRootPath)/.config/dotnet-tools.json")) - <_ConfigContainsPaket Condition=" '$(_DotnetToolsJson)' != ''">$(_DotnetToolsJson.Contains('"paket"')) - <_ConfigContainsPaket Condition=" '$(_ConfigContainsPaket)' == ''">false - - - - - - - - - - - <_PaketCommand>dotnet paket - - - - - - $(PaketToolsPath)paket - $(PaketBootStrapperExeDir)paket - - - paket - - - - - <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) - <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(_PaketExeExtension)' == '.dll' ">dotnet "$(PaketExePath)" - <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(OS)' != 'Windows_NT' AND '$(_PaketExeExtension)' == '.exe' ">$(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" - <_PaketCommand Condition=" '$(_PaketCommand)' == '' ">"$(PaketExePath)" - - - - - - - - - - - - - - - - - - - - - true - $(NoWarn);NU1603;NU1604;NU1605;NU1608 - false - true - - - - - - - - - $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) - - - - - - - $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[0].Replace(`"`, ``).Replace(` `, ``)) - $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[1].Replace(`"`, ``).Replace(` `, ``)) - - - - - %(PaketRestoreCachedKeyValue.Value) - %(PaketRestoreCachedKeyValue.Value) - - - - - true - false - true - - - - - true - - - - - - - - - - - - - - - - - - - $(PaketIntermediateOutputPath)\$(MSBuildProjectFile).paket.references.cached - - $(MSBuildProjectFullPath).paket.references - - $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references - - $(MSBuildProjectDirectory)\paket.references - - false - true - true - references-file-or-cache-not-found - - - - - $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)')) - $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)')) - references-file - false - - - - - false - - - - - true - target-framework '$(TargetFramework)' or '$(TargetFrameworks)' files @(PaketResolvedFilePaths) - - - - - - - - - - - false - true - - - - - - - - - - - $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',').Length) - $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) - $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) - $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) - $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5]) - - - %(PaketReferencesFileLinesInfo.PackageVersion) - All - runtime - runtime - true - true - - - - - $(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools - - - - - - - - - $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0]) - $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1]) - - - %(PaketCliToolFileLinesInfo.PackageVersion) - - - - - - - - - - false - - - - - - <_NuspecFilesNewLocation Include="$(PaketIntermediateOutputPath)\$(Configuration)\*.nuspec"/> - - - - - - $(MSBuildProjectDirectory)/$(MSBuildProjectFile) - true - false - true - false - true - false - true - false - true - $(PaketIntermediateOutputPath)\$(Configuration) - $(PaketIntermediateOutputPath) - - - - <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.$(PackageVersion.Split(`+`)[0]).nuspec"/> - - - - - - - - - - - - - - - - - - - - - diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..1d992c909 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,29 @@ + + + + + 6.0 + + true + + + + John Bazinga, Andrii Chebukin, + FSharp.Data.GraphQL + F# implementation of Facebook GraphQL query language + + 1.1.0 + FSharp GraphQL Relay React Middleware + README.md + + + https://fsprojects.github.io/FSharp.Data.GraphQL + MIT + true + true + snupkg + true + + v + + diff --git a/FSharp.Data.GraphQL.sln b/FSharp.Data.GraphQL.sln index f08c5fa9d..7d7c0d599 100644 --- a/FSharp.Data.GraphQL.sln +++ b/FSharp.Data.GraphQL.sln @@ -1,14 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28922.388 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32811.315 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{63297B98-5CED-492C-A5B7-A5B4F73CF142}" - ProjectSection(SolutionItems) = preProject - paket.dependencies = paket.dependencies - paket.lock = paket.lock - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Server", "src\FSharp.Data.GraphQL.Server\FSharp.Data.GraphQL.Server.fsproj", "{474179D3-0090-49E9-88F8-2971C0966077}" @@ -137,6 +131,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "server", "server", "{9D5C46 samples\client-provider\file-upload\server\types.mjs = samples\client-provider\file-upload\server\types.mjs EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E3330910-8B6C-4191-8046-D6D57FBC39B1}" + ProjectSection(SolutionItems) = preProject + Directory.Build.targets = Directory.Build.targets + global.json = global.json + LICENSE.txt = LICENSE.txt + Packages.props = Packages.props + README.md = README.md + RELEASE_NOTES.md = RELEASE_NOTES.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6B17B8F2-DD8B-4FA1-BA9C-6FF4F668923C}" + ProjectSection(SolutionItems) = preProject + build.cmd = build.cmd + build.fsx = build.fsx + build.sh = build.sh + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Packages.props b/Packages.props new file mode 100644 index 000000000..eaf9232e0 --- /dev/null +++ b/Packages.props @@ -0,0 +1,31 @@ + + + + 6.0.* + 6.0.* + 2.* + + + + + + contentFiles + + + + + + + + + + + + + + + + + + + diff --git a/build.cmd b/build.cmd index e7ef31ba2..0d921341a 100644 --- a/build.cmd +++ b/build.cmd @@ -1,2 +1,2 @@ dotnet tool restore -dotnet fake run build.fsx %* \ No newline at end of file +dotnet fsi build.fsx %* \ No newline at end of file diff --git a/build.fsx b/build.fsx index 60688bcf2..ef755347b 100644 --- a/build.fsx +++ b/build.fsx @@ -1,21 +1,15 @@ -#r "paket: -nuget Fake.Core.Target -nuget Fake.DotNet.Cli -nuget Fake.Tools.Git -nuget Fake.DotNet.AssemblyInfoFile -nuget Fake.Core.ReleaseNotes -nuget Fake.Core.UserInput -nuget Fake.DotNet.MSBuild -nuget Fake.IO.FileSystem -nuget Fake.DotNet.Fsc -nuget Fake.Api.GitHub -nuget Fake.DotNet.Paket -nuget Octokit -nuget FSharp.Core //" - -#if !FAKE -#load ".fake/build.fsx/intellisense.fsx" -#endif +#r "nuget: Fake.Api.GitHub" +#r "nuget: Fake.Core.ReleaseNotes" +#r "nuget: Fake.Core.Target" +#r "nuget: Fake.Core.UserInput" +#r "nuget: Fake.DotNet.AssemblyInfoFile" +#r "nuget: Fake.DotNet.Cli" +#r "nuget: Fake.DotNet.Fsc" +#r "nuget: Fake.DotNet.MSBuild" +#r "nuget: Fake.IO.FileSystem" +#r "nuget: Fake.Tools.Git" +#r "nuget: System.Reactive" +#r "nuget: Octokit" open System open System.IO @@ -24,6 +18,7 @@ open System.Threading open Fake open Fake.Tools.Git open Fake.DotNet +open Fake.DotNet.NuGet open Fake.IO open Fake.IO.FileSystemOperators open Fake.IO.Globbing.Operators @@ -33,57 +28,32 @@ open Fake.Core open Fake.Api open Octokit +// https://github.com/fsprojects/FAKE/issues/2517 +// Regular header and `#load ".fake/build.fsx/intellisense.fsx"` + +#if !FAKE +let execContext = + System.Environment.GetCommandLineArgs() + |> Array.skip 2 // skip fsi.exe; build.fsx + |> Array.toList + |> Fake.Core.Context.FakeExecutionContext.Create false __SOURCE_FILE__ +execContext +|> Fake.Core.Context.RuntimeContext.Fake +|> Fake.Core.Context.setExecutionContext +#endif + // -------------------------------------------------------------------------------------- // Information about the project are used // -------------------------------------------------------------------------------------- -// - for version and project name in generated AssemblyInfo file // - by the generated NuGet package // - to run tests and to publish documentation on GitHub gh-pages // - for documentation, you also need to edit info in "docs/tools/generate.fsx" - +let [] DotNetMoniker = "net6.0" let project = "FSharp.Data.GraphQL" -let summary = "FSharp implementation of Facebook GraphQL query language" -let gitName = "FSharp.Data.GraphQL" let release = ReleaseNotes.load "RELEASE_NOTES.md" let projectRepo = "https://github.com/fsprojects/FSharp.Data.GraphQL.git" -// Generate assembly info files with the right version & up-to-date information -Target.create "AssemblyInfo" (fun _ -> - let getAssemblyInfoAttributes projectName = - [ AssemblyInfo.Title projectName - AssemblyInfo.Product project - AssemblyInfo.Description summary - AssemblyInfo.Version release.AssemblyVersion - AssemblyInfo.FileVersion release.AssemblyVersion ] - let internalsVisibility (fsproj: string) = - match fsproj with - | f when f.EndsWith "FSharp.Data.GraphQL.Shared.fsproj" -> - [ AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Server" - AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Client" - AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Client.DesignTime" - AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Tests" ] - | f when f.EndsWith "FSharp.Data.GraphQL.Server.fsproj" -> - [ AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Benchmarks" - AssemblyInfo.InternalsVisibleTo "FSharp.Data.GraphQL.Tests" ] - | _ -> [] - - let getProjectDetails (projectPath:string) = - let projectName = System.IO.Path.GetFileNameWithoutExtension(projectPath) - ( projectPath, - projectName, - System.IO.Path.GetDirectoryName(projectPath), - (getAssemblyInfoAttributes projectName) - ) - - !! "src/**/*.fsproj" - //-- "src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj" - |> Seq.map getProjectDetails - |> Seq.iter (fun (projFileName, _, folderName, attributes) -> - AssemblyInfoFile.createFSharp (folderName "AssemblyInfo.fs") (attributes @ internalsVisibility projFileName) - ) -) - // -------------------------------------------------------------------------------------- // Clean build results @@ -98,16 +68,10 @@ Target.create "CleanDocs" (fun _ -> // -------------------------------------------------------------------------------------- // Build library & test project -// We need to disable parallel restoring of projects to because running paket in parallel from Mono -// is giving errors in Unix based operating systems. Target.create "Restore" (fun _ -> !! "src/**/*.??proj" -- "src/**/*.shproj" - |> Seq.iter (fun pattern -> - DotNet.restore (fun options -> - { options with MSBuildParams = { options.MSBuildParams with DisableInternalBinLog = true } } - ) pattern - )) + |> Seq.iter (fun pattern -> DotNet.restore id pattern)) Target.create "Build" <| fun _ -> @@ -123,7 +87,7 @@ let startGraphQLServer (project: string) (streamRef: DataRef) = let projectName = Path.GetFileNameWithoutExtension(project) let projectPath = Path.GetDirectoryName(project) - let serverExe = projectPath "bin" "Release" "net5.0" (projectName + ".dll") + let serverExe = projectPath "bin" "Release" DotNetMoniker (projectName + ".dll") CreateProcess.fromRawCommandLine "dotnet" serverExe |> CreateProcess.withStandardInput (CreatePipe streamRef) @@ -215,22 +179,19 @@ Target.create "ReleaseDocs" (fun _ -> let pack id = Shell.cleanDir <| sprintf "nuget/%s.%s" project id - Paket.pack(fun p -> + id + |> NuGet.NuGetPack(fun p -> { p with - ToolType = ToolType.CreateLocalTool() Version = release.NugetVersion OutputPath = sprintf "nuget/%s.%s" project id - TemplateFile = sprintf "src/%s.%s/%s.%s.fsproj.paket.template" project id project id - MinimumFromLockFile = true - IncludeReferencedProjects = false }) + //IncludeReferencedProjects = false + }) let publishPackage id = pack id - Paket.push(fun p -> + NuGet.NuGetPublish(fun p -> { p with - ToolType = ToolType.CreateLocalTool() - WorkingDir = sprintf "nuget/%s.%s" project id - PublishUrl = "https://www.nuget.org/api/v2/package" }) + WorkingDir = sprintf "nuget/%s.%s" project id }) Target.create "PublishServer" (fun _ -> publishPackage "Server" @@ -269,7 +230,6 @@ Target.create "PackAll" ignore "Clean" ==> "Restore" - =?> ("AssemblyInfo", BuildServer.isLocalBuild) ==> "Build" ==> "RunUnitTests" ==> "StartStarWarsServer" @@ -287,4 +247,8 @@ Target.create "PackAll" ignore ==> "PackMiddleware" ==> "PackAll" -Target.runOrDefault "All" \ No newline at end of file +Target.runOrDefaultWithArguments "All" + +#if !FAKE +execContext.Context.Clear() +#endif \ No newline at end of file diff --git a/build.fsx.lock b/build.fsx.lock index 0981d7d53..959a8dad3 100644 --- a/build.fsx.lock +++ b/build.fsx.lock @@ -1,681 +1,5 @@ STORAGE: NONE -RESTRICTION: == netstandard2.0 +RESTRICTION: || (== net6.0) (== netstandard2.0) NUGET remote: https://api.nuget.org/v3/index.json - BlackFox.VsWhere (1.1) - FSharp.Core (>= 4.2.3) - Microsoft.Win32.Registry (>= 4.7) - Fake.Api.GitHub (5.20.4) - FSharp.Core (>= 4.7.2) - Octokit (>= 0.48) - Fake.Core.CommandLineParsing (5.20.4) - FParsec (>= 1.1.1) - FSharp.Core (>= 4.7.2) - Fake.Core.Context (5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.Environment (5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.FakeVar (5.20.4) - Fake.Core.Context (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.Process (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.FakeVar (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - System.Collections.Immutable (>= 1.7.1) - Fake.Core.ReleaseNotes (5.20.4) - Fake.Core.SemVer (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.SemVer (5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.String (5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.Target (5.20.4) - Fake.Core.CommandLineParsing (>= 5.20.4) - Fake.Core.Context (>= 5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.FakeVar (>= 5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - FSharp.Control.Reactive (>= 4.4.2) - FSharp.Core (>= 4.7.2) - Fake.Core.Tasks (5.20.4) - Fake.Core.Trace (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.Trace (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.FakeVar (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.UserInput (5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Core.Xml (5.20.4) - Fake.Core.String (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.DotNet.AssemblyInfoFile (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.DotNet.Cli (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.DotNet.MSBuild (>= 5.20.4) - Fake.DotNet.NuGet (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Mono.Posix.NETStandard (>= 1.0) - Newtonsoft.Json (>= 12.0.3) - Fake.DotNet.Fsc (5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Compiler.Service (>= 37.0) - FSharp.Core (>= 4.7.2) - Fake.DotNet.MSBuild (5.20.4) - BlackFox.VsWhere (>= 1.1) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - MSBuild.StructuredLogger (>= 2.1.176) - Fake.DotNet.NuGet (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.SemVer (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Tasks (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.Core.Xml (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - Fake.Net.Http (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Newtonsoft.Json (>= 12.0.3) - NuGet.Protocol (>= 5.6) - Fake.DotNet.Paket (5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.DotNet.Cli (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.IO.FileSystem (5.20.4) - Fake.Core.String (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Net.Http (5.20.4) - Fake.Core.Trace (>= 5.20.4) - FSharp.Core (>= 4.7.2) - Fake.Tools.Git (5.20.4) - Fake.Core.Environment (>= 5.20.4) - Fake.Core.Process (>= 5.20.4) - Fake.Core.SemVer (>= 5.20.4) - Fake.Core.String (>= 5.20.4) - Fake.Core.Trace (>= 5.20.4) - Fake.IO.FileSystem (>= 5.20.4) - FSharp.Core (>= 4.7.2) - FParsec (1.1.1) - FSharp.Core (>= 4.3.4) - FSharp.Compiler.Service (39.0) - FSharp.Core (5.0.1) - Microsoft.Build.Framework (>= 16.6) - Microsoft.Build.Tasks.Core (>= 16.6) - Microsoft.Build.Utilities.Core (>= 16.6) - System.Buffers (>= 4.5.1) - System.Collections.Immutable (>= 5.0) - System.Diagnostics.Process (>= 4.3) - System.Diagnostics.TraceSource (>= 4.3) - System.Linq.Expressions (>= 4.3) - System.Linq.Queryable (>= 4.3) - System.Memory (>= 4.5.4) - System.Net.Requests (>= 4.3) - System.Net.Security (>= 4.3) - System.Reflection.Emit (>= 4.3) - System.Reflection.Metadata (>= 5.0) - System.Reflection.TypeExtensions (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Runtime.Loader (>= 4.3) - System.Security.Claims (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Principal (>= 4.3) - System.Threading.Tasks.Parallel (>= 4.3) - System.Threading.Thread (>= 4.3) - System.Threading.ThreadPool (>= 4.3) - FSharp.Control.Reactive (5.0.2) - FSharp.Core (>= 4.7.2) - System.Reactive (>= 5.0) - FSharp.Core (5.0.1) - Microsoft.Build (16.9) - Microsoft.Build.Framework (16.9) - System.Security.Permissions (>= 4.7) - Microsoft.Build.Tasks.Core (16.9) - Microsoft.Build.Framework (>= 16.9) - Microsoft.Build.Utilities.Core (>= 16.9) - Microsoft.Win32.Registry (>= 4.3) - System.CodeDom (>= 4.4) - System.Collections.Immutable (>= 5.0) - System.Reflection.Metadata (>= 1.6) - System.Reflection.TypeExtensions (>= 4.1) - System.Resources.Extensions (>= 4.6) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Pkcs (>= 4.7) - System.Security.Cryptography.Xml (>= 4.7) - System.Security.Permissions (>= 4.7) - System.Threading.Tasks.Dataflow (>= 4.9) - Microsoft.Build.Utilities.Core (16.9) - Microsoft.Build.Framework (>= 16.9) - Microsoft.Win32.Registry (>= 4.3) - System.Collections.Immutable (>= 5.0) - System.Security.Permissions (>= 4.7) - System.Text.Encoding.CodePages (>= 4.0.1) - Microsoft.NETCore.Platforms (5.0.2) - Microsoft.NETCore.Targets (5.0) - Microsoft.Win32.Primitives (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - Microsoft.Win32.Registry (5.0) - System.Buffers (>= 4.5.1) - System.Memory (>= 4.5.4) - System.Security.AccessControl (>= 5.0) - System.Security.Principal.Windows (>= 5.0) - Mono.Posix.NETStandard (1.0) - MSBuild.StructuredLogger (2.1.404) - Microsoft.Build (>= 16.4) - Microsoft.Build.Framework (>= 16.4) - Microsoft.Build.Tasks.Core (>= 16.4) - Microsoft.Build.Utilities.Core (>= 16.4) - Newtonsoft.Json (13.0.1) - NuGet.Common (5.9) - NuGet.Frameworks (>= 5.9) - NuGet.Configuration (5.9) - NuGet.Common (>= 5.9) - System.Security.Cryptography.ProtectedData (>= 4.4) - NuGet.Frameworks (5.9) - NuGet.Packaging (5.9) - Newtonsoft.Json (>= 9.0.1) - NuGet.Configuration (>= 5.9) - NuGet.Versioning (>= 5.9) - System.Security.Cryptography.Cng (>= 5.0) - System.Security.Cryptography.Pkcs (>= 5.0) - NuGet.Protocol (5.9) - NuGet.Packaging (>= 5.9) - NuGet.Versioning (5.9) - Octokit (0.50) - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.27-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.28-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.native.System (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - runtime.native.System.Net.Http (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - runtime.native.System.Net.Security (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - runtime.native.System.Security.Cryptography.Apple (4.3.1) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple (>= 4.3.1) - runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.27-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.28-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.42.3-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.opensuse.42.3-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple (4.3.1) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - System.Buffers (4.5.1) - System.CodeDom (5.0) - System.Collections (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Collections.Concurrent (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.Reflection (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Collections.Immutable (5.0) - System.Memory (>= 4.5.4) - System.Diagnostics.Debug (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Diagnostics.DiagnosticSource (5.0.1) - System.Memory (>= 4.5.4) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - System.Diagnostics.Process (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.Win32.Primitives (>= 4.3) - Microsoft.Win32.Registry (>= 4.3) - runtime.native.System (>= 4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.IO.FileSystem (>= 4.3) - System.IO.FileSystem.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Text.Encoding.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Threading.Thread (>= 4.3) - System.Threading.ThreadPool (>= 4.3) - System.Diagnostics.TraceSource (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System (>= 4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Diagnostics.Tracing (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Formats.Asn1 (5.0) - System.Buffers (>= 4.5.1) - System.Memory (>= 4.5.4) - System.Globalization (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Globalization.Calendars (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Globalization (>= 4.3) - System.Runtime (>= 4.3) - System.Globalization.Extensions (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - System.Globalization (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.IO (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.IO.FileSystem (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.IO (>= 4.3) - System.IO.FileSystem.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.IO.FileSystem.Primitives (4.3) - System.Runtime (>= 4.3) - System.Linq (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Linq.Expressions (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.Linq (>= 4.3) - System.ObjectModel (>= 4.3) - System.Reflection (>= 4.3) - System.Reflection.Emit (>= 4.3) - System.Reflection.Emit.ILGeneration (>= 4.3) - System.Reflection.Emit.Lightweight (>= 4.3) - System.Reflection.Extensions (>= 4.3) - System.Reflection.Primitives (>= 4.3) - System.Reflection.TypeExtensions (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Linq.Queryable (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Linq (>= 4.3) - System.Linq.Expressions (>= 4.3) - System.Reflection (>= 4.3) - System.Reflection.Extensions (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Memory (4.5.4) - System.Buffers (>= 4.5.1) - System.Numerics.Vectors (>= 4.4) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - System.Net.Http (4.3.4) - Microsoft.NETCore.Platforms (>= 1.1.1) - runtime.native.System (>= 4.3) - runtime.native.System.Net.Http (>= 4.3) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.DiagnosticSource (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.Globalization.Extensions (>= 4.3) - System.IO (>= 4.3) - System.IO.FileSystem (>= 4.3) - System.Net.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Security.Cryptography.X509Certificates (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Net.Primitives (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime (>= 4.3.1) - System.Runtime.Handles (>= 4.3) - System.Net.Requests (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.Net.Http (>= 4.3) - System.Net.Primitives (>= 4.3) - System.Net.WebHeaderCollection (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Net.Security (4.3.2) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.Win32.Primitives (>= 4.3) - runtime.native.System (>= 4.3) - runtime.native.System.Net.Security (>= 4.3) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.Collections.Concurrent (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.Globalization.Extensions (>= 4.3) - System.IO (>= 4.3) - System.Net.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Claims (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Security.Cryptography.X509Certificates (>= 4.3) - System.Security.Principal (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Threading.ThreadPool (>= 4.3) - System.Net.WebHeaderCollection (4.3) - System.Collections (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Numerics.Vectors (4.5) - System.ObjectModel (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Threading (>= 4.3) - System.Reactive (5.0) - System.Runtime.InteropServices.WindowsRuntime (>= 4.3) - System.Threading.Tasks.Extensions (>= 4.5.4) - System.Reflection (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.IO (>= 4.3) - System.Reflection.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Reflection.Emit (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - System.Reflection.Emit.ILGeneration (4.7) - System.Reflection.Emit.Lightweight (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - System.Reflection.Extensions (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Reflection (>= 4.3) - System.Runtime (>= 4.3) - System.Reflection.Metadata (5.0) - System.Collections.Immutable (>= 5.0) - System.Reflection.Primitives (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Reflection.TypeExtensions (4.7) - System.Resources.Extensions (5.0) - System.Memory (>= 4.5.4) - System.Resources.ResourceManager (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Globalization (>= 4.3) - System.Reflection (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime.CompilerServices.Unsafe (5.0) - System.Runtime.Extensions (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime (>= 4.3.1) - System.Runtime.Handles (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Runtime.InteropServices (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Reflection (>= 4.3) - System.Reflection.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices.WindowsRuntime (4.3) - System.Runtime (>= 4.3) - System.Runtime.Loader (4.3) - System.IO (>= 4.3) - System.Reflection (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Numerics (4.3) - System.Globalization (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Security.AccessControl (5.0) - System.Security.Principal.Windows (>= 5.0) - System.Security.Claims (4.3) - System.Collections (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Security.Principal (>= 4.3) - System.Security.Cryptography.Algorithms (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System.Security.Cryptography.Apple (>= 4.3.1) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.IO (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Runtime.Numerics (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Security.Cryptography.Cng (5.0) - System.Security.Cryptography.Csp (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - System.IO (>= 4.3) - System.Reflection (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Security.Cryptography.Encoding (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3) - System.Collections (>= 4.3) - System.Collections.Concurrent (>= 4.3) - System.Linq (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (5.0) - System.Security.Cryptography.Pkcs (5.0.1) - System.Buffers (>= 4.5.1) - System.Formats.Asn1 (>= 5.0) - System.Memory (>= 4.5.4) - System.Security.Cryptography.Cng (>= 5.0) - System.Security.Cryptography.Primitives (4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Security.Cryptography.ProtectedData (5.0) - System.Memory (>= 4.5.4) - System.Security.Cryptography.X509Certificates (4.3.2) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System (>= 4.3) - runtime.native.System.Net.Http (>= 4.3) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.Globalization.Calendars (>= 4.3) - System.IO (>= 4.3) - System.IO.FileSystem (>= 4.3) - System.IO.FileSystem.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Runtime.Numerics (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Cng (>= 4.3) - System.Security.Cryptography.Csp (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Security.Cryptography.Xml (5.0) - System.Memory (>= 4.5.4) - System.Security.Cryptography.Pkcs (>= 5.0) - System.Security.Permissions (>= 5.0) - System.Security.Permissions (5.0) - System.Security.AccessControl (>= 5.0) - System.Security.Principal (4.3) - System.Runtime (>= 4.3) - System.Security.Principal.Windows (5.0) - System.Text.Encoding (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Text.Encoding.CodePages (5.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - System.Text.Encoding.Extensions (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (4.3) - System.Runtime (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Threading.Tasks (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Threading.Tasks.Dataflow (5.0) - System.Threading.Tasks.Extensions (4.5.4) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - System.Threading.Tasks.Parallel (4.3) - System.Collections.Concurrent (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Threading.Thread (4.3) - System.Runtime (>= 4.3) - System.Threading.ThreadPool (4.3) - System.Runtime (>= 4.3) - System.Runtime.Handles (>= 4.3) + FSharp.Core (6.0.5) diff --git a/build.sh b/build.sh index 038454dab..1edb1ddf3 100755 --- a/build.sh +++ b/build.sh @@ -4,4 +4,4 @@ set -eu set -o pipefail dotnet tool restore -dotnet fake run build.fsx "$@" \ No newline at end of file +dotnet fsi build.fsx "$@" \ No newline at end of file diff --git a/global.json b/global.json index c9637a5b5..e7813d8db 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { - "sdk": { - "version": "5.0.100", - "rollForward": "latestFeature" - }, - "projects": ["src", "tests"] + "sdk": { + "version": "6.0.400", + "rollForward": "latestMinor" + }, + "projects": [ "src", "tests" ] } \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies deleted file mode 100644 index 092d9ae36..000000000 --- a/paket.dependencies +++ /dev/null @@ -1,41 +0,0 @@ -source https://api.nuget.org/v3/index.json - -nuget NuGet.CommandLine - -group Common - source https://api.nuget.org/v3/index.json - frameworks: net5.0, netstandard2.0 - - nuget System.Reactive - - # Be explicit about FSharp.Core 4.7.2 when designing libraries. - # See https://fsharp.github.io/2015/04/18/fsharp-core-notes.html#fsharpcore-entries-in-project-files for additional information. - nuget FSharp.Core 4.7.2 - - # FParsec needs to be explicit for the client provider design time reference. - # This should not be a problem since FParsec almost never gets a new release. - nuget FParsec 1.1.1 - - nuget Microsoft.Extensions.Http 5 - - # Those are needed for the client type provider. - github fsprojects/FSharp.TypeProviders.SDK:377d56321ad062985ed5aa19f205c1c4f04ef328 src/ProvidedTypes.fsi - github fsprojects/FSharp.TypeProviders.SDK:377d56321ad062985ed5aa19f205c1c4f04ef328 src/ProvidedTypes.fs - -group TestsAndSamples - source https://api.nuget.org/v3/index.json - frameworks: net5.0, netstandard2.0 - - nuget Microsoft.Extensions.Http 5 - nuget System.Net.Http 4.3.4 - nuget FSharp.Core - nuget Newtonsoft.Json - nuget Suave - nuget Giraffe - nuget Microsoft.NET.Test.Sdk - nuget xunit - nuget xunit.runner.utility - nuget xunit.runner.console - nuget xunit.runner.visualstudio - nuget BenchmarkDotNet - nuget FSharp.Data.TypeProviders \ No newline at end of file diff --git a/paket.lock b/paket.lock deleted file mode 100644 index 86700f6ee..000000000 --- a/paket.lock +++ /dev/null @@ -1,641 +0,0 @@ -NUGET - remote: https://api.nuget.org/v3/index.json - NuGet.CommandLine (5.8.1) - -GROUP Common -RESTRICTION: || (== net50) (== netstandard2.0) -NUGET - remote: https://api.nuget.org/v3/index.json - FParsec (1.1.1) - FSharp.Core (>= 4.3.4) - FSharp.Core (4.7.2) - Microsoft.Bcl.AsyncInterfaces (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection (5.0.1) - Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (5.0) - Microsoft.Extensions.Http (5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Extensions.Logging (5.0) - Microsoft.Extensions.DependencyInjection (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - System.Diagnostics.DiagnosticSource (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Logging.Abstractions (5.0) - Microsoft.Extensions.Options (5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Primitives (5.0.1) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - Microsoft.NETCore.Platforms (5.0.2) - restriction: || (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (< netstandard1.2)) (&& (== net50) (< netstandard1.3)) (&& (== net50) (< netstandard1.5)) (== netstandard2.0) - Microsoft.NETCore.Targets (5.0) - restriction: || (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (< netstandard1.2)) (&& (== net50) (< netstandard1.3)) (&& (== net50) (< netstandard1.5)) (== netstandard2.0) - System.Buffers (4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Diagnostics.DiagnosticSource (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Memory (4.5.4) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (4.5) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Reactive (5.0) - System.Runtime.InteropServices.WindowsRuntime (>= 4.3) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net472)) (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime (4.3.1) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime.CompilerServices.Unsafe (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.InteropServices.WindowsRuntime (4.3) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) - System.Runtime (>= 4.3) - System.Threading.Tasks.Extensions (4.5.4) - restriction: || (&& (== net50) (>= net472)) (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= wp8)) (== netstandard2.0) -GITHUB - remote: fsprojects/FSharp.TypeProviders.SDK - src/ProvidedTypes.fs (377d56321ad062985ed5aa19f205c1c4f04ef328) - src/ProvidedTypes.fsi (377d56321ad062985ed5aa19f205c1c4f04ef328) -GROUP TestsAndSamples -RESTRICTION: || (== net50) (== netstandard2.0) -NUGET - remote: https://api.nuget.org/v3/index.json - BenchmarkDotNet (0.12.1) - BenchmarkDotNet.Annotations (>= 0.12.1) - CommandLineParser (>= 2.4.3) - Iced (>= 1.4) - Microsoft.CodeAnalysis.CSharp (>= 2.10) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - Microsoft.Diagnostics.Runtime (>= 1.1.57604) - Microsoft.Diagnostics.Tracing.TraceEvent (>= 2.0.49) - Microsoft.DotNet.PlatformAbstractions (>= 2.1) - Microsoft.Win32.Registry (>= 4.5) - Perfolizer (>= 0.2.1) - System.Management (>= 4.5) - System.Reflection.Emit (>= 4.3) - System.Reflection.Emit.Lightweight (>= 4.3) - System.Threading.Tasks.Extensions (>= 4.5.2) - System.ValueTuple (>= 4.5) - BenchmarkDotNet.Annotations (0.12.1) - CommandLineParser (2.8) - FSharp.Core (5.0.1) - FSharp.Data.TypeProviders (5.0.0.6) - FSharp.Core (>= 3.1.2.5) - Giraffe (4.1) - FSharp.Core (>= 4.7) - Microsoft.AspNetCore.Authentication (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authorization (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.ResponseCaching (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.IO.RecyclableMemoryStream (>= 1.2.2) - Newtonsoft.Json (>= 12.0.2) - TaskBuilder.fs (>= 2.1) - Utf8Json (>= 1.3.7) - Iced (1.11) - Microsoft.AspNetCore.Authentication (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authentication.Core (>= 2.2) - Microsoft.AspNetCore.DataProtection (>= 2.2) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.Extensions.WebEncoders (>= 2.2) - Microsoft.AspNetCore.Authentication.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.AspNetCore.Authentication.Core (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authentication.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.Authorization (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Metadata (>= 5.0.5) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.AspNetCore.Cryptography.Internal (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.DataProtection (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Cryptography.Internal (>= 5.0.5) - Microsoft.AspNetCore.DataProtection.Abstractions (>= 5.0.5) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Hosting.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Win32.Registry (>= 5.0) - System.Security.Cryptography.Xml (>= 5.0) - System.Security.Principal.Windows (>= 5.0) - restriction: == netstandard2.0 - Microsoft.AspNetCore.DataProtection.Abstractions (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics.Abstractions (>= 2.2) - Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.WebUtilities (>= 2.2) - Microsoft.Extensions.FileProviders.Physical (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - System.Diagnostics.DiagnosticSource (>= 4.5) - System.Reflection.Metadata (>= 1.6) - Microsoft.AspNetCore.Diagnostics.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Server.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.Hosting.Abstractions (>= 2.2) - Microsoft.AspNetCore.Hosting.Server.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Features (>= 2.2) - Microsoft.Extensions.Configuration.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http (2.2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.AspNetCore.WebUtilities (>= 2.2) - Microsoft.Extensions.ObjectPool (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.Net.Http.Headers (>= 2.2) - Microsoft.AspNetCore.Http.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Features (>= 2.2) - System.Text.Encodings.Web (>= 4.5) - Microsoft.AspNetCore.Http.Extensions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.FileProviders.Abstractions (>= 2.2) - Microsoft.Net.Http.Headers (>= 2.2) - System.Buffers (>= 4.5) - Microsoft.AspNetCore.Http.Features (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0.1) - System.IO.Pipelines (>= 5.0.1) - Microsoft.AspNetCore.Metadata (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.ResponseCaching (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.ResponseCaching.Abstractions (>= 2.2) - Microsoft.Extensions.Caching.Memory (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.AspNetCore.ResponseCaching.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 2.2) - Microsoft.AspNetCore.WebUtilities (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Net.Http.Headers (>= 2.2) - System.Text.Encodings.Web (>= 4.5) - Microsoft.Bcl.AsyncInterfaces (5.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.CodeAnalysis.Analyzers (3.3.2) - Microsoft.CodeAnalysis.Common (3.9) - Microsoft.CodeAnalysis.Analyzers (>= 3.0) - System.Collections.Immutable (>= 5.0) - System.Memory (>= 4.5.4) - System.Reflection.Metadata (>= 5.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - System.Text.Encoding.CodePages (>= 4.5.1) - System.Threading.Tasks.Extensions (>= 4.5.4) - Microsoft.CodeAnalysis.CSharp (3.9) - Microsoft.CodeAnalysis.Common (3.9) - Microsoft.CodeCoverage (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.Diagnostics.NETCore.Client (0.2.217401) - Microsoft.Bcl.AsyncInterfaces (>= 1.1) - Microsoft.Diagnostics.Runtime (2.0.217201) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - System.Buffers (>= 4.5.1) - System.Collections.Immutable (>= 1.7.1) - System.Memory (>= 4.5.4) - System.Reflection.Metadata (>= 1.8.1) - System.Runtime.CompilerServices.Unsafe (>= 4.7.1) - Microsoft.Diagnostics.Tracing.TraceEvent (2.0.66) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - Microsoft.DotNet.PlatformAbstractions (3.1.6) - Microsoft.Extensions.Caching.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Caching.Memory (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Caching.Abstractions (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Configuration.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.DependencyInjection (5.0.1) - Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (5.0) - Microsoft.Extensions.FileProviders.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.FileProviders.Physical (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.FileProviders.Abstractions (>= 5.0) - Microsoft.Extensions.FileSystemGlobbing (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.FileSystemGlobbing (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Hosting.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Configuration.Abstractions (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.FileProviders.Abstractions (>= 5.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Http (5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Extensions.Logging (5.0) - Microsoft.Extensions.DependencyInjection (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - System.Diagnostics.DiagnosticSource (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Logging.Abstractions (5.0) - Microsoft.Extensions.ObjectPool (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Options (5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Primitives (5.0.1) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - Microsoft.Extensions.WebEncoders (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - System.Text.Encodings.Web (>= 5.0.1) - restriction: || (&& (== net50) (>= net461)) (== netstandard2.0) - Microsoft.IO.RecyclableMemoryStream (2.0) - Microsoft.Net.Http.Headers (2.2.8) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 2.2) - System.Buffers (>= 4.5) - Microsoft.NET.Test.Sdk (16.9.4) - Microsoft.CodeCoverage (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.TestHost (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.NETCore.Platforms (5.0.2) - Microsoft.NETCore.Targets (5.0) - Microsoft.TestPlatform.ObjectModel (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - NuGet.Frameworks (>= 5.0) - System.Reflection.Metadata (>= 1.6) - Microsoft.TestPlatform.TestHost (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.ObjectModel (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) - Newtonsoft.Json (>= 9.0.1) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) - Microsoft.Win32.Registry (5.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Security.AccessControl (>= 5.0) - System.Security.Principal.Windows (>= 5.0) - NETStandard.Library (2.0.3) - Microsoft.NETCore.Platforms (>= 1.1) - Newtonsoft.Json (13.0.1) - NuGet.Frameworks (5.9) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Perfolizer (0.2.1) - System.Memory (>= 4.5.3) - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.27-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.fedora.28-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.native.System (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - runtime.native.System.Net.Http (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - runtime.native.System.Security.Cryptography.Apple (4.3.1) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple (>= 4.3.1) - runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.27-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.fedora.28-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.42.3-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.3) - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.opensuse.42.3-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple (4.3.1) - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - Suave (2.6) - FSharp.Core - restriction: || (== net50) (&& (== netstandard2.0) (>= netstandard2.1)) - System.Buffers (4.5.1) - System.CodeDom (5.0) - System.Collections (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Collections.Concurrent (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.Reflection (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Collections.Immutable (5.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Diagnostics.Debug (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Diagnostics.DiagnosticSource (5.0.1) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Diagnostics.Tracing (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Formats.Asn1 (5.0) - System.Globalization (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Globalization.Calendars (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Globalization (>= 4.3) - System.Runtime (>= 4.3) - System.Globalization.Extensions (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - System.Globalization (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.IO (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.IO.FileSystem (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.IO (>= 4.3) - System.IO.FileSystem.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.IO.FileSystem.Primitives (4.3) - System.Runtime (>= 4.3) - System.IO.Pipelines (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Linq (4.3) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Management (5.0) - Microsoft.NETCore.Platforms (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp2.0)) - Microsoft.Win32.Registry (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp2.0)) - System.CodeDom (>= 5.0) - System.Memory (4.5.4) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Net.Http (4.3.4) - Microsoft.NETCore.Platforms (>= 1.1.1) - runtime.native.System (>= 4.3) - runtime.native.System.Net.Http (>= 4.3) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Diagnostics.DiagnosticSource (>= 4.3) - System.Diagnostics.Tracing (>= 4.3) - System.Globalization (>= 4.3) - System.Globalization.Extensions (>= 4.3) - System.IO (>= 4.3) - System.IO.FileSystem (>= 4.3) - System.Net.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Security.Cryptography.X509Certificates (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Net.Primitives (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime (>= 4.3.1) - System.Runtime.Handles (>= 4.3) - System.Numerics.Vectors (4.5) - restriction: || (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) - System.Reflection (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.IO (>= 4.3) - System.Reflection.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Reflection.Emit (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Emit.Lightweight (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (< portable-net45+wp8)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Reflection.Metadata (5.0) - System.Collections.Immutable (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (== netstandard2.0) - System.Reflection.Primitives (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Reflection.TypeExtensions (4.7) - restriction: || (&& (== net50) (< netcoreapp1.0)) (== netstandard2.0) - System.Resources.ResourceManager (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Globalization (>= 4.3) - System.Reflection (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime.CompilerServices.Unsafe (5.0) - System.Runtime.Extensions (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1.1) - Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime (>= 4.3.1) - System.Runtime.Handles (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Runtime.InteropServices (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Reflection (>= 4.3) - System.Reflection.Primitives (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.Loader (4.3) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - System.IO (>= 4.3) - System.Reflection (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Numerics (4.3) - System.Globalization (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Security.AccessControl (5.0) - Microsoft.NETCore.Platforms (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp2.0)) - System.Security.Principal.Windows (>= 5.0) - System.Security.Cryptography.Algorithms (4.3.1) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System.Security.Cryptography.Apple (>= 4.3.1) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.IO (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Runtime.Numerics (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Security.Cryptography.Cng (5.0) - System.Formats.Asn1 (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp3.0)) - System.Security.Cryptography.Csp (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - System.IO (>= 4.3) - System.Reflection (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Security.Cryptography.Encoding (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3) - System.Collections (>= 4.3) - System.Collections.Concurrent (>= 4.3) - System.Linq (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (5.0) - System.Formats.Asn1 (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp3.0)) - System.Security.Cryptography.Pkcs (5.0.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) - System.Formats.Asn1 (>= 5.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Security.Cryptography.Cng (>= 5.0) - System.Security.Cryptography.Primitives (4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.IO (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Threading (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Security.Cryptography.X509Certificates (4.3.2) - Microsoft.NETCore.Platforms (>= 1.1) - runtime.native.System (>= 4.3) - runtime.native.System.Net.Http (>= 4.3) - runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2) - System.Collections (>= 4.3) - System.Diagnostics.Debug (>= 4.3) - System.Globalization (>= 4.3) - System.Globalization.Calendars (>= 4.3) - System.IO (>= 4.3) - System.IO.FileSystem (>= 4.3) - System.IO.FileSystem.Primitives (>= 4.3) - System.Resources.ResourceManager (>= 4.3) - System.Runtime (>= 4.3) - System.Runtime.Extensions (>= 4.3) - System.Runtime.Handles (>= 4.3) - System.Runtime.InteropServices (>= 4.3) - System.Runtime.Numerics (>= 4.3) - System.Security.Cryptography.Algorithms (>= 4.3) - System.Security.Cryptography.Cng (>= 4.3) - System.Security.Cryptography.Csp (>= 4.3) - System.Security.Cryptography.Encoding (>= 4.3) - System.Security.Cryptography.OpenSsl (>= 4.3) - System.Security.Cryptography.Primitives (>= 4.3) - System.Text.Encoding (>= 4.3) - System.Threading (>= 4.3) - System.Security.Cryptography.Xml (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (< netcoreapp2.1)) (== netstandard2.0) - System.Security.Cryptography.Pkcs (>= 5.0) - System.Security.Permissions (>= 5.0) - System.Security.Permissions (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Security.AccessControl (>= 5.0) - System.Security.Principal.Windows (5.0) - System.Text.Encoding (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Text.Encoding.CodePages (5.0) - Microsoft.NETCore.Platforms (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) (&& (== netstandard2.0) (>= netcoreapp2.0)) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) - System.Text.Encodings.Web (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Threading (4.3) - System.Runtime (>= 4.3) - System.Threading.Tasks (>= 4.3) - System.Threading.Tasks (4.3) - Microsoft.NETCore.Platforms (>= 1.1) - Microsoft.NETCore.Targets (>= 1.1) - System.Runtime (>= 4.3) - System.Threading.Tasks.Extensions (4.5.4) - System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= wp8)) (== netstandard2.0) - System.ValueTuple (4.5) - TaskBuilder.fs (2.1) - FSharp.Core (>= 4.1.17) - NETStandard.Library (>= 1.6.1) - System.ValueTuple (>= 4.4) - Utf8Json (1.3.7) - System.Reflection.Emit (>= 4.3) - System.Reflection.Emit.Lightweight (>= 4.3) - System.Threading.Tasks.Extensions (>= 4.4) - System.ValueTuple (>= 4.4) - xunit (2.4.1) - xunit.analyzers (>= 0.10) - xunit.assert (2.4.1) - xunit.core (2.4.1) - xunit.abstractions (2.0.3) - xunit.analyzers (0.10) - xunit.assert (2.4.1) - NETStandard.Library (>= 1.6.1) - xunit.core (2.4.1) - xunit.extensibility.core (2.4.1) - xunit.extensibility.execution (2.4.1) - xunit.extensibility.core (2.4.1) - NETStandard.Library (>= 1.6.1) - xunit.abstractions (>= 2.0.3) - xunit.extensibility.execution (2.4.1) - NETStandard.Library (>= 1.6.1) - xunit.extensibility.core (2.4.1) - xunit.runner.console (2.4.1) - xunit.runner.utility (2.4.1) - NETStandard.Library (>= 1.6) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - NETStandard.Library (>= 1.6.1) - restriction: || (&& (== net50) (< netcoreapp1.0)) (&& (== net50) (< netstandard1.5)) (== netstandard2.0) - System.Reflection.TypeExtensions (>= 4.3) - restriction: || (&& (== net50) (< netcoreapp1.0)) (== netstandard2.0) - System.Runtime.Loader (>= 4.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - xunit.abstractions (>= 2.0.3) - xunit.runner.visualstudio (2.4.3) diff --git a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj index 87cffddde..643e4c7c8 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -1,15 +1,18 @@ - + + Exe - net5.0 + net6.0 false + - - - + + + + @@ -18,8 +21,11 @@ - - - + + + + + + diff --git a/samples/star-wars-api/HttpHandlers.fs b/samples/star-wars-api/HttpHandlers.fs index 916b27331..141c08bde 100644 --- a/samples/star-wars-api/HttpHandlers.fs +++ b/samples/star-wars-api/HttpHandlers.fs @@ -1,15 +1,14 @@ namespace FSharp.Data.GraphQL.Samples.StarWarsApi +open System.IO open System.Text open Giraffe open Microsoft.AspNetCore.Http open Newtonsoft.Json +open Newtonsoft.Json.Linq open FSharp.Data.GraphQL.Execution -open System.IO open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types -open FSharp.Control.Tasks -open Newtonsoft.Json.Linq type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult @@ -32,7 +31,7 @@ module HttpHandlers = let serialize d = JsonConvert.SerializeObject(d, jsonSettings) let deserialize (data : string) = - let getMap (token : JToken) = + let getMap (token : JToken) = let rec mapper (name : string) (token : JToken) = match name, token.Type with | "variables", JTokenType.Object -> token.Children() |> Seq.map (fun x -> x.Name, mapper x.Name x.Value) |> Map.ofSeq |> box @@ -41,10 +40,10 @@ module HttpHandlers = token.Children() |> Seq.map (fun x -> x.Name, mapper x.Name x.Value) |> Map.ofSeq - if System.String.IsNullOrWhiteSpace(data) + if System.String.IsNullOrWhiteSpace(data) then None else data |> JToken.Parse |> getMap |> Some - + let json = function | Direct (data, _) -> @@ -52,19 +51,19 @@ module HttpHandlers = | Deferred (data, _, deferred) -> deferred |> Observable.add(fun d -> printfn "Deferred: %s" (serialize d)) JsonConvert.SerializeObject(data, jsonSettings) - | Stream data -> + | Stream data -> data |> Observable.add(fun d -> printfn "Subscription data: %s" (serialize d)) "{}" - + let removeWhitespacesAndLineBreaks (str : string) = str.Trim().Replace("\r\n", " ") - + let readStream (s : Stream) = use ms = new MemoryStream(4096) s.CopyTo(ms) ms.ToArray() - + let data = Encoding.UTF8.GetString(readStream ctx.Request.Body) |> deserialize - + let query = data |> Option.bind (fun data -> if data.ContainsKey("query") @@ -73,7 +72,7 @@ module HttpHandlers = | :? string as x -> Some x | _ -> failwith "Failure deserializing repsonse. Could not read query - it is not stringified in request." else None) - + let variables = data |> Option.bind (fun data -> if data.ContainsKey("variables") @@ -84,7 +83,7 @@ module HttpHandlers = | :? Map as x -> Some x | _ -> failwith "Failure deserializing response. Could not read variables - it is not a object in the request." else None) - + match query, variables with | Some query, Some variables -> printfn "Received query: %s" query @@ -106,7 +105,7 @@ module HttpHandlers = return! okWithStr (json result) next ctx } - let webApp : HttpHandler = + let webApp : HttpHandler = setCorsHeaders - >=> graphQL + >=> graphQL >=> setContentTypeAsJson diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj b/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj index 2cca4b284..a4dd04afd 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj @@ -1,5 +1,5 @@ - - + + Library netstandard2.0 @@ -11,16 +11,22 @@ false false ..\FSharp.Data.GraphQL.Client\bin\$(Configuration)\typeproviders\fsharp41\ + + FSharp implementation of Facebook GraphQL query language (Client) + + + + all + + + all + + + + + - - True - paket-files/ProvidedTypes.fsi - - - True - paket-files/ProvidedTypes.fs - @@ -37,10 +43,9 @@ - + - + - \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references b/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references deleted file mode 100644 index 91d46b834..000000000 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references +++ /dev/null @@ -1,6 +0,0 @@ -group Common -FSharp.Core -FParsec -Microsoft.Extensions.Http -File:ProvidedTypes.fsi -File:ProvidedTypes.fs \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Client/AssemblyInfo.fs deleted file mode 100644 index 146b60534..000000000 --- a/src/FSharp.Data.GraphQL.Client/AssemblyInfo.fs +++ /dev/null @@ -1,17 +0,0 @@ -// Auto-Generated by FAKE; do not edit -namespace System -open System.Reflection - -[] -[] -[] -[] -[] -do () - -module internal AssemblyVersionInformation = - let [] AssemblyTitle = "FSharp.Data.GraphQL.Client" - let [] AssemblyProduct = "FSharp.Data.GraphQL" - let [] AssemblyDescription = "FSharp implementation of Facebook GraphQL query language" - let [] AssemblyVersion = "1.0.8" - let [] AssemblyFileVersion = "1.0.8" diff --git a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj index 042050af4..b83efed0a 100644 --- a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj +++ b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj @@ -1,5 +1,5 @@ - - + + Library netstandard2.0 @@ -7,9 +7,16 @@ false typeproviders typeproviders + + FSharp implementation of Facebook GraphQL query language (Client) + + + + + + - @@ -24,11 +31,9 @@ - - + - + - \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj.paket.template b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj.paket.template deleted file mode 100644 index 0c7ae92d5..000000000 --- a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj.paket.template +++ /dev/null @@ -1,24 +0,0 @@ -type project -id FSharp.Data.GraphQL.Client -authors Bazinga Technologies Inc -projectUrl https://github.com/fsprojects/FSharp.Data.GraphQL -licenseUrl https://github.com/fsprojects/FSharp.Data.GraphQL/blob/dev/LICENSE.txt -requireLicenseAcceptance false -copyright Copyright (c) 2016 Bazinga Technologies Inc -tags - FSharp GraphQL Relay React -summary - FSharp implementation of Facebook GraphQL query language (Client) -description - FSharp implementation of Facebook GraphQL query language (Client) -files - bin/Release/netstandard2.0/FSharp.Data.GraphQL.Client.dll ==> lib/netstandard2.0 - bin/Release/typeproviders/fsharp41/netstandard2.0/*.dll ==> typeproviders/fsharp41/netstandard2.0 -references - FSharp.Data.GraphQL.Client.dll -dependencies - framework: netstandard2.0 - FSharp.Core >= LOCKEDVERSION-Common - FParsec >= LOCKEDVERSION-Common - Microsoft.Extensions.Http >= LOCKEDVERSION-Common - FSharp.Data.GraphQL.Shared >= CURRENTVERSION \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/paket.references b/src/FSharp.Data.GraphQL.Client/paket.references deleted file mode 100644 index 3d7ef45ed..000000000 --- a/src/FSharp.Data.GraphQL.Client/paket.references +++ /dev/null @@ -1,4 +0,0 @@ -group Common -FSharp.Core -FParsec -Microsoft.Extensions.Http \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Server.Middleware/AssemblyInfo.fs deleted file mode 100644 index 3e900e847..000000000 --- a/src/FSharp.Data.GraphQL.Server.Middleware/AssemblyInfo.fs +++ /dev/null @@ -1,17 +0,0 @@ -// Auto-Generated by FAKE; do not edit -namespace System -open System.Reflection - -[] -[] -[] -[] -[] -do () - -module internal AssemblyVersionInformation = - let [] AssemblyTitle = "FSharp.Data.GraphQL.Server.Middleware" - let [] AssemblyProduct = "FSharp.Data.GraphQL" - let [] AssemblyDescription = "FSharp implementation of Facebook GraphQL query language" - let [] AssemblyVersion = "1.0.8" - let [] AssemblyFileVersion = "1.0.8" diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj b/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj index 2f9483336..b09fd3f9d 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj +++ b/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj @@ -1,22 +1,28 @@ - + + netstandard2.0 false true + true + + Built-in, generic middlewares for FSharp.Data.GraphQL.Server Executor + + + - - - - - - - - + + + + + \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj.paket.template b/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj.paket.template deleted file mode 100644 index 3354026f8..000000000 --- a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj.paket.template +++ /dev/null @@ -1,20 +0,0 @@ -type project -id FSharp.Data.GraphQL.Server.Middleware -authors Bazinga Technologies Inc -projectUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL -licenseUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL/blob/dev/LICENSE.txt -requireLicenseAcceptance false -copyright Copyright (c) 2016 Bazinga Technologies Inc -tags - FSharp GraphQL Relay React Middleware -summary - Built-in, generic middlewares for FSharp.Data.GraphQL.Server Executor -description - Built-in, generic middlewares for FSharp.Data.GraphQL.Server Executor -dependencies - framework: netstandard2.0 - FParsec >= LOCKEDVERSION-Common - FSharp.Core >= LOCKEDVERSION-Common - FSharp.Data.GraphQL.Shared >= CURRENTVERSION -files - bin/Release/netstandard2.0/FSharp.Data.GraphQL.Server.Middleware.dll ==> lib/netstandard2.0 \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/paket.references b/src/FSharp.Data.GraphQL.Server.Middleware/paket.references deleted file mode 100644 index d5db0d1e4..000000000 --- a/src/FSharp.Data.GraphQL.Server.Middleware/paket.references +++ /dev/null @@ -1,2 +0,0 @@ -group Common -FSharp.Core \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Server/AssemblyInfo.fs deleted file mode 100644 index a8956370b..000000000 --- a/src/FSharp.Data.GraphQL.Server/AssemblyInfo.fs +++ /dev/null @@ -1,22 +0,0 @@ -// Auto-Generated by FAKE; do not edit -namespace System -open System.Reflection -open System.Runtime.CompilerServices - -[] -[] -[] -[] -[] -[] -[] -do () - -module internal AssemblyVersionInformation = - let [] AssemblyTitle = "FSharp.Data.GraphQL.Server" - let [] AssemblyProduct = "FSharp.Data.GraphQL" - let [] AssemblyDescription = "FSharp implementation of Facebook GraphQL query language" - let [] AssemblyVersion = "1.0.8" - let [] AssemblyFileVersion = "1.0.8" - let [] InternalsVisibleTo = "FSharp.Data.GraphQL.Benchmarks" - let [] InternalsVisibleTo_1 = "FSharp.Data.GraphQL.Tests" diff --git a/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj b/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj index 69dca6632..12a05bc4d 100644 --- a/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj +++ b/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj @@ -1,11 +1,37 @@ - + + netstandard2.0 - false true + true + + FSharp implementation of Facebook GraphQL query language (Server) + + + + + + + + + + + <_Parameter1>FSharp.Data.GraphQL.Benchmarks + + + <_Parameter1>FSharp.Data.GraphQL.Tests + + + + + + + + - @@ -17,11 +43,9 @@ - - + - \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj.paket.template b/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj.paket.template deleted file mode 100644 index 6fa22434f..000000000 --- a/src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj.paket.template +++ /dev/null @@ -1,23 +0,0 @@ -type project -id FSharp.Data.GraphQL.Server -authors Bazinga Technologies Inc -projectUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL -licenseUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL/blob/dev/LICENSE.txt -requireLicenseAcceptance false -copyright Copyright (c) 2016 Bazinga Technologies Inc -tags - FSharp GraphQL Relay React -summary - FSharp implementation of Facebook GraphQL query language (Server) -description - FSharp implementation of Facebook GraphQL query language (Server) -references - FSharp.Data.GraphQL.Server.dll -dependencies - framework: netstandard2.0 - FParsec >= LOCKEDVERSION-Common - FSharp.Core >= LOCKEDVERSION-Common - System.Reactive >= LOCKEDVERSION-Common - FSharp.Data.GraphQL.Shared >= CURRENTVERSION -files - bin/Release/netstandard2.0/FSharp.Data.GraphQL.Server.dll ==> lib/netstandard2.0 \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server/paket.references b/src/FSharp.Data.GraphQL.Server/paket.references deleted file mode 100644 index 0fd7b0ea0..000000000 --- a/src/FSharp.Data.GraphQL.Server/paket.references +++ /dev/null @@ -1,4 +0,0 @@ -group Common -FParsec -FSharp.Core -System.Reactive \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Shared/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Shared/AssemblyInfo.fs deleted file mode 100644 index 25cc3fed6..000000000 --- a/src/FSharp.Data.GraphQL.Shared/AssemblyInfo.fs +++ /dev/null @@ -1,26 +0,0 @@ -// Auto-Generated by FAKE; do not edit -namespace System -open System.Reflection -open System.Runtime.CompilerServices - -[] -[] -[] -[] -[] -[] -[] -[] -[] -do () - -module internal AssemblyVersionInformation = - let [] AssemblyTitle = "FSharp.Data.GraphQL.Shared" - let [] AssemblyProduct = "FSharp.Data.GraphQL" - let [] AssemblyDescription = "FSharp implementation of Facebook GraphQL query language" - let [] AssemblyVersion = "1.0.8" - let [] AssemblyFileVersion = "1.0.8" - let [] InternalsVisibleTo = "FSharp.Data.GraphQL.Server" - let [] InternalsVisibleTo_1 = "FSharp.Data.GraphQL.Client" - let [] InternalsVisibleTo_2 = "FSharp.Data.GraphQL.Client.DesignTime" - let [] InternalsVisibleTo_3 = "FSharp.Data.GraphQL.Tests" diff --git a/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj b/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj index 2a5441633..47a4e51f3 100644 --- a/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj +++ b/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj @@ -1,10 +1,37 @@ - + + netstandard2.0 true + true + + Shared library for FSharp.Data.GraphQL.Server and FSharp.Data.GraphQL.Cient + + + + + + <_Parameter1>FSharp.Data.GraphQL.Server + + + <_Parameter1>FSharp.Data.GraphQL.Client + + + <_Parameter1>FSharp.Data.GraphQL.Client.DesignTime + + + <_Parameter1>FSharp.Data.GraphQL.Tests + + + + + + + - @@ -19,7 +46,5 @@ - - \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj.paket.template b/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj.paket.template deleted file mode 100644 index f9e667c14..000000000 --- a/src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj.paket.template +++ /dev/null @@ -1,21 +0,0 @@ -type project -id FSharp.Data.GraphQL.Shared -authors Bazinga Technologies Inc -projectUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL -licenseUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL/blob/dev/LICENSE.txt -requireLicenseAcceptance false -copyright Copyright (c) 2016 Bazinga Technologies Inc -tags - FSharp GraphQL Relay React Middleware -summary - Shared library for FSharp.Data.GraphQL.Server and FSharp.Data.GraphQL.Cient -description - Shared library for FSharp.Data.GraphQL.Server and FSharp.Data.GraphQL.Cient -dependencies - framework: netstandard2.0 - FParsec >= LOCKEDVERSION-Common - FSharp.Core >= LOCKEDVERSION-Common -references - FSharp.Data.GraphQL.Shared.dll -files - bin/Release/netstandard2.0/FSharp.Data.GraphQL.Shared.dll ==> lib/netstandard2.0 \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Shared/paket.references b/src/FSharp.Data.GraphQL.Shared/paket.references deleted file mode 100644 index 4d208e057..000000000 --- a/src/FSharp.Data.GraphQL.Shared/paket.references +++ /dev/null @@ -1,3 +0,0 @@ -group Common -FSharp.Core -FParsec \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Benchmarks/FSharp.Data.GraphQL.Benchmarks.fsproj b/tests/FSharp.Data.GraphQL.Benchmarks/FSharp.Data.GraphQL.Benchmarks.fsproj index 3b8f5b5b6..bfed32230 100644 --- a/tests/FSharp.Data.GraphQL.Benchmarks/FSharp.Data.GraphQL.Benchmarks.fsproj +++ b/tests/FSharp.Data.GraphQL.Benchmarks/FSharp.Data.GraphQL.Benchmarks.fsproj @@ -1,15 +1,22 @@ - + Exe - net5.0 + net6.0 false true + false + true full + + + + + @@ -20,13 +27,10 @@ - - - + - \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Benchmarks/paket.references b/tests/FSharp.Data.GraphQL.Benchmarks/paket.references deleted file mode 100644 index c4462dfd6..000000000 --- a/tests/FSharp.Data.GraphQL.Benchmarks/paket.references +++ /dev/null @@ -1,3 +0,0 @@ -group TestsAndSamples -FSharp.Core -BenchmarkDotNet \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj index 76b64bfc3..74ec2ad50 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj @@ -1,14 +1,16 @@  + Exe - net5.0 - false + net6.0 + - - + + + @@ -18,8 +20,10 @@ - - - + + + + + diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs index aff06beb8..77310dc29 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs @@ -1,11 +1,11 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server open System +open System.Collections.Generic open System.Text open Newtonsoft.Json open Newtonsoft.Json.Linq open Newtonsoft.Json.Serialization -open System.Collections.Generic open FSharp.Data.GraphQL [] diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs index 99da1aeb2..dd841fbf1 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs @@ -1,17 +1,16 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server +open System.IO open System.Text -open Giraffe open Microsoft.AspNetCore.Http +open Microsoft.AspNetCore.WebUtilities +open Giraffe +open Giraffe.HttpStatusCodeHandlers.RequestErrors open Newtonsoft.Json +open Newtonsoft.Json.Linq open FSharp.Data.GraphQL.Execution -open System.IO open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types -open FSharp.Control.Tasks -open Newtonsoft.Json.Linq -open Giraffe.HttpStatusCodeHandlers.RequestErrors -open Microsoft.AspNetCore.WebUtilities open FSharp.Data.GraphQL.Ast type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult @@ -38,20 +37,20 @@ module HttpHandlers = |> Option.map (fun v -> v.Remove(0, v.IndexOf('=') + 1)) |> Option.map (fun v -> v.Trim('"')) + let rec parseVariables (schema : ISchema) (defs : VariableDefinition list) (variables : obj) = + let casted = + match variables with + | null -> Map.empty + | :? string as x when System.String.IsNullOrWhiteSpace(x) -> Map.empty + | :? Map as x -> x + | :? JToken as x -> x.ToObject>(jsonSerializer) + | :? string as x -> JsonConvert.DeserializeObject>(x, jsonSettings) + | _ -> failwithf "Failure deserializing variables. Unexpected variables object format." + Variables.read schema defs casted + let private graphQL (next : HttpFunc) (ctx : HttpContext) = task { let serialize d = JsonConvert.SerializeObject(d, jsonSettings) - let rec parseVariables (schema : ISchema) (defs : VariableDefinition list) (variables : obj) = - let casted = - match variables with - | null -> Map.empty - | :? string as x when System.String.IsNullOrWhiteSpace(x) -> Map.empty - | :? Map as x -> x - | :? JToken as x -> x.ToObject>(jsonSerializer) - | :? string as x -> JsonConvert.DeserializeObject>(x, jsonSettings) - | _ -> failwithf "Failure deserializing variables. Unexpected variables object format." - Variables.read schema defs casted - let json = function | Direct (data, _) -> diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs index a05d75bdc..17d9ac787 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs @@ -1,10 +1,10 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server open System -open Microsoft.AspNetCore.WebUtilities -open Newtonsoft.Json open System.Collections open System.Collections.Generic +open Microsoft.AspNetCore.WebUtilities +open Newtonsoft.Json open Newtonsoft.Json.Linq open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs index 8ffb2319a..44f8c6a93 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs @@ -1,8 +1,8 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server +open System.Text open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types -open System.Text type Root = { RequestId : string } diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs index 401872dc5..0a3fd6626 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs @@ -1,12 +1,12 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server +open System open Microsoft.AspNetCore.Builder +open Microsoft.AspNetCore.Server.Kestrel.Core open Microsoft.Extensions.Configuration open Microsoft.Extensions.DependencyInjection -open Giraffe open Microsoft.Extensions.Logging -open System -open Microsoft.AspNetCore.Server.Kestrel.Core +open Giraffe type Startup private () = new (configuration: IConfiguration) as this = diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/paket.references b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/paket.references deleted file mode 100644 index d0a0b4f1d..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/paket.references +++ /dev/null @@ -1,3 +0,0 @@ -group TestsAndSamples -FSharp.Core -Giraffe \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/FSharp.Data.GraphQL.IntegrationTests.fsproj b/tests/FSharp.Data.GraphQL.IntegrationTests/FSharp.Data.GraphQL.IntegrationTests.fsproj index 8be69600c..7b5272dc8 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/FSharp.Data.GraphQL.IntegrationTests.fsproj +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/FSharp.Data.GraphQL.IntegrationTests.fsproj @@ -1,31 +1,33 @@ - - + + Exe - net5.0 + net6.0 false true + - - - - - + + + + + - - - - PreserveNewest + + + + + + + - - - + ..\..\src\FSharp.Data.GraphQL.Client\bin\Debug\netstandard2.0\FSharp.Data.GraphQL.Client.dll @@ -38,5 +40,4 @@ ..\..\bin\FSharp.Data.GraphQL.Client\netstandard2.0\FSharp.Data.GraphQL.Shared.dll - \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/paket.references b/tests/FSharp.Data.GraphQL.IntegrationTests/paket.references deleted file mode 100644 index 8583b306d..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/paket.references +++ /dev/null @@ -1,7 +0,0 @@ -group TestsAndSamples -Microsoft.NET.Test.Sdk -xunit -xunit.runner.visualstudio -FSharp.Core -System.Net.Http -Microsoft.Extensions.Http \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/xunit.runner.json b/tests/FSharp.Data.GraphQL.IntegrationTests/xunit.runner.json index 0255865d5..933632af2 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/xunit.runner.json +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/xunit.runner.json @@ -1,3 +1,3 @@ -{ - "appDomain": "denied" +{ + "appDomain": "denied" } \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests.Sql/paket.references b/tests/FSharp.Data.GraphQL.Tests.Sql/paket.references deleted file mode 100644 index 8d3d89a67..000000000 --- a/tests/FSharp.Data.GraphQL.Tests.Sql/paket.references +++ /dev/null @@ -1,3 +0,0 @@ -group TestsAndSamples -FSharp.Core -FSharp.Data.TypeProviders \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/App.config b/tests/FSharp.Data.GraphQL.Tests/App.config deleted file mode 100644 index 529b8895c..000000000 --- a/tests/FSharp.Data.GraphQL.Tests/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/AssemblyInfo.fs b/tests/FSharp.Data.GraphQL.Tests/AssemblyInfo.fs deleted file mode 100644 index f8d430fad..000000000 --- a/tests/FSharp.Data.GraphQL.Tests/AssemblyInfo.fs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Fsharp.Data.GraphQL.Tests.AssemblyInfo - -open Xunit -open System.Reflection -open System.Runtime.InteropServices - -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] -[] - -do - () \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj b/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj index 35991e465..a1cffe8f4 100644 --- a/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj +++ b/tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj @@ -1,18 +1,29 @@ - - + + - net5.0 - false + net6.0 true false Exe + + FSharp implementation of Facebook GraphQL query language (Server) + true portable + + + + + + + + + + - @@ -45,20 +56,15 @@ + - - - - - + + + - - - - \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/Program.fs b/tests/FSharp.Data.GraphQL.Tests/Program.fs index 86d87381a..26fb8020b 100644 --- a/tests/FSharp.Data.GraphQL.Tests/Program.fs +++ b/tests/FSharp.Data.GraphQL.Tests/Program.fs @@ -16,3 +16,6 @@ module Program = finished.WaitOne() |> ignore finished.Dispose() !result + +[] +do() \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/paket.references b/tests/FSharp.Data.GraphQL.Tests/paket.references deleted file mode 100644 index 392e5df4c..000000000 --- a/tests/FSharp.Data.GraphQL.Tests/paket.references +++ /dev/null @@ -1,7 +0,0 @@ -group TestsAndSamples -FSharp.Core -Newtonsoft.Json -Microsoft.NET.Test.Sdk -xunit -xunit.runner.utility -xunit.runner.visualstudio \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.Tests/xunit.runner.json b/tests/FSharp.Data.GraphQL.Tests/xunit.runner.json index 0255865d5..933632af2 100644 --- a/tests/FSharp.Data.GraphQL.Tests/xunit.runner.json +++ b/tests/FSharp.Data.GraphQL.Tests/xunit.runner.json @@ -1,3 +1,3 @@ -{ - "appDomain": "denied" +{ + "appDomain": "denied" } \ No newline at end of file From 476c88c8f11687c48861422ffdc7235df9dd917d Mon Sep 17 00:00:00 2001 From: Juri Date: Sun, 4 Sep 2022 03:42:34 +0300 Subject: [PATCH 3/9] Fixed `error FS3511: This state machine is not statically compilable. A resumable code invocation at` See https://github.com/dotnet/fsharp/issues/12839 --- .../HttpHandlers.fs | 75 ++++++++++--------- .../MultipartRequest.fs | 14 ++-- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs index dd841fbf1..49cba5a6d 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs @@ -103,25 +103,27 @@ module HttpHandlers = ms match getMultipartRequestBoundary ctx.Request with | Some boundary -> - use ms = copyBodyToMemory(ctx.Request) - let reader = MultipartReader(boundary, ms) - let request = reader |> MultipartRequest.read |> Async.AwaitTask |> Async.RunSynchronously - let results = - request.Operations - |> List.map (fun op -> - let result = - match op.Variables with - | Some variables -> - let variables = parseVariables Schema.schema (parseVariableDefinitions op.Query) variables - Schema.executor.AsyncExecute(op.Query, variables = variables, data = root) - | None -> Schema.executor.AsyncExecute(op.Query, data = root) - result |> Async.RunSynchronously |> addRequestType "Multipart") - match results with - | [ result ] -> - return! okWithStr (json result) next ctx - | results -> - let result = JArray.FromObject(List.map json results).ToString() - return! okWithStr result next ctx + return! task { + use ms = copyBodyToMemory(ctx.Request) + let reader = MultipartReader(boundary, ms) + let! request = reader |> MultipartRequest.read ctx.RequestAborted + let results = + request.Operations + |> List.map (fun op -> + let result = + match op.Variables with + | Some variables -> + let variables = parseVariables Schema.schema (parseVariableDefinitions op.Query) variables + Schema.executor.AsyncExecute(op.Query, variables = variables, data = root) + | None -> Schema.executor.AsyncExecute(op.Query, data = root) + result |> Async.RunSynchronously |> addRequestType "Multipart") + match results with + | [ result ] -> + return! okWithStr (json result) next ctx + | results -> + let result = JArray.FromObject(List.map json results).ToString() + return! okWithStr result next ctx + } | None -> return! badRequest (text "Invalid multipart request header: missing boundary value.") next ctx else @@ -138,24 +140,23 @@ module HttpHandlers = | :? string as query -> Some (query, getVariables (parseVariableDefinitions query) data) | _ -> failwith "Failure deserializing repsonse. Could not read query - it is not stringified in request." else None) - match request with - | Some (query, Some variables) -> - printfn "Received query: %s" query - printfn "Received variables: %A" variables - let query = removeWhitespacesAndLineBreaks query - let result = Schema.executor.AsyncExecute(query, root, variables) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | Some (query, None) -> - printfn "Received query: %s" query - let query = removeWhitespacesAndLineBreaks query - let result = Schema.executor.AsyncExecute(query) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | None -> - let result = Schema.executor.AsyncExecute(Introspection.IntrospectionQuery) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx + let! result = task { + match request with + | Some (query, Some variables) -> + printfn "Received query: %s" query + printfn "Received variables: %A" variables + let query = removeWhitespacesAndLineBreaks query + return! Schema.executor.AsyncExecute(query, root, variables) + | Some (query, None) -> + printfn "Received query: %s" query + let query = removeWhitespacesAndLineBreaks query + return! Schema.executor.AsyncExecute(query) + | None -> + return! Schema.executor.AsyncExecute(Introspection.IntrospectionQuery) + } + let result = result |> addRequestType "Classic" + printfn "Result metadata: %A" result.Metadata + return! okWithStr (json result) next ctx } let webApp : HttpHandler = diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs index 17d9ac787..d67c2a3d6 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs @@ -119,12 +119,12 @@ module MultipartRequest = | operations -> operations |> List.mapi (fun ix operation -> mapOperation (Some ix) operation) /// Reads a GraphQL multipart request from a MultipartReader. - let read (reader : MultipartReader) = - async { + let read cancellationToken (reader : MultipartReader) = + task { let mutable section : GraphQLMultipartSection option = None let readNextSection () = - async { - let! next = reader.ReadNextSectionAsync() |> Async.AwaitTask + task { + let! next = reader.ReadNextSectionAsync cancellationToken section <- GraphQLMultipartSection.FromSection(next) } let mutable operations : string = null @@ -134,7 +134,7 @@ module MultipartRequest = while not section.IsNone do match section.Value with | Form section -> - let! value = section.GetValueAsync() |> Async.AwaitTask + let! value = section.GetValueAsync() match section.Name with | "operations" -> operations <- value @@ -145,7 +145,7 @@ module MultipartRequest = | _ -> failwithf "Error reading multipart request. Unexpected section name \"%s\"." section.Name | File section -> let stream = new System.IO.MemoryStream(4096) - do! section.FileStream.CopyToAsync(stream) |> Async.AwaitTask + do! section.FileStream.CopyToAsync(stream, cancellationToken) |> Async.AwaitTask stream.Position <- 0L let value = { Name = section.FileName; ContentType = section.Section.ContentType; Content = stream } files.Add(section.Name, value) @@ -156,4 +156,4 @@ module MultipartRequest = | :? JObject as op -> [ op.ToObject(jsonSerializer) ] | _ -> failwith "Unexpected operations value." return { Operations = parseOperations operations map files } - } |> Async.StartAsTask \ No newline at end of file + } \ No newline at end of file From 99c14790388c2b6d3dc6206501733453c97a1368 Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 16:53:41 +0200 Subject: [PATCH 4/9] Formatting --- FSharp.Data.GraphQL.sln | 31 - src/FSharp.Data.GraphQL.Shared/Ast.fs | 37 +- .../AstExtensions.fs | 298 ++++--- src/FSharp.Data.GraphQL.Shared/Parser.fs | 768 +++++++++++------- .../AstExtensionsTests.fs | 159 ++-- 5 files changed, 796 insertions(+), 497 deletions(-) diff --git a/FSharp.Data.GraphQL.sln b/FSharp.Data.GraphQL.sln index 7d7c0d599..e72d50225 100644 --- a/FSharp.Data.GraphQL.sln +++ b/FSharp.Data.GraphQL.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32811.315 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}" -EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Server", "src\FSharp.Data.GraphQL.Server\FSharp.Data.GraphQL.Server.fsproj", "{474179D3-0090-49E9-88F8-2971C0966077}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{BF60BC93-E09B-4E5F-9D85-95A519479D54}" @@ -14,20 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{BF60 RELEASE_NOTES.md = RELEASE_NOTES.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{83F16175-43B1-4C90-A1EE-8E351C33435D}" - ProjectSection(SolutionItems) = preProject - docs\tools\generate.fsx = docs\tools\generate.fsx - docs\tools\templates\template.cshtml = docs\tools\templates\template.cshtml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{8E6D5255-776D-4B61-85F9-73C37AA1FB9A}" - ProjectSection(SolutionItems) = preProject - docs\content\execution-pipeline.md = docs\content\execution-pipeline.md - docs\content\index.md = docs\content\index.md - docs\content\linq.md = docs\content\linq.md - docs\content\type-system.md = docs\content\type-system.md - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{ED8079DD-2B06-4030-9F0F-DC548F98E1C4}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Tests", "tests\FSharp.Data.GraphQL.Tests\FSharp.Data.GraphQL.Tests.fsproj", "{54AAFE43-FA5F-485A-AD40-0240165FC633}" @@ -91,17 +75,6 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Server. EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Benchmarks", "tests\FSharp.Data.GraphQL.Benchmarks\FSharp.Data.GraphQL.Benchmarks.fsproj", "{A6A162DF-9FBB-4C2A-913F-FD5FED35A09B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{56640EAF-82A6-4439-AD14-69F44A90DA99}" - ProjectSection(SolutionItems) = preProject - docs\files\introspection_query.graphql = docs\files\introspection_query.graphql - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{78D10788-601D-4D53-808B-36661911EDD1}" - ProjectSection(SolutionItems) = preProject - docs\files\img\logo-template.pdn = docs\files\img\logo-template.pdn - docs\files\img\logo.png = docs\files\img\logo.png - EndProjectSection -EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Client.DesignTime", "src\FSharp.Data.GraphQL.Client.DesignTime\FSharp.Data.GraphQL.Client.DesignTime.fsproj", "{B075CD55-CEA4-4C30-A088-48319AADF070}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Data.GraphQL.Client", "src\FSharp.Data.GraphQL.Client\FSharp.Data.GraphQL.Client.fsproj", "{F7858DA7-E067-486B-9E9C-697F0A56C620}" @@ -260,8 +233,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {474179D3-0090-49E9-88F8-2971C0966077} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} - {83F16175-43B1-4C90-A1EE-8E351C33435D} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1} - {8E6D5255-776D-4B61-85F9-73C37AA1FB9A} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1} {54AAFE43-FA5F-485A-AD40-0240165FC633} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4} {CAE5916B-1415-4982-B705-7318D77C029C} = {B0C25450-74BF-40C2-9E02-09AADBAE2C2F} {9B25360F-2CE4-43D2-AFF0-5EAA693E98F7} = {B0C25450-74BF-40C2-9E02-09AADBAE2C2F} @@ -271,8 +242,6 @@ Global {6768EA38-1335-4B8E-BC09-CCDED1F9AAF6} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} {8FB23F61-77CB-42C7-8EEC-B22D7C4E4067} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} {A6A162DF-9FBB-4C2A-913F-FD5FED35A09B} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4} - {56640EAF-82A6-4439-AD14-69F44A90DA99} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1} - {78D10788-601D-4D53-808B-36661911EDD1} = {56640EAF-82A6-4439-AD14-69F44A90DA99} {B075CD55-CEA4-4C30-A088-48319AADF070} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} {F7858DA7-E067-486B-9E9C-697F0A56C620} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} {F66BEE6C-0CB7-4F39-97E4-243F797E8723} = {B0C25450-74BF-40C2-9E02-09AADBAE2C2F} diff --git a/src/FSharp.Data.GraphQL.Shared/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index e94c124c4..64edb6246 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -19,6 +19,7 @@ type OperationType = | Mutation | Subscription + /// 2.9 Input Values type Value = /// 2.9.1 Int Value @@ -43,11 +44,7 @@ type Value = /// 2.6 Arguments /// Fields are conceptually functions which return values, and occasionally accept arguments which alter their behavior. These arguments often map directly to function arguments within a GraphQL server’s implementation. /// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Arguments -type Argument = - { - Name: string - Value: Value - } +type Argument = { Name: string; Value: Value } /// 2.2.10 Directives type Directive = @@ -108,10 +105,12 @@ type InputType = | ListType of InputType | NonNullType of InputType override x.ToString() = - let rec str = function + let rec str = + function | NamedType name -> name | ListType inner -> "[" + (str inner) + "]" | NonNullType inner -> (str inner) + "!" + str x @@ -148,13 +147,13 @@ type InputDefinition = string type InputFieldDefinition = { /// Name of the input field / argument. - Name : string + Name: string /// Optional input field / argument description. - Description : string option + Description: string option /// GraphQL type definition of the input type. - TypeDef : InputDefinition + TypeDef: InputDefinition /// Optional default input value - used when no input was provided. - DefaultValue : Value option + DefaultValue: Value option } /// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Operations @@ -292,7 +291,7 @@ type EnumTypeDefinition = Description: string option Name: string Directives: Directive list - Values: EnumValueDefinition[] + Values: EnumValueDefinition [] } /// Enum type extensions are used to represent an enum type which has been extended from some original enum type. For example, this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another GraphQL service. @@ -301,7 +300,7 @@ type EnumTypeExtension = { Name: string Directives: Directive list - Values: EnumValueDefinition[] + Values: EnumValueDefinition [] } @@ -364,16 +363,17 @@ type DirectiveLocation = type DirectiveDefinition = { /// Directive's name - it's NOT '@' prefixed. - Name : string + Name: string /// Optional directive description. - Description : string option + Description: string option /// Directive location - describes, which part's of the query AST /// are valid places to include current directive to. - Locations : DirectiveLocation + Locations: DirectiveLocation /// Array of arguments defined within that directive. - Args : InputFieldDefinition [] + Args: InputFieldDefinition [] } + type TypeSystemDefinition = | SchemaDefinition of SchemaDefinition | TypeDefinition of TypeDefinition @@ -440,7 +440,4 @@ type Definition = /// GraphQL services which only seek to provide GraphQL query execution may choose to only include ExecutableDefinition and omit the TypeSystemDefinition and TypeSystemExtension rules from Definition. /// /// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Document -type Document = - { - Definitions: Definition list - } \ No newline at end of file +type Document = { Definitions: Definition list } diff --git a/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs b/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs index 9f05edd27..0981660b2 100644 --- a/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs +++ b/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs @@ -10,54 +10,88 @@ type Path = string list type OperationName = string type AstTypeFieldInfo = - { Name : string - Alias : string option - Fields : AstFieldInfo list } + { + Name: string + Alias: string option + Fields: AstFieldInfo list + } member x.AliasOrName = x.Alias |> Option.defaultValue x.Name and AstFragmentFieldInfo = - { Name : string - Alias : string option - TypeCondition : string - Fields : AstFieldInfo list } + { + Name: string + Alias: string option + TypeCondition: string + Fields: AstFieldInfo list + } member x.AliasOrName = x.Alias |> Option.defaultValue x.Name and internal AstSelectionInfo = - { TypeCondition : string option - Name : string - Alias : string option - Path : Path - mutable Fields : AstSelectionInfo list } + { + TypeCondition: string option + Name: string + Alias: string option + Path: Path + mutable Fields: AstSelectionInfo list + } member x.AliasOrName = x.Alias |> Option.defaultValue x.Name - static member Create(typeCondition : string option, path : Path, name : string, alias : string option, ?fields : AstSelectionInfo list) = - { TypeCondition = typeCondition - Name = name - Alias = alias - Path = path - Fields = fields |> Option.defaultValue [] } - member x.SetFields(fields : AstSelectionInfo list) = - x.Fields <- fields + + static member Create + ( + typeCondition: string option, + path: Path, + name: string, + alias: string option, + ?fields: AstSelectionInfo list + ) = + { + TypeCondition = typeCondition + Name = name + Alias = alias + Path = path + Fields = fields |> Option.defaultValue [] + } + + member x.SetFields(fields: AstSelectionInfo list) = x.Fields <- fields and AstFieldInfo = | TypeField of AstTypeFieldInfo | FragmentField of AstFragmentFieldInfo - static member internal Create(info : AstSelectionInfo) = + static member internal Create(info: AstSelectionInfo) = let fields = List.map AstFieldInfo.Create info.Fields + match info.TypeCondition with - | Some typeCondition -> FragmentField { Name = info.Name; Alias = info.Alias; TypeCondition = typeCondition; Fields = fields } - | None -> TypeField { Name = info.Name; Alias = info.Alias; Fields = fields } + | Some typeCondition -> + FragmentField + { + Name = info.Name + Alias = info.Alias + TypeCondition = typeCondition + Fields = fields + } + | None -> + TypeField + { + Name = info.Name + Alias = info.Alias + Fields = fields + } + member x.Name = match x with | TypeField info -> info.Name | FragmentField info -> info.Name + member x.Alias = match x with | TypeField info -> info.Alias | FragmentField info -> info.Alias + member x.AliasOrName = match x with | TypeField info -> info.Alias |> Option.defaultValue info.Name | FragmentField info -> info.Alias |> Option.defaultValue info.Name + member x.Fields = match x with | TypeField info -> info.Fields @@ -68,8 +102,11 @@ type internal PaddedStringBuilder() = let mutable padCount = 0 member __.Pad() = padCount <- padCount + 2 member __.Unpad() = padCount <- padCount - 2 - member __.AppendLine() = sb.AppendLine().Append("".PadLeft(padCount, ' ')) |> ignore - member __.Append(str : string) = sb.Append(str) |> ignore + + member __.AppendLine() = + sb.AppendLine().Append("".PadLeft(padCount, ' ')) |> ignore + + member __.Append(str: string) = sb.Append(str) |> ignore override __.ToString() = sb.ToString() /// Specify options when printing an Ast.Document to a query string. @@ -85,26 +122,33 @@ type Document with /// Generates a GraphQL query string from this document. /// /// Specify custom printing options for the query string. - member x.ToQueryString(?options : QueryStringPrintingOptions) = + member x.ToQueryString(?options: QueryStringPrintingOptions) = let options = defaultArg options QueryStringPrintingOptions.None + let sb = PaddedStringBuilder() - let withQuotes (s : string) = "\"" + s + "\"" + let withQuotes (s: string) = "\"" + s + "\"" let rec printValue x = let printObjectValue (name, value) = sb.Append(name) sb.Append(": ") printValue value + let printOne f n ov = match n with | 0 -> f ov - | _ -> sb.Append(", "); f ov + | _ -> + sb.Append(", ") + f ov + let printCompound braces f xs = match xs with | [] -> sb.Append(braces) - | xs -> sb.Append(braces.Substring(0, 1) + " ") - List.iteri (printOne f) xs - sb.Append(" " + braces.Substring(1, 1)) + | xs -> + sb.Append(braces.Substring(0, 1) + " ") + List.iteri (printOne f) xs + sb.Append(" " + braces.Substring(1, 1)) + match x with | IntValue x -> sb.Append(x.ToString(CultureInfo.InvariantCulture)) | FloatValue x -> sb.Append(x.ToString(CultureInfo.InvariantCulture)) @@ -116,103 +160,160 @@ type Document with | ObjectValue x -> printCompound "{}" printObjectValue (Map.toList x) | Variable x -> sb.Append("$" + x) - let printVariables (vdefs : VariableDefinition list) = - let printVariable (vdef : VariableDefinition) = + let printVariables (vdefs: VariableDefinition list) = + let printVariable (vdef: VariableDefinition) = sb.Append("$") sb.Append(vdef.VariableName) sb.Append(": ") sb.Append(vdef.Type.ToString()) - vdef.DefaultValue |> Option.iter (fun value -> sb.Append(" = "); printValue value) + + vdef.DefaultValue + |> Option.iter (fun value -> + sb.Append(" = ") + printValue value) + if vdefs.Length > 0 then sb.Append("(") + let rec helper vdefs = match vdefs with | [] -> () - | [vdef] -> printVariable vdef; sb.Append(") ") - | vdef :: tail -> printVariable vdef; sb.Append(", "); helper tail + | [ vdef ] -> + printVariable vdef + sb.Append(") ") + | vdef :: tail -> + printVariable vdef + sb.Append(", ") + helper tail + helper vdefs - let printArguments (arguments : Argument list) = - let printArgument (arg : Argument) = + let printArguments (arguments: Argument list) = + let printArgument (arg: Argument) = sb.Append(arg.Name + ": ") printValue arg.Value + if arguments.Length > 0 then sb.Append("(") + let rec helper args = match args with | [] -> () - | [arg] -> printArgument arg; sb.Append(")") - | arg :: tail -> printArgument arg; sb.Append(", "); helper tail + | [ arg ] -> + printArgument arg + sb.Append(")") + | arg :: tail -> + printArgument arg + sb.Append(", ") + helper tail + helper arguments - let printDirectives (directives : Directive list) = - let printDirective (directive : Directive) = + let printDirectives (directives: Directive list) = + let printDirective (directive: Directive) = sb.Append("@" + directive.Name) printArguments directive.Arguments + let rec helper directives = match directives with | [] -> () - | [directive] -> printDirective directive - | directive :: tail -> printDirective directive; sb.Append(" "); helper tail + | [ directive ] -> printDirective directive + | directive :: tail -> + printDirective directive + sb.Append(" ") + helper tail + helper directives - let setSelectionSetOptions (selectionSet : Selection list) = + let setSelectionSetOptions (selectionSet: Selection list) = let typeNameMetaField = - { Alias = None - Name = "__typename" - Arguments = [] - Directives = [] - SelectionSet = [] } + { + Alias = None + Name = "__typename" + Arguments = [] + Directives = [] + SelectionSet = [] + } + let shouldIncludeTypeName = options.HasFlag(QueryStringPrintingOptions.IncludeTypeNames) - let hasTypeName = selectionSet |> List.exists (function Field f -> f.Name = "__typename" | _ -> false) - if selectionSet.Length > 0 && shouldIncludeTypeName && not (hasTypeName) - then selectionSet @ [Field typeNameMetaField] - else selectionSet - let rec printSelectionSet (selectionSet : Selection list) = - let printSelection = function + let hasTypeName = + selectionSet + |> List.exists (function + | Field f -> f.Name = "__typename" + | _ -> false) + + if selectionSet.Length > 0 && shouldIncludeTypeName && not hasTypeName then + selectionSet @ [ Field typeNameMetaField ] + + else + selectionSet + + let rec printSelectionSet (selectionSet: Selection list) = + let printSelection = + function | Field field -> field.Alias |> Option.iter (fun alias -> sb.Append(alias + ": ")) + sb.Append(field.Name) printArguments field.Arguments + if field.Directives.Length > 0 then sb.Append(" ") + printDirectives field.Directives + if field.SelectionSet.Length > 0 then sb.Append(" ") + printSelectionSet (setSelectionSetOptions field.SelectionSet) | FragmentSpread frag -> sb.Append("..." + frag.Name) + if frag.Directives.Length > 0 then sb.Append(" ") + printDirectives frag.Directives | InlineFragment frag -> sb.Append("... ") + frag.TypeCondition |> Option.iter (fun t -> sb.Append("on " + t)) + printDirectives frag.Directives sb.Append(" ") printSelectionSet (setSelectionSetOptions frag.SelectionSet) - if selectionSet.Length > 0 then sb.Append("{"); sb.Pad() + + if selectionSet.Length > 0 then + sb.Append("{") + sb.Pad() + let rec helper selectionSet = match selectionSet with | [] -> () - | [selection] -> sb.AppendLine(); printSelection selection; sb.Unpad(); sb.AppendLine(); sb.Append("}") - | selection :: tail -> sb.AppendLine(); printSelection selection; helper tail + | [ selection ] -> + sb.AppendLine() + printSelection selection + sb.Unpad() + sb.AppendLine() + sb.Append("}") + | selection :: tail -> + sb.AppendLine() + printSelection selection + helper tail + helper selectionSet - let rec printDefinitions (definitions : Definition list) = - let printDefinition = function - // TODO: type system (def | ext) - //| TypeSystemDefinition _ -> - // () - //| TypeSystemExtension _ -> - // () + let rec printDefinitions (definitions: Definition list) = + let printDefinition = + function + | TypeSystemDefinition _ -> () // TODO: unit tests for printing https://spec.graphql.org/October2021/#TypeSystemDefinitionOrExtension + | TypeSystemExtension _ -> () // TODO: unit tests for printing https://spec.graphql.org/October2021/#TypeSystemDefinitionOrExtension | OperationDefinition odef -> match odef.OperationType with | Query when odef.IsShortHandQuery -> () | Query -> sb.Append("query ") | Mutation -> sb.Append("mutation ") | Subscription -> sb.Append("subscription ") + odef.Name |> Option.iter (fun name -> - if odef.VariableDefinitions.Length = 0 - then sb.Append(name + " ") - else sb.Append(name)) + if odef.VariableDefinitions.Length = 0 then sb.Append(name + " ") else sb.Append(name)) + printVariables odef.VariableDefinitions printDirectives odef.Directives printSelectionSet (setSelectionSetOptions odef.SelectionSet) @@ -220,12 +321,20 @@ type Document with sb.Append("fragment " + fdef.Name.Value + " ") sb.Append("on " + fdef.TypeCondition.Value + " ") printDirectives fdef.Directives + if fdef.Directives.Length > 0 then sb.Append(" ") + printSelectionSet (setSelectionSetOptions fdef.SelectionSet) + match definitions with - | [] -> () - | [def] -> printDefinition def - | def :: tail -> printDefinition def; sb.AppendLine(); sb.AppendLine(); printDefinitions tail + | [] -> () + | [ def ] -> printDefinition def + | def :: tail -> + printDefinition def + sb.AppendLine() + sb.AppendLine() + printDefinitions tail + printDefinitions x.Definitions sb.ToString() @@ -236,32 +345,36 @@ type Document with let fragments = this.Definitions |> List.choose (function - // TODO: type system (def | ext) - //| TypeSystemDefinition _ -> - // () - //| TypeSystemExtension _ -> - // () + | TypeSystemDefinition _ + | TypeSystemExtension _ | OperationDefinition _ -> None | FragmentDefinition def -> Some def) |> List.map (fun def -> def.Name.Value, def) |> Map.ofList + let findFragment name = match Map.tryFind name fragments with | Some fdef -> fdef - | None -> failwith $"Can not get information about fragment \"%s{name}\". Fragment spread definition was not found in the query." + | None -> + failwith + $"Can not get information about fragment \"%s{name}\". Fragment spread definition was not found in the query." + let operations = this.Definitions |> List.choose (function - // TODO: type system (def | ext) - //| TypeSystemDefinition _ -> - // () - //| TypeSystemExtension _ -> - // () + | TypeSystemDefinition _ -> None + | TypeSystemExtension _ -> None | FragmentDefinition _ -> None | OperationDefinition def -> Some def) |> List.map (fun operation -> operation.Name, operation) - let mapper (selectionSet : Selection list) = - let rec helper (acc : AstSelectionInfo list) (typeCondition : string option) (path : string list) (selectionSet : Selection list) = + + let mapper (selectionSet: Selection list) = + let rec helper + (acc: AstSelectionInfo list) + (typeCondition: string option) + (path: string list) + (selectionSet: Selection list) + = match selectionSet with | [] -> acc | selection :: tail -> @@ -269,17 +382,18 @@ type Document with match selection with | Field f -> let finfo = AstSelectionInfo.Create(typeCondition, path, f.Name, f.Alias) + let fields = helper [] None (f.AliasOrName :: path) f.SelectionSet + finfo.SetFields(fields) finfo :: acc | FragmentSpread f -> let fdef = findFragment f.Name helper acc fdef.TypeCondition path fdef.SelectionSet - | InlineFragment fdef -> - helper acc fdef.TypeCondition path fdef.SelectionSet + | InlineFragment fdef -> helper acc fdef.TypeCondition path fdef.SelectionSet + helper acc typeCondition path tail - helper [] None [] selectionSet - |> List.map AstFieldInfo.Create - operations - |> List.map (fun (n, o) -> n, mapper o.SelectionSet) - |> Map.ofList + + helper [] None [] selectionSet |> List.map AstFieldInfo.Create + + operations |> List.map (fun (n, o) -> n, mapper o.SelectionSet) |> Map.ofList diff --git a/src/FSharp.Data.GraphQL.Shared/Parser.fs b/src/FSharp.Data.GraphQL.Shared/Parser.fs index bd3aa6df7..b1af59bc3 100644 --- a/src/FSharp.Data.GraphQL.Shared/Parser.fs +++ b/src/FSharp.Data.GraphQL.Shared/Parser.fs @@ -4,6 +4,8 @@ module FSharp.Data.GraphQL.Parser +#nowarn "3370" // https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1111-refcell-op-information-messages.md + open FSharp.Data.GraphQL.Ast open System open FParsec @@ -11,311 +13,469 @@ open FParsec [] module internal Internal = - // 2.1.7 Ignored tokens - let ignored = - // 2.1.2 WhiteSpace - // Horizontal Tab (U+0009) | Space (U+0020) - let whiteSpace = skipAnyOf [|'\u0009'; '\u000B'; '\u000C'; '\u0020'; '\u00A0'|] - - // 2.1.3 LineTerminator - // New Line (U+000A) - // Carriage Return (U+000D)New Line (U+000A) | (U+000D)New Line (U+000A) - let lineTerminators = skipAnyOf [|'\u000A'; '\u000D'; '\u2028'; '\u2029'|] - - // 2.1.4 CommentChar - // SourceCharacter but not LineTerminator - let comments = pchar '#' >>. skipManyTill anyChar (lineTerminators <|> eof) - - // 2.1.5 Comma (Insignificant) - let comma = skipChar ',' - - whiteSpace <|> lineTerminators <|> comments <|> comma "Ignored" - - // ignore zero or more whitespaces - let whitespaces = many ignored |>> ignore - - - // lexical token are indivisible terminal symbols - let token p = p .>> notFollowedBy (letter <|> digit <|> pchar '_') - let stoken = pstring >> token - let token_ws p = token p .>> whitespaces - let stoken_ws = pstring >> token_ws - let ctoken_ws = pchar >> token_ws - - // helpers to format parser results - let someOrEmpty = function | Some lst -> lst | None -> [] - let charsToString = Array.ofList >> String - - // parse one items between two char punctuators - let betweenChars left right p = - (pchar left .>> whitespaces) >>. p .>> (whitespaces .>> pchar right) - - // parse one or more items ignoring inconsequential whitespace - let betweenCharsMany1 left right p = - between (pchar left .>> whitespaces) (pchar right) (sepEndBy1 p whitespaces) - - // parse zero or more items ignoring inconsequential whitespace - let betweenCharsMany left right p = - between (pchar left .>> whitespaces) (pchar right) (sepEndBy p whitespaces) - - // run the key, seperator, then value parsers while ignoring inconsequential whitespaces - let pairBetween seperator key value = - (key .>> whitespaces) .>>. ((pchar seperator .>> whitespaces) >>. value) - - - // 2.9 Input Values - // forward ref since value is recursive in the case of lists and objects - let inputValue, inputValueRef = createParserForwardedToRef() - - - // 2.1.9 Names (ASCII only) - // /[_A-Za-z][_0-9A-Za-z]*/ - let name = - let isIdentifierFirstChar c = isAsciiLetter c || c = '_' - let isIdentifierChar c = isAsciiLetter c || isDigit c || c = '_' - many1Satisfy2 isIdentifierFirstChar isIdentifierChar - - // 2.9.4 StringValue - let stringValue = - let escapedCharacter = - let escaped = anyOf [| '"'; '\\'; '/'; 'b'; 'f'; 'n'; 'r'; 't' |] - |>> function | 'b' -> '\b' | 'f' -> '\u000C' | 'n' -> '\n' - | 'r' -> '\r' | 't' -> '\t' | c -> c - let unicode = - pchar 'u' >>. pipe4 hex hex hex hex (fun h3 h2 h1 h0 -> - let hex2int c = (int c &&& 15) + (int c >>> 6)*9 - (hex2int h3)*4096 + (hex2int h2)*256 + (hex2int h1)*16 + hex2int h0 |> char) - pchar '\\' >>. (escaped <|> unicode) - - let normalCharacter = noneOf [|'\u000A';'\u000D';'\u2028';'\u2029';'"';'\''|] - let quote = pchar '"' - - between quote quote (manyChars (escapedCharacter <|> normalCharacter)) - - - // 2.9.3 Boolean value - // one of true false - let booleanValue = - choice [ stoken "true" >>% true - stoken "false" >>% false ] - - - // Integer Part - // (NegativeSign opt) 0 - // (NegativeSign opt) NonZeroDigit (Digit list opt) - let integerPart = - let negativeSign = pchar '-' - let nonZeroDigit = anyOf [|'1';'2';'3';'4';'5';'6';'7';'8';'9'|] - let zero = pchar '0' - let zeroInteger = opt negativeSign >>. zero >>% "0" - let nonZeroInteger = opt negativeSign .>>. (many1Chars2 nonZeroDigit digit) - |>> function | (Some _, v) -> "-" + v - | (None, v) -> v - (attempt zeroInteger) <|> nonZeroInteger - - - // 2.9.1 IntValue - // IntegerPart - let integerValue = integerPart |>> int64 - - - // 2.9.2 FloatValue - // IntegerPart FractionalPart - // IntegerPart ExponentPart - // IntegerPart FractionalPart ExponentPart - let floatValue = - let exponentPart = - let sign = pchar '+' <|> pchar '-' - let exponentIndicator = pchar 'e' <|> pchar 'E' - - pipe3 exponentIndicator (opt sign) (many1 digit) - (fun exp sign digits -> - charsToString (match sign with - | Some sign -> exp::sign::digits - | None -> exp::digits )) - - let fractionPart = - pchar '.' .>>. (many1 digit) - |>> (fun (dot, digits) -> charsToString(dot::digits)) - - choice [ - integerPart .>>. (exponentPart <|> fractionPart) |>> fun(p1, p2)-> p1 + p2 - pipe3 integerPart fractionPart exponentPart - (fun integer fraction exponent -> integer + fraction + exponent ) ] |>> float - - // 2.9.5 Null Value - let nullValue = stoken "null" >>% NullValue - - // 2.9.6 Enum Value - // Name but not true or false or null - // (boolean parser is run first) - let enumValue = name - - - // 2.10 Variable - // $ Name - let variable = pchar '$' >>. name - - - // 2.9.8 Input Object Values - let inputObject = - betweenCharsMany '{' '}' (pairBetween ':' name inputValue "ObjectField") - |>> Map.ofList - - // 2.9.7 List Value - let listValue = - betweenCharsMany '[' ']' (token_ws inputValue "Value") - - - // 2.9 Value - // Variable|IntValue|FloatValue|StringValue| - // BooleanValue|NullValue|EnumValue|ListValue|ObjectValue - inputValueRef := - choice [ variable |>> Variable "Variable" - (attempt floatValue) |>> FloatValue "Float" - integerValue |>> IntValue "Integer" - stringValue |>> StringValue "String" - (attempt booleanValue) |>> BooleanValue "Boolean" - nullValue - enumValue |>> EnumValue "Enum" - inputObject |>> ObjectValue "InputObject" - listValue |>> ListValue "ListValue" ] - - - // 2.6 Arguments - // Argument list - let arguments = - // Argument - // Name:Value - let argument = - pairBetween ':' name inputValue - |>> fun (name, value) -> { Argument.Name = name; Value = value } - "Argument" - betweenCharsMany '(' ')' argument "Arguments" - - - // 2.1.2 Directives - // Directive list - let directives = - // Directive - // @ Name (Arguments opt) - let directive = - pchar '@' >>. (name .>> whitespaces) .>>. (opt arguments) - |>> fun (name, args) -> { Directive.Name = name; Arguments = someOrEmpty args} - "Directive" - sepEndBy directive whitespaces "Directives" - - - // 2.11 Type - // |NamedType|ListType|NonNullType - let inputType, inputTypeRef = createParserForwardedToRef() - let namedType = name |>> NamedType "NamedType" - let listType = - betweenChars '[' ']' inputType - |>> ListType "ListType" - let nonNullType = - (listType <|> namedType) .>> pchar '!' - |>> NonNullType "NonNullType" - inputTypeRef := - choice [ attempt nonNullType - namedType - listType ] - - - // 2.4 Selection Sets - // Selection list - let selection, selectionRef = createParserForwardedToRef() - let selectionSet, selectionSetRef = createParserForwardedToRef() - - - // 2.5 Fields - // (Alias opt) Name (Arguments opt) (Directives opt) (SelectionSet opt) - let field = - let alias = token_ws name .>> pchar ':' .>> whitespaces - pipe5 (opt(attempt alias)) (token_ws name) (opt(token_ws arguments)) (opt directives) (opt selectionSet) - (fun oalias name oargs directives oselection -> - (Field { Alias = oalias; Name = name; Arguments = someOrEmpty oargs; - Directives = someOrEmpty directives; SelectionSet = match oselection with None -> [] | Some s -> s })) - "Field" - - - // 2.8 Fragments - // FragmentSpread|FragmentDefinition|FragmentName - let selectionFragment = - let inlineFragment = - pipe3 (opt(stoken_ws "on" >>. token_ws name)) (opt(token_ws directives)) selectionSet - (fun typeCondition directives selectionSet -> - { FragmentDefinition.Name = None - Directives = someOrEmpty directives - SelectionSet = selectionSet - TypeCondition = typeCondition }) - |>> InlineFragment "InlineFragment" - - let fragmentSpread = - token_ws name .>>. opt directives - |>> fun (name, directives) -> { FragmentSpread.Name = name; Directives = someOrEmpty directives } - |>> FragmentSpread "FragmentSpread" - - pstring "..." .>> whitespaces >>. (inlineFragment <|> fragmentSpread) "Fragment" - - selectionRef := - field <|> selectionFragment "Selection" - - selectionSetRef := - betweenCharsMany1 '{' '}' selection "SelectionSet" - - - // 2.3 Operations - // OperationDefinition List - let definitions = - let operationType = - (stoken_ws "query" >>% Query) <|> (stoken_ws "mutation" >>% Mutation) <|> (stoken_ws "subscription" >>% Subscription) - - let operationDefinition = - let variableDefinition = - pipe2 (pairBetween ':' variable inputType) (whitespaces >>. opt((ctoken_ws '=') >>. inputValue)) - (fun (variableName, variableType) defaultVal -> - { VariableName = variableName; Type = variableType - DefaultValue = defaultVal }) - let variableDefinitions = - betweenCharsMany '(' ')' variableDefinition - - pipe5 operationType (opt(token_ws name)) (opt(token_ws variableDefinitions)) (opt(token_ws directives)) (token_ws selectionSet) - (fun otype name ovars directives selection -> - { OperationType = otype; Name = name; SelectionSet = selection - VariableDefinitions = someOrEmpty ovars; Directives = someOrEmpty directives }) - - // Short hand format where operation defaults to a Query - let shortHandQueryDefinition = - selectionSet |>> (fun selectionSet -> - { OperationType = Query; SelectionSet = selectionSet; - VariableDefinitions = []; Directives = []; Name = None }) - - // SelectionSet | - // (OperationType (Name opt) (VariableDefinitions opt) (Directives opt) SelectionSet) - let operationDefinition = - shortHandQueryDefinition <|> operationDefinition |>> OperationDefinition - - let fragmentDefinition = - pipe4 ((stoken_ws "fragment") >>. token_ws name .>> (stoken_ws "on")) (token_ws name) directives selectionSet - (fun name typeCond directives selSet -> - FragmentDefinition - { Name = Some name; Directives = directives; SelectionSet = selSet - TypeCondition = if String.IsNullOrEmpty typeCond then None else Some typeCond }) - - // GraphQL documents can contain zero definitions - sepEndBy (operationDefinition <|> fragmentDefinition) whitespaces - - - // 2.2 Document - // DefinitionList - let documents = - whitespaces >>. definitions .>> (skipMany ignored <|> eof) - |>> (fun definitions -> { Document.Definitions = definitions }) + // 2.1.7 Ignored tokens + let ignored = + // 2.1.2 WhiteSpace + // Horizontal Tab (U+0009) | Space (U+0020) + let whiteSpace = + skipAnyOf [| '\u0009' + '\u000B' + '\u000C' + '\u0020' + '\u00A0' |] + + // 2.1.3 LineTerminator + // New Line (U+000A) + // Carriage Return (U+000D)New Line (U+000A) | (U+000D)New Line (U+000A) + let lineTerminators = + skipAnyOf [| '\u000A' + '\u000D' + '\u2028' + '\u2029' |] + + // 2.1.4 CommentChar + // SourceCharacter but not LineTerminator + let comments = + pchar '#' + >>. skipManyTill anyChar (lineTerminators <|> eof) + + // 2.1.5 Comma (Insignificant) + let comma = skipChar ',' + + whiteSpace + <|> lineTerminators + <|> comments + <|> comma + "Ignored" + + // ignore zero or more whitespaces + let whitespaces = many ignored |>> ignore + + + // lexical token are indivisible terminal symbols + let token p = + p + .>> notFollowedBy (letter <|> digit <|> pchar '_') + + let stoken = pstring >> token + let token_ws p = token p .>> whitespaces + let stoken_ws = pstring >> token_ws + let ctoken_ws = pchar >> token_ws + + // helpers to format parser results + let someOrEmpty = + function + | Some lst -> lst + | None -> [] + + let charsToString = Array.ofList >> String + + // parse one items between two char punctuators + let betweenChars left right p = + (pchar left .>> whitespaces) >>. p + .>> (whitespaces .>> pchar right) + + // parse one or more items ignoring inconsequential whitespace + let betweenCharsMany1 left right p = + between (pchar left .>> whitespaces) (pchar right) (sepEndBy1 p whitespaces) + + // parse zero or more items ignoring inconsequential whitespace + let betweenCharsMany left right p = + between (pchar left .>> whitespaces) (pchar right) (sepEndBy p whitespaces) + + // run the key, seperator, then value parsers while ignoring inconsequential whitespaces + let pairBetween seperator key value = + (key .>> whitespaces) + .>>. ((pchar seperator .>> whitespaces) >>. value) + + + // 2.9 Input Values + // forward ref since value is recursive in the case of lists and objects + let inputValue, inputValueRef = + createParserForwardedToRef () + + + // 2.1.9 Names (ASCII only) + // /[_A-Za-z][_0-9A-Za-z]*/ + let name = + let isIdentifierFirstChar c = isAsciiLetter c || c = '_' + let isIdentifierChar c = isAsciiLetter c || isDigit c || c = '_' + many1Satisfy2 isIdentifierFirstChar isIdentifierChar + + // 2.9.4 StringValue + let stringValue = + let escapedCharacter = + let escaped = + anyOf [| '"' + '\\' + '/' + 'b' + 'f' + 'n' + 'r' + 't' |] + |>> function + | 'b' -> '\b' + | 'f' -> '\u000C' + | 'n' -> '\n' + | 'r' -> '\r' + | 't' -> '\t' + | c -> c + + let unicode = + pchar 'u' + >>. pipe4 hex hex hex hex (fun h3 h2 h1 h0 -> + let hex2int c = (int c &&& 15) + (int c >>> 6) * 9 + + (hex2int h3) * 4096 + + (hex2int h2) * 256 + + (hex2int h1) * 16 + + hex2int h0 + |> char) + + pchar '\\' >>. (escaped <|> unicode) + + let normalCharacter = + noneOf [| '\u000A' + '\u000D' + '\u2028' + '\u2029' + '"' + '\'' |] + + let quote = pchar '"' + + between quote quote (manyChars (escapedCharacter <|> normalCharacter)) + + + // 2.9.3 Boolean value + // one of true false + let booleanValue = + choice [ stoken "true" >>% true + stoken "false" >>% false ] + + + // Integer Part + // (NegativeSign opt) 0 + // (NegativeSign opt) NonZeroDigit (Digit list opt) + let integerPart = + let negativeSign = pchar '-' + + let nonZeroDigit = + anyOf [| '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' |] + + let zero = pchar '0' + + let zeroInteger = + opt negativeSign >>. zero >>% "0" + + let nonZeroInteger = + opt negativeSign + .>>. (many1Chars2 nonZeroDigit digit) + |>> function + | Some _, v -> "-" + v + | None, v -> v + + (attempt zeroInteger) <|> nonZeroInteger + + + // 2.9.1 IntValue + // IntegerPart + let integerValue = integerPart |>> int64 + + + // 2.9.2 FloatValue + // IntegerPart FractionalPart + // IntegerPart ExponentPart + // IntegerPart FractionalPart ExponentPart + let floatValue = + let exponentPart = + let sign = pchar '+' <|> pchar '-' + + let exponentIndicator = + pchar 'e' <|> pchar 'E' + + pipe3 exponentIndicator (opt sign) (many1 digit) (fun exp sign digits -> + charsToString ( + match sign with + | Some sign -> exp :: sign :: digits + | None -> exp :: digits + )) + + let fractionPart = + pchar '.' .>>. (many1 digit) + |>> (fun (dot, digits) -> charsToString (dot :: digits)) + + choice [ integerPart .>>. (exponentPart <|> fractionPart) + |>> fun (p1, p2) -> p1 + p2 + pipe3 integerPart fractionPart exponentPart (fun integer fraction exponent -> integer + fraction + exponent) ] + |>> float + + // 2.9.5 Null Value + let nullValue = stoken "null" >>% NullValue + + // 2.9.6 Enum Value + // Name but not true or false or null + // (boolean parser is run first) + let enumValue = name + + + // 2.10 Variable + // $ Name + let variable = pchar '$' >>. name + + + // 2.9.8 Input Object Values + let inputObject = + betweenCharsMany '{' '}' (pairBetween ':' name inputValue "ObjectField") + |>> Map.ofList + + // 2.9.7 List Value + let listValue = + betweenCharsMany '[' ']' (token_ws inputValue "Value") + + + // 2.9 Value + // Variable|IntValue|FloatValue|StringValue| + // BooleanValue|NullValue|EnumValue|ListValue|ObjectValue + inputValueRef + := choice [ variable |>> Variable "Variable" + (attempt floatValue) |>> FloatValue "Float" + integerValue |>> IntValue "Integer" + stringValue |>> StringValue "String" + (attempt booleanValue) |>> BooleanValue + "Boolean" + nullValue + enumValue |>> EnumValue "Enum" + inputObject |>> ObjectValue "InputObject" + listValue |>> ListValue "ListValue" ] + + + // 2.6 Arguments + // Argument list + let arguments = + // Argument + // Name:Value + let argument = + pairBetween ':' name inputValue + |>> fun (name, value) -> { Argument.Name = name; Value = value } + "Argument" + + betweenCharsMany '(' ')' argument "Arguments" + + + // 2.1.2 Directives + // Directive list + let directives = + // Directive + // @ Name (Arguments opt) + let directive = + pchar '@' >>. (name .>> whitespaces) + .>>. (opt arguments) + |>> fun (name, args) -> + { + Directive.Name = name + Arguments = someOrEmpty args + } + "Directive" + + sepEndBy directive whitespaces "Directives" + + + // 2.11 Type + // |NamedType|ListType|NonNullType + let inputType, inputTypeRef = + createParserForwardedToRef () + + let namedType = + name |>> NamedType "NamedType" + + let listType = + betweenChars '[' ']' inputType |>> ListType + "ListType" + + let nonNullType = + (listType <|> namedType) .>> pchar '!' + |>> NonNullType + "NonNullType" + + inputTypeRef + := choice [ attempt nonNullType + namedType + listType ] + + + // 2.4 Selection Sets + // Selection list + let selection, selectionRef = + createParserForwardedToRef () + + let selectionSet, selectionSetRef = + createParserForwardedToRef () + + + // 2.5 Fields + // (Alias opt) Name (Arguments opt) (Directives opt) (SelectionSet opt) + let field = + let alias = + token_ws name .>> pchar ':' .>> whitespaces + + pipe5 + (opt (attempt alias)) + (token_ws name) + (opt (token_ws arguments)) + (opt directives) + (opt selectionSet) + (fun oalias name oargs directives oselection -> + (Field + { + Alias = oalias + Name = name + Arguments = someOrEmpty oargs + Directives = someOrEmpty directives + SelectionSet = + match oselection with + | None -> [] + | Some s -> s + })) + "Field" + + + // 2.8 Fragments + // FragmentSpread|FragmentDefinition|FragmentName + let selectionFragment = + let inlineFragment = + pipe3 + (opt (stoken_ws "on" >>. token_ws name)) + (opt (token_ws directives)) + selectionSet + (fun typeCondition directives selectionSet -> + { + FragmentDefinition.Name = None + Directives = someOrEmpty directives + SelectionSet = selectionSet + TypeCondition = typeCondition + }) + |>> InlineFragment + "InlineFragment" + + let fragmentSpread = + token_ws name .>>. opt directives + |>> fun (name, directives) -> + { + FragmentSpread.Name = name + Directives = someOrEmpty directives + } + |>> FragmentSpread + "FragmentSpread" + + pstring "..." .>> whitespaces + >>. (inlineFragment <|> fragmentSpread) + "Fragment" + + selectionRef + := field <|> selectionFragment "Selection" + + selectionSetRef + := betweenCharsMany1 '{' '}' selection + "SelectionSet" + + + // 2.3 Operations + // OperationDefinition List + let definitions = + let operationType = + (stoken_ws "query" >>% Query) + <|> (stoken_ws "mutation" >>% Mutation) + <|> (stoken_ws "subscription" >>% Subscription) + + let operationDefinition = + let variableDefinition = + pipe2 + (pairBetween ':' variable inputType) + (whitespaces + >>. opt ((ctoken_ws '=') >>. inputValue)) + (fun (variableName, variableType) defaultVal -> + { + VariableName = variableName + Type = variableType + DefaultValue = defaultVal + }) + + let variableDefinitions = + betweenCharsMany '(' ')' variableDefinition + + pipe5 + operationType + (opt (token_ws name)) + (opt (token_ws variableDefinitions)) + (opt (token_ws directives)) + (token_ws selectionSet) + (fun otype name ovars directives selection -> + { + OperationType = otype + Name = name + SelectionSet = selection + VariableDefinitions = someOrEmpty ovars + Directives = someOrEmpty directives + }) + + // Short hand format where operation defaults to a Query + let shortHandQueryDefinition = + selectionSet + |>> (fun selectionSet -> + { + OperationType = Query + SelectionSet = selectionSet + VariableDefinitions = [] + Directives = [] + Name = None + }) + + // SelectionSet | + // (OperationType (Name opt) (VariableDefinitions opt) (Directives opt) SelectionSet) + let operationDefinition = + shortHandQueryDefinition <|> operationDefinition + |>> OperationDefinition + + let fragmentDefinition = + pipe4 + ((stoken_ws "fragment") >>. token_ws name + .>> (stoken_ws "on")) + (token_ws name) + directives + selectionSet + (fun name typeCond directives selSet -> + FragmentDefinition + { + Name = Some name + Directives = directives + SelectionSet = selSet + TypeCondition = if String.IsNullOrEmpty typeCond then None else Some typeCond + }) + + // GraphQL documents can contain zero definitions + sepEndBy (operationDefinition <|> fragmentDefinition) whitespaces + + + // 2.2 Document + // DefinitionList + let documents = + whitespaces >>. definitions + .>> (skipMany ignored <|> eof) + |>> (fun definitions -> { Document.Definitions = definitions }) /// Parses a GraphQL Document. Throws exception on invalid formats. let parse query = - match run documents query with - | Success(result, _, _) -> result - | Failure(errorMsg, _, _) -> raise (System.FormatException(errorMsg)) \ No newline at end of file + match run documents query with + | Success (result, _, _) -> result + | Failure (errorMsg, _, _) -> raise (FormatException(errorMsg)) diff --git a/tests/FSharp.Data.GraphQL.Tests/AstExtensionsTests.fs b/tests/FSharp.Data.GraphQL.Tests/AstExtensionsTests.fs index 8928fdc4d..191e15519 100644 --- a/tests/FSharp.Data.GraphQL.Tests/AstExtensionsTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/AstExtensionsTests.fs @@ -8,20 +8,21 @@ open FSharp.Data.GraphQL.Parser open FSharp.Data.GraphQL.Ast.Extensions /// Converts line breaks to a single standard to avoid different SO line break termination issues. -let normalize (str : string) = str.Replace("\r\n", "\n") +let normalize (str: string) = str.Replace("\r\n", "\n") /// Generates an Ast.Document from a query string, prints it to another /// query string and expects it to be equal. Input query must be formatted (with line breaks and identation). -/// Identation unit is two empty spaces. -let private printAndAssert (query : string) = +/// Indentation unit is two empty spaces. +let private printAndAssert (query: string) = let document = parse query let expected = normalize query - let actual = normalize <| document.ToQueryString() + let actual = normalize (document.ToQueryString()) actual |> equals expected [] let ``Should be able to print a simple query`` () = - printAndAssert """query q { + printAndAssert + """query q { hero { name } @@ -29,7 +30,8 @@ let ``Should be able to print a simple query`` () = [] let ``Should be able to print a simple query with 2 fields`` () = - printAndAssert """query q { + printAndAssert + """query q { hero { id name @@ -38,7 +40,8 @@ let ``Should be able to print a simple query with 2 fields`` () = [] let ``Should be able to print a query with variables`` () = - printAndAssert """query q($id: String!) { + printAndAssert + """query q($id: String!) { hero(id: $id) { id name @@ -47,25 +50,29 @@ let ``Should be able to print a query with variables`` () = [] let ``Should be able to parse a query with an object input in the internal method`` () = - printAndAssert """mutation q($id: String!, $name: String!) { + printAndAssert + """mutation q($id: String!, $name: String!) { addHero(input: { id: $id, label: $name }) }""" [] let ``Should be able to parse a query with an object having array properties input in the internal method`` () = - printAndAssert """mutation q($id: String!, $name: String!, $friend1: String!) { + printAndAssert + """mutation q($id: String!, $name: String!, $friend1: String!) { addHero(input: { friends: [ $friend1 ], id: $id, label: $name }) }""" [] let ``Should be able to parse a query with an object having multi-element array input in the internal method`` () = - printAndAssert """mutation q($id: String!, $name: String!) { + printAndAssert + """mutation q($id: String!, $name: String!) { addHero(input: { friends: [ 7, 5, -3 ], id: $id, label: $name }) }""" [] let ``Should be able print ObjectValue names properly`` () = - printAndAssert """query GetCampaigns { + printAndAssert + """query GetCampaigns { campaigns(params: { limit: 100, offset: 0 }) { campaigns { code @@ -75,7 +82,8 @@ let ``Should be able print ObjectValue names properly`` () = [] let ``Should be able to print a query with aliases`` () = - printAndAssert """query q($myId: String!, $hisId: String!) { + printAndAssert + """query q($myId: String!, $hisId: String!) { myHero: hero(id: $myId) { id name @@ -91,7 +99,8 @@ let ``Should be able to print a query with aliases`` () = [] let ``Should be able to print a query with fragment spreads`` () = - printAndAssert """query q($myId: String!, $hisId: String!) { + printAndAssert + """query q($myId: String!, $hisId: String!) { myHero: hero(id: $myId) { id name @@ -121,14 +130,16 @@ fragment friend on Character { [] let ``Should be able to print a short hand format query`` () = - printAndAssert """{ + printAndAssert + """{ field1 field2 }""" [] let ``Should not print query without name in short hand format`` () = - printAndAssert """query ($rId: Int) { + printAndAssert + """query ($rId: Int) { answer(id: $rId) { id answer @@ -137,7 +148,8 @@ let ``Should not print query without name in short hand format`` () = [] let ``Should be able to print a query with inline fragments`` () = - printAndAssert """query q($myId: String!, $hisId: String!) { + printAndAssert + """query q($myId: String!, $hisId: String!) { myHero: hero(id: $myId) { id name @@ -175,7 +187,8 @@ fragment friend on Character { [] let ``Should be able to print arguments inside fragment spreads and default variable values`` () = - printAndAssert """query HeroComparison($first: Int = 3) { + printAndAssert + """query HeroComparison($first: Int = 3) { leftComparison: hero(episode: EMPIRE) { ...comparisonFields } @@ -198,7 +211,8 @@ fragment comparisonFields on Character { [] let ``Should be able to print directives`` () = - printAndAssert """query Hero($episode: Episode, $withFriends: Boolean!) { + printAndAssert + """query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { @@ -209,7 +223,8 @@ let ``Should be able to print directives`` () = [] let ``Should be able to print multiple directives and arguments`` () = - printAndAssert """query q($skip: Boolean!) { + printAndAssert + """query q($skip: Boolean!) { hero(id: "1000") { name friends(first: 1, name_starts_with: "D") @defer @skip(if: $skip) { @@ -227,7 +242,8 @@ let ``Should be able to print multiple directives and arguments`` () = [] let ``Should be able to print a mutation`` () = - printAndAssert """mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { + printAndAssert + """mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { createReview(episode: $ep, review: $review) { stars commentary @@ -236,7 +252,8 @@ let ``Should be able to print a mutation`` () = [] let ``Should be able to print a subscription`` () = - printAndAssert """subscription onCommentAdded($repoFullName: String!) { + printAndAssert + """subscription onCommentAdded($repoFullName: String!) { commentAdded(repoFullName: $repoFullName) { id content @@ -245,7 +262,9 @@ let ``Should be able to print a subscription`` () = [] let ``Should be able to print type name meta field`` () = - let expected = normalize """query q { + let expected = + normalize + """query q { hero(id: "1000") { name friends { @@ -265,7 +284,9 @@ let ``Should be able to print type name meta field`` () = } __typename }""" - let query = """query q { + + let query = + """query q { hero(id: "1000") { name friends { @@ -281,13 +302,17 @@ let ``Should be able to print type name meta field`` () = } } """ + let document = parse query + let actual = normalize <| document.ToQueryString(QueryStringPrintingOptions.IncludeTypeNames) + actual |> equals expected [] let ``Should generate information map correctly`` () = - let query = """query q { + let query = + """query q { hero(id: "1000") { name friends { @@ -303,32 +328,66 @@ let ``Should generate information map correctly`` () = } } """ + let document = parse query + let actual = document.GetInfoMap() |> Map.toList - let expected = [(Some "q", [TypeField - {Name = "hero"; - Alias = None; - Fields = - [TypeField - {Name = "friends"; - Alias = None; - Fields = - [FragmentField {Name = "primaryFunction"; - Alias = None; - TypeCondition = "Droid"; - Fields = [];}; - FragmentField {Name = "id"; - Alias = None; - TypeCondition = "Droid"; - Fields = [];}; - FragmentField {Name = "homePlanet"; - Alias = None; - TypeCondition = "Human"; - Fields = [];}; - FragmentField {Name = "id"; - Alias = None; - TypeCondition = "Human"; - Fields = [];}];}; TypeField {Name = "name"; - Alias = None; - Fields = [];}];}])] + + let expected = + [ + (Some "q", + [ + TypeField + { + Name = "hero" + Alias = None + Fields = + [ + TypeField + { + Name = "friends" + Alias = None + Fields = + [ + FragmentField + { + Name = "primaryFunction" + Alias = None + TypeCondition = "Droid" + Fields = [] + } + FragmentField + { + Name = "id" + Alias = None + TypeCondition = "Droid" + Fields = [] + } + FragmentField + { + Name = "homePlanet" + Alias = None + TypeCondition = "Human" + Fields = [] + } + FragmentField + { + Name = "id" + Alias = None + TypeCondition = "Human" + Fields = [] + } + ] + } + TypeField + { + Name = "name" + Alias = None + Fields = [] + } + ] + } + ]) + ] + actual |> equals expected From beef9bcf824b921ac5ba8faa7166e4736aea39c0 Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 17:59:40 +0200 Subject: [PATCH 5/9] Update AST to 2021 version --- src/FSharp.Data.GraphQL.Shared/Ast.fs | 381 +++++++++++++++++++++++--- 1 file changed, 345 insertions(+), 36 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index 64edb6246..e4b947d50 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -6,21 +6,30 @@ namespace FSharp.Data.GraphQL.Ast open System -/// There are three types of operations that GraphQL models: -/// -/// - query – a read‐only fetch. -/// - mutation – a write followed by a fetch. -/// - subscription – a long‐lived request that fetches data in response to source events. +/// There are three types of operations that GraphQL models. /// /// Each operation is represented by an optional operation name and a selection set. -/// https://graphql.github.io/graphql-spec/June2018/#OperationType +/// +/// https://spec.graphql.org/October2021/#sec-Language.Operations +/// https://spec.graphql.org/October2021/#OperationType type OperationType = + /// A read-only fetch. | Query + /// A write followed by a fetch. | Mutation + /// A long-lived request that fetches data in response to source events. | Subscription /// 2.9 Input Values +/// +/// Field and directive arguments accept input values of various literal primitives; input values can be scalars, enumeration +/// values, lists, or input objects. +/// +/// If not defined as constant (for example, in DefaultValue), input values can be specified as a variable. List and inputs +/// objects may also contain variables (unless defined to be constant). +/// +/// https://spec.graphql.org/October2021/#sec-Input-Values type Value = /// 2.9.1 Int Value | IntValue of int64 @@ -42,55 +51,204 @@ type Value = | Variable of string /// 2.6 Arguments -/// Fields are conceptually functions which return values, and occasionally accept arguments which alter their behavior. These arguments often map directly to function arguments within a GraphQL server’s implementation. -/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Arguments -type Argument = { Name: string; Value: Value } +/// +/// Fields are conceptually functions which return values, and occasionally accept arguments which alter their behavior. +/// These arguments often map directly to function arguments within a GraphQL service’s implementation. +/// +/// https://spec.graphql.org/October2021/#sec-Language.Arguments +/// https://spec.graphql.org/October2021/#Arguments +type Argument = + { + Name: string + Value: Value + } -/// 2.2.10 Directives +/// 2.12 Directives +/// +/// Directives provide a way to describe alternate runtime execution and type validation behavior in a GraphQL document. +/// +/// In some cases, you need to provide options to alter GraphQL’s execution behavior in ways field arguments will not suffice, +/// such as conditionally including or skipping a field. Directives provide this by describing additional information to the +/// executor. +/// +/// Directives have a name along with a list of arguments which may accept values of any input type. +/// +/// Directives can be used to describe additional information for types, fields, fragments and operations. +/// +/// As future versions of GraphQL adopt new configurable execution capabilities, they may be exposed via directives. GraphQL +/// services and tools may also provide any additional custom directive beyond those described here. +/// +/// Directives may be provided in a specific syntactic order which may have semantic interpretation. +/// +/// https://spec.graphql.org/October2021/#sec-Language.Directives +/// https://spec.graphql.org/October2021/#Directive type Directive = { + /// https://spec.graphql.org/October2021/#Name Name: string + /// https://spec.graphql.org/October2021/#Arguments Arguments: Argument list } member x.If = x.Arguments |> List.find (fun arg -> arg.Name = "if") -/// 2.2.6 Fragments +/// 2.8 Fragments +/// +/// Fragments are the primary unit of composition in GraphQL. +/// +/// Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document. Inline +/// Fragments can be used directly within a selection to condition upon a type condition when querying against an interface or +/// union. +/// +/// https://spec.graphql.org/October2021/#sec-Language.Fragments +/// https://spec.graphql.org/October2021/#FragmentSpread type FragmentSpread = { Name: string + /// Maybe empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list } +/// +/// 2.8 Fragments +/// +/// +/// Fragments are the primary unit of composition in GraphQL. +/// +/// Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document. Inline +/// Fragments can be used directly within a selection to condition upon a type condition when querying against an interface or +/// union. +/// +/// Example: fragment userFragment on User +/// +/// https://spec.graphql.org/October2021/#sec-Language.Fragments +/// +/// https://spec.graphql.org/October2021/#FragmentDefinition +/// type FragmentDefinition = { - Name: string option - /// 2.2.6.1 Type Conditions + /// Name, but not the constant "on" + Name: string + + /// 2.8.1 Type Conditions + /// + /// Fragments must specify the type they apply to. In this example, friendFields can be used in the context of querying a + /// User. + /// + /// Fragments cannot be specified on any input value (scalar, enumeration, or input object). + /// + /// Selections within fragments only return values when the concrete type of the object it is operating on matches the + /// type of the fragment. + /// + /// Example: `fragment userFragment on User` + /// + /// https://spec.graphql.org/October2021/#sec-Type-Conditions + TypeCondition: string + + /// May be empty + Directives: Directive list + + /// May not be empty + SelectionSet: Selection list + } + +/// Fragments can be defined inline within a selection set. This is done to conditionally include fields based on their runtime +/// type. This feature of standard fragment inclusion was demonstrated in the query FragmentTyping example. We could accomplish +/// the same thing using inline fragments. +/// +/// https://spec.graphql.org/October2021/#sec-Inline-Fragments +/// https://spec.graphql.org/October2021/#InlineFragment +and InlineFragment = + { + /// 2.8.1 Type Conditions + /// + /// Fragments must specify the type they apply to. In this example, friendFields can be used in the context of querying a + /// User. + /// + /// Fragments cannot be specified on any input value (scalar, enumeration, or input object). + /// + /// Selections within fragments only return values when the concrete type of the object it is operating on matches the + /// type of the fragment. + /// + /// Example: `fragment userFragment on User` + /// + /// https://spec.graphql.org/October2021/#sec-Type-Conditions TypeCondition: string option + + /// May be empty Directives: Directive list + + /// May not be empty SelectionSet: Selection list } -/// 2.2.2 Selection Sets +/// +/// 2.4 Selection Sets +/// +/// An operation selects the set of information it needs, and will receive exactly that information and nothing more, avoiding +/// over-fetching and under-fetching data. +/// +/// +/// +/// { +/// id +/// firstName +/// lastName +/// } +/// +/// +/// https://spec.graphql.org/October2021/#sec-Selection-Sets +/// +/// https://spec.graphql.org/October2021/#Selection +/// and Selection = + /// https://spec.graphql.org/October2021/#Field | Field of Field + /// https://spec.graphql.org/October2021/#FragmentSpread | FragmentSpread of FragmentSpread - /// 2.2.6.2 Inline Fragments - | InlineFragment of FragmentDefinition + /// https://spec.graphql.org/October2021/#InlineFragment + | InlineFragment of InlineFragment member x.Directives = match x with | Field f -> f.Directives | FragmentSpread s -> s.Directives | InlineFragment f -> f.Directives -/// 2.2.3 Fields +/// 2.5 Fields +/// https://spec.graphql.org/October2021/#sec-Language.Fields +/// +/// A selection set is primarily composed of fields. A field describes one discrete piece of information available to request +/// within a selection set. +/// +/// Some fields describe complex data or relationships to other data. In order to further explore this data, a field may itself +/// contain a selection set, allowing for deeply nested requests. All GraphQL operations must specify their selections down to +/// fields which return scalar values to ensure an unambiguously shaped response. and Field = { - /// 2.2.5 Field Alias + /// 2.7 Field Alias + /// + /// By default a field’s response key in the response object will use that field’s name. However, you can define a + /// different response key by specifying an alias. + /// + /// https://spec.graphql.org/October2021/#sec-Field-Alias + /// https://spec.graphql.org/October2021/#Alias Alias: string option + + /// https://spec.graphql.org/October2021/#Name Name: string + + /// Arguments are unordered. + /// https://spec.graphql.org/October2021/#Arguments Arguments: Argument list + + /// May be empty. + /// Arguments are ordered. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list + + /// May be empty. + /// https://spec.graphql.org/October2021/#SelectionSet SelectionSet: Selection list } member x.AliasOrName = @@ -115,28 +273,68 @@ type InputType = -/// 2.2.8 Variables +/// 2.10 Variables +/// A GraphQL operation can be parameterized with variables, maximizing reuse, and avoiding costly string building in clients at +/// runtime. +/// +/// If not defined as constant (for example, in DefaultValue), a Variable can be supplied for an input value. +/// +/// Variables must be defined at the top of an operation and are in scope throughout the execution of that operation. Values for +/// those variables are provided to a GraphQL service as part of a request so they may be substituted in during execution. +/// +/// Variables can be used within fragments. Variables have global scope with a given operation, so a variable used within a +/// fragment must be declared in any top-level operation that transitively consumes that fragment. If a variable is referenced in +/// a fragment and is included by an operation that does not define that variable, that operation is invalid (see All Variable +/// Uses Defined). (from https://spec.graphql.org/October2021/#sec-Language.Variables.Variable-use-within-Fragments) +/// +/// https://spec.graphql.org/October2021/#sec-Language.Variables type VariableDefinition = { VariableName: string Type: InputType DefaultValue: Value option + Directives: Directive list } -//NOTE: For references, see https://facebook.github.io/graphql/ /// 2.3 Operations +/// +/// There are three types of operations that GraphQL models: +/// +/// - query – a read-only fetch. +/// - mutation – a write followed by a fetch. +/// - subscription – a long-lived request that fetches data in response to source events. +/// +/// Each operation is represented by an optional operation name and a selection set. +/// +/// https://spec.graphql.org/October2021/#sec-Language.Operations type OperationDefinition = { - /// Defaults to `query`; "query shorthand" + /// Defaults to `query`; called "query shorthand". OperationType: OperationType + + /// https://spec.graphql.org/October2021/#Name Name: string option + /// May be empty + /// https://spec.graphql.org/October2021/#VariableDefinitions VariableDefinitions: VariableDefinition list + /// May be empty Directives: Directive list + /// May not be empty SelectionSet: Selection list } + /// Create a new Operation Definition. + static member Create(selectionSet, ?op: OperationType) = + { + OperationType = defaultArg op Query + Name = None + VariableDefinitions = [] + Directives = [] + SelectionSet = selectionSet + } + member x.IsShortHandQuery = x.OperationType = Query && x.Name.IsNone && x.VariableDefinitions.IsEmpty && x.Directives.IsEmpty @@ -156,20 +354,75 @@ type InputFieldDefinition = DefaultValue: Value option } -/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Operations -type OperationTypeDefinition = +/// 3.3.1 Root Operation Types +/// +/// A schema defines the initial root operation type for each kind of operation it supports: query, mutation, and subscription; +/// this determines the place in the type system where those operations begin. +/// +/// The query root operation type must be provided and must be an Object type. +/// +/// The mutation root operation type is optional; if it is not provided, the service does not support mutations. If it is +/// provided, it must be an Object type. +/// +/// Similarly, the subscription root operation type is also optional; if it is not provided, the service does not support +/// subscriptions. If it is provided, it must be an Object type. +/// +/// The query, mutation, and subscription root types must all be different types if provided. +/// +/// The fields on the query root operation type indicate what fields are available at the top level of a GraphQL query operation. +/// +/// Default Root Operation Type Names: +/// +/// While any type can be the root operation type for a GraphQL operation, the type system definition language can omit the schema +/// definition when the query, mutation, and subscription root types are named "Query", "Mutation", and "Subscription" respectively. +/// +/// Likewise, when representing a GraphQL schema using the type system definition language, a schema definition should be omitted +/// if it only uses the default root operation type names. +/// +/// This example describes a valid complete GraphQL schema, despite not explicitly including a schema definition. +/// The "Query" type is presumed to be the query root operation type of the schema. +/// +/// Also see 3.3 Schema. +/// https://spec.graphql.org/October2021/#RootOperationTypeDefinition +type RootOperationTypeDefinition = { - NamedType: string + /// https://spec.graphql.org/October2021/#OperationType Operation: OperationType + /// https://spec.graphql.org/October2021/#NamedType + NamedType: string } -/// A GraphQL service’s collective type system capabilities are referred to as that service’s “schema”. A schema is defined in terms of the types and directives it supports as well as the root operation types for each kind of operation: query, mutation, and subscription; this determines the place in the type system where those operations begin. +/// 3.3 Schema +/// +/// A GraphQL service’s collective type system capabilities are referred to as that service’s “schema”. A schema is defined in +/// terms of the types and directives it supports as well as the root operation types for each kind of operation: query, mutation, +/// and subscription; this determines the place in the type system where those operations begin. +/// /// A GraphQL schema must itself be internally valid. This section describes the rules for this validation process where relevant. -/// https://graphql.github.io/graphql-spec/June2018/#SchemaDefinition +/// +/// All types within a GraphQL schema must have unique names. No two provided types may have the same name. No provided type may +/// have a name which conflicts with any built in types (including Scalar and Introspection types). +/// +/// All directives within a GraphQL schema must have unique names. +/// +/// All types and directives defined within a schema must not have a name which begins with "__" (two underscores), as this is +/// used exclusively by GraphQL’s introspection system. +/// +/// Example "My schema" schema @important { ... } or schema { ... }. +/// +/// https://spec.graphql.org/October2021/#sec-Schema +/// https://spec.graphql.org/October2021/#SchemaDefinition type SchemaDefinition = { + /// https://spec.graphql.org/October2021/#Description + Description: string option + + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - OperationTypes: OperationTypeDefinition + + /// https://spec.graphql.org/October2021/#RootOperationTypeDefinition + RootOperationTypes: RootOperationTypeDefinition } type InputValueDefinition = @@ -374,9 +627,28 @@ type DirectiveDefinition = } +/// 3.0 Type System +/// +/// The GraphQL Type system describes the capabilities of a GraphQL service and is used to determine if a requested operation is +/// valid, to guarantee the type of response results, and describes the input types of variables to determine if values provided +/// at request time are valid. +/// +/// The GraphQL language includes an IDL used to describe a GraphQL service’s type system. Tools may use this definition language +/// to provide utilities such as client code generation or service boot-strapping. +/// +/// GraphQL tools or services which only seek to execute GraphQL requests and not construct a new GraphQL schema may choose not to +/// allow TypeSystemDefinition. Tools which only seek to produce schema and not execute requests may choose to only allow +/// TypeSystemDocument and not allow ExecutableDefinition or TypeSystemExtension but should provide a descriptive error if +/// present. +/// +/// https://spec.graphql.org/October2021/#sec-Type-System +/// https://spec.graphql.org/October2021/#TypeSystemDefinition type TypeSystemDefinition = + /// https://spec.graphql.org/October2021/#SchemaDefinition | SchemaDefinition of SchemaDefinition + /// https://spec.graphql.org/October2021/#TypeDefinition | TypeDefinition of TypeDefinition + /// https://spec.graphql.org/October2021/#DirectiveDefinition | DirectiveDefinition of DirectiveDefinition /// Schema extensions are used to represent a schema which has been extended from an original schema. For example, this might be used by a GraphQL service which adds additional operation types, or additional directives to an existing schema. @@ -386,7 +658,7 @@ type SchemaExtension = /// May be empty. Any directives provided must not already apply to the original Schema. Directives: Directive list /// May be empty - OperationTypes: OperationTypeDefinition list + OperationTypes: RootOperationTypeDefinition list } /// Type extensions are used to represent a GraphQL type which has been extended from some original type. For example, this might be used by a local service to represent additional fields a GraphQL client only accesses locally. @@ -405,16 +677,22 @@ type TypeSystemExtension = | SchemaExtension of SchemaExtension | TypeExtension of TypeExtension + +/// https://spec.graphql.org/October2021/#Definition type Definition = + /// https://spec.graphql.org/October2021/#OperationDefinition | OperationDefinition of OperationDefinition + /// https://spec.graphql.org/October2021/#FragmentDefinition | FragmentDefinition of FragmentDefinition + /// https://spec.graphql.org/October2021/#TypeSystemDefinition | TypeSystemDefinition of TypeSystemDefinition + /// https://spec.graphql.org/October2021/#TypeSystemExtension | TypeSystemExtension of TypeSystemExtension member x.Name = match x with | OperationDefinition op -> op.Name - | FragmentDefinition frag -> frag.Name + | FragmentDefinition frag -> Some frag.Name | TypeSystemDefinition _ -> None | TypeSystemExtension _ -> None @@ -430,14 +708,45 @@ type Definition = | FragmentDefinition frag -> frag.SelectionSet | _ -> [] -/// 2.2 Query Document -/// A GraphQL Document describes a complete file or request string operated on by a GraphQL service or client. A document contains multiple definitions, either executable or representative of a GraphQL type system. + +/// https://spec.graphql.org/October2021/#ExecutableDefinition +type ExecutableDefinition = + /// https://spec.graphql.org/October2021/#OperationDefinition + | OperationDefinition of OperationDefinition + /// https://spec.graphql.org/October2021/#FragmentDefinition + | FragmentDefinition of FragmentDefinition + + +/// https://spec.graphql.org/October2021/#ExecutableDocument +type ExecutableDocument = + { + Definitions: ExecutableDefinition list + } + + +/// 2.2 Document +/// +/// A GraphQL Document describes a complete file or request string operated on by a GraphQL service or client. A document contains +/// multiple definitions, either executable or representative of a GraphQL type system. /// -/// Documents are only executable by a GraphQL service if they contain an OperationDefinition and otherwise only contain ExecutableDefinition. However documents which do not contain OperationDefinition or do contain TypeSystemDefinition or TypeSystemExtension may still be parsed and validated to allow client tools to represent many GraphQL uses which may appear across many individual files. +/// Documents are only executable by a GraphQL service if they are ExecutableDocument and contain at least one +/// OperationDefinition. A Document which contains TypeSystemDefinitionOrExtension must not be executed; GraphQL execution +/// services which receive a Document containing these should return a descriptive error. /// -/// If a Document contains only one operation, that operation may be unnamed or represented in the shorthand form, which omits both the query keyword and operation name. Otherwise, if a GraphQL Document contains multiple operations, each operation must be named. When submitting a Document with multiple operations to a GraphQL service, the name of the desired operation to be executed must also be provided. +/// GraphQL services which only seek to execute GraphQL requests and not construct a new GraphQL schema may choose to only permit +/// ExecutableDocument. /// -/// GraphQL services which only seek to provide GraphQL query execution may choose to only include ExecutableDefinition and omit the TypeSystemDefinition and TypeSystemExtension rules from Definition. +/// Documents which do not contain OperationDefinition or do contain TypeSystemDefinitionOrExtension may still be parsed and +/// validated to allow client tools to represent many GraphQL uses which may appear across many individual files. /// -/// https://graphql.github.io/graphql-spec/June2018/#sec-Language.Document -type Document = { Definitions: Definition list } +/// If a Document contains only one operation, that operation may be unnamed. If that operation is a query without variables or +/// directives then it may also be represented in the shorthand form, omitting both the query keyword as well as the operation +/// name. Otherwise, if a GraphQL Document contains multiple operations, each operation must be named. When submitting a Document +/// with multiple operations to a GraphQL service, the name of the desired operation to be executed must also be provided. +/// +/// https://spec.graphql.org/October2021/#sec-Document-Syntax +/// https://spec.graphql.org/October2021/#Document +type Document = + { + Definitions: Definition list + } From 2cf1b5c8f1423e0997ada4307d1e2ae2bb3d8545 Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 18:09:39 +0200 Subject: [PATCH 6/9] More docs --- src/FSharp.Data.GraphQL.Shared/Ast.fs | 82 +++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index e4b947d50..ce000f4a7 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -5,6 +5,7 @@ namespace FSharp.Data.GraphQL.Ast open System +open FSharp.Data.GraphQL.Ast /// There are three types of operations that GraphQL models. /// @@ -445,18 +446,60 @@ type FieldDefinition = // TypeDefinitions: -/// Scalar types represent primitive leaf values in a GraphQL type system. GraphQL responses take the form of a hierarchical tree; the leaves on these trees are GraphQL scalars. -/// https://graphql.github.io/graphql-spec/June2018/#ScalarTypeDefinition +/// 3.5 Scalars +/// +/// Scalar types represent primitive leaf values in a GraphQL type system. GraphQL responses take the form of a hierarchical tree; +/// the leaves of this tree are typically GraphQL Scalar types (but may also be Enum types or null values). +/// +/// GraphQL provides a number of built-in scalars which are fully defined in the sections below, however type systems may also add +/// additional custom scalars to introduce additional semantic meaning. +/// +/// Built-in Scalars: +/// +/// GraphQL specifies a basic set of well-defined Scalar types: Int, Float, String, Boolean, and ID. A GraphQL framework should +/// support all of these types, and a GraphQL service which provides a type by these names must adhere to the behavior described +/// for them in this document. As an example, a service must not include a type called Int and use it to represent 64-bit numbers, +/// internationalization information, or anything other than what is defined in this document. +/// +/// When returning the set of types from the __Schema introspection type, all referenced built-in scalars must be included. If a +/// built-in scalar type is not referenced anywhere in a schema (there is no field, argument, or input field of that type) then it +/// must not be included. +/// +/// When representing a GraphQL schema using the type system definition language, all built-in scalars must be omitted for +/// brevity. +/// +/// https://spec.graphql.org/October2021/#ScalarTypeDefinition type ScalarTypeDefinition = { Description: string option + Name: string + + /// May be empty. Directives: Directive list } +/// 3.5.6 Scalar Extensions +/// +/// Scalar type extensions are used to represent a scalar type which has been extended from some original scalar type. For example, +/// this might be used by a GraphQL tool or service which adds directives to an existing scalar. +/// +/// Type Validation: +/// +/// Scalar type extensions have the potential to be invalid if incorrectly defined. +/// +/// 1. The named type must already be defined and must be a Scalar type. +/// 2. Any non-repeatable directives provided must not already apply to the original Scalar type. +/// +/// Example: extend scalar [name] directive... +/// +/// https://spec.graphql.org/October2021/#sec-Scalar-Extensions type ScalarTypeExtension = { Name: string + + /// May not be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list } @@ -577,6 +620,7 @@ type InputObjectTypeExtension = } type TypeDefinition = + /// https://spec.graphql.org/October2021/#ScalarTypeExtension | ScalarTypeDefinition of ScalarTypeDefinition | ObjectTypeDefinition of ObjectTypeDefinition | InterfaceTypeDefinition of InterfaceTypeDefinition @@ -661,8 +705,14 @@ type SchemaExtension = OperationTypes: RootOperationTypeDefinition list } -/// Type extensions are used to represent a GraphQL type which has been extended from some original type. For example, this might be used by a local service to represent additional fields a GraphQL client only accesses locally. -/// https://graphql.github.io/graphql-spec/June2018/#TypeExtension +// Type Extensions: + +/// 3.4.3 Type Extensions +/// +/// Type extensions are used to represent a GraphQL type which has been extended from some original type. For example, this might +/// be used by a local service to represent additional fields a GraphQL client only accesses locally. +/// +/// https://spec.graphql.org/October2021/#sec-Type-Extensions type TypeExtension = | ScalarTypeExtension of ScalarTypeExtension | ObjectTypeExtension of ObjectTypeExtension @@ -671,13 +721,33 @@ type TypeExtension = | EnumTypeExtension of EnumTypeExtension | InputObjectTypeExtension of InputObjectTypeExtension -/// Type system extensions are used to represent a GraphQL type system which has been extended from some original type system. For example, this might be used by a local service to represent data a GraphQL client only accesses locally, or by a GraphQL service which is itself an extension of another GraphQL service. -/// 3.1 https://graphql.github.io/graphql-spec/June2018/#sec-Type-System-Extensions +/// 3.1 Type System Extensions +/// +/// Type system extensions are used to represent a GraphQL type system which has been extended from some original type system. For +/// example, this might be used by a local service to represent data a GraphQL client only accesses locally, or by a GraphQL +/// service which is itself an extension of another GraphQL service. +/// +/// Tools which only seek to produce and extend schema and not execute requests may choose to only allow +/// TypeSystemExtensionDocument and not allow ExecutableDefinition but should provide a descriptive error if present. +/// +/// https://spec.graphql.org/October2021/#sec-Type-System-Extensions +/// https://spec.graphql.org/October2021/#TypeSystemExtension type TypeSystemExtension = + /// https://spec.graphql.org/October2021/#SchemaExtension | SchemaExtension of SchemaExtension + /// https://spec.graphql.org/October2021/#TypeExtension | TypeExtension of TypeExtension +/// https://spec.graphql.org/October2021/#TypeSystemDocument +type TypeSystemDocument = + { + /// https://spec.graphql.org/October2021/#TypeSystemDefinition + Definitions: TypeSystemDefinition list + } + + +/// Prefer to use either TypeSystemDocument or ExecutableDocument. /// https://spec.graphql.org/October2021/#Definition type Definition = /// https://spec.graphql.org/October2021/#OperationDefinition From 0dceeb2d0c2baf16d4dc2da0aae0a87cbc895884 Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 20:56:37 +0200 Subject: [PATCH 7/9] Finished upgrading AST to 2021 --- .../ProvidedTypesHelper.fs | 1468 +++++++++++------ src/FSharp.Data.GraphQL.Server/Values.fs | 106 +- src/FSharp.Data.GraphQL.Shared/Ast.fs | 445 ++++- src/FSharp.Data.GraphQL.Shared/Parser.fs | 2 +- src/FSharp.Data.GraphQL.Shared/Validation.fs | 1280 +++++++++----- 5 files changed, 2284 insertions(+), 1017 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs index 3ee46e852..8966e7eb3 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs @@ -1,6 +1,5 @@ /// The MIT License (MIT) /// Copyright (c) 2016 Bazinga Technologies Inc - namespace FSharp.Data.GraphQL open System @@ -21,32 +20,41 @@ open System.Collections module internal QuotationHelpers = let rec coerceValues fieldTypeLookup fields = - let arrayExpr (arrayType : Type) (v : obj) = + let arrayExpr (arrayType: Type) (v: obj) = let typ = arrayType.GetElementType() + let instance = match v with | :? IEnumerable as x -> Seq.cast x |> Array.ofSeq | _ -> failwith "Unexpected array value." + let exprs = coerceValues (fun _ -> typ) instance Expr.NewArray(typ, exprs) - let tupleExpr (tupleType : Type) (v : obj) = + + let tupleExpr (tupleType: Type) (v: obj) = let typ = FSharpType.GetTupleElements tupleType |> Array.mapi (fun i t -> i, t) |> Map.ofArray let fieldTypeLookup i = typ.[i] let fields = FSharpValue.GetTupleFields v let exprs = coerceValues fieldTypeLookup fields Expr.NewTuple(exprs) - Array.mapi (fun i v -> + + Array.mapi + (fun i v -> let expr = - if isNull v then simpleTypeExpr v + if isNull v then + simpleTypeExpr v else let tpy = v.GetType() + if tpy.IsArray then arrayExpr tpy v elif FSharpType.IsTuple tpy then tupleExpr tpy v elif FSharpType.IsUnion tpy then unionExpr v |> snd elif FSharpType.IsRecord tpy then recordExpr v |> snd else simpleTypeExpr v - Expr.Coerce(expr, fieldTypeLookup i) - ) fields |> List.ofArray + + Expr.Coerce(expr, fieldTypeLookup i)) + fields + |> List.ofArray and simpleTypeExpr instance = Expr.Value(instance) @@ -63,7 +71,7 @@ module internal QuotationHelpers = let fieldTypeLookup indx = fieldInfo.[indx].PropertyType typ, Expr.NewRecord(instance.GetType(), coerceValues fieldTypeLookup fields) - and arrayExpr (instance : 'a array) = + and arrayExpr (instance: 'a array) = let typ = typeof<'a> let arrayType = instance.GetType() let exprs = coerceValues (fun _ -> typ) (instance |> Array.map box) @@ -88,34 +96,41 @@ module internal QuotationHelpers = module internal ProvidedEnum = let ctor = typeof.GetConstructors().[0] - let makeProvidedType(name, items : string seq) = + let makeProvidedType (name, items: string seq) = let tdef = ProvidedTypeDefinition(name, Some typeof, nonNullable = true, isSealed = true) - tdef.AddMembersDelayed(fun _ -> + + tdef.AddMembersDelayed (fun _ -> items |> Seq.map (fun item -> - let getterCode (_ : Expr list) = - Expr.NewObject(ctor, [ <@@ name @@>; <@@ item @@> ]) + let getterCode (_: Expr list) = Expr.NewObject(ctor, [ <@@ name @@>; <@@ item @@> ]) ProvidedProperty(item, tdef, getterCode, isStatic = true)) |> Seq.cast |> List.ofSeq) + tdef type internal ProvidedTypeMetadata = - { Name : string - Description : string option } + { + Name: string + Description: string option + } module internal ProvidedInterface = - let makeProvidedType(metadata : ProvidedTypeMetadata) = - let tdef = ProvidedTypeDefinition("I" + metadata.Name.FirstCharUpper(), None, nonNullable = true, isInterface = true) + let makeProvidedType (metadata: ProvidedTypeMetadata) = + let tdef = + ProvidedTypeDefinition("I" + metadata.Name.FirstCharUpper(), None, nonNullable = true, isInterface = true) + metadata.Description |> Option.iter tdef.AddXmlDoc tdef type internal RecordPropertyMetadata = - { Name : string - Alias : string option - Description : string option - DeprecationReason : string option - Type : Type } + { + Name: string + Alias: string option + Description: string option + DeprecationReason: string option + Type: Type + } member x.AliasOrName = match x.Alias with | Some x -> x @@ -124,7 +139,7 @@ type internal RecordPropertyMetadata = type internal ProvidedRecordTypeDefinition(className, baseType) = inherit ProvidedTypeDefinition(className, baseType, nonNullable = true) - let mutable properties : RecordPropertyMetadata list = [] + let mutable properties: RecordPropertyMetadata list = [] member __.GetRecordProperties() = properties @@ -133,27 +148,40 @@ type internal ProvidedRecordTypeDefinition(className, baseType) = [] module internal Failures = let uploadTypeIsNotScalar uploadTypeName = - failwithf "Upload type \"%s\" was found on the schema, but it is not a Scalar type. Upload types can only be used if they are defined as scalar types." uploadTypeName + failwithf + "Upload type \"%s\" was found on the schema, but it is not a Scalar type. Upload types can only be used if they are defined as scalar types." + uploadTypeName module internal ProvidedRecord = let ctor = typeof.GetConstructors().[0] - let makeProvidedType(tdef : ProvidedRecordTypeDefinition, properties : RecordPropertyMetadata list, explicitOptionalParameters: bool) = + let makeProvidedType + ( + tdef: ProvidedRecordTypeDefinition, + properties: RecordPropertyMetadata list, + explicitOptionalParameters: bool + ) = let name = tdef.Name - tdef.AddMembersDelayed(fun _ -> - properties |> List.map (fun metadata -> + + tdef.AddMembersDelayed (fun _ -> + properties + |> List.map (fun metadata -> let pname = metadata.AliasOrName.FirstCharUpper() - let getterCode (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase + + let getterCode (args: Expr list) = + <@@ let this: RecordBase = %%args.[0] + match this.GetProperties() |> List.tryFind (fun prop -> prop.Name = pname) with | Some prop -> prop.Value | None -> failwithf "Expected to find property \"%s\", but the property was not found." pname @@> + let pdef = ProvidedProperty(pname, metadata.Type, getterCode) metadata.Description |> Option.iter pdef.AddXmlDoc metadata.DeprecationReason |> Option.iter pdef.AddObsoleteAttribute pdef)) - let addConstructorDelayed (propertiesGetter : unit -> (string * string option * Type) list) = - tdef.AddMembersDelayed(fun _ -> + + let addConstructorDelayed (propertiesGetter: unit -> (string * string option * Type) list) = + tdef.AddMembersDelayed (fun _ -> // We need to build a constructor that takes all optional properties wrapped in another option. // We need to do this because optional parameters have issues with non-nullable types // in the type provider SDK. They require a default value, and if the type is not nullable @@ -172,41 +200,59 @@ module internal ProvidedRecord = // combinations of all possible overloads, and for each one we map the user's provided values and // fill the others with a null value. This way we can construct the RecordBase type providing all // needed properties. - let properties = propertiesGetter() + let properties = propertiesGetter () + let optionalProperties, requiredProperties = properties |> List.map (fun (name, alias, t) -> Option.defaultValue name alias, t) |> List.partition (fun (_, t) -> isOption t) + if explicitOptionalParameters then let constructorProperties = requiredProperties @ optionalProperties let propertyNames = constructorProperties |> List.map (fst >> (fun x -> x.FirstCharUpper())) let constructorPropertyTypes = constructorProperties |> List.map snd - let invoker (args : Expr list) = + + let invoker (args: Expr list) = let properties = let baseConstructorArgs = let coercedArgs = (constructorPropertyTypes, args) ||> List.map2 (fun t arg -> let arg = Expr.Coerce(arg, typeof) + match t with | Option (Option t) -> <@@ makeValue t %%arg @@> | _ -> <@@ %%arg @@>) + (propertyNames, coercedArgs) - ||> List.map2 (fun name value -> <@@ { RecordProperty.Name = name; Value = %%value } @@>) + ||> List.map2 (fun name value -> + <@@ { + RecordProperty.Name = name + Value = %%value + } @@>) + Expr.NewArray(typeof, baseConstructorArgs) - Expr.NewObject(ctor, [Expr.Value(name); properties]) + + Expr.NewObject(ctor, [ Expr.Value(name); properties ]) + let constructorParams = constructorProperties - |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) - [ProvidedConstructor(constructorParams, invoker)] + |> List.map (fun (name, t) -> + ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + + [ ProvidedConstructor(constructorParams, invoker) ] else List.combinations optionalProperties |> List.map (fun (optionalProperties, nullValuedProperties) -> let constructorProperties = requiredProperties @ optionalProperties - let propertyNames = (constructorProperties @ nullValuedProperties) |> List.map (fst >> (fun x -> x.FirstCharUpper())) + + let propertyNames = + (constructorProperties @ nullValuedProperties) |> List.map (fst >> (fun x -> x.FirstCharUpper())) + let constructorPropertyTypes = constructorProperties |> List.map snd let nullValuedPropertyTypes = nullValuedProperties |> List.map snd - let invoker (args : Expr list) = + + let invoker (args: Expr list) = let properties = let baseConstructorArgs = let coercedArgs = @@ -214,49 +260,75 @@ module internal ProvidedRecord = ||> List.map2 (fun t arg -> let arg = Expr.Coerce(arg, typeof) if isOption t then <@@ makeSome %%arg @@> else <@@ %%arg @@>) + let nullValuedArgs = nullValuedPropertyTypes |> List.map (fun _ -> <@@ null @@>) + (propertyNames, (coercedArgs @ nullValuedArgs)) - ||> List.map2 (fun name value -> <@@ { RecordProperty.Name = name; Value = %%value } @@>) + ||> List.map2 (fun name value -> + <@@ { + RecordProperty.Name = name + Value = %%value + } @@>) + Expr.NewArray(typeof, baseConstructorArgs) - Expr.NewObject(ctor, [Expr.Value(name); properties]) + + Expr.NewObject(ctor, [ Expr.Value(name); properties ]) + let constructorParams = constructorProperties |> List.map (fun (name, t) -> match t with | Option t -> ProvidedParameter(name, t) | _ -> ProvidedParameter(name, t)) + ProvidedConstructor(constructorParams, invoker))) + match tdef.BaseType with | :? ProvidedRecordTypeDefinition as bdef -> - bdef.AddMembersDelayed(fun _ -> + bdef.AddMembersDelayed (fun _ -> let asType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - if this.GetName() = name then this - else failwithf "Expected type to be \"%s\", but it is \"%s\". Make sure to check the type by calling \"Is%s\" method before calling \"As%s\" method." name (this.GetName()) name name @@> + let invoker (args: Expr list) = + <@@ let this: RecordBase = %%args.[0] + + if this.GetName() = name then + this + else + failwithf + "Expected type to be \"%s\", but it is \"%s\". Make sure to check the type by calling \"Is%s\" method before calling \"As%s\" method." + name + (this.GetName()) + name + name @@> + ProvidedMethod("As" + name, [], tdef, invoker) + let tryAsType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - if this.GetName() = name then Some this - else None @@> - ProvidedMethod("TryAs" + name, [], typedefof<_ option>.MakeGenericType(tdef), invoker) + let invoker (args: Expr list) = + <@@ let this: RecordBase = %%args.[0] + if this.GetName() = name then Some this else None @@> + + ProvidedMethod("TryAs" + name, [], typedefof<_ option>.MakeGenericType (tdef), invoker) + let isType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase + let invoker (args: Expr list) = + <@@ let this: RecordBase = %%args.[0] this.GetName() = name @@> + ProvidedMethod("Is" + name, [], typeof, invoker) - let members : MemberInfo list = [asType; tryAsType; isType] + + let members: MemberInfo list = [ asType; tryAsType; isType ] members) - let propertiesGetter() = bdef.GetRecordProperties() @ properties |> List.map (fun p -> p.Name, p.Alias, p.Type) + + let propertiesGetter () = bdef.GetRecordProperties() @ properties |> List.map (fun p -> p.Name, p.Alias, p.Type) addConstructorDelayed propertiesGetter | _ -> - let propertiesGetter() = properties |> List.map (fun p -> p.Name, p.Alias, p.Type) + let propertiesGetter () = properties |> List.map (fun p -> p.Name, p.Alias, p.Type) addConstructorDelayed propertiesGetter + tdef.SetRecordProperties(properties) tdef - let preBuildProvidedType(metadata : ProvidedTypeMetadata, baseType : Type option) = + let preBuildProvidedType (metadata: ProvidedTypeMetadata, baseType: Type option) = let baseType = Option.defaultValue typeof baseType let name = metadata.Name.FirstCharUpper() let tdef = ProvidedRecordTypeDefinition(name, Some baseType) @@ -265,39 +337,49 @@ module internal ProvidedRecord = #nowarn "10001" module internal ProvidedOperationResult = - let makeProvidedType(operationType : Type) = - let tdef = ProvidedTypeDefinition("OperationResult", Some typeof, nonNullable = true) - tdef.AddMemberDelayed(fun _ -> - let getterCode (args : Expr list) = - <@@ let this = %%args.[0] : OperationResultBase + let makeProvidedType (operationType: Type) = + let tdef = + ProvidedTypeDefinition("OperationResult", Some typeof, nonNullable = true) + + tdef.AddMemberDelayed (fun _ -> + let getterCode (args: Expr list) = + <@@ let this: OperationResultBase = %%args.[0] this.RawData @@> + let prop = ProvidedProperty("Data", operationType, getterCode) prop.AddXmlDoc("Contains the data returned by the operation on the server.") prop) + tdef module internal ProvidedOperation = - let makeProvidedType(actualQuery : string, - operationDefinition : OperationDefinition, - operationTypeName : string, - operationFieldsExpr : Expr, - schemaTypes: Map, - schemaProvidedTypes : Map, - operationType : Type, - contextInfo : GraphQLRuntimeContextInfo option, - uploadInputTypeName : string option, - className : string, - explicitOptionalParameters: bool) = + let makeProvidedType + ( + actualQuery: string, + operationDefinition: OperationDefinition, + operationTypeName: string, + operationFieldsExpr: Expr, + schemaTypes: Map, + schemaProvidedTypes: Map, + operationType: Type, + contextInfo: GraphQLRuntimeContextInfo option, + uploadInputTypeName: string option, + className: string, + explicitOptionalParameters: bool + ) = let tdef = ProvidedTypeDefinition(className, Some typeof) tdef.AddXmlDoc("Represents a GraphQL operation on the server.") - tdef.AddMembersDelayed(fun _ -> - let operationResultDef = ProvidedOperationResult.makeProvidedType(operationType) + + tdef.AddMembersDelayed (fun _ -> + let operationResultDef = ProvidedOperationResult.makeProvidedType (operationType) + let isScalar (typeName: string) = match schemaTypes.TryFind typeName with | Some introspectionType -> introspectionType.Kind = TypeKind.SCALAR | None -> false + let variables = - let rec mapVariable (variableName : string) (variableType : InputType) = + let rec mapVariable (variableName: string) (variableType: TypeReference) = match variableType with | NamedType typeName -> match uploadInputTypeName with @@ -314,14 +396,18 @@ module internal ProvidedOperation = | ListType itype -> let name, t = mapVariable variableName itype name, t |> TypeMapping.makeArray |> TypeMapping.makeOption - | NonNullType itype -> + | NonNullNameType itype -> let name, t = mapVariable variableName itype name, TypeMapping.unwrapOption t - operationDefinition.VariableDefinitions |> List.map (fun vdef -> mapVariable vdef.VariableName vdef.Type) - let buildVariablesExprFromArgs (varNames : string list) (args : Expr list) = - let mapVariableExpr (name : string) (value : Expr) = + + operationDefinition.VariableDefinitions + |> List.map (fun vdef -> mapVariable vdef.VariableName vdef.Type) + + let buildVariablesExprFromArgs (varNames: string list) (args: Expr list) = + let mapVariableExpr (name: string) (value: Expr) = let value = Expr.Coerce(value, typeof) - <@@ let rec mapVariableValue (value : obj) = + + <@@ let rec mapVariableValue (value: obj) = match value with | null -> null | :? string -> value // We need this because strings are enumerables, and we don't want to enumerate them recursively as an object @@ -330,18 +416,27 @@ module internal ProvidedOperation = | OptionValue v -> v |> Option.map mapVariableValue |> box | EnumerableValue v -> v |> Array.map mapVariableValue |> box | v -> v + (name, mapVariableValue %%value) @@> + let args = let varArgs = List.skip (args.Length - variables.Length) args (varNames, varArgs) ||> List.map2 mapVariableExpr + Expr.NewArray(typeof, args) + let defaultContextExpr = match contextInfo with | Some info -> let serverUrl = info.ServerUrl let headerNames = info.HttpHeaders |> Seq.map fst |> Array.ofSeq let headerValues = info.HttpHeaders |> Seq.map snd |> Array.ofSeq - <@@ { ServerUrl = serverUrl; HttpHeaders = Array.zip headerNames headerValues; Connection = new GraphQLClientConnection() } @@> + + <@@ { + ServerUrl = serverUrl + HttpHeaders = Array.zip headerNames headerValues + Connection = new GraphQLClientConnection() + } @@> | None -> <@@ Unchecked.defaultof @@> // We need to use the combination strategy to generate overloads for variables in the Run/AsyncRun methods. // The strategy follows the same principle with ProvidedRecord constructor overloads, @@ -349,44 +444,60 @@ module internal ProvidedOperation = // if no default context is provided. let methodOverloadDefinitions = let overloadsWithoutContext = - let optionalVariables, requiredVariables = - variables |> List.partition (fun (_, t) -> isOption t) + let optionalVariables, requiredVariables = variables |> List.partition (fun (_, t) -> isOption t) + if explicitOptionalParameters then - [requiredVariables @ optionalVariables] + [ requiredVariables @ optionalVariables ] else List.combinations optionalVariables |> List.map (fun (optionalVariables, _) -> - let optionalVariables = optionalVariables |> List.map (fun (name, t) -> name, (TypeMapping.unwrapOption t)) + let optionalVariables = + optionalVariables |> List.map (fun (name, t) -> name, (TypeMapping.unwrapOption t)) + requiredVariables @ optionalVariables) + let overloadsWithContext = overloadsWithoutContext |> List.map (fun var -> ("runtimeContext", typeof) :: var) + match contextInfo with | Some _ -> overloadsWithoutContext @ overloadsWithContext | None -> overloadsWithContext // Multipart requests should only be used when the user specifies a upload type name AND the type // is present in the query as an input value. If not, we fallback to classic requests. let shouldUseMultipartRequest = - let rec existsUploadType (foundTypes : ProvidedTypeDefinition list) (t : Type) = + let rec existsUploadType (foundTypes: ProvidedTypeDefinition list) (t: Type) = match t with - | :? ProvidedTypeDefinition as tdef when not (List.contains tdef foundTypes) -> tdef.DeclaredProperties |> Seq.exists ((fun p -> p.PropertyType) >> existsUploadType (tdef :: foundTypes)) + | :? ProvidedTypeDefinition as tdef when not (List.contains tdef foundTypes) -> + tdef.DeclaredProperties + |> Seq.exists ((fun p -> p.PropertyType) >> existsUploadType (tdef :: foundTypes)) | Option t -> existsUploadType foundTypes t | Array t -> existsUploadType foundTypes t | _ -> t = typeof + variables |> Seq.exists (snd >> existsUploadType []) - let runMethodOverloads : MemberInfo list = + + let runMethodOverloads: MemberInfo list = let operationName = Option.toObj operationDefinition.Name - methodOverloadDefinitions |> List.map (fun overloadParameters -> - let variableNames = overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") - let invoker (args : Expr list) = + + methodOverloadDefinitions + |> List.map (fun overloadParameters -> + let variableNames = + overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") + + let invoker (args: Expr list) = // First arg is the operation instance, second should be the context, if the overload asks for one. // We determine it by seeing if the variable names have one less item than the arguments without the instance. let argsWithoutInstance = args.Tail + let variableArgs, isDefaultContext, context = - if argsWithoutInstance.Length - variableNames.Length = 1 - then argsWithoutInstance.Tail, false, argsWithoutInstance.Head - else argsWithoutInstance, true, defaultContextExpr + if argsWithoutInstance.Length - variableNames.Length = 1 then + argsWithoutInstance.Tail, false, argsWithoutInstance.Head + else + argsWithoutInstance, true, defaultContextExpr + let variables = buildVariablesExprFromArgs variableNames variableArgs + let variables = if explicitOptionalParameters then <@@ (%%variables: (string * obj) []) @@ -396,38 +507,63 @@ module internal ProvidedOperation = | _ -> true) @@> else variables - <@@ let context = %%context : GraphQLProviderRuntimeContext - let request = - { ServerUrl = context.ServerUrl - HttpHeaders = context.HttpHeaders - OperationName = Option.ofObj operationName - Query = actualQuery - Variables = %%variables } - let response = - if shouldUseMultipartRequest - then Tracer.runAndMeasureExecutionTime "Ran a multipart GraphQL query request" (fun _ -> GraphQLClient.sendMultipartRequest context.Connection request) - else Tracer.runAndMeasureExecutionTime "Ran a GraphQL query request" (fun _ -> GraphQLClient.sendRequest context.Connection request) - let responseJson = Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> JsonValue.Parse response) - // If the user does not provide a context, we should dispose the default one after running the query - if isDefaultContext then (context :> IDisposable).Dispose() - OperationResultBase(responseJson, %%operationFieldsExpr, operationTypeName) @@> - let methodParameters = overloadParameters |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + + <@@ let context: GraphQLProviderRuntimeContext = %%context + + let request = + { + ServerUrl = context.ServerUrl + HttpHeaders = context.HttpHeaders + OperationName = Option.ofObj operationName + Query = actualQuery + Variables = %%variables + } + + let response = + if shouldUseMultipartRequest then + Tracer.runAndMeasureExecutionTime "Ran a multipart GraphQL query request" (fun _ -> + GraphQLClient.sendMultipartRequest context.Connection request) + else + Tracer.runAndMeasureExecutionTime "Ran a GraphQL query request" (fun _ -> + GraphQLClient.sendRequest context.Connection request) + + let responseJson = + Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> + JsonValue.Parse response) + // If the user does not provide a context, we should dispose the default one after running the query + if isDefaultContext then (context :> IDisposable).Dispose() + OperationResultBase(responseJson, %%operationFieldsExpr, operationTypeName) @@> + + let methodParameters = + overloadParameters + |> List.map (fun (name, t) -> + ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + let methodDef = ProvidedMethod("Run", methodParameters, operationResultDef, invoker) methodDef.AddXmlDoc("Executes the operation on the server and fetch its results.") upcast methodDef) - let asyncRunMethodOverloads : MemberInfo list = + + let asyncRunMethodOverloads: MemberInfo list = let operationName = Option.toObj operationDefinition.Name - methodOverloadDefinitions |> List.map (fun overloadParameters -> - let variableNames = overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") - let invoker (args : Expr list) = + + methodOverloadDefinitions + |> List.map (fun overloadParameters -> + let variableNames = + overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") + + let invoker (args: Expr list) = // First arg is the operation instance, second should be the context, if the overload asks for one. // We determine it by seeing if the variable names have one less item than the arguments without the instance. let argsWithoutInstance = args.Tail + let variableArgs, isDefaultContext, context = - if argsWithoutInstance.Length - variableNames.Length = 1 - then argsWithoutInstance.Tail, false, argsWithoutInstance.Head - else argsWithoutInstance, true, defaultContextExpr + if argsWithoutInstance.Length - variableNames.Length = 1 then + argsWithoutInstance.Tail, false, argsWithoutInstance.Head + else + argsWithoutInstance, true, defaultContextExpr + let variables = buildVariablesExprFromArgs variableNames variableArgs + let variables = if explicitOptionalParameters then <@@ (%%variables: (string * obj) []) @@ -437,104 +573,187 @@ module internal ProvidedOperation = | _ -> true) @@> else variables - <@@ let context = %%context : GraphQLProviderRuntimeContext + + <@@ let context: GraphQLProviderRuntimeContext = %%context + let request = - { ServerUrl = context.ServerUrl - HttpHeaders = context.HttpHeaders - OperationName = Option.ofObj operationName - Query = actualQuery - Variables = %%variables } + { + ServerUrl = context.ServerUrl + HttpHeaders = context.HttpHeaders + OperationName = Option.ofObj operationName + Query = actualQuery + Variables = %%variables + } + async { let! response = - if shouldUseMultipartRequest - then Tracer.asyncRunAndMeasureExecutionTime "Ran a multipart GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendMultipartRequestAsync context.Connection request) - else Tracer.asyncRunAndMeasureExecutionTime "Ran a GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendRequestAsync context.Connection request) - let responseJson = Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> JsonValue.Parse response) + if shouldUseMultipartRequest then + Tracer.asyncRunAndMeasureExecutionTime + "Ran a multipart GraphQL query request asynchronously" + (fun _ -> GraphQLClient.sendMultipartRequestAsync context.Connection request) + else + Tracer.asyncRunAndMeasureExecutionTime + "Ran a GraphQL query request asynchronously" + (fun _ -> GraphQLClient.sendRequestAsync context.Connection request) + + let responseJson = + Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> + JsonValue.Parse response) // If the user does not provide a context, we should dispose the default one after running the query if isDefaultContext then (context :> IDisposable).Dispose() return OperationResultBase(responseJson, %%operationFieldsExpr, operationTypeName) } @@> - let methodParameters = overloadParameters |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) - let methodDef = ProvidedMethod("AsyncRun", methodParameters, TypeMapping.makeAsync operationResultDef, invoker) + + let methodParameters = + overloadParameters + |> List.map (fun (name, t) -> + ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + + let methodDef = + ProvidedMethod("AsyncRun", methodParameters, TypeMapping.makeAsync operationResultDef, invoker) + methodDef.AddXmlDoc("Executes the operation asynchronously on the server and fetch its results.") upcast methodDef) + let parseResultDef = - let invoker (args : Expr list) = <@@ OperationResultBase(JsonValue.Parse %%args.[1], %%operationFieldsExpr, operationTypeName) @@> - let parameters = [ProvidedParameter("responseJson", typeof)] + let invoker (args: Expr list) = + <@@ OperationResultBase(JsonValue.Parse %%args.[1], %%operationFieldsExpr, operationTypeName) @@> + + let parameters = [ ProvidedParameter("responseJson", typeof) ] let methodDef = ProvidedMethod("ParseResult", parameters, operationResultDef, invoker) - methodDef.AddXmlDoc("Parses a JSON response that matches the response pattern of the current operation into a OperationResult type.") + + methodDef.AddXmlDoc( + "Parses a JSON response that matches the response pattern of the current operation into a OperationResult type." + ) + methodDef - let members : MemberInfo list = [operationResultDef; parseResultDef] @ runMethodOverloads @ asyncRunMethodOverloads + + let members: MemberInfo list = + [ operationResultDef; parseResultDef ] @ runMethodOverloads @ asyncRunMethodOverloads + members) + tdef type internal ProvidedOperationMetadata = - { OperationType : Type - UploadInputTypeName : string option - TypeWrapper : ProvidedTypeDefinition } + { + OperationType: Type + UploadInputTypeName: string option + TypeWrapper: ProvidedTypeDefinition + } module internal Provider = - let getOperationMetadata (schemaTypes : Map, uploadInputTypeName : string option, enumProvidedTypes : Map, operationAstFields, operationTypeRef, explicitOptionalParameters: bool) = + let getOperationMetadata + ( + schemaTypes: Map, + uploadInputTypeName: string option, + enumProvidedTypes: Map, + operationAstFields, + operationTypeRef, + explicitOptionalParameters: bool + ) = let generateWrapper name = let rec resolveWrapperName actual = - if schemaTypes.ContainsKey(actual) - then resolveWrapperName (actual + "Fields") - else actual + if schemaTypes.ContainsKey(actual) then resolveWrapperName (actual + "Fields") else actual + ProvidedTypeDefinition(resolveWrapperName name, None, isSealed = true) + let wrappersByPath = Dictionary() let rootWrapper = generateWrapper "Types" wrappersByPath.Add([], rootWrapper) - let rec getWrapper (path : string list) = - if wrappersByPath.ContainsKey path - then wrappersByPath.[path] + + let rec getWrapper (path: string list) = + if wrappersByPath.ContainsKey path then + wrappersByPath.[path] else let wrapper = generateWrapper (path.Head.FirstCharUpper() + "Fields") + let upperWrapper = let path = path.Tail - if wrappersByPath.ContainsKey(path) - then wrappersByPath.[path] - else getWrapper path + if wrappersByPath.ContainsKey(path) then wrappersByPath.[path] else getWrapper path + upperWrapper.AddMember(wrapper) wrappersByPath.Add(path, wrapper) wrapper - let includeType (path : string list) (t : ProvidedTypeDefinition) = + + let includeType (path: string list) (t: ProvidedTypeDefinition) = let wrapper = getWrapper path wrapper.AddMember(t) + let providedTypes = Dictionary() - let rec getProvidedType (providedTypes : Dictionary) (schemaTypes : Map) (path : Path) (astFields : AstFieldInfo list) (tref : IntrospectionTypeRef) : Type = + + let rec getProvidedType + (providedTypes: Dictionary) + (schemaTypes: Map) + (path: Path) + (astFields: AstFieldInfo list) + (tref: IntrospectionTypeRef) + : Type = match tref.Kind with - | TypeKind.SCALAR when tref.Name.IsSome -> TypeMapping.mapScalarType uploadInputTypeName tref.Name.Value |> TypeMapping.makeOption - | _ when uploadInputTypeName.IsSome && tref.Name.IsSome && uploadInputTypeName.Value = tref.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when tref.Name.IsNone && tref.OfType.IsSome -> getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value |> TypeMapping.unwrapOption - | TypeKind.LIST when tref.Name.IsNone && tref.OfType.IsSome -> getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value |> TypeMapping.makeArray |> TypeMapping.makeOption + | TypeKind.SCALAR when tref.Name.IsSome -> + TypeMapping.mapScalarType uploadInputTypeName tref.Name.Value |> TypeMapping.makeOption + | _ when uploadInputTypeName.IsSome && tref.Name.IsSome && uploadInputTypeName.Value = tref.Name.Value -> + uploadTypeIsNotScalar uploadInputTypeName.Value + | TypeKind.NON_NULL when tref.Name.IsNone && tref.OfType.IsSome -> + getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value + |> TypeMapping.unwrapOption + | TypeKind.LIST when tref.Name.IsNone && tref.OfType.IsSome -> + getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value + |> TypeMapping.makeArray + |> TypeMapping.makeOption | TypeKind.ENUM when tref.Name.IsSome -> match enumProvidedTypes.TryFind(tref.Name.Value) with | Some providedEnum -> TypeMapping.makeOption providedEnum - | None -> failwithf "Could not find a enum type based on a type reference. The reference is an \"%s\" enum, but that enum was not found in the introspection schema." tref.Name.Value - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION) when tref.Name.IsSome -> - if providedTypes.ContainsKey(path, tref.Name.Value) - then TypeMapping.makeOption providedTypes.[path, tref.Name.Value] + | None -> + failwithf + "Could not find a enum type based on a type reference. The reference is an \"%s\" enum, but that enum was not found in the introspection schema." + tref.Name.Value + | (TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.UNION) when tref.Name.IsSome -> + if providedTypes.ContainsKey(path, tref.Name.Value) then + TypeMapping.makeOption providedTypes.[path, tref.Name.Value] else let getIntrospectionFields typeName = - if schemaTypes.ContainsKey(typeName) - then schemaTypes.[typeName].Fields |> Option.defaultValue [||] - else failwithf "Could not find a schema type based on a type reference. The reference is to a \"%s\" type, but that type was not found in the schema types." typeName - let getPropertyMetadata typeName (info : AstFieldInfo) : RecordPropertyMetadata = + if schemaTypes.ContainsKey(typeName) then + schemaTypes.[typeName].Fields |> Option.defaultValue [||] + else + failwithf + "Could not find a schema type based on a type reference. The reference is to a \"%s\" type, but that type was not found in the schema types." + typeName + + let getPropertyMetadata typeName (info: AstFieldInfo) : RecordPropertyMetadata = let ifield = - match getIntrospectionFields typeName |> Array.tryFind(fun f -> f.Name = info.Name) with + match getIntrospectionFields typeName |> Array.tryFind (fun f -> f.Name = info.Name) with | Some ifield -> ifield - | None -> failwithf "Could not find field \"%s\" of type \"%s\". The schema type does not have a field with the specified name." info.Name tref.Name.Value + | None -> + failwithf + "Could not find field \"%s\" of type \"%s\". The schema type does not have a field with the specified name." + info.Name + tref.Name.Value + let path = info.AliasOrName :: path let astFields = info.Fields let ftype = getProvidedType providedTypes schemaTypes path astFields ifield.Type - { Name = info.Name; Alias = info.Alias; Description = ifield.Description; DeprecationReason = ifield.DeprecationReason; Type = ftype } + + { + Name = info.Name + Alias = info.Alias + Description = ifield.Description + DeprecationReason = ifield.DeprecationReason + Type = ftype + } + let fragmentProperties = astFields - |> List.choose (function FragmentField f when f.TypeCondition <> tref.Name.Value -> Some f | _ -> None) + |> List.choose (function + | FragmentField f when f.TypeCondition <> tref.Name.Value -> Some f + | _ -> None) |> List.groupBy (fun field -> field.TypeCondition) |> List.map (fun (typeCondition, fields) -> let conditionFields = fields |> List.distinctBy (fun x -> x.AliasOrName) |> List.map FragmentField typeCondition, List.map (getPropertyMetadata typeCondition) conditionFields) + let baseProperties = astFields |> List.choose (fun x -> @@ -544,118 +763,205 @@ module internal Provider = | _ -> None) |> List.distinctBy (fun x -> x.AliasOrName) |> List.map (getPropertyMetadata tref.Name.Value) + let baseType = - let metadata : ProvidedTypeMetadata = { Name = tref.Name.Value; Description = tref.Description } - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) + let metadata: ProvidedTypeMetadata = + { + Name = tref.Name.Value + Description = tref.Description + } + + let tdef = ProvidedRecord.preBuildProvidedType (metadata, None) providedTypes.Add((path, tref.Name.Value), tdef) includeType path tdef - ProvidedRecord.makeProvidedType(tdef, baseProperties, explicitOptionalParameters) + ProvidedRecord.makeProvidedType (tdef, baseProperties, explicitOptionalParameters) + let createFragmentType (typeName, properties) = let itype = - if schemaTypes.ContainsKey(typeName) - then schemaTypes.[typeName] - else failwithf "Could not find schema type based on the query. Type \"%s\" does not exist on the schema definition." typeName - let metadata : ProvidedTypeMetadata = { Name = itype.Name; Description = itype.Description } - let tdef = ProvidedRecord.preBuildProvidedType(metadata, Some (upcast baseType)) + if schemaTypes.ContainsKey(typeName) then + schemaTypes.[typeName] + else + failwithf + "Could not find schema type based on the query. Type \"%s\" does not exist on the schema definition." + typeName + + let metadata: ProvidedTypeMetadata = + { + Name = itype.Name + Description = itype.Description + } + + let tdef = ProvidedRecord.preBuildProvidedType (metadata, Some(upcast baseType)) providedTypes.Add((path, typeName), tdef) includeType path tdef - ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) |> ignore + ProvidedRecord.makeProvidedType (tdef, properties, explicitOptionalParameters) |> ignore + fragmentProperties |> List.iter createFragmentType TypeMapping.makeOption baseType - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." + | _ -> + failwith + "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." + let operationType = getProvidedType providedTypes schemaTypes [] operationAstFields operationTypeRef - { OperationType = operationType - UploadInputTypeName = uploadInputTypeName - TypeWrapper = rootWrapper } - let getSchemaProvidedTypes(schema : IntrospectionSchema, uploadInputTypeName : string option, explicitOptionalParameters: bool) = + { + OperationType = operationType + UploadInputTypeName = uploadInputTypeName + TypeWrapper = rootWrapper + } + + let getSchemaProvidedTypes + ( + schema: IntrospectionSchema, + uploadInputTypeName: string option, + explicitOptionalParameters: bool + ) = let providedTypes = ref Map.empty let schemaTypes = TypeMapping.getSchemaTypes schema - let getSchemaType (tref : IntrospectionTypeRef) = + + let getSchemaType (tref: IntrospectionTypeRef) = match tref.Name with | Some name -> match schemaTypes.TryFind(name) with | Some itype -> itype | None -> failwithf "Type \"%s\" was not found on the schema custom types." name | None -> failwith "Expected schema type to have a name, but it does not have one." - let typeModifier (modifier : Type -> Type) (metadata : RecordPropertyMetadata) = { metadata with Type = modifier metadata.Type } + + let typeModifier (modifier: Type -> Type) (metadata: RecordPropertyMetadata) = + { metadata with + Type = modifier metadata.Type + } + let makeOption = typeModifier TypeMapping.makeOption let makeArrayOption = typeModifier (TypeMapping.makeArray >> TypeMapping.makeOption) let unwrapOption = typeModifier TypeMapping.unwrapOption - let ofFieldType (field : IntrospectionField) = { field with Type = field.Type.OfType.Value } - let ofInputFieldType (field : IntrospectionInputVal) = { field with Type = field.Type.OfType.Value } - let rec resolveFieldMetadata (field : IntrospectionField) : RecordPropertyMetadata = + + let ofFieldType (field: IntrospectionField) = + { field with + Type = field.Type.OfType.Value + } + + let ofInputFieldType (field: IntrospectionInputVal) = + { field with + Type = field.Type.OfType.Value + } + + let rec resolveFieldMetadata (field: IntrospectionField) : RecordPropertyMetadata = match field.Type.Kind with | TypeKind.SCALAR when field.Type.Name.IsSome -> let providedType = TypeMapping.mapScalarType uploadInputTypeName field.Type.Name.Value - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = field.DeprecationReason - Type = providedType } + + { + Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = field.DeprecationReason + Type = providedType + } |> makeOption - | _ when uploadInputTypeName.IsSome && field.Type.Name.IsSome && uploadInputTypeName.Value = field.Type.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofFieldType field |> resolveFieldMetadata |> unwrapOption - | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofFieldType field |> resolveFieldMetadata |> makeArrayOption - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.INPUT_OBJECT | TypeKind.UNION | TypeKind.ENUM) when field.Type.Name.IsSome -> + | _ when + uploadInputTypeName.IsSome + && field.Type.Name.IsSome + && uploadInputTypeName.Value = field.Type.Name.Value + -> + uploadTypeIsNotScalar uploadInputTypeName.Value + | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> + ofFieldType field |> resolveFieldMetadata |> unwrapOption + | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> + ofFieldType field |> resolveFieldMetadata |> makeArrayOption + | (TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.INPUT_OBJECT + | TypeKind.UNION + | TypeKind.ENUM) when field.Type.Name.IsSome -> let itype = getSchemaType field.Type let providedType = resolveProvidedType itype - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = field.DeprecationReason - Type = providedType } + + { + Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = field.DeprecationReason + Type = providedType + } |> makeOption - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." - and resolveInputFieldMetadata (field : IntrospectionInputVal) : RecordPropertyMetadata = + | _ -> + failwith + "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." + + and resolveInputFieldMetadata (field: IntrospectionInputVal) : RecordPropertyMetadata = match field.Type.Kind with | TypeKind.SCALAR when field.Type.Name.IsSome -> let providedType = TypeMapping.mapScalarType uploadInputTypeName field.Type.Name.Value - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = None - Type = providedType } + + { + Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = None + Type = providedType + } |> makeOption - | _ when uploadInputTypeName.IsSome && field.Type.Name.IsSome && uploadInputTypeName.Value = field.Type.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofInputFieldType field |> resolveInputFieldMetadata |> unwrapOption - | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofInputFieldType field |> resolveInputFieldMetadata |> makeArrayOption - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.INPUT_OBJECT | TypeKind.UNION | TypeKind.ENUM) when field.Type.Name.IsSome -> + | _ when + uploadInputTypeName.IsSome + && field.Type.Name.IsSome + && uploadInputTypeName.Value = field.Type.Name.Value + -> + uploadTypeIsNotScalar uploadInputTypeName.Value + | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> + ofInputFieldType field |> resolveInputFieldMetadata |> unwrapOption + | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> + ofInputFieldType field |> resolveInputFieldMetadata |> makeArrayOption + | (TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.INPUT_OBJECT + | TypeKind.UNION + | TypeKind.ENUM) when field.Type.Name.IsSome -> let itype = getSchemaType field.Type let providedType = resolveProvidedType itype - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = None - Type = providedType } + + { + Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = None + Type = providedType + } |> makeOption - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." - and resolveProvidedType (itype : IntrospectionType) : ProvidedTypeDefinition = - if (!providedTypes).ContainsKey(itype.Name) - then (!providedTypes).[itype.Name] + | _ -> + failwith + "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." + + and resolveProvidedType (itype: IntrospectionType) : ProvidedTypeDefinition = + if (!providedTypes).ContainsKey(itype.Name) then + (!providedTypes).[itype.Name] else - let metadata = { Name = itype.Name; Description = itype.Description } + let metadata = + { + Name = itype.Name + Description = itype.Description + } + match itype.Kind with | TypeKind.OBJECT -> - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) + let tdef = ProvidedRecord.preBuildProvidedType (metadata, None) providedTypes := (!providedTypes).Add(itype.Name, tdef) + let properties = - itype.Fields - |> Option.defaultValue [||] - |> Array.map resolveFieldMetadata - |> List.ofArray - upcast ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) + itype.Fields |> Option.defaultValue [||] |> Array.map resolveFieldMetadata |> List.ofArray + + upcast ProvidedRecord.makeProvidedType (tdef, properties, explicitOptionalParameters) | TypeKind.INPUT_OBJECT -> - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) + let tdef = ProvidedRecord.preBuildProvidedType (metadata, None) providedTypes := (!providedTypes).Add(itype.Name, tdef) + let properties = - itype.InputFields - |> Option.defaultValue [||] - |> Array.map resolveInputFieldMetadata - |> List.ofArray - upcast ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) - | TypeKind.INTERFACE | TypeKind.UNION -> - let bdef = ProvidedInterface.makeProvidedType(metadata) + itype.InputFields |> Option.defaultValue [||] |> Array.map resolveInputFieldMetadata |> List.ofArray + + upcast ProvidedRecord.makeProvidedType (tdef, properties, explicitOptionalParameters) + | TypeKind.INTERFACE + | TypeKind.UNION -> + let bdef = ProvidedInterface.makeProvidedType (metadata) providedTypes := (!providedTypes).Add(itype.Name, bdef) bdef | TypeKind.ENUM -> @@ -663,278 +969,472 @@ module internal Provider = match itype.EnumValues with | Some values -> values |> Array.map (fun value -> value.Name) | None -> [||] - let tdef = ProvidedEnum.makeProvidedType(itype.Name, items) + + let tdef = ProvidedEnum.makeProvidedType (itype.Name, items) providedTypes := (!providedTypes).Add(itype.Name, tdef) tdef | _ -> failwithf "Type \"%s\" is not a Record, Union, Enum, Input Object, or Interface type." itype.Name - let ignoredKinds = [TypeKind.SCALAR; TypeKind.LIST; TypeKind.NON_NULL] - schemaTypes |> Map.iter (fun _ itype -> if not (List.contains itype.Kind ignoredKinds) then resolveProvidedType itype |> ignore) - let possibleTypes (itype : IntrospectionType) = + + let ignoredKinds = [ TypeKind.SCALAR; TypeKind.LIST; TypeKind.NON_NULL ] + + schemaTypes + |> Map.iter (fun _ itype -> if not (List.contains itype.Kind ignoredKinds) then resolveProvidedType itype |> ignore) + + let possibleTypes (itype: IntrospectionType) = match itype.PossibleTypes with | Some trefs -> trefs |> Array.map (getSchemaType >> resolveProvidedType) | None -> [||] + let getProvidedType typeName = match (!providedTypes).TryFind(typeName) with | Some ptype -> ptype | None -> failwithf "Expected to find a type \"%s\" on the schema type map, but it was not found." typeName + schemaTypes |> Seq.iter (fun kvp -> if kvp.Value.Kind = TypeKind.INTERFACE || kvp.Value.Kind = TypeKind.UNION then let itype = getProvidedType kvp.Value.Name let ptypes = possibleTypes kvp.Value ptypes |> Array.iter (fun ptype -> ptype.AddInterfaceImplementation(itype))) + !providedTypes - let makeProvidedType(asm : Assembly, ns : string, resolutionFolder : string) = + let makeProvidedType (asm: Assembly, ns: string, resolutionFolder: string) = let generator = ProvidedTypeDefinition(asm, ns, "GraphQLProvider", None) + let staticParams = - [ ProvidedStaticParameter("introspection", typeof) - ProvidedStaticParameter("httpHeaders", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) - ProvidedStaticParameter("uploadInputTypeName", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("clientQueryValidation", typeof, parameterDefaultValue = true) - ProvidedStaticParameter("explicitOptionalParameters", typeof, parameterDefaultValue = false) ] - generator.DefineStaticParameters(staticParams, fun tname args -> - let clientQueryValidation : bool = downcast args.[4] - let explicitOptionalParameters : bool = downcast args.[5] - let introspectionLocation = IntrospectionLocation.Create(downcast args.[0], downcast args.[2]) - let httpHeadersLocation = StringLocation.Create(downcast args.[1], resolutionFolder) - let uploadInputTypeName = - let name : string = unbox args.[3] - match name with - | null | "" -> None - | _ -> Some name - let maker = - lazy - let tdef = ProvidedTypeDefinition(asm, ns, tname, None) - tdef.AddXmlDoc("A type provider for GraphQL operations.") - tdef.AddMembersDelayed (fun _ -> - let httpHeaders = HttpHeaders.load httpHeadersLocation - let schemaJson = - match introspectionLocation with - | Uri serverUrl -> - use connection = new GraphQLClientConnection() - GraphQLClient.sendIntrospectionRequest connection serverUrl httpHeaders - | IntrospectionFile path -> - System.IO.File.ReadAllText path - let schema = Serialization.deserializeSchema schemaJson - - let schemaProvidedTypes = getSchemaProvidedTypes(schema, uploadInputTypeName, explicitOptionalParameters) - let typeWrapper = ProvidedTypeDefinition("Types", None, isSealed = true) - typeWrapper.AddMembers(schemaProvidedTypes |> Seq.map (fun kvp -> kvp.Value) |> List.ofSeq) - let operationWrapper = ProvidedTypeDefinition("Operations", None, isSealed = true) - let getContextMethodDef = - let methodParameters = - let serverUrl = - match introspectionLocation with - | Uri serverUrl -> ProvidedParameter("serverUrl", typeof, optionalValue = serverUrl) - | _ -> ProvidedParameter("serverUrl", typeof) - let httpHeaders = ProvidedParameter("httpHeaders", typeof>, optionalValue = null) - let connectionFactory = ProvidedParameter("connectionFactory", typeof GraphQLClientConnection>, optionalValue = null) - [serverUrl; httpHeaders; connectionFactory] - let defaultHttpHeadersExpr = - let names = httpHeaders |> Seq.map fst |> Array.ofSeq - let values = httpHeaders |> Seq.map snd |> Array.ofSeq - Expr.Coerce(<@@ Array.zip names values @@>, typeof>) - let invoker (args : Expr list) = - let serverUrl = args.[0] - <@@ let httpHeaders = - match %%args.[1] : seq with - | null -> %%defaultHttpHeadersExpr - | argHeaders -> argHeaders + [ + ProvidedStaticParameter("introspection", typeof) + ProvidedStaticParameter("httpHeaders", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) + ProvidedStaticParameter("uploadInputTypeName", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("clientQueryValidation", typeof, parameterDefaultValue = true) + ProvidedStaticParameter("explicitOptionalParameters", typeof, parameterDefaultValue = false) + ] + + generator.DefineStaticParameters( + staticParams, + fun tname args -> + let clientQueryValidation: bool = downcast args.[4] + let explicitOptionalParameters: bool = downcast args.[5] + let introspectionLocation = IntrospectionLocation.Create(downcast args.[0], downcast args.[2]) + let httpHeadersLocation = StringLocation.Create(downcast args.[1], resolutionFolder) + + let uploadInputTypeName = + let name: string = unbox args.[3] + + match name with + | null + | "" -> None + | _ -> Some name + + let maker = + lazy + let tdef = ProvidedTypeDefinition(asm, ns, tname, None) + tdef.AddXmlDoc("A type provider for GraphQL operations.") + + tdef.AddMembersDelayed (fun _ -> + let httpHeaders = HttpHeaders.load httpHeadersLocation + + let schemaJson = + match introspectionLocation with + | Uri serverUrl -> + use connection = new GraphQLClientConnection() + GraphQLClient.sendIntrospectionRequest connection serverUrl httpHeaders + | IntrospectionFile path -> System.IO.File.ReadAllText path + + let schema = Serialization.deserializeSchema schemaJson + + let schemaProvidedTypes = + getSchemaProvidedTypes (schema, uploadInputTypeName, explicitOptionalParameters) + + let typeWrapper = ProvidedTypeDefinition("Types", None, isSealed = true) + typeWrapper.AddMembers(schemaProvidedTypes |> Seq.map (fun kvp -> kvp.Value) |> List.ofSeq) + let operationWrapper = ProvidedTypeDefinition("Operations", None, isSealed = true) + + let getContextMethodDef = + let methodParameters = + let serverUrl = + match introspectionLocation with + | Uri serverUrl -> + ProvidedParameter("serverUrl", typeof, optionalValue = serverUrl) + | _ -> ProvidedParameter("serverUrl", typeof) + + let httpHeaders = + ProvidedParameter("httpHeaders", typeof>, optionalValue = null) + let connectionFactory = - match %%args.[2] : unit -> GraphQLClientConnection with - | argHeaders when obj.Equals(argHeaders, null) -> fun () -> new GraphQLClientConnection() - | argHeaders -> argHeaders - { ServerUrl = %%serverUrl; HttpHeaders = httpHeaders; Connection = connectionFactory() } @@> - ProvidedMethod("GetContext", methodParameters, typeof, invoker, isStatic = true) - let operationMethodDef = - let staticParams = - [ ProvidedStaticParameter("query", typeof) - ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) - ProvidedStaticParameter("operationName", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("typeName", typeof, parameterDefaultValue = "") ] - let staticMethodDef = ProvidedMethod("Operation", [], typeof, isStatic = true) - let instanceBuilder (methodName : string) (args : obj []) = - let queryLocation = StringLocation.Create(downcast args.[0], downcast args.[1]) - let query = - match queryLocation with - | String query -> query - | File path -> System.IO.File.ReadAllText(path) - let queryAst = Parser.parse query - #if IS_DESIGNTIME - let throwExceptionIfValidationFailed (validationResult : ValidationResult) = - let rec formatValidationExceptionMessage(errors : AstError list) = - match errors with - | [] -> "Query validation resulted in invalid query, but no validation messages were produced." - | errors -> - errors - |> List.map (fun err -> - match err.Path with - | Some path -> sprintf "%s Path: %A" err.Message path - | None -> err.Message) - |> List.reduce (fun x y -> x + Environment.NewLine + y) - match validationResult with - | ValidationError msgs -> failwith (formatValidationExceptionMessage msgs) - | Success -> () - let key = { DocumentId = queryAst.GetHashCode(); SchemaId = schema.GetHashCode() } - let refMaker = lazy Validation.Ast.validateDocument schema queryAst - if clientQueryValidation then - refMaker.Force - |> QueryValidationDesignTimeCache.getOrAdd key - |> throwExceptionIfValidationFailed - #endif - let operationName : OperationName option = - match args.[2] :?> string with - | null | "" -> - let operationDefinitions = queryAst.Definitions |> List.filter (function OperationDefinition _ -> true | _ -> false) - match operationDefinitions with - | opdef :: _ -> opdef.Name - | _ -> failwith "Error parsing query. Can not choose a default operation: query document has no operation definitions." - | x -> Some x - let explicitOperationTypeName : TypeName option = - match args.[3] :?> string with - | null | "" -> None - | x -> Some x - let operationDefinition = - queryAst.Definitions - |> List.choose (function OperationDefinition odef -> Some odef | _ -> None) - |> List.find (fun d -> d.Name = operationName) - let operationAstFields = - let infoMap = queryAst.GetInfoMap() - match infoMap.TryFind(operationName) with - | Some fields -> fields - | None -> failwith "Error parsing query. Could not find field information for requested operation." - let operationTypeRef = - let tref = - match operationDefinition.OperationType with - | Query -> schema.QueryType - | Mutation -> - match schema.MutationType with - | Some tref -> tref - | None -> failwith "The operation is a mutation operation, but the schema does not have a mutation type." - | Subscription -> - match schema.SubscriptionType with - | Some tref -> tref - | None -> failwithf "The operation is a subscription operation, but the schema does not have a subscription type." - let tinst = - match tref.Name with - | Some name -> schema.Types |> Array.tryFind (fun t -> t.Name = name) - | None -> None - match tinst with - | Some t -> { tref with Kind = t.Kind } - | None -> failwith "The operation was found in the schema, but it does not have a name." - let schemaTypes = TypeMapping.getSchemaTypes schema - let enumProvidedTypes = schemaProvidedTypes |> Map.filter (fun _ t -> t.BaseType = typeof) - let actualQuery = queryAst.ToQueryString(QueryStringPrintingOptions.IncludeTypeNames).Replace("\r\n", "\n") - let className = - match explicitOperationTypeName, operationDefinition.Name with - | Some name, _ -> name.FirstCharUpper() - | None, Some name -> name.FirstCharUpper() - | None, None -> "Operation" + actualQuery.MD5Hash() - let metadata = getOperationMetadata(schemaTypes, uploadInputTypeName, enumProvidedTypes, operationAstFields, operationTypeRef, explicitOptionalParameters) - let operationTypeName : TypeName = - match operationTypeRef.Name with - | Some name -> name - | None -> failwith "Error parsing query. Operation type does not have a name." - let rec getKind (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getKind tref.OfType.Value - | _ -> tref.Kind - let rec getTypeName (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getTypeName tref.OfType.Value - | _ -> - match tref.Name with - | Some tname -> tname - | None -> failwithf "Expected type kind \"%s\" to have a name, but it does not have a name." (tref.Kind.ToString()) - let rec getIntrospectionType (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getIntrospectionType tref.OfType.Value - | _ -> - let typeName = getTypeName tref - match schemaTypes.TryFind(typeName) with - | Some t -> t - | None -> failwithf "Type \"%s\" was not found in the introspection schema." typeName - let getOperationFields (operationAstFields : AstFieldInfo list) (operationType : IntrospectionType) = - let rec helper (acc : SchemaFieldInfo list) (astFields : AstFieldInfo list) (introspectionType : IntrospectionType) = - match introspectionType.Kind with - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> - match astFields with - | [] -> acc - | field :: tail -> - let throw typeName = failwithf "Field \"%s\" of type \"%s\" was not found in the introspection schema." field.Name typeName - let tref = - match field with - | FragmentField fragf -> - let fragmentType = - let tref = - Option.defaultValue [||] introspectionType.PossibleTypes - |> Array.map getIntrospectionType - |> Array.append [|introspectionType|] - |> Array.tryFind (fun pt -> pt.Name = fragf.TypeCondition) - match tref with - | Some t -> t - | None -> failwithf "Fragment field defines a type condition \"%s\", but that type was not found in the schema definition." fragf.TypeCondition - let field = - fragmentType.Fields - |> Option.map (Array.tryFind (fun f -> f.Name = fragf.Name)) - |> Option.flatten - match field with - | Some f -> f.Type - | None -> throw fragmentType.Name - | TypeField typef -> - let field = - introspectionType.Fields - |> Option.map (Array.tryFind (fun f -> f.Name = typef.Name)) - |> Option.flatten + ProvidedParameter( + "connectionFactory", + typeof GraphQLClientConnection>, + optionalValue = null + ) + + [ serverUrl; httpHeaders; connectionFactory ] + + let defaultHttpHeadersExpr = + let names = httpHeaders |> Seq.map fst |> Array.ofSeq + let values = httpHeaders |> Seq.map snd |> Array.ofSeq + Expr.Coerce(<@@ Array.zip names values @@>, typeof>) + + let invoker (args: Expr list) = + let serverUrl = args.[0] + + <@@ let httpHeaders = + match %%args.[1]: seq with + | null -> %%defaultHttpHeadersExpr + | argHeaders -> argHeaders + + let connectionFactory = + match %%args.[2]: unit -> GraphQLClientConnection with + | argHeaders when obj.Equals(argHeaders, null) -> + fun () -> new GraphQLClientConnection() + | argHeaders -> argHeaders + + { + ServerUrl = %%serverUrl + HttpHeaders = httpHeaders + Connection = connectionFactory () + } @@> + + ProvidedMethod( + "GetContext", + methodParameters, + typeof, + invoker, + isStatic = true + ) + + let operationMethodDef = + let staticParams = + [ + ProvidedStaticParameter("query", typeof) + ProvidedStaticParameter( + "resolutionFolder", + typeof, + parameterDefaultValue = resolutionFolder + ) + ProvidedStaticParameter("operationName", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("typeName", typeof, parameterDefaultValue = "") + ] + + let staticMethodDef = ProvidedMethod("Operation", [], typeof, isStatic = true) + + let instanceBuilder (methodName: string) (args: obj []) = + let queryLocation = StringLocation.Create(downcast args.[0], downcast args.[1]) + + let query = + match queryLocation with + | String query -> query + | File path -> System.IO.File.ReadAllText(path) + + let queryAst = Parser.parse query +#if IS_DESIGNTIME + let throwExceptionIfValidationFailed (validationResult: ValidationResult) = + let rec formatValidationExceptionMessage (errors: AstError list) = + match errors with + | [] -> + "Query validation resulted in invalid query, but no validation messages were produced." + | errors -> + errors + |> List.map (fun err -> + match err.Path with + | Some path -> sprintf "%s Path: %A" err.Message path + | None -> err.Message) + |> List.reduce (fun x y -> x + Environment.NewLine + y) + + match validationResult with + | ValidationError msgs -> failwith (formatValidationExceptionMessage msgs) + | Success -> () + + let key = + { + DocumentId = queryAst.GetHashCode() + SchemaId = schema.GetHashCode() + } + + let refMaker = lazy Validation.Ast.validateDocument schema queryAst + + if clientQueryValidation then + refMaker.Force + |> QueryValidationDesignTimeCache.getOrAdd key + |> throwExceptionIfValidationFailed +#endif + let operationName: OperationName option = + match args.[2] :?> string with + | null + | "" -> + let operationDefinitions = + queryAst.Definitions + |> List.filter (function + | OperationDefinition _ -> true + | _ -> false) + + match operationDefinitions with + | opdef :: _ -> opdef.Name + | _ -> + failwith + "Error parsing query. Can not choose a default operation: query document has no operation definitions." + | x -> Some x + + let explicitOperationTypeName: TypeName option = + match args.[3] :?> string with + | null + | "" -> None + | x -> Some x + + let operationDefinition = + queryAst.Definitions + |> List.choose (function + | OperationDefinition odef -> Some odef + | _ -> None) + |> List.find (fun d -> d.Name = operationName) + + let operationAstFields = + let infoMap = queryAst.GetInfoMap() + + match infoMap.TryFind(operationName) with + | Some fields -> fields + | None -> + failwith + "Error parsing query. Could not find field information for requested operation." + + let operationTypeRef = + let tref = + match operationDefinition.OperationType with + | Query -> schema.QueryType + | Mutation -> + match schema.MutationType with + | Some tref -> tref + | None -> + failwith + "The operation is a mutation operation, but the schema does not have a mutation type." + | Subscription -> + match schema.SubscriptionType with + | Some tref -> tref + | None -> + failwithf + "The operation is a subscription operation, but the schema does not have a subscription type." + + let tinst = + match tref.Name with + | Some name -> schema.Types |> Array.tryFind (fun t -> t.Name = name) + | None -> None + + match tinst with + | Some t -> { tref with Kind = t.Kind } + | None -> failwith "The operation was found in the schema, but it does not have a name." + + let schemaTypes = TypeMapping.getSchemaTypes schema + + let enumProvidedTypes = + schemaProvidedTypes |> Map.filter (fun _ t -> t.BaseType = typeof) + + let actualQuery = + queryAst + .ToQueryString(QueryStringPrintingOptions.IncludeTypeNames) + .Replace("\r\n", "\n") + + let className = + match explicitOperationTypeName, operationDefinition.Name with + | Some name, _ -> name.FirstCharUpper() + | None, Some name -> name.FirstCharUpper() + | None, None -> "Operation" + actualQuery.MD5Hash() + + let metadata = + getOperationMetadata ( + schemaTypes, + uploadInputTypeName, + enumProvidedTypes, + operationAstFields, + operationTypeRef, + explicitOptionalParameters + ) + + let operationTypeName: TypeName = + match operationTypeRef.Name with + | Some name -> name + | None -> failwith "Error parsing query. Operation type does not have a name." + + let rec getKind (tref: IntrospectionTypeRef) = + match tref.Kind with + | TypeKind.NON_NULL + | TypeKind.LIST when tref.OfType.IsSome -> getKind tref.OfType.Value + | _ -> tref.Kind + + let rec getTypeName (tref: IntrospectionTypeRef) = + match tref.Kind with + | TypeKind.NON_NULL + | TypeKind.LIST when tref.OfType.IsSome -> getTypeName tref.OfType.Value + | _ -> + match tref.Name with + | Some tname -> tname + | None -> + failwithf + "Expected type kind \"%s\" to have a name, but it does not have a name." + (tref.Kind.ToString()) + + let rec getIntrospectionType (tref: IntrospectionTypeRef) = + match tref.Kind with + | TypeKind.NON_NULL + | TypeKind.LIST when tref.OfType.IsSome -> getIntrospectionType tref.OfType.Value + | _ -> + let typeName = getTypeName tref + + match schemaTypes.TryFind(typeName) with + | Some t -> t + | None -> failwithf "Type \"%s\" was not found in the introspection schema." typeName + + let getOperationFields + (operationAstFields: AstFieldInfo list) + (operationType: IntrospectionType) + = + let rec helper + (acc: SchemaFieldInfo list) + (astFields: AstFieldInfo list) + (introspectionType: IntrospectionType) + = + match introspectionType.Kind with + | TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.UNION -> + match astFields with + | [] -> acc + | field :: tail -> + let throw typeName = + failwithf + "Field \"%s\" of type \"%s\" was not found in the introspection schema." + field.Name + typeName + + let tref = match field with - | Some f -> f.Type - | None -> throw introspectionType.Name - let fields = - match getKind tref with - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> - let schemaType = getIntrospectionType tref - helper [] field.Fields schemaType - | _ -> [] - let info = { AliasOrName = field.AliasOrName.FirstCharUpper(); SchemaTypeRef = tref; Fields = Array.ofList fields } - helper (info :: acc) tail introspectionType - | _ -> [] - helper [] operationAstFields operationType |> Array.ofList - - // Every time we run the query, we will need the schema types information as an expression. - // To avoid creating the type map expression every time we call Run method, we cache it here. - let operationFieldsExpr = getOperationFields operationAstFields (getIntrospectionType operationTypeRef) |> QuotationHelpers.arrayExpr |> snd - let contextInfo : GraphQLRuntimeContextInfo option = - match introspectionLocation with - | Uri serverUrl -> Some { ServerUrl = serverUrl; HttpHeaders = httpHeaders } - | _ -> None - let operationDef = ProvidedOperation.makeProvidedType(actualQuery, operationDefinition, operationTypeName, operationFieldsExpr, schemaTypes, schemaProvidedTypes, metadata.OperationType, contextInfo, metadata.UploadInputTypeName, className, explicitOptionalParameters) - operationDef.AddMember(metadata.TypeWrapper) - let invoker (_ : Expr list) = <@@ OperationBase(query) @@> - let methodDef = ProvidedMethod(methodName, [], operationDef, invoker, isStatic = true) - methodDef.AddXmlDoc("Creates an operation to be executed on the server and provide its return types.") - operationWrapper.AddMember(operationDef) - operationDef.AddMember(methodDef) - methodDef - staticMethodDef.DefineStaticParameters(staticParams, instanceBuilder) - staticMethodDef - let schemaPropertyDef = - let getter = QuotationHelpers.quoteRecord schema (fun (_ : Expr list) schema -> schema) - ProvidedProperty("Schema", typeof, getter, isStatic = true) - let members : MemberInfo list = [typeWrapper; operationWrapper; getContextMethodDef; operationMethodDef; schemaPropertyDef] - members) - tdef - #if IS_DESIGNTIME - let providerKey = - { IntrospectionLocation = introspectionLocation - CustomHttpHeadersLocation = httpHeadersLocation - UploadInputTypeName = uploadInputTypeName - ResolutionFolder = resolutionFolder - ClientQueryValidation = clientQueryValidation - ExplicitOptionalParameters = explicitOptionalParameters } - ProviderDesignTimeCache.getOrAdd providerKey maker.Force) - #else - maker.Force()) - #endif + | FragmentField fragf -> + let fragmentType = + let tref = + Option.defaultValue [||] introspectionType.PossibleTypes + |> Array.map getIntrospectionType + |> Array.append [| introspectionType |] + |> Array.tryFind (fun pt -> pt.Name = fragf.TypeCondition) + + match tref with + | Some t -> t + | None -> + failwithf + "Fragment field defines a type condition \"%s\", but that type was not found in the schema definition." + fragf.TypeCondition + + let field = + fragmentType.Fields + |> Option.map (Array.tryFind (fun f -> f.Name = fragf.Name)) + |> Option.flatten + + match field with + | Some f -> f.Type + | None -> throw fragmentType.Name + | TypeField typef -> + let field = + introspectionType.Fields + |> Option.map (Array.tryFind (fun f -> f.Name = typef.Name)) + |> Option.flatten + + match field with + | Some f -> f.Type + | None -> throw introspectionType.Name + + let fields = + match getKind tref with + | TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.UNION -> + let schemaType = getIntrospectionType tref + helper [] field.Fields schemaType + | _ -> [] + + let info = + { + AliasOrName = field.AliasOrName.FirstCharUpper() + SchemaTypeRef = tref + Fields = Array.ofList fields + } + + helper (info :: acc) tail introspectionType + | _ -> [] + + helper [] operationAstFields operationType |> Array.ofList + + // Every time we run the query, we will need the schema types information as an expression. + // To avoid creating the type map expression every time we call Run method, we cache it here. + let operationFieldsExpr = + getOperationFields operationAstFields (getIntrospectionType operationTypeRef) + |> QuotationHelpers.arrayExpr + |> snd + + let contextInfo: GraphQLRuntimeContextInfo option = + match introspectionLocation with + | Uri serverUrl -> + Some + { + ServerUrl = serverUrl + HttpHeaders = httpHeaders + } + | _ -> None + + let operationDef = + ProvidedOperation.makeProvidedType ( + actualQuery, + operationDefinition, + operationTypeName, + operationFieldsExpr, + schemaTypes, + schemaProvidedTypes, + metadata.OperationType, + contextInfo, + metadata.UploadInputTypeName, + className, + explicitOptionalParameters + ) + + operationDef.AddMember(metadata.TypeWrapper) + let invoker (_: Expr list) = <@@ OperationBase(query) @@> + let methodDef = ProvidedMethod(methodName, [], operationDef, invoker, isStatic = true) + + methodDef.AddXmlDoc( + "Creates an operation to be executed on the server and provide its return types." + ) + + operationWrapper.AddMember(operationDef) + operationDef.AddMember(methodDef) + methodDef + + staticMethodDef.DefineStaticParameters(staticParams, instanceBuilder) + staticMethodDef + + let schemaPropertyDef = + let getter = QuotationHelpers.quoteRecord schema (fun (_: Expr list) schema -> schema) + ProvidedProperty("Schema", typeof, getter, isStatic = true) + + let members: MemberInfo list = + [ typeWrapper; operationWrapper; getContextMethodDef; operationMethodDef; schemaPropertyDef ] + + members) + + tdef +#if IS_DESIGNTIME + let providerKey = + { + IntrospectionLocation = introspectionLocation + CustomHttpHeadersLocation = httpHeadersLocation + UploadInputTypeName = uploadInputTypeName + ResolutionFolder = resolutionFolder + ClientQueryValidation = clientQueryValidation + ExplicitOptionalParameters = explicitOptionalParameters + } + + ProviderDesignTimeCache.getOrAdd providerKey maker.Force + ) +#else + maker.Force() + ) +#endif generator diff --git a/src/FSharp.Data.GraphQL.Server/Values.fs b/src/FSharp.Data.GraphQL.Server/Values.fs index 61243aad7..03e93d601 100644 --- a/src/FSharp.Data.GraphQL.Server/Values.fs +++ b/src/FSharp.Data.GraphQL.Server/Values.fs @@ -14,40 +14,41 @@ open Microsoft.FSharp.Reflection /// Tries to convert type defined in AST into one of the type defs known in schema. let inline tryConvertAst schema ast = - let rec convert isNullable (schema: ISchema) (ast: InputType) : TypeDef option = + let rec convert isNullable (schema: ISchema) (ast: TypeReference) : TypeDef option = match ast with | NamedType name -> match schema.TryFindType name with - | Some namedDef -> - Some (if isNullable then upcast namedDef.MakeNullable() else upcast namedDef) + | Some namedDef -> Some(if isNullable then upcast namedDef.MakeNullable() else upcast namedDef) | None -> None | ListType inner -> convert true schema inner - |> Option.map (fun i -> - if isNullable - then upcast i.MakeList().MakeNullable() - else upcast i.MakeList()) - | NonNullType inner -> - convert false schema inner + |> Option.map (fun i -> if isNullable then upcast i.MakeList().MakeNullable() else upcast i.MakeList()) + | NonNullNameType inner -> convert false schema inner + convert true schema ast let inline private notAssignableMsg (innerDef: InputDef) value : string = sprintf "value of type %s is not assignable from %s" innerDef.Type.Name (value.GetType().Name) -let rec internal compileByType (errMsg: string) (inputDef: InputDef): ExecuteInput = +let rec internal compileByType (errMsg: string) (inputDef: InputDef) : ExecuteInput = match inputDef with - | Scalar scalardef -> - variableOrElse (scalardef.CoerceInput >> Option.toObj) + | Scalar scalardef -> variableOrElse (scalardef.CoerceInput >> Option.toObj) | InputObject objdef -> let objtype = objdef.Type let ctor = ReflectionHelper.matchConstructor objtype (objdef.Fields |> Array.map (fun x -> x.Name)) + let mapper = ctor.GetParameters() - |> Array.map(fun param -> - match objdef.Fields |> Array.tryFind(fun field -> field.Name = param.Name) with + |> Array.map (fun param -> + match objdef.Fields |> Array.tryFind (fun field -> field.Name = param.Name) with | Some x -> x | None -> - failwithf "Input object '%s' refers to type '%O', but constructor parameter '%s' doesn't match any of the defined input fields" objdef.Name objtype param.Name) + failwithf + "Input object '%s' refers to type '%O', but constructor parameter '%s' doesn't match any of the defined input fields" + objdef.Name + objtype + param.Name) + fun value variables -> match value with | ObjectValue props -> @@ -57,6 +58,7 @@ let rec internal compileByType (errMsg: string) (inputDef: InputDef): ExecuteInp match Map.tryFind field.Name props with | None -> null | Some prop -> field.ExecuteInput prop variables) + let instance = ctor.Invoke(args) instance | Variable variableName -> @@ -73,6 +75,7 @@ let rec internal compileByType (errMsg: string) (inputDef: InputDef): ExecuteInp match value with | ListValue list -> let mappedValues = list |> List.map (fun value -> inner value variables) + if isArray then ReflectionHelper.arrayOfList innerdef.Type mappedValues else @@ -81,21 +84,22 @@ let rec internal compileByType (errMsg: string) (inputDef: InputDef): ExecuteInp | _ -> // try to construct a list from single element let single = inner value variables - if single = null then null else - if isArray then ReflectionHelper.arrayOfList innerdef.Type [single] - else cons single nil + + if single = null then null + else if isArray then ReflectionHelper.arrayOfList innerdef.Type [ single ] + else cons single nil | Nullable (Input innerdef) -> let inner = compileByType errMsg innerdef let some, none, _ = ReflectionHelper.optionOfType innerdef.Type fun variables value -> let i = inner variables value + match i with | null -> none | coerced -> let c = some coerced - if c <> null then c - else raise(GraphQLException (errMsg + notAssignableMsg innerdef coerced)) + if c <> null then c else raise (GraphQLException(errMsg + notAssignableMsg innerdef coerced)) | Enum enumdef -> fun value variables -> match value with @@ -105,6 +109,7 @@ let rec internal compileByType (errMsg: string) (inputDef: InputDef): ExecuteInp | None -> failwithf "Variable '%s' not supplied.\nVariables: %A" variableName variables | _ -> let coerced = coerceEnumInput value + match coerced with | None -> null | Some s -> ReflectionHelper.parseUnion enumdef.Type s @@ -115,25 +120,35 @@ let rec private coerceVariableValue isNullable typedef (vardef: VarDef) (input: | Scalar scalardef -> match scalardef.CoerceValue input with | None when isNullable -> null - | None -> - raise (GraphQLException <| errMsg + (sprintf "expected value of type %s but got None" scalardef.Name)) + | None -> raise (GraphQLException <| errMsg + (sprintf "expected value of type %s but got None" scalardef.Name)) | Some res -> res | Nullable (Input innerdef) -> let some, none, innerValue = ReflectionHelper.optionOfType innerdef.Type let input = innerValue input let coerced = coerceVariableValue true innerdef vardef input errMsg - if coerced <> null - then + + if coerced <> null then let s = some coerced - if s <> null - then s - else raise (GraphQLException <| errMsg + (sprintf "value of type %O is not assignable from %O" innerdef.Type (coerced.GetType()))) - else none + + if s <> null then + s + else + raise ( + GraphQLException + <| errMsg + (sprintf "value of type %O is not assignable from %O" innerdef.Type (coerced.GetType())) + ) + else + none | List (Input innerdef) -> let cons, nil = ReflectionHelper.listOfType innerdef.Type + match input with | null when isNullable -> null - | null -> raise(GraphQLException <| errMsg + (sprintf "expected value of type %s, but no value was found." (vardef.TypeDef.ToString()))) + | null -> + raise ( + GraphQLException + <| errMsg + (sprintf "expected value of type %s, but no value was found." (vardef.TypeDef.ToString())) + ) // special case - while single values should be wrapped with a list in this scenario, // string would be treat as IEnumerable and coerced into a list of chars | :? string as s -> @@ -148,21 +163,29 @@ let rec private coerceVariableValue isNullable typedef (vardef: VarDef) (input: |> Seq.toList |> List.rev |> List.fold (fun acc coerced -> cons coerced acc) nil + mapped | other -> raise (GraphQLException <| errMsg + (sprintf "Cannot coerce value of type '%O' to list." (other.GetType()))) | InputObject objdef -> coerceVariableInputObject objdef vardef input (errMsg + (sprintf "in input object '%s': " objdef.Name)) | Enum enumdef -> match input with - | :? string as s -> - ReflectionHelper.parseUnion enumdef.Type s + | :? string as s -> ReflectionHelper.parseUnion enumdef.Type s | null when isNullable -> null - | null -> raise(GraphQLException <| errMsg + (sprintf "Expected Enum '%s', but no value was found." enumdef.Name)) + | null -> raise (GraphQLException <| errMsg + (sprintf "Expected Enum '%s', but no value was found." enumdef.Name)) | u when FSharpType.IsUnion(enumdef.Type) && enumdef.Type = input.GetType() -> u | o when Enum.IsDefined(enumdef.Type, o) -> o | _ -> - raise (GraphQLException <| errMsg + (sprintf "Cannot coerce value of type '%O' to type Enum '%s'" (input.GetType()) enumdef.Name)) - | _ -> raise (GraphQLException <| errMsg + "Only Scalars, Nullables, Lists and InputObjects are valid type definitions.") + raise ( + GraphQLException + <| errMsg + + (sprintf "Cannot coerce value of type '%O' to type Enum '%s'" (input.GetType()) enumdef.Name) + ) + | _ -> + raise ( + GraphQLException + <| errMsg + "Only Scalars, Nullables, Lists and InputObjects are valid type definitions." + ) and private coerceVariableInputObject (objdef) (vardef: VarDef) (input: obj) errMsg = //TODO: this should be eventually coerced to complex object @@ -172,13 +195,17 @@ and private coerceVariableInputObject (objdef) (vardef: VarDef) (input: obj) err objdef.Fields |> Array.map (fun field -> let valueFound = Map.tryFind field.Name map |> Option.toObj - (field.Name, coerceVariableValue false field.TypeDef vardef valueFound (errMsg + (sprintf "in field '%s': " field.Name)))) + + (field.Name, + coerceVariableValue false field.TypeDef vardef valueFound (errMsg + (sprintf "in field '%s': " field.Name)))) |> Map.ofArray + upcast mapped | _ -> input let internal coerceVariable (vardef: VarDef) (inputs) = let vname = vardef.Name + match Map.tryFind vname inputs with | None -> match vardef.DefaultValue with @@ -189,5 +216,10 @@ let internal coerceVariable (vardef: VarDef) (inputs) = | None -> match vardef.TypeDef with | Nullable _ -> null - | _ -> raise (GraphQLException (sprintf "Variable '$%s' of required type %s has no value provided." vname (vardef.TypeDef.ToString()))) - | Some input -> coerceVariableValue false vardef.TypeDef vardef input (sprintf "Variable '$%s': " vname) \ No newline at end of file + | _ -> + raise ( + GraphQLException( + sprintf "Variable '$%s' of required type %s has no value provided." vname (vardef.TypeDef.ToString()) + ) + ) + | Some input -> coerceVariableValue false vardef.TypeDef vardef input (sprintf "Variable '$%s': " vname) diff --git a/src/FSharp.Data.GraphQL.Shared/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index ce000f4a7..e4cf47dae 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -5,7 +5,6 @@ namespace FSharp.Data.GraphQL.Ast open System -open FSharp.Data.GraphQL.Ast /// There are three types of operations that GraphQL models. /// @@ -33,22 +32,36 @@ type OperationType = /// https://spec.graphql.org/October2021/#sec-Input-Values type Value = /// 2.9.1 Int Value + /// https://spec.graphql.org/October2021/#sec-Int-Value | IntValue of int64 /// 2.9.2 Float Value + /// https://spec.graphql.org/October2021/#sec-Float-Value | FloatValue of double /// 2.9.3 Boolean Value + /// https://spec.graphql.org/October2021/#sec-Boolean-Value | BooleanValue of bool /// 2.9.4 String Value + /// https://spec.graphql.org/October2021/#sec-String-Value | StringValue of string /// 2.9.5 Null Value + /// https://spec.graphql.org/October2021/#sec-Null-Value | NullValue /// 2.9.6 Enum Value + /// not "true", "false" or "null" + /// https://spec.graphql.org/October2021/#sec-Enum-Value | EnumValue of string /// 2.9.7 List Value + /// May be empty. + /// https://spec.graphql.org/October2021/#sec-List-Value | ListValue of Value list /// 2.9.8 Input Object Values + /// May be empty. + /// https://spec.graphql.org/October2021/#sec-Input-Object-Values + /// Contains ObjectField: https://spec.graphql.org/October2021/#ObjectField | ObjectValue of Map /// 2.10 Variables + /// if not Const + /// https://spec.graphql.org/October2021/#Variable | Variable of string /// 2.6 Arguments @@ -258,17 +271,31 @@ and Field = | None -> x.Name -/// 2.2.9 Input Types -type InputType = +/// 2.11 Type References +/// Renamed from "Type" as this is frequently used in .Net. +/// https://spec.graphql.org/October2021/#sec-Type-References +/// https://spec.graphql.org/October2021/#Type +type TypeReference = + /// https://spec.graphql.org/October2021/#NamedType | NamedType of string - | ListType of InputType - | NonNullType of InputType + /// https://spec.graphql.org/October2021/#ListType + | ListType of TypeReference list + /// https://spec.graphql.org/October2021/#NamedType + /// https://spec.graphql.org/October2021/#NonNullType + | NonNullNameType of string + /// https://spec.graphql.org/October2021/#ListType + /// https://spec.graphql.org/October2021/#NonNullType + | NonNullListType of TypeReference list override x.ToString() = let rec str = function | NamedType name -> name - | ListType inner -> "[" + (str inner) + "]" - | NonNullType inner -> (str inner) + "!" + | ListType inner -> + String.Concat [ "[" + String.concat ", " (List.map str inner) + "]" ] + | NonNullNameType inner -> $"{inner}!" + | NonNullListType inner -> $"{str (ListType inner)}!" str x @@ -289,11 +316,17 @@ type InputType = /// Uses Defined). (from https://spec.graphql.org/October2021/#sec-Language.Variables.Variable-use-within-Fragments) /// /// https://spec.graphql.org/October2021/#sec-Language.Variables +/// https://spec.graphql.org/October2021/#VariableDefinition type VariableDefinition = { + /// https://spec.graphql.org/October2021/#Variable VariableName: string - Type: InputType + /// https://spec.graphql.org/October2021/#Type + Type: TypeReference + /// https://spec.graphql.org/October2021/#DefaultValue DefaultValue: Value option + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list } @@ -426,21 +459,46 @@ type SchemaDefinition = RootOperationTypes: RootOperationTypeDefinition } +/// 3.6.1 Field Arguments +/// +/// Object fields are conceptually functions which yield values. Occasionally object fields can accept arguments to further +/// specify the return value. Object field arguments are defined as a list of all possible argument names and their expected input +/// types. +/// +/// All arguments defined within a field must not have a name which begins with "__" (two underscores), as this is used +/// exclusively by GraphQL’s introspection system. +/// +/// https://spec.graphql.org/October2021/#InputValueDefinition type InputValueDefinition = { Description: string option + Name: string - Type: InputType + + /// https://spec.graphql.org/October2021/#Type + Type: TypeReference + + /// https://spec.graphql.org/October2021/#DefaultValue DefaultValue: Value option + + /// May be empty + /// https://spec.graphql.org/October2021/#Directives + Directives: Directive list } -/// https://graphql.github.io/graphql-spec/June2018/#FieldDefinition +/// https://spec.graphql.org/October2021/#FieldDefinition type FieldDefinition = { + /// https://spec.graphql.org/October2021/#Description Description: string option Name: string - Arguments: InputValueDefinition [] - Type: InputType + /// May be empty + /// https://spec.graphql.org/October2021/#ArgumentsDefinition + Arguments: InputValueDefinition list + /// https://spec.graphql.org/October2021/#Type + Type: TypeReference + /// May be empty + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list } @@ -503,115 +561,271 @@ type ScalarTypeExtension = Directives: Directive list } -/// GraphQL queries are hierarchical and composed, describing a tree of information. While Scalar types describe the leaf values of these hierarchical queries, Objects describe the intermediate levels. -/// https://graphql.github.io/graphql-spec/June2018/#sec-Objects +/// 3.6 Objects +/// +/// GraphQL operations are hierarchical and composed, describing a tree of information. While Scalar types describe the leaf +/// values of these hierarchical operations, Objects describe the intermediate levels. +/// +/// GraphQL Objects represent a list of named fields, each of which yield a value of a specific type. Object values should be +/// serialized as ordered maps, where the selected field names (or aliases) are the keys and the result of evaluating the field +/// is the value, ordered by the order in which they appear in the selection set. +/// +/// All fields defined within an Object type must not have a name which begins with "__" (two underscores), as this is used +/// exclusively by GraphQL’s introspection system. +/// +/// https://spec.graphql.org/October2021/#sec-Objects type ObjectTypeDefinition = { Description: string option + // "type" Name: string + // "implements .. & .. & .." /// May be empty - Interfaces: string [] - /// May be empty + /// https://spec.graphql.org/October2021/#ImplementsInterfaces + ImplementsInterfaces: string list + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Fields: FieldDefinition [] + // "{" fields "}" + Fields: FieldDefinition list } -/// Object type extensions are used to represent a type which has been extended from some original type. For example, this might be used to represent local data, or by a GraphQL service which is itself an extension of another GraphQL service. +/// 3.6.3 Object Extensions /// -/// Either: -/// - `Fields` is non-empty, and all others may be, xor -/// - `Directives` is non-empty, and all others may be, xor -/// - `Interfaces` is non-empty, and all others may be. +/// Object type extensions are used to represent a type which has been extended from some original type. For example, this might +/// be used to represent local data, or by a GraphQL service which is itself an extension of another GraphQL service. /// -/// https://graphql.github.io/graphql-spec/June2018/#ObjectTypeExtension +/// Object type extensions may choose not to add additional fields, instead only adding interfaces or directives. +/// +/// https://spec.graphql.org/October2021/#sec-Object-Extensions +/// https://spec.graphql.org/October2021/#ObjectTypeExtension type ObjectTypeExtension = { - /// May be empty - Interfaces: string [] - /// May be empty + /// May be empty. + Interfaces: string list + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - /// May be empty - Fields: FieldDefinition [] + /// May be empty. + Fields: FieldDefinition list } -/// GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects can then implement these interfaces which requires that the object type will define all fields defined by those interfaces. -/// https://graphql.github.io/graphql-spec/June2018/#InterfaceTypeDefinition +/// 3.7 Interfaces +/// +/// GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement +/// these interfaces which requires that the implementing type will define all fields defined by those interfaces. +/// +/// Fields on a GraphQL interface have the same rules as fields on a GraphQL object; their type can be Scalar, Object, Enum, +/// Interface, or Union, or any wrapping type whose base type is one of those five. +/// +/// For example, an interface NamedEntity may describe a required field and types such as Person or Business may then implement +/// this interface to guarantee this field will always exist. +/// +/// Types may also implement multiple interfaces. For example, Business implements both the NamedEntity and ValuedEntity +/// interfaces in the example below. +/// +/// https://spec.graphql.org/October2021/#sec-Interfaces type InterfaceTypeDefinition = { Description: string option Name: string + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Fields: FieldDefinition [] + /// May not be empty. + Fields: FieldDefinition list } +/// 3.7.1 Interface Extensions +/// +/// Interface type extensions are used to represent an interface which has been extended from some original interface. For +/// example, this might be used to represent common local data on many types, or by a GraphQL service which is itself an +/// extension of another GraphQL service. +/// +/// Interface type extensions may choose not to add additional fields, instead only adding directives. +/// +/// https://spec.graphql.org/October2021/#sec-Interface-Extensions type InterfaceTypeExtension = { Name: string + + /// May be empty if fields is non-empty. + /// + /// May not be empty if fields is empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Fields: FieldDefinition [] + + /// May not empty if directives is empty. + /// + /// May be empty if directives is not empty. + Fields: FieldDefinition list } +/// https://spec.graphql.org/October2021/#UnionMemberTypes type UnionMemberType = string -/// GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed fields between those types. They also differ from interfaces in that Object types declare what interfaces they implement, but are not aware of what unions contain them. -/// https://graphql.github.io/graphql-spec/June2018/#UnionTypeDefinition +/// 3.8 Unions +/// +/// GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed +/// fields between those types. They also differ from interfaces in that Object types declare what interfaces they implement, +/// but are not aware of what unions contain them. +/// +/// With interfaces and objects, only those fields defined on the type can be queried directly; to query other fields on an +/// interface, typed fragments must be used. This is the same as for unions, but unions do not define any fields, so no fields +/// may be queried on this type without the use of type refining fragments or inline fragments (with the exception of the meta- +/// field __typename). +/// +/// https://spec.graphql.org/October2021/#sec-Unions type UnionTypeDefinition = { + /// Optional description Description: string option + + /// The name of the union Name: string + + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Types: UnionMemberType [] + + /// May be empty. (!) + /// https://spec.graphql.org/October2021/#UnionMemberTypes + Types: UnionMemberType list } +/// 3.8.1 Union Extensions +/// +/// Union type extensions are used to represent a union type which has been extended from some original union type. For example, +/// this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another +/// GraphQL service. +/// +/// https://spec.graphql.org/October2021/#sec-Unions type UnionTypeExtension = { Name: string + + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Types: UnionMemberType [] + + /// May not be empty. + /// https://spec.graphql.org/October2021/#UnionMemberTypes + Types: UnionMemberType list } -/// Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the name of the represented value. -/// Most often a name like "BLUE" or "RED". -/// https://graphql.github.io/graphql-spec/June2018/#EnumValueDefinition +/// 3.9 Enums +/// +/// GraphQL Enum types, like Scalar types, also represent leaf values in a GraphQL type system. However Enum types describe the +/// set of possible values. +/// +/// Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the +/// name of the represented value. +/// +/// https://spec.graphql.org/October2021/#sec-Enums +/// https://spec.graphql.org/October2021/#EnumValueDefinition type EnumValueDefinition = { Description: string option + Name: string + + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list } -/// GraphQL Enum types, like scalar types, also represent leaf values in a GraphQL type system. However Enum types describe the set of possible values. -/// https://graphql.github.io/graphql-spec/June2018/#EnumTypeDefinition +/// 3.9 Enums +/// +/// GraphQL Enum types, like Scalar types, also represent leaf values in a GraphQL type system. However Enum types describe the +/// set of possible values. +/// +/// Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the +/// name of the represented value. +/// +/// https://spec.graphql.org/October2021/#sec-Enums +/// https://spec.graphql.org/October2021/#EnumTypeDefinition type EnumTypeDefinition = { Description: string option Name: string + Directives: Directive list - Values: EnumValueDefinition [] + Values: EnumValueDefinition list } -/// Enum type extensions are used to represent an enum type which has been extended from some original enum type. For example, this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another GraphQL service. -/// https://graphql.github.io/graphql-spec/June2018/#sec-Enum-Extensions +/// 3.9.1 Enum Extensions +/// +/// Enum type extensions are used to represent an enum type which has been extended from some original enum type. For example, +/// this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another +/// GraphQL service. +/// +/// https://spec.graphql.org/October2021/#sec-Enum-Extensions type EnumTypeExtension = { Name: string + + /// May be empty if fields is non-empty. + /// + /// May not be empty if fields is empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Values: EnumValueDefinition [] + + /// May not empty if directives is empty. + /// + /// May be empty if directives is not empty. + Values: EnumValueDefinition list } -/// Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to represent more complex values. -/// https://graphql.github.io/graphql-spec/June2018/#InputObjectTypeDefinition +/// 3.10 Input Objects +/// +/// Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to +/// represent more complex values. +/// +/// A GraphQL Input Object defines a set of input fields; the input fields are either scalars, enums, or other input objects. +/// This allows arguments to accept arbitrarily complex structs. +/// +/// Circular References: +/// +/// Input Objects are allowed to reference other Input Objects as field types. A circular reference occurs when an Input Object +/// references itself either directly or through referenced Input Objects. +/// +/// Circular references are generally allowed, however they may not be defined as an unbroken chain of Non-Null singular fields. +/// Such Input Objects are invalid because there is no way to provide a legal value for them. +/// +/// Note: there's a long section on how input values are coerced in the spec. +/// +/// https://spec.graphql.org/October2021/#sec-Input-Objects type InputObjectTypeDefinition = { Description: string option + + // "input" + Name: string + + /// May be empty if fields is non-empty. + /// + /// May not be empty if fields is empty. + /// https://spec.graphql.org/October2021/#Directives Directives: Directive list - Fields: InputValueDefinition [] + + /// "{" Fields "}" + /// + /// May not empty if directives is empty. + /// + /// May be empty if directives is not empty. + /// + /// https://spec.graphql.org/October2021/#InputFieldsDefinition + Fields: InputValueDefinition list } -/// 3.10.1 https://graphql.github.io/graphql-spec/June2018/#sec-Input-Object-Extensions -/// Input object type extensions are used to represent an input object type which has been extended from some original input object type. For example, this might be used by a GraphQL service which is itself an extension of another GraphQL service. +/// 3.10.1 Input Object Extensions +/// +/// Input object type extensions are used to represent an input object type which has been extended from some original input +/// object type. For example, this might be used by a GraphQL service which is itself an extension of another GraphQL service. +/// +/// https://spec.graphql.org/October2021/#sec-Input-Object-Extensions type InputObjectTypeExtension = { Name: string @@ -628,10 +842,18 @@ type TypeDefinition = | EnumTypeDefinition of EnumTypeDefinition | InputObjectTypeDefinition of InputObjectTypeDefinition -/// Enum describing parts of the GraphQL query document AST, where -/// related directive is valid to be used. + member x.Directives = + match x with + | ScalarTypeDefinition std -> std.Directives + | ObjectTypeDefinition otd -> otd.Directives + | InterfaceTypeDefinition itd -> itd.Directives + | UnionTypeDefinition utd -> utd.Directives + | EnumTypeDefinition etd -> etd.Directives + | InputObjectTypeDefinition iotd -> iotd.Directives + +/// https://spec.graphql.org/October2021/#ExecutableDirectiveLocation [] -type DirectiveLocation = +type ExecutableDirectiveLocation = | QUERY = 1 | MUTATION = 2 | SUBSCRIPTION = 4 @@ -639,35 +861,55 @@ type DirectiveLocation = | FRAGMENT_DEFINITION = 16 | FRAGMENT_SPREAD = 32 | INLINE_FRAGMENT = 64 - | SCHEMA = 128 - | SCALAR = 256 - | OBJECT = 512 - | FIELD_DEFINITION = 1024 - | ARGUMENT_DEFINITION = 2048 - | INTERFACE = 4096 - | UNION = 8192 - | ENUM = 16384 - | ENUM_VALUE = 32768 - | INPUT_OBJECT = 65536 - | INPUT_FIELD_DEFINITION = 131072 - -/// 3.13 + + +/// https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation +[] +type TypeSystemDirectiveLocation = + | SCHEMA = 1 + | SCALAR = 2 + | OBJECT = 4 + | FIELD_DEFINITION = 8 + | ARGUMENT_DEFINITION = 16 + | INTERFACE = 32 + | UNION = 64 + | ENUM = 128 + | ENUM_VALUE = 256 + | INPUT_OBJECT = 512 + | INPUT_FIELD_DEFINITION = 1024 + + +type DirectiveLocation = + /// https://spec.graphql.org/October2021/#ExecutableDirectiveLocation + | ExecutableDirectiveLocation of ExecutableDirectiveLocation + /// https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation + | TypeSystemDirectiveLocation of TypeSystemDirectiveLocation + +/// 3.13 Directives +/// /// DirectiveDefinition: /// Description opt directive @ `Name` `ArgumentsDefinition` opt on `DirectiveLocations` /// -/// https://graphql.github.io/graphql-spec/June2018/#DirectiveDefinition -/// https://graphql.github.io/graphql-spec/June2018/#sec-Type-System.Directives +/// https://spec.graphql.org/October2021/#sec-Type-System.Directives +/// https://spec.graphql.org/October2021/#DirectiveDefinition type DirectiveDefinition = { - /// Directive's name - it's NOT '@' prefixed. - Name: string /// Optional directive description. Description: string option - /// Directive location - describes, which part's of the query AST - /// are valid places to include current directive to. + + // "directive" "@" + + /// Directive's name - it's NOT '@' prefixed. + Name: string + + /// https://spec.graphql.org/October2021/#ArgumentsDefinition + Arguments: InputValueDefinition list + + // [ "repeatable" ] "on" + + /// Directive location - describes, which part's of the query AST are valid places to include current directive to. + /// https://spec.graphql.org/October2021/#DirectiveLocation Locations: DirectiveLocation - /// Array of arguments defined within that directive. - Args: InputFieldDefinition [] } @@ -695,13 +937,27 @@ type TypeSystemDefinition = /// https://spec.graphql.org/October2021/#DirectiveDefinition | DirectiveDefinition of DirectiveDefinition -/// Schema extensions are used to represent a schema which has been extended from an original schema. For example, this might be used by a GraphQL service which adds additional operation types, or additional directives to an existing schema. -/// https://graphql.github.io/graphql-spec/June2018/#SchemaExtension + member x.Directives = + match x with + | SchemaDefinition sd -> sd.Directives + | TypeDefinition td -> td.Directives + | DirectiveDefinition _ -> [] + +/// 3.3.2 Schema Extension +/// +/// Schema extensions are used to represent a schema which has been extended from an original schema. For example, this might +/// be used by a GraphQL service which adds additional operation types, or additional directives to an existing schema. +/// +/// Note: Schema extensions without additional operation type definitions must not be followed by a { (such as a query shorthand) +/// to avoid parsing ambiguity. The same limitation applies to the type definitions and extensions below. +/// +/// https://spec.graphql.org/October2021/#sec-Schema-Extension +/// https://spec.graphql.org/October2021/#SchemaExtension type SchemaExtension = { - /// May be empty. Any directives provided must not already apply to the original Schema. + /// May be empty if OperationTypes is not, else it may not be empty. Directives: Directive list - /// May be empty + /// May be empty if the Directives list is not, else it may not be empty. OperationTypes: RootOperationTypeDefinition list } @@ -714,13 +970,28 @@ type SchemaExtension = /// /// https://spec.graphql.org/October2021/#sec-Type-Extensions type TypeExtension = + /// https://spec.graphql.org/October2021/#sec-Scalar-Extensions | ScalarTypeExtension of ScalarTypeExtension + /// https://spec.graphql.org/October2021/#sec-Object-Extensions | ObjectTypeExtension of ObjectTypeExtension + /// https://spec.graphql.org/October2021/#InterfaceTypeExtension | InterfaceTypeExtension of InterfaceTypeExtension + /// https://spec.graphql.org/October2021/#sec-Union-Extensions | UnionTypeExtension of UnionTypeExtension + /// https://spec.graphql.org/October2021/#sec-Enum-Extensions | EnumTypeExtension of EnumTypeExtension + /// https://spec.graphql.org/October2021/#sec-Input-Object-Extensions | InputObjectTypeExtension of InputObjectTypeExtension + member x.Directives = + match x with + | ScalarTypeExtension ste -> ste.Directives + | ObjectTypeExtension ote -> ote.Directives + | InterfaceTypeExtension ite -> ite.Directives + | UnionTypeExtension ute -> ute.Directives + | EnumTypeExtension ete -> ete.Directives + | InputObjectTypeExtension iote -> iote.Directives + /// 3.1 Type System Extensions /// /// Type system extensions are used to represent a GraphQL type system which has been extended from some original type system. For @@ -738,6 +1009,11 @@ type TypeSystemExtension = /// https://spec.graphql.org/October2021/#TypeExtension | TypeExtension of TypeExtension + member x.Directives = + match x with + | SchemaExtension se -> se.Directives + | TypeExtension te -> te.Directives + /// https://spec.graphql.org/October2021/#TypeSystemDocument type TypeSystemDocument = @@ -770,7 +1046,8 @@ type Definition = match x with | OperationDefinition op -> op.Directives | FragmentDefinition frag -> frag.Directives - | _ -> [] + | TypeSystemDefinition tsd -> tsd.Directives + | TypeSystemExtension tse -> tse.Directives member x.SelectionSet = match x with @@ -786,12 +1063,20 @@ type ExecutableDefinition = /// https://spec.graphql.org/October2021/#FragmentDefinition | FragmentDefinition of FragmentDefinition + member x.Directives = + match x with + | OperationDefinition op -> op.Directives + | FragmentDefinition frag -> frag.Directives + + /// https://spec.graphql.org/October2021/#ExecutableDocument type ExecutableDocument = { Definitions: ExecutableDefinition list } + member x.Directives = x.Definitions |> List.collect (fun y -> y.Directives) + /// 2.2 Document @@ -820,3 +1105,5 @@ type Document = { Definitions: Definition list } + + member x.Directives = x.Definitions |> List.collect (fun y -> y.Directives) diff --git a/src/FSharp.Data.GraphQL.Shared/Parser.fs b/src/FSharp.Data.GraphQL.Shared/Parser.fs index b1af59bc3..05fc75071 100644 --- a/src/FSharp.Data.GraphQL.Shared/Parser.fs +++ b/src/FSharp.Data.GraphQL.Shared/Parser.fs @@ -304,7 +304,7 @@ module internal Internal = let nonNullType = (listType <|> namedType) .>> pchar '!' - |>> NonNullType + |>> NonNullNameType "NonNullType" inputTypeRef diff --git a/src/FSharp.Data.GraphQL.Shared/Validation.fs b/src/FSharp.Data.GraphQL.Shared/Validation.fs index 6ca657dd6..f4324f5b9 100644 --- a/src/FSharp.Data.GraphQL.Shared/Validation.fs +++ b/src/FSharp.Data.GraphQL.Shared/Validation.fs @@ -13,15 +13,22 @@ open FSharp.Data.GraphQL.Types.Introspection module Types = let validateImplements (objdef: ObjectDef) (idef: InterfaceDef) = - let objectFields = - objdef.Fields + let objectFields = objdef.Fields + let errors = idef.Fields - |> Array.fold (fun acc f -> - match Map.tryFind f.Name objectFields with - | None -> $"'%s{f.Name}' field is defined by interface %s{idef.Name}, but not implemented in object %s{objdef.Name}"::acc - | Some objf when objf = f -> acc - | Some _ -> $"'%s{objdef.Name}.%s{f.Name}' field signature does not match it's definition in interface %s{idef.Name}"::acc) [] + |> Array.fold + (fun acc f -> + match Map.tryFind f.Name objectFields with + | None -> + $"'%s{f.Name}' field is defined by interface %s{idef.Name}, but not implemented in object %s{objdef.Name}" + :: acc + | Some objf when objf = f -> acc + | Some _ -> + $"'%s{objdef.Name}.%s{f.Name}' field signature does not match it's definition in interface %s{idef.Name}" + :: acc) + [] + match errors with | [] -> Success | err -> ValidationError err @@ -30,20 +37,45 @@ module Types = match typedef with | Scalar _ -> Success | Object objdef -> - let nonEmptyResult = if objdef.Fields.Count > 0 then Success else ValidationError [ objdef.Name + " must have at least one field defined" ] + let nonEmptyResult = + if objdef.Fields.Count > 0 then + Success + else + ValidationError [ objdef.Name + " must have at least one field defined" ] + let implementsResult = objdef.Implements |> collectResults (validateImplements objdef) nonEmptyResult @@ implementsResult | InputObject indef -> - let nonEmptyResult = if indef.Fields.Length > 0 then Success else ValidationError [ indef.Name + " must have at least one field defined" ] + let nonEmptyResult = + if indef.Fields.Length > 0 then + Success + else + ValidationError [ indef.Name + " must have at least one field defined" ] + nonEmptyResult | Union uniondef -> - let nonEmptyResult = if uniondef.Options.Length > 0 then Success else ValidationError [ uniondef.Name + " must have at least one type definition option" ] + let nonEmptyResult = + if uniondef.Options.Length > 0 then + Success + else + ValidationError [ uniondef.Name + " must have at least one type definition option" ] + nonEmptyResult | Enum enumdef -> - let nonEmptyResult = if enumdef.Options.Length > 0 then Success else ValidationError [ enumdef.Name + " must have at least one enum value defined" ] + let nonEmptyResult = + if enumdef.Options.Length > 0 then + Success + else + ValidationError [ enumdef.Name + " must have at least one enum value defined" ] + nonEmptyResult | Interface idef -> - let nonEmptyResult = if idef.Fields.Length > 0 then Success else ValidationError [ idef.Name + " must have at least one field defined" ] + let nonEmptyResult = + if idef.Fields.Length > 0 then + Success + else + ValidationError [ idef.Name + " must have at least one field defined" ] + nonEmptyResult | _ -> failwith $"Unexpected value of typedef: {typedef}" @@ -53,77 +85,100 @@ module Types = module Ast = type MetaTypeFieldInfo = { - Name : string - ArgumentNames : string[] + Name: string + ArgumentNames: string [] } let private metaTypeFields = - [| { Name = "__type"; ArgumentNames = [|"name"|] } - { Name = "__schema"; ArgumentNames = [||] } - { Name = "__typename"; ArgumentNames = [||] } |] + [| + { + Name = "__type" + ArgumentNames = [| "name" |] + } + { + Name = "__schema" + ArgumentNames = [||] + } + { + Name = "__typename" + ArgumentNames = [||] + } + |] |> Array.map (fun x -> x.Name, x) |> Map.ofArray - let rec private tryGetSchemaTypeByRef (schemaTypes : Map) (tref : IntrospectionTypeRef) = + let rec private tryGetSchemaTypeByRef (schemaTypes: Map) (tref: IntrospectionTypeRef) = match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> tryGetSchemaTypeByRef schemaTypes tref.OfType.Value + | TypeKind.NON_NULL + | TypeKind.LIST when tref.OfType.IsSome -> tryGetSchemaTypeByRef schemaTypes tref.OfType.Value | _ -> tref.Name |> Option.bind schemaTypes.TryFind type SchemaInfo = { - SchemaTypes : Map - QueryType : IntrospectionType option - SubscriptionType : IntrospectionType option - MutationType : IntrospectionType option - Directives : IntrospectionDirective [] + SchemaTypes: Map + QueryType: IntrospectionType option + SubscriptionType: IntrospectionType option + MutationType: IntrospectionType option + Directives: IntrospectionDirective [] } - member x.TryGetTypeByRef(tref : IntrospectionTypeRef) = - tryGetSchemaTypeByRef x.SchemaTypes tref - static member FromIntrospectionSchema(schema : IntrospectionSchema) = + member x.TryGetTypeByRef(tref: IntrospectionTypeRef) = tryGetSchemaTypeByRef x.SchemaTypes tref + + static member FromIntrospectionSchema(schema: IntrospectionSchema) = let schemaTypes = schema.Types |> Seq.map (fun x -> x.Name, x) |> Map.ofSeq - { SchemaTypes = schema.Types |> Seq.map (fun x -> x.Name, x) |> Map.ofSeq - QueryType = tryGetSchemaTypeByRef schemaTypes schema.QueryType - MutationType = schema.MutationType |> Option.bind (tryGetSchemaTypeByRef schemaTypes) - SubscriptionType = schema.SubscriptionType |> Option.bind (tryGetSchemaTypeByRef schemaTypes) - Directives = schema.Directives } - member x.TryGetOperationType(ot : OperationType) = + + { + SchemaTypes = schema.Types |> Seq.map (fun x -> x.Name, x) |> Map.ofSeq + QueryType = tryGetSchemaTypeByRef schemaTypes schema.QueryType + MutationType = schema.MutationType |> Option.bind (tryGetSchemaTypeByRef schemaTypes) + SubscriptionType = schema.SubscriptionType |> Option.bind (tryGetSchemaTypeByRef schemaTypes) + Directives = schema.Directives + } + + member x.TryGetOperationType(ot: OperationType) = match ot with | Query -> x.QueryType | Mutation -> x.MutationType | Subscription -> x.SubscriptionType - member x.TryGetTypeByName(name : string) = - x.SchemaTypes.TryFind(name) - member x.TryGetInputType(input : InputType) = + + member x.TryGetTypeByName(name: string) = x.SchemaTypes.TryFind(name) + + member x.TryGetInputType(input: TypeReference) = match input with | NamedType name -> x.TryGetTypeByName(name) - |> Option.bind (fun x -> match x.Kind with | TypeKind.INPUT_OBJECT | TypeKind.SCALAR | TypeKind.ENUM -> Some x | _ -> None) + |> Option.bind (fun x -> + match x.Kind with + | TypeKind.INPUT_OBJECT + | TypeKind.SCALAR + | TypeKind.ENUM -> Some x + | _ -> None) |> Option.map IntrospectionTypeRef.Named | ListType inner -> x.TryGetInputType(inner) |> Option.map IntrospectionTypeRef.List - | NonNullType inner -> x.TryGetInputType(inner) |> Option.map IntrospectionTypeRef.NonNull + | NonNullNameType inner -> x.TryGetInputType(inner) |> Option.map IntrospectionTypeRef.NonNull /// Contains information about the selection in a field or a fragment, with related GraphQL schema type information. and SelectionInfo = { /// Contains the information about the field inside the selection set of the parent type. - Field : Field + Field: Field /// Contains the reference to tye field type in the schema. - FieldType : IntrospectionTypeRef option + FieldType: IntrospectionTypeRef option /// Contains the reference to the parent type of the current field in the schema. - ParentType : IntrospectionType + ParentType: IntrospectionType /// If the field is inside a fragment selection, gets the reference to the fragment type of the current field in the schema. - FragmentType : IntrospectionType option + FragmentType: IntrospectionType option /// In case this field is part of a selection of a fragment spread, gets the name of the fragment spread. - FragmentSpreadName : string option + FragmentSpreadName: string option /// Contains the selection info of this field, if it is an Object, Interface or Union type. - SelectionSet : SelectionInfo list + SelectionSet: SelectionInfo list /// In case the schema definition fo this field has input values, gets information about the input values of the field in the schema. - InputValues : IntrospectionInputVal [] + InputValues: IntrospectionInputVal [] /// Contains the path of the field in the document. - Path : Path + Path: Path } /// If the field has an alias, return its alias. Otherwise, returns its name. member x.AliasOrName = x.Field.AliasOrName + /// If the field is inside a selection of a fragment definition, returns the fragment type containing the field. /// Otherwise, return the parent type in the schema definition. member x.FragmentOrParentType = x.FragmentType |> Option.defaultValue x.ParentType @@ -132,9 +187,9 @@ module Ast = type OperationDefinitionInfo = { /// Returns the definition from the parsed document. - Definition : OperationDefinition + Definition: OperationDefinition /// Returns the selection information about the operation, with related GraphQL schema type information. - SelectionSet : SelectionInfo list + SelectionSet: SelectionInfo list } /// Returns the name of the operation definition, if it does have a name. member x.Name = x.Definition.Name @@ -143,9 +198,9 @@ module Ast = type FragmentDefinitionInfo = { /// Returns the definition from the parsed document. - Definition : FragmentDefinition + Definition: FragmentDefinition /// Returns the selection information about the fragment, with related GraphQL schema type information. - SelectionSet : SelectionInfo list + SelectionSet: SelectionInfo list } /// Returns the name of the fragment definition, if it does have a name. member x.Name = x.Definition.Name @@ -159,13 +214,16 @@ module Ast = match x with | OperationDefinitionInfo x -> OperationDefinition x.Definition | FragmentDefinitionInfo x -> FragmentDefinition x.Definition + /// Returns the name of the definition, if it does have a name. member x.Name = x.Definition.Name + /// Returns the selection information about the definition, with related GraphQL schema type information. member x.SelectionSet = match x with | OperationDefinitionInfo x -> x.SelectionSet | FragmentDefinitionInfo x -> x.SelectionSet + /// Returns the directives from the parsed definition in the document. member x.Directives = match x with @@ -177,26 +235,36 @@ module Ast = type ValidationContext = { /// Gets information about all definitions in the original document, including related GraphQL schema type information for them. - Definitions : DefinitionInfo list + Definitions: DefinitionInfo list /// Gets information about the schema which the document is validated against. - Schema : SchemaInfo + Schema: SchemaInfo /// Gets the document that is being validated. - Document : Document + Document: Document } /// Gets the information about all the operations in the original document, including related GraphQL schema type information for them. - member x.OperationDefinitions = x.Definitions |> List.choose (function | OperationDefinitionInfo x -> Some x | _ -> None) + member x.OperationDefinitions = + x.Definitions + |> List.choose (function + | OperationDefinitionInfo x -> Some x + | _ -> None) + /// Gets the information about all the fragments in the original document, including related GraphQL schema type information for them. - member x.FragmentDefinitions = x.Definitions |> List.choose (function | FragmentDefinitionInfo x -> Some x | _ -> None) + member x.FragmentDefinitions = + x.Definitions + |> List.choose (function + | FragmentDefinitionInfo x -> Some x + | _ -> None) /// Contains information about a fragment type and its related GraphQL schema type. type FragmentTypeInfo = - | Inline of typeCondition : IntrospectionType - | Spread of name : string * directives : Directive list * typeCondition : IntrospectionType + | Inline of typeCondition: IntrospectionType + | Spread of name: string * directives: Directive list * typeCondition: IntrospectionType /// Gets the schema type related to the type condition of this fragment. member x.TypeCondition = match x with | Inline fragType -> fragType | Spread (_, _, fragType) -> fragType + /// In case this fragment is a fragment spread, get its name. member x.Name = match x with @@ -205,53 +273,66 @@ module Ast = type SelectionInfoContext = { - Schema : SchemaInfo - FragmentDefinitions : FragmentDefinition list - ParentType : IntrospectionType - FragmentType : FragmentTypeInfo option - Path : Path - SelectionSet : Selection list + Schema: SchemaInfo + FragmentDefinitions: FragmentDefinition list + ParentType: IntrospectionType + FragmentType: FragmentTypeInfo option + Path: Path + SelectionSet: Selection list } member x.FragmentOrParentType = - x.FragmentType - |> Option.map (fun x -> x.TypeCondition) - |> Option.defaultValue x.ParentType + x.FragmentType |> Option.map (fun x -> x.TypeCondition) |> Option.defaultValue x.ParentType - let private tryFindInArrayOption (finder : 'T -> bool) = Option.bind (Array.tryFind finder) + let private tryFindInArrayOption (finder: 'T -> bool) = Option.bind (Array.tryFind finder) + + let private onAllSelections (ctx: ValidationContext) (onSelection: SelectionInfo -> ValidationResult) = + let rec traverseSelections selection = + (onSelection selection) @@ (selection.SelectionSet |> collectResults traverseSelections) - let private onAllSelections (ctx : ValidationContext) (onSelection : SelectionInfo -> ValidationResult) = - let rec traverseSelections selection = (onSelection selection) @@ (selection.SelectionSet |> collectResults traverseSelections) ctx.Definitions |> collectResults (fun def -> def.SelectionSet |> collectResults traverseSelections) - let rec private getFragSelectionSetInfo (visitedFragments : string list) (fragmentTypeInfo : FragmentTypeInfo) (fragmentSelectionSet : Selection list) (parentCtx : SelectionInfoContext) = + let rec private getFragSelectionSetInfo + (visitedFragments: string list) + (fragmentTypeInfo: FragmentTypeInfo) + (fragmentSelectionSet: Selection list) + (parentCtx: SelectionInfoContext) + = match fragmentTypeInfo with | Inline fragType -> let fragCtx = { parentCtx with ParentType = parentCtx.FragmentOrParentType - FragmentType = Some (Inline fragType) - SelectionSet = fragmentSelectionSet } + FragmentType = Some(Inline fragType) + SelectionSet = fragmentSelectionSet + } + getSelectionSetInfo visitedFragments fragCtx | Spread (fragName, _, _) when List.contains fragName visitedFragments -> [] | Spread (fragName, directives, fragType) -> let fragCtx = { parentCtx with ParentType = parentCtx.FragmentOrParentType - FragmentType = Some (Spread (fragName, directives, fragType)) - SelectionSet = fragmentSelectionSet } + FragmentType = Some(Spread(fragName, directives, fragType)) + SelectionSet = fragmentSelectionSet + } + getSelectionSetInfo (fragName :: visitedFragments) fragCtx - and private getSelectionSetInfo (visitedFragments : string list) (ctx : SelectionInfoContext) : SelectionInfo list = + and private getSelectionSetInfo (visitedFragments: string list) (ctx: SelectionInfoContext) : SelectionInfo list = // When building the selection info, we should not raise any error when a type referred by the document // is not found in the schema. Not found types are validated in another validation, without the need of the // selection info to do it. Info types are helpers to validate against their schema types when the match is found. // Because of that, whenever a field or fragment type does not have a matching type in the schema, we skip the selection production. - ctx.SelectionSet |> List.collect (function + ctx.SelectionSet + |> List.collect (function | Field field -> - let introspectionField = ctx.FragmentOrParentType.Fields |> tryFindInArrayOption (fun f -> f.Name = field.Name) + let introspectionField = + ctx.FragmentOrParentType.Fields |> tryFindInArrayOption (fun f -> f.Name = field.Name) + let inputValues = introspectionField |> Option.map (fun f -> f.Args) let fieldTypeRef = introspectionField |> Option.map (fun f -> f.Type) let fieldPath = field.AliasOrName :: ctx.Path + let fieldSelectionSet = fieldTypeRef |> Option.bind ctx.Schema.TryGetTypeByRef @@ -261,17 +342,22 @@ module Ast = ParentType = fieldType FragmentType = None Path = fieldPath - SelectionSet = field.SelectionSet } + SelectionSet = field.SelectionSet + } + getSelectionSetInfo visitedFragments fieldCtx) |> Option.defaultValue [] - { Field = field - SelectionSet = fieldSelectionSet - FieldType = fieldTypeRef - ParentType = ctx.ParentType - FragmentType = ctx.FragmentType |> Option.map (fun x -> x.TypeCondition) - FragmentSpreadName = ctx.FragmentType |> Option.bind (fun x -> x.Name) - InputValues = inputValues |> Option.defaultValue [||] - Path = fieldPath } + + { + Field = field + SelectionSet = fieldSelectionSet + FieldType = fieldTypeRef + ParentType = ctx.ParentType + FragmentType = ctx.FragmentType |> Option.map (fun x -> x.TypeCondition) + FragmentSpreadName = ctx.FragmentType |> Option.bind (fun x -> x.Name) + InputValues = inputValues |> Option.defaultValue [||] + Path = fieldPath + } |> List.singleton | InlineFragment inlineFrag -> inlineFrag.TypeCondition @@ -287,20 +373,25 @@ module Ast = fragDef.TypeCondition |> Option.bind ctx.Schema.TryGetTypeByName |> Option.map (fun fragType -> - let fragType = (Spread (fragSpread.Name, fragSpread.Directives, fragType)) + let fragType = (Spread(fragSpread.Name, fragSpread.Directives, fragType)) getFragSelectionSetInfo visitedFragments fragType fragDef.SelectionSet ctx)) |> Option.defaultValue List.empty) - let private getOperationDefinitions (ast : Document) = + let private getOperationDefinitions (ast: Document) = ast.Definitions - |> List.choose (function | OperationDefinition x -> Some x | _ -> None) + |> List.choose (function + | OperationDefinition x -> Some x + | _ -> None) - let private getFragmentDefinitions (ast : Document) = + let private getFragmentDefinitions (ast: Document) = ast.Definitions - |> List.choose (function | FragmentDefinition x when x.Name.IsSome -> Some x | _ -> None) + |> List.choose (function + | FragmentDefinition x when x.Name.IsSome -> Some x + | _ -> None) - let internal getValidationContext (schemaInfo : SchemaInfo) (ast : Document) = + let internal getValidationContext (schemaInfo: SchemaInfo) (ast: Document) = let fragmentDefinitions = getFragmentDefinitions ast + let fragmentInfos = fragmentDefinitions |> List.choose (fun def -> @@ -309,160 +400,232 @@ module Ast = schemaInfo.TryGetTypeByName(typeCondition) |> Option.map (fun fragType -> let fragCtx = - { Schema = schemaInfo - FragmentDefinitions = fragmentDefinitions - ParentType = fragType - FragmentType = Some (Spread (def.Name.Value, def.Directives, fragType)) - Path = [def.Name.Value] - SelectionSet = def.SelectionSet } - FragmentDefinitionInfo { Definition = def - SelectionSet = getSelectionSetInfo [] fragCtx }))) + { + Schema = schemaInfo + FragmentDefinitions = fragmentDefinitions + ParentType = fragType + FragmentType = Some(Spread(def.Name.Value, def.Directives, fragType)) + Path = [ def.Name.Value ] + SelectionSet = def.SelectionSet + } + + FragmentDefinitionInfo + { + Definition = def + SelectionSet = getSelectionSetInfo [] fragCtx + }))) + let operationInfos = getOperationDefinitions ast |> List.choose (fun def -> schemaInfo.TryGetOperationType(def.OperationType) |> Option.map (fun parentType -> - let path = match def.Name with | Some name -> [name] | None -> [] + let path = + match def.Name with + | Some name -> [ name ] + | None -> [] + let opCtx = - { Schema = schemaInfo - FragmentDefinitions = fragmentDefinitions - ParentType = parentType - FragmentType = None - Path = path - SelectionSet = def.SelectionSet } - OperationDefinitionInfo { Definition = def - SelectionSet = getSelectionSetInfo [] opCtx })) - { Definitions = fragmentInfos @ operationInfos - Schema = schemaInfo - Document = ast } - - let internal validateOperationNameUniqueness (ctx : ValidationContext) = + { + Schema = schemaInfo + FragmentDefinitions = fragmentDefinitions + ParentType = parentType + FragmentType = None + Path = path + SelectionSet = def.SelectionSet + } + + OperationDefinitionInfo + { + Definition = def + SelectionSet = getSelectionSetInfo [] opCtx + })) + + { + Definitions = fragmentInfos @ operationInfos + Schema = schemaInfo + Document = ast + } + + let internal validateOperationNameUniqueness (ctx: ValidationContext) = let names = ctx.Document.Definitions |> List.choose (fun x -> x.Name) + names |> List.map (fun name -> name, names |> List.filter (fun x -> x = name) |> List.length) |> List.distinctBy fst |> collectResults (fun (name, count) -> - if count <= 1 - then Success - else AstError.AsResult $"Operation '%s{name}' has %i{count} definitions. Each operation name must be unique.") + if count <= 1 then + Success + else + AstError.AsResult $"Operation '%s{name}' has %i{count} definitions. Each operation name must be unique.") - let internal validateLoneAnonymousOperation (ctx : ValidationContext) = + let internal validateLoneAnonymousOperation (ctx: ValidationContext) = let operations = ctx.OperationDefinitions |> List.map (fun x -> x.Definition) let unamed = operations |> List.filter (fun x -> x.Name.IsNone) - if unamed.Length = 0 then Success - elif unamed.Length = 1 && operations.Length = 1 - then Success - else AstError.AsResult("An anonymous operation must be the only operation in a document. This document has at least one anonymous operation and more than one operation.") - let internal validateSubscriptionSingleRootField (ctx : ValidationContext) = + if unamed.Length = 0 then + Success + elif unamed.Length = 1 && operations.Length = 1 then + Success + else + AstError.AsResult( + "An anonymous operation must be the only operation in a document. This document has at least one anonymous operation and more than one operation." + ) + + let internal validateSubscriptionSingleRootField (ctx: ValidationContext) = let fragmentDefinitions = getFragmentDefinitions ctx.Document - let rec getFieldNames (selectionSet : Selection list) = + + let rec getFieldNames (selectionSet: Selection list) = ([], selectionSet) - ||> List.fold (fun acc -> function + ||> List.fold (fun acc -> + function | Field field -> field.AliasOrName :: acc | InlineFragment frag -> List.append (getFieldNames frag.SelectionSet) acc | FragmentSpread spread -> fragmentDefinitions |> List.tryFind (fun x -> x.Name.IsSome && x.Name.Value = spread.Name) |> Option.unwrap acc (fun frag -> getFieldNames frag.SelectionSet)) + ctx.Document.Definitions |> collectResults (function | OperationDefinition def when def.OperationType = Subscription -> let fieldNames = getFieldNames def.SelectionSet - if fieldNames.Length <= 1 - then Success + + if fieldNames.Length <= 1 then + Success else let fieldNamesAsString = System.String.Join(", ", fieldNames) + match def.Name with - | Some operationName -> AstError.AsResult $"Subscription operations should have only one root field. Operation '%s{operationName}' has %i{fieldNames.Length} fields (%s{fieldNamesAsString})." - | None -> AstError.AsResult $"Subscription operations should have only one root field. Operation has %i{fieldNames.Length} fields (%s{fieldNamesAsString})." + | Some operationName -> + AstError.AsResult + $"Subscription operations should have only one root field. Operation '%s{operationName}' has %i{fieldNames.Length} fields (%s{fieldNamesAsString})." + | None -> + AstError.AsResult + $"Subscription operations should have only one root field. Operation has %i{fieldNames.Length} fields (%s{fieldNamesAsString})." | _ -> Success) - let internal validateSelectionFieldTypes (ctx : ValidationContext) = + let internal validateSelectionFieldTypes (ctx: ValidationContext) = onAllSelections ctx (fun selection -> - if metaTypeFields.ContainsKey(selection.Field.Name) - then Success + if metaTypeFields.ContainsKey(selection.Field.Name) then + Success else - let exists = selection.FragmentOrParentType.Fields |> Option.map (Array.exists(fun f -> f.Name = selection.Field.Name)) |> Option.defaultValue false - if not exists - then AstError.AsResult($"Field '%s{selection.Field.Name}' is not defined in schema type '%s{selection.FragmentOrParentType.Name}'.", selection.Path) - else Success) + let exists = + selection.FragmentOrParentType.Fields + |> Option.map (Array.exists (fun f -> f.Name = selection.Field.Name)) + |> Option.defaultValue false + + if not exists then + AstError.AsResult( + $"Field '%s{selection.Field.Name}' is not defined in schema type '%s{selection.FragmentOrParentType.Name}'.", + selection.Path + ) + else + Success) - let private typesAreApplicable (parentType : IntrospectionType, fragmentType : IntrospectionType) = + let private typesAreApplicable (parentType: IntrospectionType, fragmentType: IntrospectionType) = let parentPossibleTypes = parentType.PossibleTypes |> Option.defaultValue [||] |> Array.choose (fun x -> x.Name) - |> Array.append [|parentType.Name|] + |> Array.append [| parentType.Name |] |> Set.ofArray + let fragmentPossibleTypes = fragmentType.PossibleTypes |> Option.defaultValue [||] |> Array.choose (fun x -> x.Name) - |> Array.append [|fragmentType.Name|] + |> Array.append [| fragmentType.Name |] |> Set.ofArray + let applicableTypes = Set.intersect parentPossibleTypes fragmentPossibleTypes applicableTypes.Count > 0 - let rec private sameResponseShape (fieldA : SelectionInfo, fieldB : SelectionInfo) = - if fieldA.FieldType = fieldB.FieldType - then + let rec private sameResponseShape (fieldA: SelectionInfo, fieldB: SelectionInfo) = + if fieldA.FieldType = fieldB.FieldType then let fieldsForName = Dictionary() + fieldA.SelectionSet - |> List.iter (fun selection -> Dictionary.addWith (List.append) selection.AliasOrName [selection] fieldsForName) + |> List.iter (fun selection -> Dictionary.addWith (List.append) selection.AliasOrName [ selection ] fieldsForName) + fieldB.SelectionSet - |> List.iter (fun selection -> Dictionary.addWith (List.append) selection.AliasOrName [selection] fieldsForName) + |> List.iter (fun selection -> Dictionary.addWith (List.append) selection.AliasOrName [ selection ] fieldsForName) + fieldsForName |> collectResults (fun (KeyValue (_, selectionSet)) -> - if selectionSet.Length < 2 - then Success - else List.pairwise selectionSet |> collectResults sameResponseShape) - else AstError.AsResult($"Field name or alias '%s{fieldA.AliasOrName}' appears two times, but they do not have the same return types in the scope of the parent type.", fieldA.Path) + if selectionSet.Length < 2 then + Success + else + List.pairwise selectionSet |> collectResults sameResponseShape) + else + AstError.AsResult( + $"Field name or alias '%s{fieldA.AliasOrName}' appears two times, but they do not have the same return types in the scope of the parent type.", + fieldA.Path + ) - let rec private fieldsInSetCanMerge (set : SelectionInfo list) = + let rec private fieldsInSetCanMerge (set: SelectionInfo list) = let fieldsForName = set |> List.groupBy (fun x -> x.AliasOrName) + fieldsForName |> collectResults (fun (aliasOrName, selectionSet) -> - if selectionSet.Length < 2 - then Success + if selectionSet.Length < 2 then + Success else List.pairwise selectionSet |> collectResults (fun (fieldA, fieldB) -> let hasSameShape = sameResponseShape (fieldA, fieldB) + if fieldA.FragmentOrParentType = fieldB.FragmentOrParentType - || fieldA.FragmentOrParentType.Kind <> TypeKind.OBJECT - || fieldB.FragmentOrParentType.Kind <> TypeKind.OBJECT - then + || fieldA.FragmentOrParentType.Kind <> TypeKind.OBJECT + || fieldB.FragmentOrParentType.Kind <> TypeKind.OBJECT then if fieldA.Field.Name <> fieldB.Field.Name then - hasSameShape @@ AstError.AsResult($"Field name or alias '%s{aliasOrName}' is referring to fields '%s{fieldA.Field.Name}' and '%s{fieldB.Field.Name}', but they are different fields in the scope of the parent type.", fieldA.Path) + hasSameShape + @@ AstError.AsResult( + $"Field name or alias '%s{aliasOrName}' is referring to fields '%s{fieldA.Field.Name}' and '%s{fieldB.Field.Name}', but they are different fields in the scope of the parent type.", + fieldA.Path + ) else if fieldA.Field.Arguments <> fieldB.Field.Arguments then - hasSameShape @@ AstError.AsResult($"Field name or alias '%s{aliasOrName}' refers to field '%s{fieldA.Field.Name}' two times, but each reference has different argument sets.", fieldA.Path) + hasSameShape + @@ AstError.AsResult( + $"Field name or alias '%s{aliasOrName}' refers to field '%s{fieldA.Field.Name}' two times, but each reference has different argument sets.", + fieldA.Path + ) else let mergedSet = fieldA.SelectionSet @ fieldB.SelectionSet hasSameShape @@ (fieldsInSetCanMerge mergedSet) - else hasSameShape)) + else + hasSameShape)) - let internal validateFieldSelectionMerging (ctx : ValidationContext) = + let internal validateFieldSelectionMerging (ctx: ValidationContext) = ctx.Definitions |> collectResults (fun def -> fieldsInSetCanMerge def.SelectionSet) - let rec private checkLeafFieldSelection (selection : SelectionInfo) = - let rec validateByKind (fieldType : IntrospectionTypeRef) (selectionSetLength : int) = + let rec private checkLeafFieldSelection (selection: SelectionInfo) = + let rec validateByKind (fieldType: IntrospectionTypeRef) (selectionSetLength: int) = match fieldType.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when fieldType.OfType.IsSome -> - validateByKind fieldType.OfType.Value selectionSetLength - | TypeKind.SCALAR | TypeKind.ENUM when selectionSetLength > 0 -> - AstError.AsResult($"Field '%s{selection.Field.Name}' of '%s{selection.FragmentOrParentType.Name}' type is of type kind %s{fieldType.Kind.ToString()}, and therefore should not contain inner fields in its selection.", selection.Path) - | TypeKind.INTERFACE | TypeKind.UNION | TypeKind.OBJECT when selectionSetLength = 0 -> - AstError.AsResult($"Field '%s{selection.Field.Name}' of '%s{selection.FragmentOrParentType.Name}' type is of type kind %s{fieldType.Kind.ToString()}, and therefore should have inner fields in its selection.", selection.Path) + | TypeKind.NON_NULL + | TypeKind.LIST when fieldType.OfType.IsSome -> validateByKind fieldType.OfType.Value selectionSetLength + | TypeKind.SCALAR + | TypeKind.ENUM when selectionSetLength > 0 -> + AstError.AsResult( + $"Field '%s{selection.Field.Name}' of '%s{selection.FragmentOrParentType.Name}' type is of type kind %s{fieldType.Kind.ToString()}, and therefore should not contain inner fields in its selection.", + selection.Path + ) + | TypeKind.INTERFACE + | TypeKind.UNION + | TypeKind.OBJECT when selectionSetLength = 0 -> + AstError.AsResult( + $"Field '%s{selection.Field.Name}' of '%s{selection.FragmentOrParentType.Name}' type is of type kind %s{fieldType.Kind.ToString()}, and therefore should have inner fields in its selection.", + selection.Path + ) | _ -> Success + match selection.FieldType with | Some fieldType -> validateByKind fieldType selection.SelectionSet.Length | None -> Success - let internal validateLeafFieldSelections (ctx : ValidationContext) = - onAllSelections ctx checkLeafFieldSelection + let internal validateLeafFieldSelections (ctx: ValidationContext) = onAllSelections ctx checkLeafFieldSelection - let private checkFieldArgumentNames (schemaInfo : SchemaInfo) (selection : SelectionInfo) = + let private checkFieldArgumentNames (schemaInfo: SchemaInfo) (selection: SelectionInfo) = let argumentsValid = selection.Field.Arguments |> collectResults (fun arg -> @@ -470,9 +633,15 @@ module Ast = metaTypeFields.TryFind(selection.Field.Name) |> Option.map (fun x -> x.ArgumentNames) |> Option.defaultValue (selection.InputValues |> Array.map (fun x -> x.Name)) + match schemaArgumentNames |> Array.tryFind (fun x -> x = arg.Name) with | Some _ -> Success - | None -> AstError.AsResult($"Field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' does not have an input named '%s{arg.Name}' in its definition.", selection.Path)) + | None -> + AstError.AsResult( + $"Field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' does not have an input named '%s{arg.Name}' in its definition.", + selection.Path + )) + let directivesValid = selection.Field.Directives |> collectResults (fun directive -> @@ -482,29 +651,42 @@ module Ast = |> collectResults (fun arg -> match directiveType.Args |> Array.tryFind (fun argt -> argt.Name = arg.Name) with | Some _ -> Success - | _ -> AstError.AsResult($"Directive '%s{directiveType.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' does not have an argument named '%s{arg.Name}' in its definition.", selection.Path)) + | _ -> + AstError.AsResult( + $"Directive '%s{directiveType.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' does not have an argument named '%s{arg.Name}' in its definition.", + selection.Path + )) | None -> Success) + argumentsValid @@ directivesValid - let internal validateArgumentNames (ctx : ValidationContext) = - onAllSelections ctx (checkFieldArgumentNames ctx.Schema) + let internal validateArgumentNames (ctx: ValidationContext) = onAllSelections ctx (checkFieldArgumentNames ctx.Schema) - let rec private validateArgumentUniquenessInSelection (selection : SelectionInfo) = - let validateArgs (fieldOrDirective : string) (path : Path) (args : Argument list) = + let rec private validateArgumentUniquenessInSelection (selection: SelectionInfo) = + let validateArgs (fieldOrDirective: string) (path: Path) (args: Argument list) = args - |> List.countBy(fun x -> x.Name) + |> List.countBy (fun x -> x.Name) |> collectResults (fun (name, length) -> - if length > 1 - then AstError.AsResult($"There are %i{length} arguments with name '%s{name}' defined in %s{fieldOrDirective}. Field arguments must be unique.", path) - else Success) - let argsValid = validateArgs $"alias or field '%s{selection.AliasOrName}'" selection.Path selection.Field.Arguments - let directiveArgsValid = selection.Field.Directives |> collectResults (fun directive -> validateArgs $"directive '%s{directive.Name}'" selection.Path directive.Arguments) + if length > 1 then + AstError.AsResult( + $"There are %i{length} arguments with name '%s{name}' defined in %s{fieldOrDirective}. Field arguments must be unique.", + path + ) + else + Success) + + let argsValid = + validateArgs $"alias or field '%s{selection.AliasOrName}'" selection.Path selection.Field.Arguments + + let directiveArgsValid = + selection.Field.Directives + |> collectResults (fun directive -> validateArgs $"directive '%s{directive.Name}'" selection.Path directive.Arguments) + argsValid @@ directiveArgsValid - let internal validateArgumentUniqueness (ctx : ValidationContext) = - onAllSelections ctx validateArgumentUniquenessInSelection + let internal validateArgumentUniqueness (ctx: ValidationContext) = onAllSelections ctx validateArgumentUniquenessInSelection - let private checkRequiredArguments (schemaInfo : SchemaInfo) (selection : SelectionInfo) = + let private checkRequiredArguments (schemaInfo: SchemaInfo) (selection: SelectionInfo) = let inputsValid = selection.InputValues |> collectResults (fun argDef -> @@ -512,8 +694,13 @@ module Ast = | TypeKind.NON_NULL when argDef.DefaultValue.IsNone -> match selection.Field.Arguments |> List.tryFind (fun arg -> arg.Name = argDef.Name) with | Some arg when arg.Value <> NullValue -> Success - | _ -> AstError.AsResult($"Argument '%s{argDef.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' is required and does not have a default value.", selection.Path) + | _ -> + AstError.AsResult( + $"Argument '%s{argDef.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' is required and does not have a default value.", + selection.Path + ) | _ -> Success) + let directivesValid = selection.Field.Directives |> collectResults (fun directive -> @@ -525,43 +712,71 @@ module Ast = | TypeKind.NON_NULL when argDef.DefaultValue.IsNone -> match directive.Arguments |> List.tryFind (fun arg -> arg.Name = argDef.Name) with | Some arg when arg.Value <> NullValue -> Success - | _ -> AstError.AsResult($"Argument '%s{argDef.Name}' of directive '%s{directiveType.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' is required and does not have a default value.", selection.Path) + | _ -> + AstError.AsResult( + $"Argument '%s{argDef.Name}' of directive '%s{directiveType.Name}' of field '%s{selection.Field.Name}' of type '%s{selection.FragmentOrParentType.Name}' is required and does not have a default value.", + selection.Path + ) | _ -> Success) | None -> Success) + inputsValid @@ directivesValid - let internal validateRequiredArguments (ctx : ValidationContext) = - onAllSelections ctx (checkRequiredArguments ctx.Schema) + let internal validateRequiredArguments (ctx: ValidationContext) = onAllSelections ctx (checkRequiredArguments ctx.Schema) - let internal validateFragmentNameUniqueness (ctx : ValidationContext) = + let internal validateFragmentNameUniqueness (ctx: ValidationContext) = let counts = Dictionary() + ctx.FragmentDefinitions - |> List.iter(fun frag -> frag.Definition.Name |> Option.iter(fun name -> Dictionary.addWith (+) name 1 counts)) - counts - |> collectResults (fun (KeyValue(name, length)) -> - if length > 1 - then AstError.AsResult $"There are %i{length} fragments with name '%s{name}' in the document. Fragment definitions must have unique names." - else Success) + |> List.iter (fun frag -> frag.Definition.Name |> Option.iter (fun name -> Dictionary.addWith (+) name 1 counts)) - let rec private checkFragmentTypeExistence (fragmentDefinitions : FragmentDefinition list) (schemaInfo : SchemaInfo) (path : Path) (frag : FragmentDefinition) = + counts + |> collectResults (fun (KeyValue (name, length)) -> + if length > 1 then + AstError.AsResult + $"There are %i{length} fragments with name '%s{name}' in the document. Fragment definitions must have unique names." + else + Success) + + let rec private checkFragmentTypeExistence + (fragmentDefinitions: FragmentDefinition list) + (schemaInfo: SchemaInfo) + (path: Path) + (frag: FragmentDefinition) + = let typeConditionsValid = match frag.TypeCondition |> Option.bind schemaInfo.TryGetTypeByName with | Some _ -> Success - | None when frag.Name.IsSome -> AstError.AsResult $"Fragment '%s{frag.Name.Value}' has type condition '%s{frag.TypeCondition.Value}', but that type does not exist in the schema." - | None -> AstError.AsResult( $"Inline fragment has type condition '%s{frag.TypeCondition.Value}', but that type does not exist in the schema.", path) - typeConditionsValid @@ (frag.SelectionSet |> collectResults (checkFragmentTypeExistenceInSelection fragmentDefinitions schemaInfo path)) - - and private checkFragmentTypeExistenceInSelection (fragmentDefinitions : FragmentDefinition list) (schemaInfo : SchemaInfo) (path : Path) = + | None when frag.Name.IsSome -> + AstError.AsResult + $"Fragment '%s{frag.Name.Value}' has type condition '%s{frag.TypeCondition.Value}', but that type does not exist in the schema." + | None -> + AstError.AsResult( + $"Inline fragment has type condition '%s{frag.TypeCondition.Value}', but that type does not exist in the schema.", + path + ) + + typeConditionsValid + @@ (frag.SelectionSet + |> collectResults (checkFragmentTypeExistenceInSelection fragmentDefinitions schemaInfo path)) + + and private checkFragmentTypeExistenceInSelection + (fragmentDefinitions: FragmentDefinition list) + (schemaInfo: SchemaInfo) + (path: Path) + = function | Field field -> let path = field.AliasOrName :: path + field.SelectionSet |> collectResults (checkFragmentTypeExistenceInSelection fragmentDefinitions schemaInfo path) | InlineFragment frag -> checkFragmentTypeExistence fragmentDefinitions schemaInfo path frag | _ -> Success - let internal validateFragmentTypeExistence (ctx : ValidationContext) = + let internal validateFragmentTypeExistence (ctx: ValidationContext) = let fragmentDefinitions = getFragmentDefinitions ctx.Document + ctx.Document.Definitions |> collectResults (function // TODO: type system (def | ext) @@ -574,56 +789,75 @@ module Ast = checkFragmentTypeExistence fragmentDefinitions ctx.Schema path frag | OperationDefinition odef -> let path = odef.Name |> Option.toList + odef.SelectionSet |> collectResults (checkFragmentTypeExistenceInSelection fragmentDefinitions ctx.Schema path)) - let rec private checkFragmentOnCompositeType (selection : SelectionInfo) = + let rec private checkFragmentOnCompositeType (selection: SelectionInfo) = let fragmentTypeValid = match selection.FragmentType with | Some fragType -> match fragType.Kind with - | TypeKind.UNION | TypeKind.OBJECT | TypeKind.INTERFACE -> Success - | _ when selection.FragmentSpreadName.IsSome -> AstError.AsResult($"Fragment '%s{selection.FragmentSpreadName.Value}' has type kind %s{fragType.Kind.ToString()}, but fragments can only be defined in UNION, OBJECT or INTERFACE types.", selection.Path) - | _ -> AstError.AsResult($"Inline fragment has type kind %s{fragType.Kind.ToString()}, but fragments can only be defined in UNION, OBJECT or INTERFACE types.", selection.Path) + | TypeKind.UNION + | TypeKind.OBJECT + | TypeKind.INTERFACE -> Success + | _ when selection.FragmentSpreadName.IsSome -> + AstError.AsResult( + $"Fragment '%s{selection.FragmentSpreadName.Value}' has type kind %s{fragType.Kind.ToString()}, but fragments can only be defined in UNION, OBJECT or INTERFACE types.", + selection.Path + ) + | _ -> + AstError.AsResult( + $"Inline fragment has type kind %s{fragType.Kind.ToString()}, but fragments can only be defined in UNION, OBJECT or INTERFACE types.", + selection.Path + ) | None -> Success + fragmentTypeValid @@ (selection.SelectionSet |> collectResults checkFragmentOnCompositeType) - let internal validateFragmentsOnCompositeTypes (ctx : ValidationContext) = - onAllSelections ctx checkFragmentOnCompositeType + let internal validateFragmentsOnCompositeTypes (ctx: ValidationContext) = onAllSelections ctx checkFragmentOnCompositeType - let internal validateFragmentsMustBeUsed (ctx : ValidationContext) = - let rec getSpreadNames (acc : Set) = + let internal validateFragmentsMustBeUsed (ctx: ValidationContext) = + let rec getSpreadNames (acc: Set) = function | Field field -> field.SelectionSet |> Set.ofList |> Set.collect (getSpreadNames acc) | InlineFragment frag -> frag.SelectionSet |> Set.ofList |> Set.collect (getSpreadNames acc) | FragmentSpread spread -> acc.Add spread.Name + let fragmentSpreadNames = Set.ofList ctx.Document.Definitions - |> Set.collect (fun def -> - Set.ofList def.SelectionSet - |> Set.collect (getSpreadNames Set.empty)) + |> Set.collect (fun def -> Set.ofList def.SelectionSet |> Set.collect (getSpreadNames Set.empty)) + getFragmentDefinitions ctx.Document |> collectResults (fun def -> - if def.Name.IsSome && Set.contains def.Name.Value fragmentSpreadNames - then Success - else AstError.AsResult $"Fragment '%s{def.Name.Value}' is not used in any operation in the document. Fragments must be used in at least one operation.") + if def.Name.IsSome && Set.contains def.Name.Value fragmentSpreadNames then + Success + else + AstError.AsResult + $"Fragment '%s{def.Name.Value}' is not used in any operation in the document. Fragments must be used in at least one operation.") - let rec private fragmentSpreadTargetDefinedInSelection (fragmentDefinitionNames : string list) (path : Path) = + let rec private fragmentSpreadTargetDefinedInSelection (fragmentDefinitionNames: string list) (path: Path) = function | Field field -> let path = field.AliasOrName :: path + field.SelectionSet |> collectResults (fragmentSpreadTargetDefinedInSelection fragmentDefinitionNames path) | InlineFragment frag -> frag.SelectionSet |> collectResults (fragmentSpreadTargetDefinedInSelection fragmentDefinitionNames path) | FragmentSpread spread -> - if List.contains spread.Name fragmentDefinitionNames - then Success - else AstError.AsResult($"Fragment spread '%s{spread.Name}' refers to a non-existent fragment definition in the document.", path) + if List.contains spread.Name fragmentDefinitionNames then + Success + else + AstError.AsResult( + $"Fragment spread '%s{spread.Name}' refers to a non-existent fragment definition in the document.", + path + ) - let internal validateFragmentSpreadTargetDefined (ctx : ValidationContext) = + let internal validateFragmentSpreadTargetDefined (ctx: ValidationContext) = let fragmentDefinitionNames = ctx.FragmentDefinitions |> List.choose (fun def -> def.Name) + ctx.Document.Definitions |> collectResults (function // TODO: type system (def | ext) @@ -633,23 +867,30 @@ module Ast = // () | FragmentDefinition frag -> let path = Option.toList frag.Name + frag.SelectionSet |> collectResults (fragmentSpreadTargetDefinedInSelection fragmentDefinitionNames path) | OperationDefinition odef -> let path = Option.toList odef.Name + odef.SelectionSet |> collectResults (fragmentSpreadTargetDefinedInSelection fragmentDefinitionNames path)) - let rec private checkFragmentMustNotHaveCycles (fragmentDefinitions : FragmentDefinition list) (visited : string list) (fragName : string) (fragSelectionSet : Selection list) = + let rec private checkFragmentMustNotHaveCycles + (fragmentDefinitions: FragmentDefinition list) + (visited: string list) + (fragName: string) + (fragSelectionSet: Selection list) + = let visitCount = visited |> List.filter (fun x -> x = fragName) |> List.length - if visitCount > 1 - then + + if visitCount > 1 then AstError.AsResult $"Fragment '%s{fragName}' is making a cyclic reference." else fragSelectionSet |> collectResults (checkFragmentsMustNotHaveCyclesInSelection fragmentDefinitions (fragName :: visited)) - and private checkFragmentsMustNotHaveCyclesInSelection (fragmentDefinitions : FragmentDefinition list) (visited : string list) = + and private checkFragmentsMustNotHaveCyclesInSelection (fragmentDefinitions: FragmentDefinition list) (visited: string list) = function | Field field -> field.SelectionSet @@ -662,45 +903,69 @@ module Ast = | Some frag -> checkFragmentMustNotHaveCycles fragmentDefinitions visited spread.Name frag.SelectionSet | None -> Success - let internal validateFragmentsMustNotFormCycles (ctx : ValidationContext) = + let internal validateFragmentsMustNotFormCycles (ctx: ValidationContext) = let fragmentDefinitions = ctx.FragmentDefinitions |> List.map (fun frag -> frag.Definition) - let fragNamesAndSelections = fragmentDefinitions |> List.choose (fun frag -> frag.Name |> Option.map (fun x -> x, frag.SelectionSet)) + + let fragNamesAndSelections = + fragmentDefinitions + |> List.choose (fun frag -> frag.Name |> Option.map (fun x -> x, frag.SelectionSet)) + fragNamesAndSelections |> collectResults (fun (name, selectionSet) -> checkFragmentMustNotHaveCycles fragmentDefinitions [] name selectionSet) - let private checkFragmentSpreadIsPossibleInSelection (path : Path, parentType : IntrospectionType, fragmentType : IntrospectionType) = - if not (typesAreApplicable (parentType, fragmentType)) - then AstError.AsResult($"Fragment type condition '%s{fragmentType.Name}' is not applicable to the parent type of the field '%s{parentType.Name}'.", path) - else Success + let private checkFragmentSpreadIsPossibleInSelection + ( + path: Path, + parentType: IntrospectionType, + fragmentType: IntrospectionType + ) = + if not (typesAreApplicable (parentType, fragmentType)) then + AstError.AsResult( + $"Fragment type condition '%s{fragmentType.Name}' is not applicable to the parent type of the field '%s{parentType.Name}'.", + path + ) + else + Success - let rec private getFragmentAndParentTypes (set : SelectionInfo list) = + let rec private getFragmentAndParentTypes (set: SelectionInfo list) = ([], set) ||> List.fold (fun acc selection -> match selection.FragmentType with - | Some fragType when fragType.Name <> selection.ParentType.Name -> (selection.Path, selection.ParentType, fragType) :: acc + | Some fragType when fragType.Name <> selection.ParentType.Name -> + (selection.Path, selection.ParentType, fragType) :: acc | _ -> acc) - let internal validateFragmentSpreadIsPossible (ctx : ValidationContext) = + let internal validateFragmentSpreadIsPossible (ctx: ValidationContext) = ctx.Definitions |> collectResults (fun def -> def.SelectionSet |> getFragmentAndParentTypes |> collectResults (checkFragmentSpreadIsPossibleInSelection)) - let private checkInputValue (schemaInfo : SchemaInfo) (variables : VariableDefinition list option) (selection : SelectionInfo) = - let rec getFieldMap (fields : (string * IntrospectionTypeRef) seq) : Map = - (Map.empty, fields) - ||> Seq.fold (fun acc (name, tref) -> Map.add name tref acc) + let private checkInputValue (schemaInfo: SchemaInfo) (variables: VariableDefinition list option) (selection: SelectionInfo) = + let rec getFieldMap (fields: (string * IntrospectionTypeRef) seq) : Map = + (Map.empty, fields) ||> Seq.fold (fun acc (name, tref) -> Map.add name tref acc) + + let rec checkIsCoercible (tref: IntrospectionTypeRef) (argName: string) (value: Value) = + let canNotCoerce = + AstError.AsResult( + $"Argument field or value named '%s{argName}' can not be coerced. It does not match a valid literal representation for the type.", + selection.Path + ) - let rec checkIsCoercible (tref : IntrospectionTypeRef) (argName : string) (value : Value) = - let canNotCoerce = AstError.AsResult($"Argument field or value named '%s{argName}' can not be coerced. It does not match a valid literal representation for the type.", selection.Path) match value with - | NullValue when tref.Kind = TypeKind.NON_NULL -> AstError.AsResult($"Argument '%s{argName}' value can not be coerced. It's type is non-nullable but the argument has a null value.", selection.Path) + | NullValue when tref.Kind = TypeKind.NON_NULL -> + AstError.AsResult( + $"Argument '%s{argName}' value can not be coerced. It's type is non-nullable but the argument has a null value.", + selection.Path + ) | NullValue -> Success | _ when tref.Kind = TypeKind.NON_NULL -> checkIsCoercible tref.OfType.Value argName value | IntValue _ -> match tref.Name, tref.Kind with - | Some ("Int" | "Float"), TypeKind.SCALAR -> Success + | Some ("Int" + | "Float"), + TypeKind.SCALAR -> Success | _ -> canNotCoerce | FloatValue _ -> match tref.Name, tref.Kind with @@ -712,6 +977,7 @@ module Ast = | _ -> canNotCoerce | StringValue _ -> let invalidScalars = [| "Int"; "Float"; "Boolean" |] + match tref.Name, tref.Kind with | (Some x, TypeKind.SCALAR) when not (Array.contains x invalidScalars) -> Success | _ -> canNotCoerce @@ -725,22 +991,37 @@ module Ast = | _ -> canNotCoerce | ObjectValue props -> match tref.Kind with - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION | TypeKind.INPUT_OBJECT when tref.Name.IsSome -> + | TypeKind.OBJECT + | TypeKind.INTERFACE + | TypeKind.UNION + | TypeKind.INPUT_OBJECT when tref.Name.IsSome -> match schemaInfo.TryGetTypeByRef(tref) with | Some itype -> - let fieldMap = itype.InputFields |> Option.defaultValue [||] |> Array.map (fun x -> x.Name, x.Type) |> getFieldMap + let fieldMap = + itype.InputFields |> Option.defaultValue [||] |> Array.map (fun x -> x.Name, x.Type) |> getFieldMap + let canCoerceFields = fieldMap |> collectResults (fun kvp -> - if kvp.Value.Kind = TypeKind.NON_NULL && not (props.ContainsKey(kvp.Key)) - then AstError.AsResult($"Can not coerce argument '%s{argName}'. Argument definition '%s{tref.Name.Value}' have a required field '%s{kvp.Key}', but that field does not exist in the literal value for the argument.", selection.Path) - else Success) + if kvp.Value.Kind = TypeKind.NON_NULL && not (props.ContainsKey(kvp.Key)) then + AstError.AsResult( + $"Can not coerce argument '%s{argName}'. Argument definition '%s{tref.Name.Value}' have a required field '%s{kvp.Key}', but that field does not exist in the literal value for the argument.", + selection.Path + ) + else + Success) + let canCoerceProps = props |> collectResults (fun kvp -> match Map.tryFind kvp.Key fieldMap with | Some fieldTypeRef -> checkIsCoercible fieldTypeRef kvp.Key kvp.Value - | None -> AstError.AsResult($"Can not coerce argument '%s{argName}'. The field '%s{kvp.Key}' is not a valid field in the argument definition.", selection.Path)) + | None -> + AstError.AsResult( + $"Can not coerce argument '%s{argName}'. The field '%s{kvp.Key}' is not a valid field in the argument definition.", + selection.Path + )) + canCoerceFields @@ canCoerceProps | None -> canNotCoerce | _ -> canNotCoerce @@ -748,157 +1029,222 @@ module Ast = let variableDefinition = variables |> Option.defaultValue [] - |> List.tryPick (fun v -> if v.VariableName = varName then Some (v, schemaInfo.TryGetInputType(v.Type)) else None) + |> List.tryPick (fun v -> + if v.VariableName = varName then Some(v, schemaInfo.TryGetInputType(v.Type)) else None) + match variableDefinition with | Some (vdef, Some vtype) when vdef.DefaultValue.IsSome -> checkIsCoercible vtype argName vdef.DefaultValue.Value | Some (vdef, None) when vdef.DefaultValue.IsSome -> canNotCoerce | _ -> Success + selection.Field.Arguments |> collectResults (fun arg -> - let argumentTypeRef = selection.InputValues |> Array.tryPick (fun x -> if x.Name = arg.Name then Some x.Type else None) + let argumentTypeRef = + selection.InputValues |> Array.tryPick (fun x -> if x.Name = arg.Name then Some x.Type else None) + match argumentTypeRef with | Some argumentTypeRef -> checkIsCoercible argumentTypeRef arg.Name arg.Value | None -> Success) - let internal validateInputValues (ctx : ValidationContext) = + let internal validateInputValues (ctx: ValidationContext) = ctx.Definitions |> collectResults (fun def -> let (vars, selectionSet) = match def with | OperationDefinitionInfo odef -> (Some odef.Definition.VariableDefinitions, odef.SelectionSet) | FragmentDefinitionInfo fdef -> (None, fdef.SelectionSet) + selectionSet |> collectResults (checkInputValue ctx.Schema vars)) - let rec private getDistinctDirectiveNamesInSelection (path : Path) (selection : Selection) : (Path * Set) list = + let rec private getDistinctDirectiveNamesInSelection (path: Path) (selection: Selection) : (Path * Set) list = match selection with | Field field -> let path = field.AliasOrName :: path let fieldDirectives = [ path, field.Directives |> List.map (fun x -> x.Name) |> Set.ofList ] - let selectionSetDirectives = field.SelectionSet |> List.collect (getDistinctDirectiveNamesInSelection path) + + let selectionSetDirectives = + field.SelectionSet |> List.collect (getDistinctDirectiveNamesInSelection path) + fieldDirectives |> List.append selectionSetDirectives | InlineFragment frag -> getDistinctDirectiveNamesInDefinition path (FragmentDefinition frag) | FragmentSpread spread -> [ path, spread.Directives |> List.map (fun x -> x.Name) |> Set.ofList ] - and private getDistinctDirectiveNamesInDefinition (path : Path) (frag : Definition) : (Path * Set) list = + and private getDistinctDirectiveNamesInDefinition (path: Path) (frag: Definition) : (Path * Set) list = let fragDirectives = [ path, frag.Directives |> List.map (fun x -> x.Name) |> Set.ofList ] - let selectionSetDirectives = frag.SelectionSet |> List.collect (getDistinctDirectiveNamesInSelection path) + + let selectionSetDirectives = + frag.SelectionSet |> List.collect (getDistinctDirectiveNamesInSelection path) + fragDirectives |> List.append selectionSetDirectives - let internal validateDirectivesDefined (ctx : ValidationContext) = + let internal validateDirectivesDefined (ctx: ValidationContext) = ctx.Definitions |> List.collect (fun def -> - let path = match def.Name with | Some name -> [name] | None -> [] + let path = + match def.Name with + | Some name -> [ name ] + | None -> [] + getDistinctDirectiveNamesInDefinition path def.Definition) |> collectResults (fun (path, names) -> names |> collectResults (fun name -> - if ctx.Schema.Directives |> Array.exists (fun x -> x.Name = name) - then Success - else AstError.AsResult($"Directive '%s{name}' is not defined in the schema.", path))) - - let private validateDirective (schemaInfo : SchemaInfo) (path : Path) (location : DirectiveLocation) (onError : Directive -> string) (directive : Directive) = + if ctx.Schema.Directives |> Array.exists (fun x -> x.Name = name) then + Success + else + AstError.AsResult($"Directive '%s{name}' is not defined in the schema.", path))) + + let private validateDirective + (schemaInfo: SchemaInfo) + (path: Path) + (location: DirectiveLocation) + (onError: Directive -> string) + (directive: Directive) + = schemaInfo.Directives |> collectResults (fun d -> - if d.Name = directive.Name - then - if d.Locations |> Array.contains location then Success - else AstError.AsResult (onError directive, path) - else Success) + if d.Name = directive.Name then + if d.Locations |> Array.contains location then + Success + else + AstError.AsResult(onError directive, path) + else + Success) type private InlineFragmentContext = - { Schema : SchemaInfo - FragmentDefinitions : FragmentDefinition list - Path : Path - Directives : Directive list - SelectionSet : Selection list } + { + Schema: SchemaInfo + FragmentDefinitions: FragmentDefinition list + Path: Path + Directives: Directive list + SelectionSet: Selection list + } - let rec private checkDirectivesInValidLocationOnInlineFragment (ctx : InlineFragmentContext) = + let rec private checkDirectivesInValidLocationOnInlineFragment (ctx: InlineFragmentContext) = let directivesValid = ctx.Directives - |> collectResults (validateDirective ctx.Schema ctx.Path DirectiveLocation.INLINE_FRAGMENT (fun d -> - $"An inline fragment has a directive '%s{d.Name}', but this directive location is not supported by the schema definition.")) + |> collectResults ( + validateDirective ctx.Schema ctx.Path DirectiveLocation.INLINE_FRAGMENT (fun d -> + $"An inline fragment has a directive '%s{d.Name}', but this directive location is not supported by the schema definition.") + ) + let directivesValidInSelectionSet = ctx.SelectionSet |> collectResults (checkDirectivesInValidLocationOnSelection ctx.Schema ctx.FragmentDefinitions ctx.Path) + directivesValid @@ directivesValidInSelectionSet - and private checkDirectivesInValidLocationOnSelection (schemaInfo : SchemaInfo) (fragmentDefinitions : FragmentDefinition list) (path : Path) = + and private checkDirectivesInValidLocationOnSelection + (schemaInfo: SchemaInfo) + (fragmentDefinitions: FragmentDefinition list) + (path: Path) + = function | Field field -> let path = field.AliasOrName :: path + let directivesValid = field.Directives - |> collectResults (validateDirective schemaInfo path DirectiveLocation.FIELD (fun directiveDef -> - $"Field or alias '%s{field.AliasOrName}' has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition.")) + |> collectResults ( + validateDirective schemaInfo path DirectiveLocation.FIELD (fun directiveDef -> + $"Field or alias '%s{field.AliasOrName}' has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition.") + ) + let directivesValidInSelectionSet = field.SelectionSet |> collectResults (checkDirectivesInValidLocationOnSelection schemaInfo fragmentDefinitions path) + directivesValid @@ directivesValidInSelectionSet | InlineFragment frag -> let fragCtx = - { Schema = schemaInfo - FragmentDefinitions = fragmentDefinitions - Path = path - Directives = frag.Directives - SelectionSet = frag.SelectionSet } + { + Schema = schemaInfo + FragmentDefinitions = fragmentDefinitions + Path = path + Directives = frag.Directives + SelectionSet = frag.SelectionSet + } + checkDirectivesInValidLocationOnInlineFragment fragCtx | _ -> Success // We don't validate spreads here, they are being validated in another function type private FragmentSpreadContext = - { Schema : SchemaInfo - FragmentDefinitions : FragmentDefinition list - Path : Path - FragmentName : string - Directives : Directive list - SelectionSet : Selection list } - - let rec private checkDirectivesInValidLocationOnFragmentSpread (ctx : FragmentSpreadContext) = + { + Schema: SchemaInfo + FragmentDefinitions: FragmentDefinition list + Path: Path + FragmentName: string + Directives: Directive list + SelectionSet: Selection list + } + + let rec private checkDirectivesInValidLocationOnFragmentSpread (ctx: FragmentSpreadContext) = let directivesValid = ctx.Directives - |> collectResults (validateDirective ctx.Schema ctx.Path DirectiveLocation.FRAGMENT_SPREAD (fun d -> - $"Fragment '%s{ctx.FragmentName}' has a directive '%s{d.Name}', but this directive location is not supported by the schema definition.")) + |> collectResults ( + validateDirective ctx.Schema ctx.Path DirectiveLocation.FRAGMENT_SPREAD (fun d -> + $"Fragment '%s{ctx.FragmentName}' has a directive '%s{d.Name}', but this directive location is not supported by the schema definition.") + ) + let directivesValidInSelectionSet = ctx.SelectionSet |> collectResults (checkDirectivesInValidLocationOnSelection ctx.Schema ctx.FragmentDefinitions ctx.Path) + directivesValid @@ directivesValidInSelectionSet - let private checkDirectivesInOperation (schemaInfo : SchemaInfo) (fragmentDefinitions : FragmentDefinition list) (path : Path) (operation : OperationDefinition) = + let private checkDirectivesInOperation + (schemaInfo: SchemaInfo) + (fragmentDefinitions: FragmentDefinition list) + (path: Path) + (operation: OperationDefinition) + = let expectedLocation = match operation.OperationType with | Query -> DirectiveLocation.QUERY | Mutation -> DirectiveLocation.MUTATION | Subscription -> DirectiveLocation.SUBSCRIPTION + let directivesValid = operation.Directives - |> collectResults (validateDirective schemaInfo path expectedLocation (fun directiveDef -> - match operation.Name with - | Some operationName -> $"%s{operation.OperationType.ToString()} operation '%s{operationName}' has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition." - | None -> $"This %s{operation.OperationType.ToString()} operation has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition.")) + |> collectResults ( + validateDirective schemaInfo path expectedLocation (fun directiveDef -> + match operation.Name with + | Some operationName -> + $"%s{operation.OperationType.ToString()} operation '%s{operationName}' has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition." + | None -> + $"This %s{operation.OperationType.ToString()} operation has a directive '%s{directiveDef.Name}', but this directive location is not supported by the schema definition.") + ) + let directivesValidInSelectionSet = operation.SelectionSet |> collectResults (checkDirectivesInValidLocationOnSelection schemaInfo fragmentDefinitions path) + directivesValid @@ directivesValidInSelectionSet - let internal validateDirectivesAreInValidLocations (ctx : ValidationContext) = + let internal validateDirectivesAreInValidLocations (ctx: ValidationContext) = let fragmentDefinitions = ctx.FragmentDefinitions |> List.map (fun x -> x.Definition) + ctx.Document.Definitions |> collectResults (fun def -> let path = def.Name |> Option.toList + match def with - | OperationDefinition odef -> - checkDirectivesInOperation ctx.Schema fragmentDefinitions path odef + | OperationDefinition odef -> checkDirectivesInOperation ctx.Schema fragmentDefinitions path odef | FragmentDefinition frag when frag.Name.IsSome -> let fragCtx = - { Schema = ctx.Schema - FragmentDefinitions = fragmentDefinitions - Path = path - FragmentName = frag.Name.Value - Directives = frag.Directives - SelectionSet = frag.SelectionSet } + { + Schema = ctx.Schema + FragmentDefinitions = fragmentDefinitions + Path = path + FragmentName = frag.Name.Value + Directives = frag.Directives + SelectionSet = frag.SelectionSet + } + checkDirectivesInValidLocationOnFragmentSpread fragCtx | _ -> Success) - let rec private getDirectiveNamesInSelection (path : Path) (selection : Selection) : (Path * string list) list = + let rec private getDirectiveNamesInSelection (path: Path) (selection: Selection) : (Path * string list) list = match selection with | Field field -> let path = field.AliasOrName :: path @@ -908,27 +1254,38 @@ module Ast = | InlineFragment frag -> getDirectiveNamesInDefinition path (FragmentDefinition frag) | FragmentSpread spread -> [ path, spread.Directives |> List.map (fun x -> x.Name) ] - and private getDirectiveNamesInDefinition (path : Path) (frag : Definition) : (Path * string list) list = + and private getDirectiveNamesInDefinition (path: Path) (frag: Definition) : (Path * string list) list = let fragDirectives = [ path, frag.Directives |> List.map (fun x -> x.Name) ] let selectionSetDirectives = frag.SelectionSet |> List.collect (getDirectiveNamesInSelection path) fragDirectives |> List.append selectionSetDirectives - let internal validateUniqueDirectivesPerLocation (ctx : ValidationContext) = + let internal validateUniqueDirectivesPerLocation (ctx: ValidationContext) = ctx.Definitions |> List.collect (fun def -> - let path = match def.Name with | Some name -> [name] | None -> [] + let path = + match def.Name with + | Some name -> [ name ] + | None -> [] + let defDirectives = path, def.Directives |> List.map (fun x -> x.Name) - let selectionSetDirectives = def.Definition.SelectionSet |> List.collect (getDirectiveNamesInSelection path) + + let selectionSetDirectives = + def.Definition.SelectionSet |> List.collect (getDirectiveNamesInSelection path) + defDirectives :: selectionSetDirectives) |> collectResults (fun (path, directives) -> directives |> List.countBy id |> collectResults (fun (name, count) -> - if count <= 1 - then Success - else AstError.AsResult($"Directive '%s{name}' appears %i{count} times in the location it is used. Directives must be unique in their locations.", path))) + if count <= 1 then + Success + else + AstError.AsResult( + $"Directive '%s{name}' appears %i{count} times in the location it is used. Directives must be unique in their locations.", + path + ))) - let internal validateVariableUniqueness (ctx : ValidationContext) = + let internal validateVariableUniqueness (ctx: ValidationContext) = ctx.Document.Definitions |> collectResults (function | OperationDefinition def -> @@ -937,11 +1294,15 @@ module Ast = |> collectResults (fun (var, count) -> match def.Name with | _ when count < 2 -> Success - | Some operationName -> AstError.AsResult $"Variable '%s{var.VariableName}' in operation '%s{operationName}' is declared %i{count} times. Variables must be unique in their operations." - | None -> AstError.AsResult $"Variable '%s{var.VariableName}' is declared %i{count} times in the operation. Variables must be unique in their operations.") + | Some operationName -> + AstError.AsResult + $"Variable '%s{var.VariableName}' in operation '%s{operationName}' is declared %i{count} times. Variables must be unique in their operations." + | None -> + AstError.AsResult + $"Variable '%s{var.VariableName}' is declared %i{count} times in the operation. Variables must be unique in their operations.") | _ -> Success) - let internal validateVariablesAsInputTypes (ctx : ValidationContext) = + let internal validateVariablesAsInputTypes (ctx: ValidationContext) = ctx.Document.Definitions |> collectResults (function | OperationDefinition def -> @@ -949,147 +1310,224 @@ module Ast = |> collectResults (fun var -> match def.Name, ctx.Schema.TryGetInputType(var.Type) with | Some operationName, None -> - AstError.AsResult $"Variable '%s{var.VariableName}' in operation '%s{operationName}' has a type that is not an input type defined by the schema (%s{var.Type.ToString()})." + AstError.AsResult + $"Variable '%s{var.VariableName}' in operation '%s{operationName}' has a type that is not an input type defined by the schema (%s{var.Type.ToString()})." | None, None -> - AstError.AsResult $"Variable '%s{var.VariableName}' has a type is not an input type defined by the schema (%s{var.Type.ToString()})." + AstError.AsResult + $"Variable '%s{var.VariableName}' has a type is not an input type defined by the schema (%s{var.Type.ToString()})." | _ -> Success) | _ -> Success) - let private checkVariablesDefinedInDirective (variableDefinitions : Set) (path : Path) (directive : Directive) = + let private checkVariablesDefinedInDirective (variableDefinitions: Set) (path: Path) (directive: Directive) = directive.Arguments |> collectResults (fun arg -> match arg.Value with | Variable varName -> - if variableDefinitions |> Set.contains varName - then Success - else AstError.AsResult($"A variable '%s{varName}' is referenced in an argument '%s{arg.Name}' of directive '%s{directive.Name}' of field with alias or name '%s{path.Head}', but that variable is not defined in the operation.", path) + if variableDefinitions |> Set.contains varName then + Success + else + AstError.AsResult( + $"A variable '%s{varName}' is referenced in an argument '%s{arg.Name}' of directive '%s{directive.Name}' of field with alias or name '%s{path.Head}', but that variable is not defined in the operation.", + path + ) | _ -> Success) - let rec private checkVariablesDefinedInSelection (fragmentDefinitions : FragmentDefinition list) (variableDefinitions : Set) (path : Path) = + let rec private checkVariablesDefinedInSelection + (fragmentDefinitions: FragmentDefinition list) + (variableDefinitions: Set) + (path: Path) + = function | Field field -> let path = field.AliasOrName :: path + let variablesValid = field.Arguments |> collectResults (fun arg -> match arg.Value with | Variable varName -> - if variableDefinitions |> Set.contains varName - then Success - else AstError.AsResult $"A variable '%s{varName}' is referenced in argument '%s{arg.Name}' of field with alias or name '%s{field.AliasOrName}', but that variable is not defined in the operation." + if variableDefinitions |> Set.contains varName then + Success + else + AstError.AsResult + $"A variable '%s{varName}' is referenced in argument '%s{arg.Name}' of field with alias or name '%s{field.AliasOrName}', but that variable is not defined in the operation." | _ -> Success) + variablesValid - @@ (field.SelectionSet |> collectResults (checkVariablesDefinedInSelection fragmentDefinitions variableDefinitions path)) - @@ (field.Directives |> collectResults (checkVariablesDefinedInDirective variableDefinitions path)) + @@ (field.SelectionSet + |> collectResults (checkVariablesDefinedInSelection fragmentDefinitions variableDefinitions path)) + @@ (field.Directives |> collectResults (checkVariablesDefinedInDirective variableDefinitions path)) | InlineFragment frag -> let variablesValid = frag.SelectionSet |> collectResults (checkVariablesDefinedInSelection fragmentDefinitions variableDefinitions path) - variablesValid @@ (frag.Directives |> collectResults (checkVariablesDefinedInDirective variableDefinitions path)) + + variablesValid + @@ (frag.Directives |> collectResults (checkVariablesDefinedInDirective variableDefinitions path)) | _ -> Success // Spreads can't have variable definitions - let internal validateVariablesUsesDefined (ctx : ValidationContext) = + let internal validateVariablesUsesDefined (ctx: ValidationContext) = let fragmentDefinitions = getFragmentDefinitions ctx.Document + ctx.Document.Definitions |> collectResults (function | OperationDefinition def -> let path = Option.toList def.Name - let varNames = def.VariableDefinitions |> List.map(fun x -> x.VariableName) |> Set.ofList - def.SelectionSet |> collectResults (checkVariablesDefinedInSelection fragmentDefinitions varNames path) + let varNames = def.VariableDefinitions |> List.map (fun x -> x.VariableName) |> Set.ofList + + def.SelectionSet + |> collectResults (checkVariablesDefinedInSelection fragmentDefinitions varNames path) | _ -> Success) - let private argumentsContains (name : string) (args : Argument list) = - let rec go xs = xs |> List.exists (function - | Variable varName -> varName = name - | ObjectValue obj -> go (Map.toList obj |> List.map snd) - | ListValue xs -> go xs - | _ -> false) + let private argumentsContains (name: string) (args: Argument list) = + let rec go xs = + xs + |> List.exists (function + | Variable varName -> varName = name + | ObjectValue obj -> go (Map.toList obj |> List.map snd) + | ListValue xs -> go xs + | _ -> false) + go (args |> List.map (fun x -> x.Value)) - let rec private variableIsUsedInFragmentSpread (name : string) (fragmentDefinitions : FragmentDefinition list) (visitedFragments : string list) (spread : FragmentSpread) = - if List.contains spread.Name visitedFragments - then false + let rec private variableIsUsedInFragmentSpread + (name: string) + (fragmentDefinitions: FragmentDefinition list) + (visitedFragments: string list) + (spread: FragmentSpread) + = + if List.contains spread.Name visitedFragments then + false else let usedInSpread = match fragmentDefinitions |> List.tryFind (fun x -> x.Name.IsSome && x.Name.Value = spread.Name) with | Some frag -> - let usedInSelection = frag.SelectionSet |> List.exists (variableIsUsedInSelection name fragmentDefinitions (spread.Name :: visitedFragments)) - usedInSelection || (frag.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) + let usedInSelection = + frag.SelectionSet + |> List.exists (variableIsUsedInSelection name fragmentDefinitions (spread.Name :: visitedFragments)) + + usedInSelection + || (frag.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) | None -> false - usedInSpread || (spread.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) - and private variableIsUsedInSelection (name : string) (fragmentDefinitions : FragmentDefinition list) (visitedFragments : string list) = + usedInSpread + || (spread.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) + + and private variableIsUsedInSelection + (name: string) + (fragmentDefinitions: FragmentDefinition list) + (visitedFragments: string list) + = function | Field field -> - if argumentsContains name field.Arguments - then true + if argumentsContains name field.Arguments then + true else - let usedInSelection = field.SelectionSet |> List.exists (variableIsUsedInSelection name fragmentDefinitions visitedFragments) - usedInSelection || (field.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) + let usedInSelection = + field.SelectionSet + |> List.exists (variableIsUsedInSelection name fragmentDefinitions visitedFragments) + + usedInSelection + || (field.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) | InlineFragment frag -> - let usedInSelection = frag.SelectionSet |> List.exists (variableIsUsedInSelection name fragmentDefinitions visitedFragments) - usedInSelection || (frag.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) + let usedInSelection = + frag.SelectionSet + |> List.exists (variableIsUsedInSelection name fragmentDefinitions visitedFragments) + + usedInSelection + || (frag.Directives |> List.exists (fun directive -> argumentsContains name directive.Arguments)) | FragmentSpread spread -> variableIsUsedInFragmentSpread name fragmentDefinitions visitedFragments spread - let internal validateAllVariablesUsed (ctx : ValidationContext) = + let internal validateAllVariablesUsed (ctx: ValidationContext) = let fragmentDefinitions = getFragmentDefinitions ctx.Document + ctx.Document.Definitions |> collectResults (function | OperationDefinition def -> def.VariableDefinitions |> collectResults (fun varDef -> - let isUsed = def.SelectionSet |> List.exists (variableIsUsedInSelection varDef.VariableName fragmentDefinitions []) + let isUsed = + def.SelectionSet + |> List.exists (variableIsUsedInSelection varDef.VariableName fragmentDefinitions []) + match def.Name, isUsed with | _, true -> Success - | Some operationName, _ -> AstError.AsResult $"Variable definition '%s{varDef.VariableName}' is not used in operation '%s{operationName}'. Every variable must be used." - | None, _ -> AstError.AsResult $"Variable definition '%s{varDef.VariableName}' is not used in operation. Every variable must be used.") + | Some operationName, _ -> + AstError.AsResult + $"Variable definition '%s{varDef.VariableName}' is not used in operation '%s{operationName}'. Every variable must be used." + | None, _ -> + AstError.AsResult + $"Variable definition '%s{varDef.VariableName}' is not used in operation. Every variable must be used.") | _ -> Success) - let rec private areTypesCompatible (variableTypeRef : IntrospectionTypeRef) (locationTypeRef : IntrospectionTypeRef) = - if locationTypeRef.Kind = TypeKind.NON_NULL && locationTypeRef.OfType.IsSome - then - if variableTypeRef.Kind <> TypeKind.NON_NULL - then false - elif variableTypeRef.OfType.IsSome then areTypesCompatible variableTypeRef.OfType.Value locationTypeRef.OfType.Value - else false - elif variableTypeRef.Kind = TypeKind.NON_NULL && variableTypeRef.OfType.IsSome then areTypesCompatible variableTypeRef.OfType.Value locationTypeRef - elif locationTypeRef.Kind = TypeKind.LIST && locationTypeRef.OfType.IsSome - then - if variableTypeRef.Kind <> TypeKind.LIST - then false - elif variableTypeRef.OfType.IsSome then areTypesCompatible variableTypeRef.OfType.Value locationTypeRef.OfType.Value - else false - elif variableTypeRef.Kind = TypeKind.LIST then false - else variableTypeRef.Name = locationTypeRef.Name && variableTypeRef.Kind = locationTypeRef.Kind - - let private checkVariableUsageAllowedOnArguments (inputs : IntrospectionInputVal []) (varNamesAndTypeRefs : Map) (path : Path) (args : Argument list) = + let rec private areTypesCompatible (variableTypeRef: IntrospectionTypeRef) (locationTypeRef: IntrospectionTypeRef) = + if locationTypeRef.Kind = TypeKind.NON_NULL && locationTypeRef.OfType.IsSome then + if variableTypeRef.Kind <> TypeKind.NON_NULL then + false + elif variableTypeRef.OfType.IsSome then + areTypesCompatible variableTypeRef.OfType.Value locationTypeRef.OfType.Value + else + false + elif variableTypeRef.Kind = TypeKind.NON_NULL && variableTypeRef.OfType.IsSome then + areTypesCompatible variableTypeRef.OfType.Value locationTypeRef + elif locationTypeRef.Kind = TypeKind.LIST && locationTypeRef.OfType.IsSome then + if variableTypeRef.Kind <> TypeKind.LIST then + false + elif variableTypeRef.OfType.IsSome then + areTypesCompatible variableTypeRef.OfType.Value locationTypeRef.OfType.Value + else + false + elif variableTypeRef.Kind = TypeKind.LIST then + false + else + variableTypeRef.Name = locationTypeRef.Name && variableTypeRef.Kind = locationTypeRef.Kind + + let private checkVariableUsageAllowedOnArguments + (inputs: IntrospectionInputVal []) + (varNamesAndTypeRefs: Map) + (path: Path) + (args: Argument list) + = args |> collectResults (fun arg -> match arg.Value with | Variable varName -> match varNamesAndTypeRefs.TryFind(varName) with | Some (varDef, variableTypeRef) -> - let err = AstError.AsResult( $"Variable '%s{varName}' can not be used in its reference. The type of the variable definition is not compatible with the type of its reference.", path) + let err = + AstError.AsResult( + $"Variable '%s{varName}' can not be used in its reference. The type of the variable definition is not compatible with the type of its reference.", + path + ) + match inputs |> Array.tryFind (fun x -> x.Name = arg.Name) with | Some input -> let locationTypeRef = input.Type - if locationTypeRef.Kind = TypeKind.NON_NULL && locationTypeRef.OfType.IsSome && variableTypeRef.Kind <> TypeKind.NON_NULL - then + + if locationTypeRef.Kind = TypeKind.NON_NULL + && locationTypeRef.OfType.IsSome + && variableTypeRef.Kind <> TypeKind.NON_NULL then let hasNonNullVariableDefaultValue = varDef.DefaultValue.IsSome let hasLocationDefaultValue = input.DefaultValue.IsSome - if not hasNonNullVariableDefaultValue && not hasLocationDefaultValue - then err + + if not hasNonNullVariableDefaultValue && not hasLocationDefaultValue then + err else let nullableLocationType = locationTypeRef.OfType.Value - if not (areTypesCompatible variableTypeRef nullableLocationType) - then err else Success - elif not (areTypesCompatible variableTypeRef locationTypeRef) - then err else Success + if not (areTypesCompatible variableTypeRef nullableLocationType) then err else Success + elif not (areTypesCompatible variableTypeRef locationTypeRef) then + err + else + Success | None -> Success | None -> Success | _ -> Success) - let rec private checkVariableUsageAllowedOnSelection (varNamesAndTypeRefs : Map) (visitedFragments : string list) (selection : SelectionInfo) = + let rec private checkVariableUsageAllowedOnSelection + (varNamesAndTypeRefs: Map) + (visitedFragments: string list) + (selection: SelectionInfo) + = match selection.FragmentSpreadName with | Some spreadName when List.contains spreadName visitedFragments -> Success | _ -> @@ -1097,57 +1535,67 @@ module Ast = match selection.FragmentSpreadName with | Some _ -> selection.FragmentSpreadName.Value :: visitedFragments | None -> visitedFragments + match selection.FieldType with | Some _ -> let argumentsValid = selection.Field.Arguments |> checkVariableUsageAllowedOnArguments selection.InputValues varNamesAndTypeRefs selection.Path + let selectionValid = selection.SelectionSet |> collectResults (checkVariableUsageAllowedOnSelection varNamesAndTypeRefs visitedFragments) + let directivesValid = selection.Field.Directives - |> collectResults (fun directive -> directive.Arguments |> checkVariableUsageAllowedOnArguments selection.InputValues varNamesAndTypeRefs selection.Path) + |> collectResults (fun directive -> + directive.Arguments + |> checkVariableUsageAllowedOnArguments selection.InputValues varNamesAndTypeRefs selection.Path) + argumentsValid @@ selectionValid @@ directivesValid | None -> Success - let internal validateVariableUsagesAllowed (ctx : ValidationContext) = + let internal validateVariableUsagesAllowed (ctx: ValidationContext) = ctx.OperationDefinitions |> collectResults (fun def -> let varNamesAndTypeRefs = def.Definition.VariableDefinitions - |> List.choose (fun varDef -> ctx.Schema.TryGetInputType(varDef.Type) |> Option.map(fun t -> varDef.VariableName, (varDef, t))) + |> List.choose (fun varDef -> + ctx.Schema.TryGetInputType(varDef.Type) |> Option.map (fun t -> varDef.VariableName, (varDef, t))) |> Map.ofList + def.SelectionSet |> collectResults (checkVariableUsageAllowedOnSelection varNamesAndTypeRefs [])) let private allValidations = - [ validateFragmentsMustNotFormCycles - validateOperationNameUniqueness - validateLoneAnonymousOperation - validateSubscriptionSingleRootField - validateSelectionFieldTypes - validateFieldSelectionMerging - validateLeafFieldSelections - validateArgumentNames - validateArgumentUniqueness - validateRequiredArguments - validateFragmentNameUniqueness - validateFragmentTypeExistence - validateFragmentsOnCompositeTypes - validateFragmentsMustBeUsed - validateFragmentSpreadTargetDefined - validateFragmentSpreadIsPossible - validateInputValues - validateDirectivesDefined - validateDirectivesAreInValidLocations - validateUniqueDirectivesPerLocation - validateVariableUniqueness - validateVariablesAsInputTypes - validateVariablesUsesDefined - validateAllVariablesUsed - validateVariableUsagesAllowed ] - - let validateDocument (schema : IntrospectionSchema) (ast : Document) = + [ + validateFragmentsMustNotFormCycles + validateOperationNameUniqueness + validateLoneAnonymousOperation + validateSubscriptionSingleRootField + validateSelectionFieldTypes + validateFieldSelectionMerging + validateLeafFieldSelections + validateArgumentNames + validateArgumentUniqueness + validateRequiredArguments + validateFragmentNameUniqueness + validateFragmentTypeExistence + validateFragmentsOnCompositeTypes + validateFragmentsMustBeUsed + validateFragmentSpreadTargetDefined + validateFragmentSpreadIsPossible + validateInputValues + validateDirectivesDefined + validateDirectivesAreInValidLocations + validateUniqueDirectivesPerLocation + validateVariableUniqueness + validateVariablesAsInputTypes + validateVariablesUsesDefined + validateAllVariablesUsed + validateVariableUsagesAllowed + ] + + let validateDocument (schema: IntrospectionSchema) (ast: Document) = let schemaInfo = SchemaInfo.FromIntrospectionSchema(schema) let context = getValidationContext schemaInfo ast allValidations |> collectResults (fun validate -> validate context) From f9224851451182b29a5742d3acc9ee6f4da8fb0e Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 21:09:30 +0200 Subject: [PATCH 8/9] Formatting TypeSystem, starting to use AST inside --- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 3033 +++++++++++------- 1 file changed, 1786 insertions(+), 1247 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index 40393b94a..f168b1b71 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -1,5 +1,6 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc +// The MIT License (MIT) +// Copyright (c) 2016 Bazinga Technologies Inc + namespace FSharp.Data.GraphQL.Types open System @@ -21,7 +22,7 @@ module Introspection = /// Type kind. GraphQL type system puts all types into one of eight categories. type TypeKind = | SCALAR = 1 - | OBJECT = 2 + | OBJECT = 2 | INTERFACE = 3 | UNION = 4 | ENUM = 5 @@ -31,54 +32,60 @@ module Introspection = /// Introspection descriptor of a directive (i.e. @skip(if:...), @include(if:...) etc). type IntrospectionDirective = - { /// Directive name. - Name : string - /// Description of a target directive. - Description : string option - /// Array of AST locations, where it's valid to place target directive. - Locations : DirectiveLocation [] - /// Array of arguments, current directive can be parametrized with. - Args : IntrospectionInputVal [] } + { + /// Directive name. + Name: string + /// Description of a target directive. + Description: string option + /// Array of AST locations, where it's valid to place target directive. + Locations: DirectiveLocation [] + /// Array of arguments, current directive can be parametrized with. + Args: IntrospectionInputVal [] + } /// Introspection descriptor of a GraphQL type defintion. and IntrospectionType = - { /// Which kind category current type belongs to. - Kind : TypeKind - /// Type name. Must be unique in scope of the defined schema. - Name : string - /// Optional type description. - Description : string option - /// Array of field descriptors defined within current type. - /// Only present for Object and Interface types. - Fields : IntrospectionField [] option - /// Array of interfaces implemented by output object type defintion. - Interfaces : IntrospectionTypeRef [] option - /// Array of type references being possible implementation of current type. - /// Only present for Union types (list of union cases) and Interface types - /// (list of all objects implementing interface in scope of the schema). - PossibleTypes : IntrospectionTypeRef [] option - /// Array of enum values defined by current Enum type. - EnumValues : IntrospectionEnumVal [] option - /// Array of input fields defined by current InputObject type. - InputFields : IntrospectionInputVal [] option - /// Type param reference - used only by List and NonNull types. - OfType : IntrospectionTypeRef option } + { + /// Which kind category current type belongs to. + Kind: TypeKind + /// Type name. Must be unique in scope of the defined schema. + Name: string + /// Optional type description. + Description: string option + /// Array of field descriptors defined within current type. + /// Only present for Object and Interface types. + Fields: IntrospectionField [] option + /// Array of interfaces implemented by output object type defintion. + Interfaces: IntrospectionTypeRef [] option + /// Array of type references being possible implementation of current type. + /// Only present for Union types (list of union cases) and Interface types + /// (list of all objects implementing interface in scope of the schema). + PossibleTypes: IntrospectionTypeRef [] option + /// Array of enum values defined by current Enum type. + EnumValues: IntrospectionEnumVal [] option + /// Array of input fields defined by current InputObject type. + InputFields: IntrospectionInputVal [] option + /// Type param reference - used only by List and NonNull types. + OfType: IntrospectionTypeRef option + } /// /// Constructs an introspection descriptor for a types. /// /// Type name (unique in the scope of current schema). /// Optional type description. - static member Scalar(name : string, description : string option) = - { Kind = TypeKind.SCALAR - Name = name - Description = description - Fields = None - Interfaces = None - PossibleTypes = None - EnumValues = None - InputFields = None - OfType = None } + static member Scalar(name: string, description: string option) = + { + Kind = TypeKind.SCALAR + Name = name + Description = description + Fields = None + Interfaces = None + PossibleTypes = None + EnumValues = None + InputFields = None + OfType = None + } /// /// Constructs an introspection descriptor for a types. @@ -87,17 +94,24 @@ module Introspection = /// Optional type description. /// Array of fields defined in current object. /// Array of interfaces, current object implements. - static member Object(name : string, description : string option, fields : IntrospectionField [], - interfaces : IntrospectionTypeRef []) = - { Kind = TypeKind.OBJECT - Name = name - Description = description - Fields = Some fields - Interfaces = Some interfaces - PossibleTypes = None - EnumValues = None - InputFields = None - OfType = None } + static member Object + ( + name: string, + description: string option, + fields: IntrospectionField [], + interfaces: IntrospectionTypeRef [] + ) = + { + Kind = TypeKind.OBJECT + Name = name + Description = description + Fields = Some fields + Interfaces = Some interfaces + PossibleTypes = None + EnumValues = None + InputFields = None + OfType = None + } /// /// Constructs an introspection descriptor for a types. @@ -105,16 +119,18 @@ module Introspection = /// Type name (unique in the scope of current schema). /// Optional type description. /// Array of input fields defined in current input object. - static member InputObject(name : string, description : string option, inputFields : IntrospectionInputVal []) = - { Kind = TypeKind.INPUT_OBJECT - Name = name - Description = description - Fields = None - Interfaces = None - PossibleTypes = None - EnumValues = None - InputFields = Some inputFields - OfType = None } + static member InputObject(name: string, description: string option, inputFields: IntrospectionInputVal []) = + { + Kind = TypeKind.INPUT_OBJECT + Name = name + Description = description + Fields = None + Interfaces = None + PossibleTypes = None + EnumValues = None + InputFields = Some inputFields + OfType = None + } /// /// Constructs an introspection descriptor for a types. @@ -122,16 +138,18 @@ module Introspection = /// Type name (unique in the scope of current schema). /// Optional type description. /// Array of union case types. They can be any type defined in GraphQL schema. - static member Union(name : string, description : string option, possibleTypes : IntrospectionTypeRef []) = - { Kind = TypeKind.UNION - Name = name - Description = description - Fields = None - Interfaces = None - PossibleTypes = Some possibleTypes - EnumValues = None - InputFields = None - OfType = None } + static member Union(name: string, description: string option, possibleTypes: IntrospectionTypeRef []) = + { + Kind = TypeKind.UNION + Name = name + Description = description + Fields = None + Interfaces = None + PossibleTypes = Some possibleTypes + EnumValues = None + InputFields = None + OfType = None + } /// /// Constructs an introspection descriptor for a types. @@ -139,16 +157,18 @@ module Introspection = /// Type name (unique in the scope of current schema). /// Optional type description. /// Array of enum value descriptors. - static member Enum(name : string, description : string option, enumValues : IntrospectionEnumVal []) = - { Kind = TypeKind.ENUM - Name = name - Description = description - Fields = None - Interfaces = None - PossibleTypes = None - EnumValues = Some enumValues - InputFields = None - OfType = None } + static member Enum(name: string, description: string option, enumValues: IntrospectionEnumVal []) = + { + Kind = TypeKind.ENUM + Name = name + Description = description + Fields = None + Interfaces = None + PossibleTypes = None + EnumValues = Some enumValues + InputFields = None + OfType = None + } /// /// Constructs an introspection descriptor for a types. @@ -157,132 +177,158 @@ module Introspection = /// Optional type description. /// Array of fields being part of the interface contract. /// Array of schema objects implementing target interface. - static member Interface(name : string, description : string option, fields : IntrospectionField [], - possibleTypes : IntrospectionTypeRef []) = - { Kind = TypeKind.INTERFACE - Name = name - Description = description - Fields = Some fields - Interfaces = None - PossibleTypes = Some possibleTypes - EnumValues = None - InputFields = None - OfType = None } + static member Interface + ( + name: string, + description: string option, + fields: IntrospectionField [], + possibleTypes: IntrospectionTypeRef [] + ) = + { + Kind = TypeKind.INTERFACE + Name = name + Description = description + Fields = Some fields + Interfaces = None + PossibleTypes = Some possibleTypes + EnumValues = None + InputFields = None + OfType = None + } /// Introspection type reference. Used to navigate between type dependencies inside introspected schema. and IntrospectionTypeRef = - { /// Referenced type kind. - Kind : TypeKind - /// Type name. None if referenced type is List or NonNull. - Name : string option - /// Optional type description. - Description : string option - /// Type param reference. Used only by List and NonNull types. - OfType : IntrospectionTypeRef option } + { + /// Referenced type kind. + Kind: TypeKind + /// Type name. None if referenced type is List or NonNull. + Name: string option + /// Optional type description. + Description: string option + /// Type param reference. Used only by List and NonNull types. + OfType: IntrospectionTypeRef option + } /// /// Constructs an introspection type reference for List types. /// /// Type reference for type used as List's type param. - static member List(inner : IntrospectionTypeRef) = - { Kind = TypeKind.LIST - Name = None - Description = None - OfType = Some inner } + static member List(inner: IntrospectionTypeRef) = + { + Kind = TypeKind.LIST + Name = None + Description = None + OfType = Some inner + } /// /// Constructs an introspection type reference for NonNull types. /// /// Type reference for type used as NonNull's type param. - static member NonNull(inner : IntrospectionTypeRef) = - { Kind = TypeKind.NON_NULL - Name = None - Description = None - OfType = Some inner } + static member NonNull(inner: IntrospectionTypeRef) = + { + Kind = TypeKind.NON_NULL + Name = None + Description = None + OfType = Some inner + } /// /// Constructs an introspection type reference for any named type defintion /// (any type other than List or NonNull) with unique name included. /// /// Introspection type descriptor to construct reference from. - static member Named(inner : IntrospectionType) = - { Kind = inner.Kind - Name = Some inner.Name - Description = inner.Description - OfType = None } + static member Named(inner: IntrospectionType) = + { + Kind = inner.Kind + Name = Some inner.Name + Description = inner.Description + OfType = None + } /// Introspection descriptor for input values (InputObject fields or field arguments). and IntrospectionInputVal = - { /// Input argument name. - Name : string - /// Optional input argument description. - Description : string option - /// Introspection reference to argument's type. - Type : IntrospectionTypeRef - /// Default arguments value, if provided. - DefaultValue : string option } + { + /// Input argument name. + Name: string + /// Optional input argument description. + Description: string option + /// Introspection reference to argument's type. + Type: IntrospectionTypeRef + /// Default arguments value, if provided. + DefaultValue: string option + } /// Introspection descriptor for enum values. and IntrospectionEnumVal = - { /// Enum value name - must be unique in scope of defining enum. - Name : string - /// Optional enum value description. - Description : string option - /// If true, marks current value as deprecated, but still - /// available for compatibility reasons. - IsDeprecated : bool - /// If value is deprecated this field may describe a deprecation reason. - DeprecationReason : string option } + { + /// Enum value name - must be unique in scope of defining enum. + Name: string + /// Optional enum value description. + Description: string option + /// If true, marks current value as deprecated, but still + /// available for compatibility reasons. + IsDeprecated: bool + /// If value is deprecated this field may describe a deprecation reason. + DeprecationReason: string option + } /// Introspection descriptor for Object and Interface fields. and IntrospectionField = - { /// Field name. Must be unique in scope of the definin object/interface. - Name : string - /// Optional field description. - Description : string option - /// Array of field arguments. In GraphQL fields can be parametrized, - /// working effectively like methods. - Args : IntrospectionInputVal [] - /// Introspection reference to field's type. - Type : IntrospectionTypeRef - /// If true, marks current field as deprecated, but still - /// available for compatibility reasons. - IsDeprecated : bool - /// If field is deprecated here a deprecation reason may be set. - DeprecationReason : string option } + { + /// Field name. Must be unique in scope of the definin object/interface. + Name: string + /// Optional field description. + Description: string option + /// Array of field arguments. In GraphQL fields can be parametrized, + /// working effectively like methods. + Args: IntrospectionInputVal [] + /// Introspection reference to field's type. + Type: IntrospectionTypeRef + /// If true, marks current field as deprecated, but still + /// available for compatibility reasons. + IsDeprecated: bool + /// If field is deprecated here a deprecation reason may be set. + DeprecationReason: string option + } /// Introspection descriptor for target schema. Contains informations about /// all types defined within current schema. and IntrospectionSchema = - { /// Introspection reference to schema's query root. - QueryType : IntrospectionTypeRef - /// Introspection reference to schema's mutation root. - MutationType : IntrospectionTypeRef option - /// Introspection reference to schema's subscription root. - SubscriptionType : IntrospectionTypeRef option - /// Array of all introspection types defined within current schema. - /// Includes types for queries, mutations and subscriptions. - Types : IntrospectionType [] - /// Array of all directives supported by current schema. - Directives : IntrospectionDirective [] } + { + /// Introspection reference to schema's query root. + QueryType: IntrospectionTypeRef + /// Introspection reference to schema's mutation root. + MutationType: IntrospectionTypeRef option + /// Introspection reference to schema's subscription root. + SubscriptionType: IntrospectionTypeRef option + /// Array of all introspection types defined within current schema. + /// Includes types for queries, mutations and subscriptions. + Types: IntrospectionType [] + /// Array of all directives supported by current schema. + Directives: IntrospectionDirective [] + } /// Represents a subscription as described in the schema. -type Subscription = { - /// The name of the subscription type in the schema. - Name : string - /// Filter function, used to determine what events we will propagate. - /// The first object is the boxed root value, the second is the boxed value of the input object. - Filter : ResolveFieldContext -> obj -> obj -> Async } +type Subscription = + { + /// The name of the subscription type in the schema. + Name: string + /// Filter function, used to determine what events we will propagate. + /// The first object is the boxed root value, the second is the boxed value of the input object. + Filter: ResolveFieldContext -> obj -> obj -> Async + } /// Describes the backing implementation for a subscription system. and ISubscriptionProvider = interface /// Registers a new subscription type, called at schema compilation time. - abstract member AsyncRegister : Subscription -> Async + abstract member AsyncRegister: Subscription -> Async /// Creates an active subscription, and returns the IObservable stream of POCO objects that will be projected on. - abstract member Add : ResolveFieldContext -> obj -> SubscriptionFieldDef -> IObservable + abstract member Add: ResolveFieldContext -> obj -> SubscriptionFieldDef -> IObservable /// Publishes an event to the subscription system given the identifier of the subscription type. abstract member AsyncPublish<'T> : string -> 'T -> Async + /// Publishes an event to the subscription system given the identifier of the subscription type /// and a filter identity that can be used to choose which filter functions will be applied. abstract member AsyncPublishTag<'T> : string -> Tag -> 'T -> Async @@ -292,13 +338,13 @@ and ISubscriptionProvider = and ILiveFieldSubscription = interface /// Determine if we should propagate the event - abstract member Filter : obj -> obj -> bool + abstract member Filter: obj -> obj -> bool /// Project out the field marked with the @live directive - abstract member Project : obj -> obj + abstract member Project: obj -> obj /// The type name of the object that is ready for live query in this subscription. - abstract member TypeName : string + abstract member TypeName: string /// The field name of the object that is ready for live query in this subscription. - abstract member FieldName : string + abstract member FieldName: string end /// Represents a generic typed, subscription field in a live query. @@ -306,21 +352,23 @@ and ILiveFieldSubscription<'Object, 'Field> = interface inherit ILiveFieldSubscription /// Determine if we should propagate the event - abstract member Filter : 'Object -> 'Object -> bool + abstract member Filter: 'Object -> 'Object -> bool /// Project out the field marked with the @live directive - abstract member Project : 'Object -> 'Field + abstract member Project: 'Object -> 'Field end /// Represents a subscription of a field in a live query. and LiveFieldSubscription = - { /// Determine if we should propagate the event - Filter : obj -> obj -> bool - /// Project out the field marked with the @live directive - Project : obj -> obj - /// The type name of the object that is ready for live query in this subscription. - TypeName : string - /// The field name of the object that is ready for live query in this subscription. - FieldName : string } + { + /// Determine if we should propagate the event + Filter: obj -> obj -> bool + /// Project out the field marked with the @live directive + Project: obj -> obj + /// The type name of the object that is ready for live query in this subscription. + TypeName: string + /// The field name of the object that is ready for live query in this subscription. + FieldName: string + } interface ILiveFieldSubscription with member this.Filter x y = this.Filter x y member this.Project x = this.Project x @@ -329,20 +377,23 @@ and LiveFieldSubscription = /// Represents a generic typed, subscription field in a live query. and LiveFieldSubscription<'Object, 'Field> = - { /// Determine if we should propagate the event - Filter : 'Object -> 'Object -> bool - /// Project out the field marked with the @live directive - Project : 'Object -> 'Field - /// The type name of the object that is ready for live query in this subscription. - TypeName : string - /// The field name of the object that is ready for live query in this subscription. - FieldName : string } + { + /// Determine if we should propagate the event + Filter: 'Object -> 'Object -> bool + /// Project out the field marked with the @live directive + Project: 'Object -> 'Field + /// The type name of the object that is ready for live query in this subscription. + TypeName: string + /// The field name of the object that is ready for live query in this subscription. + FieldName: string + } interface ILiveFieldSubscription<'Object, 'Field> with member this.Filter x y = this.Filter x y member this.Project x = this.Project x + interface ILiveFieldSubscription with member this.Filter x y = this.Filter (downcast x) (downcast y) - member this.Project x = upcast this.Project (downcast x) + member this.Project x = upcast this.Project(downcast x) member this.TypeName = this.TypeName member this.FieldName = this.FieldName @@ -350,15 +401,15 @@ and LiveFieldSubscription<'Object, 'Field> = and ILiveFieldSubscriptionProvider = interface /// Checks if a live field subscription has subscribers. - abstract member HasSubscribers : string -> string -> bool + abstract member HasSubscribers: string -> string -> bool /// Checks if a type and a field is registered in the provider. - abstract member IsRegistered : string -> string -> bool + abstract member IsRegistered: string -> string -> bool /// Registers a new live query subscription type, called at schema compilation time. - abstract member AsyncRegister : ILiveFieldSubscription -> Async + abstract member AsyncRegister: ILiveFieldSubscription -> Async /// Tries to find a subscription based on the type name and field name. - abstract member TryFind : string -> string -> ILiveFieldSubscription option + abstract member TryFind: string -> string -> ILiveFieldSubscription option /// Creates an active subscription, and returns the IObservable stream of projected POCO objects - abstract member Add : (obj -> bool) -> string -> string -> IObservable + abstract member Add: (obj -> bool) -> string -> string -> IObservable /// Publishes an event to the subscription system, given the key of the subscription type. abstract member AsyncPublish<'T> : string -> string -> 'T -> Async end @@ -370,60 +421,60 @@ and ISchema = inherit seq /// Map of defined types by their names. - abstract TypeMap : TypeMap + abstract TypeMap: TypeMap /// A query root object. Defines all top level fields, /// that can be accessed from GraphQL queries. - abstract Query : ObjectDef + abstract Query: ObjectDef /// A mutation root object. Defines all top level operations, /// that can be performed from GraphQL mutations. - abstract Mutation : ObjectDef option + abstract Mutation: ObjectDef option // A subscription root object. Defines all top level operations, // that can be performed from GraphQL subscriptions. - abstract Subscription : SubscriptionObjectDef option + abstract Subscription: SubscriptionObjectDef option /// List of all directives supported by the current schema. - abstract Directives : DirectiveDef [] + abstract Directives: DirectiveDefinition [] /// Method which, given type name, returns Some if provided /// type has been defined in current schema. Otherwise None. - abstract TryFindType : string -> NamedDef option + abstract TryFindType: string -> NamedDef option /// Returns array of all possible types for provided abstract /// type. For Union types, it's the array of all union options. /// For Interface types, it's an array of all types - within /// schema - implementing target interface. - abstract GetPossibleTypes : TypeDef -> ObjectDef [] + abstract GetPossibleTypes: TypeDef -> ObjectDef [] /// Checks if provided object is a possible type type (case /// for Unions and implementation for Interfaces) of provided /// abstract type. - abstract IsPossibleType : AbstractDef -> ObjectDef -> bool + abstract IsPossibleType: AbstractDef -> ObjectDef -> bool /// Returns an introspected representation of current schema. - abstract Introspected : Introspection.IntrospectionSchema + abstract Introspected: Introspection.IntrospectionSchema /// Returns a function called when errors occurred during query execution. /// It's used to retrieve messages shown as output to the client. /// May be also used to log messages before returning them. - abstract ParseError : exn -> string + abstract ParseError: exn -> string /// Returns the subscription provider implementation for this schema. - abstract SubscriptionProvider : ISubscriptionProvider + abstract SubscriptionProvider: ISubscriptionProvider /// Returns the live query subscription provider implementation for this schema. - abstract LiveFieldSubscriptionProvider : ILiveFieldSubscriptionProvider - + abstract LiveFieldSubscriptionProvider: ILiveFieldSubscriptionProvider end + and ISchema<'Root> = interface inherit ISchema - abstract Query : ObjectDef<'Root> - abstract Mutation : ObjectDef<'Root> option - abstract Subscription : SubscriptionObjectDef<'Root> option + abstract Query: ObjectDef<'Root> + abstract Mutation: ObjectDef<'Root> option + abstract Subscription: SubscriptionObjectDef<'Root> option end /// A type alias for a field execute compiler function. @@ -431,13 +482,14 @@ and FieldExecuteCompiler = FieldDef -> ExecuteField /// A field execute map object. /// Field execute maps are mutable objects built to compile fields at runtime. -and FieldExecuteMap(compiler : FieldExecuteCompiler) = +and FieldExecuteMap(compiler: FieldExecuteCompiler) = let map = new Dictionary() let getKey typeName fieldName = - if List.exists ((=) fieldName) ["__schema"; "__type"; "__typename" ] - then "", fieldName - else typeName, fieldName + if List.exists ((=) fieldName) [ "__schema"; "__type"; "__typename" ] then + "", fieldName + else + typeName, fieldName /// /// Sets an execute function for a field of a named type of the schema. @@ -448,13 +500,16 @@ and FieldExecuteMap(compiler : FieldExecuteCompiler) = /// If set to true, and an exists an entry with the and the name of the FieldDef, /// then it will be overwritten. /// - member __.SetExecute(typeName: string, def: FieldDef, ?overwrite : bool) = + member __.SetExecute(typeName: string, def: FieldDef, ?overwrite: bool) = let overwrite = defaultArg overwrite false let key = typeName, def.Name let compiled = compiler def let args = def.Args + match map.ContainsKey(key), overwrite with - | true, true -> map.Remove(key) |> ignore; map.Add(key, (compiled, args)) + | true, true -> + map.Remove(key) |> ignore + map.Add(key, (compiled, args)) | false, _ -> map.Add(key, (compiled, args)) | _ -> () @@ -463,7 +518,7 @@ and FieldExecuteMap(compiler : FieldExecuteCompiler) = /// /// The FieldDef that will have its execute function configured into the FieldExecuteMap. /// If set to true, and an exists an entry with the FieldDef name, then it will be overwritten. - member this.SetExecute(def : FieldDef, ?overwrite : bool) = + member this.SetExecute(def: FieldDef, ?overwrite: bool) = let overwrite = defaultArg overwrite false this.SetExecute("", def, overwrite) @@ -474,25 +529,25 @@ and FieldExecuteMap(compiler : FieldExecuteCompiler) = /// The field name of the object that has the field that needs to be executed. member __.GetExecute(typeName: string, fieldName: string) = let key = getKey typeName fieldName - if map.ContainsKey(key) then fst map.[key] else Unchecked.defaultof + if map.ContainsKey(key) then fst map[key] else Unchecked.defaultof /// /// Gets the field arguments based on the name of the type and the name of the field. /// /// The type name of the parent object that has the field that needs to be executed. /// The field name of the object that has the field that needs to be executed. - member __.GetArgs(typeName : string, fieldName : string) = + member __.GetArgs(typeName: string, fieldName: string) = let key = getKey typeName fieldName - if map.ContainsKey(key) then snd map.[key] else Unchecked.defaultof + if map.ContainsKey(key) then snd map[key] else Unchecked.defaultof interface IEnumerable with member __.GetEnumerator() = - let seq = map |> Seq.map(fun kvp -> fst kvp.Key, snd kvp.Key, fst kvp.Value) + let seq = map |> Seq.map (fun kvp -> fst kvp.Key, snd kvp.Key, fst kvp.Value) seq.GetEnumerator() interface IEnumerable with member __.GetEnumerator() = - let seq = map |> Seq.map(fun kvp -> fst kvp.Value) + let seq = map |> Seq.map (fun kvp -> fst kvp.Value) upcast seq.GetEnumerator() /// Root of GraphQL type system. All type definitions use TypeDef as @@ -501,13 +556,13 @@ and TypeDef = interface /// Return .NET CLR type associated with current type definition. - abstract Type : Type + abstract Type: Type /// INTERNAL API: creates a List definition of a current type. - abstract MakeList : unit -> ListOfDef + abstract MakeList: unit -> ListOfDef /// INTERNAL API: creates a Nullable definition of a current type. - abstract MakeNullable : unit -> NullableDef + abstract MakeNullable: unit -> NullableDef end /// Root of GraphQL type system. Constrained to represent .NET type @@ -588,18 +643,20 @@ and NamedDef = /// Returns a name of the current named type. It must be unique /// in scope of the defining schema. - abstract Name : string + abstract Name: string end /// A context holding all the information needed for planning an operation. and PlanningContext = - { Schema : ISchema - RootDef : ObjectDef - Document : Document - Operation : OperationDefinition - DocumentId : int - Metadata : Metadata - ValidationResult : ValidationResult } + { + Schema: ISchema + RootDef: ObjectDef + Document: Document + Operation: OperationDefinition + DocumentId: int + Metadata: Metadata + ValidationResult: ValidationResult + } /// A function type, which upon execution returns true if related field should /// be included in result set for the query. @@ -609,28 +666,30 @@ and Includer = Map -> bool /// It contains info about both document AST fragment of incoming query as well, /// as field defintion and type info of related fields, defined in schema. and ExecutionInfo = - { /// Field identifier, which may be either field name or alias. For top level execution plan it will be None. - Identifier : string - /// Field definition of corresponding type found in current schema. - Definition : FieldDef - /// AST node of the parsed query document. - Ast : Field - /// A type of execution plan. - Kind : ExecutionInfoKind - /// Logic describing, if correlated field should be included in result set. - Include : Includer - /// Composite definition being the parent of the current field, execution plan refers to. - ParentDef : OutputDef - /// Type definition marking returned type. - ReturnDef : OutputDef - /// Flag determining if flag allows to have nullable output. - IsNullable : bool } + { + /// Field identifier, which may be either field name or alias. For top level execution plan it will be None. + Identifier: string + /// Field definition of corresponding type found in current schema. + Definition: FieldDef + /// AST node of the parsed query document. + Ast: Field + /// A type of execution plan. + Kind: ExecutionInfoKind + /// Logic describing, if correlated field should be included in result set. + Include: Includer + /// Composite definition being the parent of the current field, execution plan refers to. + ParentDef: OutputDef + /// Type definition marking returned type. + ReturnDef: OutputDef + /// Flag determining if flag allows to have nullable output. + IsNullable: bool + } /// Get a nested info recognized by path provided as parameter. Path may consist of fields names or aliases. - member this.GetPath (keys: string list) : ExecutionInfo option = + member this.GetPath(keys: string list) : ExecutionInfo option = let rec path info segments = match segments with - | [] -> + | [] -> match info.Kind with | ResolveCollection inner -> Some inner | _ -> Some info @@ -642,9 +701,7 @@ and ExecutionInfo = | ResolveValue -> None | ResolveCollection inner -> path inner segments | SelectFields fields -> - fields - |> List.tryFind (fun f -> f.Identifier = head) - |> Option.bind (fun f -> path f tail) + fields |> List.tryFind (fun f -> f.Identifier = head) |> Option.bind (fun f -> path f tail) | ResolveAbstraction typeMap -> typeMap |> Map.toSeq @@ -652,49 +709,89 @@ and ExecutionInfo = |> Seq.collect id |> Seq.tryFind (fun f -> f.Identifier = head) |> Option.bind (fun f -> path f tail) + path this keys - override this.ToString () = - let pad indent (sb: Text.StringBuilder) = for i = 0 to indent do sb.Append '\t' |> ignore + + override this.ToString() = + let pad indent (sb: Text.StringBuilder) = + for i = 0 to indent do + sb.Append '\t' |> ignore + let nameAs info = match info.Ast.Alias with | Some alias -> - info.Ast.Name + " as " + alias + " of " + info.ReturnDef.ToString() + (if info.IsNullable then "" else "!") + info.Ast.Name + + " as " + + alias + + " of " + + info.ReturnDef.ToString() + + (if info.IsNullable then "" else "!") | None -> info.Ast.Name + " of " + info.ReturnDef.ToString() + (if info.IsNullable then "" else "!") + let rec str indent sb info = match info.Kind with | ResolveValue -> pad indent sb - sb.Append("ResolveValue: ").AppendLine(nameAs info) |> ignore + + sb + .Append("ResolveValue: ") + .AppendLine(nameAs info) + |> ignore | ResolveDeferred inner -> pad indent sb - sb.Append("ResolveDeferred: ").AppendLine(nameAs info) |> ignore - str (indent+1) sb inner + + sb + .Append("ResolveDeferred: ") + .AppendLine(nameAs info) + |> ignore + + str (indent + 1) sb inner | ResolveLive inner -> pad indent sb sb.Append("ResolveLive: ").AppendLine(nameAs info) |> ignore - str (indent+1) sb inner + str (indent + 1) sb inner | ResolveStreamed (inner, mode) -> pad indent sb - sb.Append("ResolveStreamed: ").AppendLine(nameAs info) |> ignore - str (indent+1) sb inner + + sb + .Append("ResolveStreamed: ") + .AppendLine(nameAs info) + |> ignore + + str (indent + 1) sb inner | SelectFields fields -> pad indent sb - sb.Append("SelectFields: ").AppendLine(nameAs info) |> ignore - fields |> List.iter (str (indent+1) sb) + + sb + .Append("SelectFields: ") + .AppendLine(nameAs info) + |> ignore + + fields |> List.iter (str (indent + 1) sb) | ResolveCollection inner -> pad indent sb - sb.Append("ResolveCollection: ").AppendLine(nameAs info) |> ignore - str (indent+1) sb inner + + sb + .Append("ResolveCollection: ") + .AppendLine(nameAs info) + |> ignore + + str (indent + 1) sb inner | ResolveAbstraction types -> pad indent sb - sb.Append("ResolveAbstraction: ").AppendLine(nameAs info) |> ignore + + sb + .Append("ResolveAbstraction: ") + .AppendLine(nameAs info) + |> ignore + types |> Map.iter (fun tname fields -> - pad (indent+1) sb + pad (indent + 1) sb sb.Append("Case: ").AppendLine(tname) |> ignore - fields |> List.iter (str (indent+2) sb)) + fields |> List.iter (str (indent + 2) sb)) - let sb = Text.StringBuilder () + let sb = Text.StringBuilder() str 0 sb this sb.ToString() @@ -705,14 +802,14 @@ and ExecutionInfoKind = /// Reduce result set by selecting provided set of fields, /// defined inside composite type, current execution info /// refers to. - | SelectFields of fields : ExecutionInfo list + | SelectFields of fields: ExecutionInfo list /// Reduce current field as a collection, applying provided /// execution info on each of the collection's element. - | ResolveCollection of elementPlan : ExecutionInfo + | ResolveCollection of elementPlan: ExecutionInfo /// Reduce union or interface types by applying provided set of /// field infos depending on what concrete object implementation /// will be found. - | ResolveAbstraction of typeFields : Map + | ResolveAbstraction of typeFields: Map /// Reduce result set as a deferred result. | ResolveDeferred of ExecutionInfo /// Reduce the current field as a stream, applying @@ -724,10 +821,11 @@ and ExecutionInfoKind = /// Buffered stream options. Used to specify how the buffer will behavior in a stream. and BufferedStreamOptions = - /// The maximum time in milliseconds that the buffer will be filled before being sent to the subscriber. - { Interval : int option - /// The maximum number of items that will be buffered before being sent to the subscriber. - PreferredBatchSize : int option } + { + Interval: int option + /// The maximum number of items that will be buffered before being sent to the subscriber. + PreferredBatchSize: int option + } /// Wrapper for a resolve method defined by the user or generated by a runtime. and Resolve = @@ -738,37 +836,37 @@ and Resolve = /// input defines .NET type of the provided object /// output defines .NET type of the returned value /// expr is untyped version of Expr'Input->'Output> - | Sync of input:Type * output:Type * expr:Expr + | Sync of input: Type * output: Type * expr: Expr /// Resolve field value as part of Async computation. /// input defines .NET type of the provided object /// output defines .NET type of the returned value /// expr is untyped version of Expr'Input->Async<'Output>> - | Async of input:Type * output:Type * expr:Expr + | Async of input: Type * output: Type * expr: Expr /// Resolves the filter function of a subscription. /// root defines the .NET type of the root object /// input defines the .NET type of the value being subscribed to /// expr is the untyped version of Expr 'Root -> 'Input -> bool> - | Filter of root: Type * input:Type * output:Type * expr:Expr + | Filter of root: Type * input: Type * output: Type * expr: Expr /// Resolves the filter function of a subscription that has asyncronous fields. /// root defines the .NET type of the root object /// input defines the .NET type of the value being subscribed to /// expr is the untyped version of Expr 'Root -> 'Input -> bool> - | AsyncFilter of root: Type * input:Type * output:Type * expr:Expr + | AsyncFilter of root: Type * input: Type * output: Type * expr: Expr - | ResolveExpr of expr:Expr + | ResolveExpr of expr: Expr /// Returns an expression defining resolver function. member x.Expr = match x with - | Sync(_,_,e) -> e - | Async(_,_,e) -> e - | ResolveExpr(e) -> e + | Sync (_, _, e) -> e + | Async (_, _, e) -> e + | ResolveExpr e -> e | Undefined -> failwith "Resolve function was not defined" - | x -> failwith <| sprintf "Unexpected resolve function %A" x + | x -> failwith $"Unexpected resolve function %A{x}" /// Execution strategy for provided queries. Defines if object fields should /// be resolved either sequentially one-by-one or in parallel. @@ -782,98 +880,117 @@ and ExecutionStrategy = /// Type representing a variable definition inside GraphQL query. and VarDef = - { /// Variable name without prefixed '$'. - Name: string - /// Type definition in corresponding GraphQL schema. - TypeDef: InputDef - /// Optional default value. - DefaultValue: Value option } + { + /// Variable name without prefixed '$'. + Name: string + /// Type definition in corresponding GraphQL schema. + TypeDef: InputDef + /// Optional default value. + DefaultValue: Value option + } /// The context used to hold all the information for a schema compiling proccess. and SchemaCompileContext = - { Schema : ISchema - TypeMap : TypeMap - FieldExecuteMap : FieldExecuteMap } + { + Schema: ISchema + TypeMap: TypeMap + FieldExecuteMap: FieldExecuteMap + } /// A planning of an execution phase. /// It is used by the execution process to execute an operation. and ExecutionPlan = - { /// Unique identifier of the current execution plan. - DocumentId : int - /// AST defintion of current operation. - Operation : OperationDefinition - /// Definition of the root type (either query or mutation) used by the - /// current operation. - RootDef : ObjectDef - /// Execution strategy applied on the underlying object's fields. - Strategy : ExecutionStrategy - /// List of fields of top level query/mutation object to be resolved. - Fields : ExecutionInfo list - /// List of variables defined within executed query. - Variables : VarDef list - /// A dictionary of metadata associated with custom operations on the planning of this plan. - Metadata : Metadata - /// The validation result for the document being processed. - ValidationResult : ValidationResult } - member x.Item with get(id) = x.Fields |> List.find (fun f -> f.Identifier = id) + { + /// Unique identifier of the current execution plan. + DocumentId: int + /// AST defintion of current operation. + Operation: OperationDefinition + /// Definition of the root type (either query or mutation) used by the + /// current operation. + RootDef: ObjectDef + /// Execution strategy applied on the underlying object's fields. + Strategy: ExecutionStrategy + /// List of fields of top level query/mutation object to be resolved. + Fields: ExecutionInfo list + /// List of variables defined within executed query. + Variables: VarDef list + /// A dictionary of metadata associated with custom operations on the planning of this plan. + Metadata: Metadata + /// The validation result for the document being processed. + ValidationResult: ValidationResult + } + member x.Item + with get (id) = x.Fields |> List.find (fun f -> f.Identifier = id) /// Execution context of the current GraphQL operation. It contains a full /// knowledge about which fields will be accessed, what types are associated /// with them and what variable values have been set by incoming query. and ExecutionContext = - { /// GraphQL schema definition. - Schema : ISchema - /// Boxed value of the top level type, root query/mutation. - RootValue : obj - /// Execution plan describing, what fiedls are going to be resolved. - ExecutionPlan : ExecutionPlan - /// Collection of variables provided to execute current operation. - Variables : Map - /// Collection of errors that occurred while executing current operation. - Errors : ConcurrentBag - /// A map of all fields of the query and their respective execution operations. - FieldExecuteMap : FieldExecuteMap - /// A simple dictionary to hold metadata that can be used by execution customizations. - Metadata : Metadata } + { + /// GraphQL schema definition. + Schema: ISchema + /// Boxed value of the top level type, root query/mutation. + RootValue: obj + /// Execution plan describing, what fiedls are going to be resolved. + ExecutionPlan: ExecutionPlan + /// Collection of variables provided to execute current operation. + Variables: Map + /// Collection of errors that occurred while executing current operation. + Errors: ConcurrentBag + /// A map of all fields of the query and their respective execution operations. + FieldExecuteMap: FieldExecuteMap + /// A simple dictionary to hold metadata that can be used by execution customizations. + Metadata: Metadata + } /// An execution context for the particular field, applied as the first /// parameter for target resolve function. and ResolveFieldContext = - { /// Fragment of the overall execution plan related to current field. - ExecutionInfo : ExecutionInfo - /// Current operation execution context. - Context : ExecutionContext - /// GraphQL type definition for the returned value. - ReturnType : TypeDef - /// GraphQL type definition for a parent object, current value needs - /// to be resolved from. - ParentType : ObjectDef - /// Current GraphQL schema. - Schema : ISchema - /// Untyped map of all argument values used for as current field's - /// parametrized inputs. - Args : Map - /// Variables provided by the operation caller. - Variables : Map - /// Field path - Path : obj list } + { + /// Fragment of the overall execution plan related to current field. + ExecutionInfo: ExecutionInfo + /// Current operation execution context. + Context: ExecutionContext + /// GraphQL type definition for the returned value. + ReturnType: TypeDef + /// GraphQL type definition for a parent object, current value needs + /// to be resolved from. + ParentType: ObjectDef + /// Current GraphQL schema. + Schema: ISchema + /// Untyped map of all argument values used for as current field's + /// parametrized inputs. + Args: Map + /// Variables provided by the operation caller. + Variables: Map + /// Field path + Path: obj list + } /// Remembers an exception, so it can be included in the final response. - member x.AddError(error : exn) = x.Context.Errors.Add error + member x.AddError(error: exn) = x.Context.Errors.Add error /// Tries to find an argument by provided name. - member x.TryArg(name : string) : 't option = + member x.TryArg(name: string) : 't option = match Map.tryFind name x.Args with | Some o -> Some(o :?> 't) | None -> None /// Returns an argument by provided name. If argument was not found /// and exception will be thrown. - member x.Arg(name : string) : 't = + member x.Arg(name: string) : 't = match Map.tryFind name x.Args with | Some found -> downcast found - | None -> raise (System.Collections.Generic.KeyNotFoundException(sprintf "Argument '%s' was not provided within context of a field '%s'. Check if it was supplied within GraphQL query." name x.ExecutionInfo.Identifier)) + | None -> + raise ( + System.Collections.Generic.KeyNotFoundException( + sprintf + "Argument '%s' was not provided within context of a field '%s'. Check if it was supplied within GraphQL query." + name + x.ExecutionInfo.Identifier + ) + ) /// Function type for the compiled field executor. and ExecuteField = ResolveFieldContext -> obj -> AsyncVal @@ -883,20 +1000,19 @@ and ExecuteField = ResolveFieldContext -> obj -> AsyncVal and FieldDef = interface /// Name of the field. - abstract Name : string + abstract Name: string /// Optional field description. - abstract Description : string option + abstract Description: string option /// Optional field deprecation warning. - abstract DeprecationReason : string option + abstract DeprecationReason: string option /// Field's GraphQL type definition. - abstract TypeDef : OutputDef + abstract TypeDef: OutputDef /// Field's arguments list. - abstract Args : InputFieldDef [] + abstract Args: InputFieldDef [] /// Field's metadata. - abstract Metadata : Metadata + abstract Metadata: Metadata /// Field resolution function. - abstract Resolve : Resolve - /// INTERNAL API: Compiled field executor. To be set only by the runtime. + abstract Resolve: Resolve inherit IEquatable end @@ -909,21 +1025,22 @@ and FieldDef<'Val> = end and [] internal FieldDefinition<'Val, 'Res> = - { /// Name of the field. - Name : string - /// Optional field description. - Description : string option - /// Field's GraphQL type definition. - TypeDef : OutputDef<'Res> - /// Field resolution function. - Resolve : Resolve - /// Field's arguments list. - Args : InputFieldDef [] - /// Optional field deprecation warning. - DeprecationReason : string option - /// Field metadata definition. - Metadata : Metadata - } + { + /// Name of the field. + Name: string + /// Optional field description. + Description: string option + /// Field's GraphQL type definition. + TypeDef: OutputDef<'Res> + /// Field resolution function. + Resolve: Resolve + /// Field's arguments list. + Args: InputFieldDef [] + /// Optional field deprecation warning. + DeprecationReason: string option + /// Field metadata definition. + Metadata: Metadata + } interface FieldDef with member x.Name = x.Name @@ -951,22 +1068,25 @@ and [] internal FieldDefinition<'Val, 'Res> = hash override x.ToString() = - if not (Array.isEmpty x.Args) - then x.Name + "(" + String.Join(", ", x.Args) + "): " + x.TypeDef.ToString() - else x.Name + ": " + x.TypeDef.ToString() + if not (Array.isEmpty x.Args) then + x.Name + "(" + String.Join(", ", x.Args) + "): " + x.TypeDef.ToString() + else + x.Name + ": " + x.TypeDef.ToString() /// An untyped representation of GraphQL scalar type. and ScalarDef = interface /// Name of the scalar type. - abstract Name : string + abstract Name: string /// Optional scalar type description. - abstract Description : string option + abstract Description: string option /// A function used to retrieve a .NET object from provided GraphQL query. - abstract CoerceInput : Value -> obj option + abstract CoerceInput: Value -> obj option + /// A function used to set a surrogate representation to be /// returned as a query result. - abstract CoerceValue : obj -> obj option + abstract CoerceValue: obj -> obj option + inherit TypeDef inherit NamedDef inherit InputDef @@ -976,25 +1096,27 @@ and ScalarDef = /// Concrete representation of the scalar types. and [] ScalarDefinition<'Val> = - { /// Name of the scalar type. - Name : string - /// Optional type description. - Description : string option - /// A function used to retrieve a .NET object from provided GraphQL query. - CoerceInput : Value -> 'Val option - /// A function used to set a surrogate representation to be - /// returned as a query result. - CoerceValue : obj -> 'Val option } + { + /// Name of the scalar type. + Name: string + /// Optional type description. + Description: string option + /// A function used to retrieve a .NET object from provided GraphQL query. + CoerceInput: Value -> 'Val option + /// A function used to set a surrogate representation to be + /// returned as a query result. + CoerceValue: obj -> 'Val option + } interface TypeDef with member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface InputDef @@ -1026,26 +1148,28 @@ and [] ScalarDefinition<'Val> = and EnumVal = interface /// Identifier of the enum value. - abstract Name : string + abstract Name: string /// Optional enum value description. - abstract Description : string option + abstract Description: string option /// Value to be stringified as a result to the user. - abstract Value : obj + abstract Value: obj /// Optional description of the deprecation reason. - abstract DeprecationReason : string option + abstract DeprecationReason: string option end /// A GraphQL representation of single case of the enum type. /// Enum value return value is always represented as string. and EnumValue<'Val> = - { /// Identifier of the enum value. - Name : string - /// Value to be stringified as a result to the user. - Value : 'Val - /// Optional enum value description. - Description : string option - /// Optional description of the deprecation reason. - DeprecationReason : string option } + { + /// Identifier of the enum value. + Name: string + /// Value to be stringified as a result to the user. + Value: 'Val + /// Optional enum value description. + Description: string option + /// Optional description of the deprecation reason. + DeprecationReason: string option + } interface EnumVal with member x.Name = x.Name @@ -1061,11 +1185,11 @@ and EnumValue<'Val> = and EnumDef = interface /// Enum type name. - abstract Name : string + abstract Name: string /// Optional enum type description. - abstract Description : string option + abstract Description: string option /// List of available enum cases. - abstract Options : EnumVal [] + abstract Options: EnumVal [] inherit TypeDef inherit InputDef inherit OutputDef @@ -1079,7 +1203,7 @@ and EnumDef = and EnumDef<'Val> = interface /// List of available enum cases (typed). - abstract Options : EnumValue<'Val> [] + abstract Options: EnumValue<'Val> [] inherit EnumDef inherit TypeDef<'Val> inherit InputDef<'Val> @@ -1087,12 +1211,14 @@ and EnumDef<'Val> = end and internal EnumDefinition<'Val> = - { /// Enum type name. - Name : string - /// Optional enum type description. - Description : string option - /// List of available enum cases. - Options : EnumValue<'Val> [] } + { + /// Enum type name. + Name: string + /// Optional enum type description. + Description: string option + /// List of available enum cases. + Options: EnumValue<'Val> [] + } interface InputDef interface OutputDef @@ -1100,11 +1226,11 @@ and internal EnumDefinition<'Val> = member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface EnumDef<'Val> with @@ -1113,11 +1239,7 @@ and internal EnumDefinition<'Val> = interface EnumDef with member x.Name = x.Name member x.Description = x.Description - member x.Options = - x.Options - |> Seq.ofArray - |> Seq.cast - |> Seq.toArray + member x.Options = x.Options |> Seq.ofArray |> Seq.cast |> Seq.toArray interface NamedDef with member x.Name = x.Name @@ -1130,16 +1252,18 @@ and internal EnumDefinition<'Val> = and ObjectDef = interface /// Name of the object type definition. - abstract Name : string + abstract Name: string /// Optional object definition description. - abstract Description : string option + abstract Description: string option /// Collection of fields defined by the current object. - abstract Fields : Map + abstract Fields: Map /// Collection of interfaces implemented by the current object. - abstract Implements : InterfaceDef [] + abstract Implements: InterfaceDef [] + /// Optional function used to recognize of provided /// .NET object is valid for this GraphQL object definition. - abstract IsTypeOf : (obj -> bool) option + abstract IsTypeOf: (obj -> bool) option + inherit TypeDef inherit NamedDef inherit OutputDef @@ -1152,35 +1276,37 @@ and ObjectDef = and ObjectDef<'Val> = interface /// Collection of fields defined by the current object. - abstract Fields : Map> + abstract Fields: Map> inherit ObjectDef inherit TypeDef<'Val> inherit OutputDef<'Val> end and [] internal ObjectDefinition<'Val> = - { /// Name of the object type definition. - Name : string - /// Optional object definition description. - Description : string option - /// Lazy resolver for the object fields. It must be lazy in - /// order to allow self-recursive type references. - FieldsFn : Lazy>> - /// Collection of interfaces implemented by the current object. - Implements : InterfaceDef [] - /// Optional function used to recognize of provided - /// .NET object is valid for this GraphQL object definition. - IsTypeOf : (obj -> bool) option } + { + /// Name of the object type definition. + Name: string + /// Optional object definition description. + Description: string option + /// Lazy resolver for the object fields. It must be lazy in + /// order to allow self-recursive type references. + FieldsFn: Lazy>> + /// Collection of interfaces implemented by the current object. + Implements: InterfaceDef [] + /// Optional function used to recognize of provided + /// .NET object is valid for this GraphQL object definition. + IsTypeOf: (obj -> bool) option + } interface TypeDef with member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface OutputDef @@ -1214,16 +1340,19 @@ and [] internal ObjectDefinition<'Val> = and InterfaceDef = interface /// Name of the interface type definition. - abstract Name : string + abstract Name: string /// Optional interface description. - abstract Description : string option + abstract Description: string option + /// List of fields to be defined by implementing object /// definition in order to satisfy current interface. - abstract Fields : FieldDef [] + abstract Fields: FieldDef [] + /// Optional funciton used to determine, which object /// definition is a concrete implementation of the current /// interface for provided .NET object. - abstract ResolveType : (obj -> ObjectDef) option + abstract ResolveType: (obj -> ObjectDef) option + inherit TypeDef inherit OutputDef inherit CompositeDef @@ -1237,35 +1366,38 @@ and InterfaceDef<'Val> = interface /// List of fields to be defined by implementing object /// definition in order to satisfy current interface. - abstract Fields : FieldDef<'Val> [] + abstract Fields: FieldDef<'Val> [] + inherit TypeDef<'Val> inherit OutputDef<'Val> inherit InterfaceDef end and [] internal InterfaceDefinition<'Val> = - { /// Name of the interface type definition. - Name : string - /// Optional interface description. - Description : string option - /// Lazy defintion of fields to be defined by implementing - /// object definition in order to satisfy current interface. - /// Must be lazy in order to allow self-referencing types. - FieldsFn : unit -> FieldDef<'Val> [] - /// Optional funciton used to determine, which object - /// definition is a concrete implementation of the current - /// interface for provided .NET object. - ResolveType : (obj -> ObjectDef) option } + { + /// Name of the interface type definition. + Name: string + /// Optional interface description. + Description: string option + /// Lazy defintion of fields to be defined by implementing + /// object definition in order to satisfy current interface. + /// Must be lazy in order to allow self-referencing types. + FieldsFn: unit -> FieldDef<'Val> [] + /// Optional funciton used to determine, which object + /// definition is a concrete implementation of the current + /// interface for provided .NET object. + ResolveType: (obj -> ObjectDef) option + } interface TypeDef with member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface OutputDef @@ -1298,18 +1430,21 @@ and [] internal InterfaceDefinition<'Val> = and UnionDef = interface /// Name of the union type definition. - abstract Name : string + abstract Name: string /// Optiona union type description. - abstract Description : string option + abstract Description: string option /// Collection of object cases represented by this union. - abstract Options : ObjectDef [] + abstract Options: ObjectDef [] + /// Optional funciton used to determine, which object /// definition is a concrete implementation of the current /// union for provided .NET object. - abstract ResolveType : (obj -> ObjectDef) option + abstract ResolveType: (obj -> ObjectDef) option + /// Helper function which provides ability to retrieve /// specific values, that are wrapped in F# discriminated unions. - abstract ResolveValue : obj -> obj + abstract ResolveValue: obj -> obj + inherit TypeDef inherit OutputDef inherit CompositeDef @@ -1324,10 +1459,12 @@ and UnionDef<'In> = /// Optional funciton used to determine, which object /// definition is a concrete implementation of the current /// union for provided .NET object. - abstract ResolveType : ('In -> ObjectDef) option + abstract ResolveType: ('In -> ObjectDef) option + /// Helper function which provides ability to retrieve /// specific values, that are wrapped in F# discriminated unions. - abstract ResolveValue : 'In -> obj + abstract ResolveValue: 'In -> obj + inherit UnionDef inherit TypeDef<'In> inherit OutputDef<'In> @@ -1335,29 +1472,31 @@ and UnionDef<'In> = /// 3.1.4 Unions and [] internal UnionDefinition<'In, 'Out> = - { /// Name of the union type definition. - Name : string - /// Optiona union type description. - Description : string option - /// Collection of object cases represented by this union. - Options : ObjectDef [] - /// Optional funciton used to determine, which object - /// definition is a concrete implementation of the current - /// union for provided .NET object. - ResolveType : ('In -> ObjectDef) option - /// Helper function which provides ability to retrieve - /// specific values, that are wrapped in F# discriminated unions. - ResolveValue : 'In -> 'Out } + { + /// Name of the union type definition. + Name: string + /// Optiona union type description. + Description: string option + /// Collection of object cases represented by this union. + Options: ObjectDef [] + /// Optional funciton used to determine, which object + /// definition is a concrete implementation of the current + /// union for provided .NET object. + ResolveType: ('In -> ObjectDef) option + /// Helper function which provides ability to retrieve + /// specific values, that are wrapped in F# discriminated unions. + ResolveValue: 'In -> 'Out + } interface TypeDef with member __.Type = typeof<'Out> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface OutputDef @@ -1393,7 +1532,7 @@ and [] internal UnionDefinition<'In, 'Out> = and ListOfDef = interface /// GraphQL type definition of the container element type. - abstract OfType : TypeDef + abstract OfType: TypeDef inherit InputDef inherit OutputDef end @@ -1403,7 +1542,7 @@ and ListOfDef = and ListOfDef<'Val, 'Seq when 'Seq :> 'Val seq> = interface /// GraphQL type definition of the container element type. - abstract OfType : TypeDef<'Val> + abstract OfType: TypeDef<'Val> inherit TypeDef<'Seq> inherit InputDef<'Seq> inherit OutputDef<'Seq> @@ -1411,13 +1550,16 @@ and ListOfDef<'Val, 'Seq when 'Seq :> 'Val seq> = end and internal ListOfDefinition<'Val, 'Seq when 'Seq :> 'Val seq> = - { OfType : TypeDef<'Val> } + { + OfType: TypeDef<'Val> + } interface InputDef interface TypeDef with member __.Type = typeof<'Seq> + member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = @@ -1441,7 +1583,7 @@ and internal ListOfDefinition<'Val, 'Seq when 'Seq :> 'Val seq> = and NullableDef = interface /// GraphQL type definition of the nested type. - abstract OfType : TypeDef + abstract OfType: TypeDef inherit InputDef inherit OutputDef end @@ -1453,21 +1595,24 @@ and NullableDef = and NullableDef<'Val> = interface /// GraphQL type definition of the nested type. - abstract OfType : TypeDef<'Val> + abstract OfType: TypeDef<'Val> inherit InputDef<'Val option> inherit OutputDef<'Val option> inherit NullableDef end and internal NullableDefinition<'Val> = - { OfType : TypeDef<'Val> } + { + OfType: TypeDef<'Val> + } interface InputDef interface TypeDef with member __.Type = typeof<'Val option> member x.MakeNullable() = upcast x + member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list interface OutputDef @@ -1489,11 +1634,11 @@ and internal NullableDefinition<'Val> = and InputObjectDef = interface /// Name of the input object. - abstract Name : string + abstract Name: string /// Optional input object description. - abstract Description : string option + abstract Description: string option /// Collection of input object fields. - abstract Fields : InputFieldDef [] + abstract Fields: InputFieldDef [] inherit NamedDef inherit InputDef end @@ -1501,13 +1646,15 @@ and InputObjectDef = /// GraphQL tye definition for input objects. They are different /// from object types (which can be used only as outputs). and InputObjectDefinition<'Val> = - { /// Name of the input object. - Name : string - /// Optional input object description. - Description : string option - /// Function used to define field inputs. It must be lazy - /// in order to support self-referencing types. - FieldsFn : unit -> InputFieldDef [] } + { + /// Name of the input object. + Name: string + /// Optional input object description. + Description: string option + /// Function used to define field inputs. It must be lazy + /// in order to support self-referencing types. + FieldsFn: unit -> InputFieldDef [] + } interface InputDef interface InputObjectDef with @@ -1525,11 +1672,11 @@ and InputObjectDefinition<'Val> = member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list /// Function type used for resolving input object field values. @@ -1540,32 +1687,37 @@ and ExecuteInput = Value -> Map -> obj and InputFieldDef = interface /// Name of the input field / argument. - abstract Name : string + abstract Name: string /// Optional input field / argument description. - abstract Description : string option + abstract Description: string option /// GraphQL type definition of the input type. - abstract TypeDef : InputDef + abstract TypeDef: InputDef /// Optional default input value - used when no input was provided. - abstract DefaultValue : obj option + abstract DefaultValue: obj option + /// INTERNAL API: input execution function - /// compiled by the runtime. - abstract ExecuteInput : ExecuteInput with get, set + abstract ExecuteInput: ExecuteInput with get, set + inherit IEquatable end /// INTERNAL API: 3.1.2.1 Object Field Arguments +/// TODO: compose with InputValueDefinition and [] InputFieldDefinition<'In> = - { /// Name of the input field / argument. - Name : string - /// Optional input field / argument description. - Description : string option - /// GraphQL type definition of the input type. - TypeDef : InputDef<'In> - /// Optional default input value - used when no input was provided. - DefaultValue : 'In option - /// INTERNAL API: input execution function - - /// compiled by the runtime. - mutable ExecuteInput : ExecuteInput } + { + /// Name of the input field / argument. + Name: string + /// Optional input field / argument description. + Description: string option + /// GraphQL type definition of the input type. + TypeDef: InputDef<'In> + /// Optional default input value - used when no input was provided. + DefaultValue: 'In option + /// INTERNAL API: input execution function - + /// compiled by the runtime. + mutable ExecuteInput: ExecuteInput + } interface InputFieldDef with member x.Name = x.Name @@ -1598,8 +1750,8 @@ and TagsResolver = ResolveFieldContext -> Tag seq and SubscriptionFieldDef = interface - abstract OutputTypeDef : OutputDef - abstract TagsResolver : TagsResolver + abstract OutputTypeDef: OutputDef + abstract TagsResolver: TagsResolver inherit FieldDef end @@ -1616,17 +1768,17 @@ and SubscriptionFieldDef<'Root, 'Input, 'Output> = and [] SubscriptionFieldDefinition<'Root, 'Input, 'Output> = { - Name : string - Description : string option - DeprecationReason : string option + Name: string + Description: string option + DeprecationReason: string option // The type of the value that the subscription consumes, used to make sure that our filter function is properly typed - OutputTypeDef : OutputDef<'Output> + OutputTypeDef: OutputDef<'Output> // The type of the root value, we need to thread this into our filter function - RootTypeDef : OutputDef<'Root> - Filter : Resolve - Args : InputFieldDef [] - Metadata : Metadata - TagsResolver : TagsResolver + RootTypeDef: OutputDef<'Root> + Filter: Resolve + Args: InputFieldDef [] + Metadata: Metadata + TagsResolver: TagsResolver } interface FieldDef with member x.Name = x.Name @@ -1636,18 +1788,22 @@ and [] SubscriptionFieldDefinition<'Root, 'Input, member x.Resolve = x.Filter member x.Args = x.Args member x.Metadata = x.Metadata + interface SubscriptionFieldDef with member x.OutputTypeDef = x.OutputTypeDef :> OutputDef member x.TagsResolver = x.TagsResolver + interface FieldDef<'Root> - member x.TypeDef = x.RootTypeDef + member x.TypeDef = x.RootTypeDef interface SubscriptionFieldDef<'Root, 'Input, 'Output> + interface IEquatable with member x.Equals f = - x.Name = f.Name && - x.TypeDef :> OutputDef = f.TypeDef && - x.Args = f.Args && - f :? SubscriptionFieldDef<'Root> + x.Name = f.Name + && x.TypeDef :> OutputDef = f.TypeDef + && x.Args = f.Args + && f :? SubscriptionFieldDef<'Root> + override x.Equals y = match y with | :? SubscriptionFieldDef as f -> (x :> IEquatable).Equals(f) @@ -1660,56 +1816,62 @@ and [] SubscriptionFieldDefinition<'Root, 'Input, hash override x.ToString() = - if not (Array.isEmpty x.Args) - then x.Name + "(" + String.Join(", ", x.Args) + "): " + x.TypeDef.ToString() - else x.Name + ": " + x.TypeDef.ToString() + if not (Array.isEmpty x.Args) then + x.Name + "(" + String.Join(", ", x.Args) + "): " + x.TypeDef.ToString() + else + x.Name + ": " + x.TypeDef.ToString() and SubscriptionObjectDef = interface - abstract Fields : Map + abstract Fields: Map inherit ObjectDef end and SubscriptionObjectDef<'Val> = interface inherit SubscriptionObjectDef - abstract Fields : Map> + abstract Fields: Map> inherit ObjectDef<'Val> end and [] SubscriptionObjectDefinition<'Val> = { - Name : string - Description : string option - Fields : Map> + Name: string + Description: string option + Fields: Map> } interface TypeDef with member __.Type = typeof<'Val> member x.MakeNullable() = - let nullable : NullableDefinition<_> = { OfType = x } + let nullable: NullableDefinition<_> = { OfType = x } upcast nullable member x.MakeList() = - let list: ListOfDefinition<_,_> = { OfType = x } + let list: ListOfDefinition<_, _> = { OfType = x } upcast list + interface ObjectDef with member x.Name = x.Name member x.Description = x.Description - member x.Fields = x.Fields |> Map.map(fun _ f -> f :> FieldDef) - member x.Implements = Array.empty : InterfaceDef [] + member x.Fields = x.Fields |> Map.map (fun _ f -> f :> FieldDef) + member x.Implements: InterfaceDef [] = Array.empty // TODO: Actually add istypeof member x.IsTypeOf = None + interface ObjectDef<'Val> with - member x.Fields = x.Fields |> Map.map(fun _ f -> f :> FieldDef<'Val>) + member x.Fields = x.Fields |> Map.map (fun _ f -> f :> FieldDef<'Val>) interface NamedDef with member x.Name = x.Name + interface SubscriptionObjectDef with member x.Fields = x.Fields |> Map.map (fun _ f -> upcast f) + interface SubscriptionObjectDef<'Val> with member x.Fields = x.Fields + override x.Equals y = match y with | :? SubscriptionObjectDefinition<'Val> as f -> f.Name = x.Name @@ -1721,22 +1883,11 @@ and [] SubscriptionObjectDefinition<'Val> = override x.ToString() = x.Name + "!" -/// 3.13 GraphQL directive definition. -and DirectiveDef = - { /// Directive's name - it's NOT '@' prefixed. - Name : string - /// Optional directive description. - Description : string option - /// Directive location - describes, which part's of the query AST - /// are valid places to include current directive to. - Locations : DirectiveLocation - /// Array of arguments defined within that directive. - Args : InputFieldDef [] } /// Metadata object. /// Metadata objects are used to hold custom information inside fields and contexts /// used by the GraphQL executor and ISchema. -and Metadata(data : Map) = +and Metadata(data: Map) = new() = Metadata(Map.empty) /// @@ -1744,28 +1895,28 @@ and Metadata(data : Map) = /// /// The key to be used to search information for. /// The value to be stored inside the metadata. - member __.Add(key : string, value : obj) = Metadata(data.Add (key, value)) + member __.Add(key: string, value: obj) = Metadata(data.Add(key, value)) /// /// Generates a new Metadata instance, filled with items of a string * obj list. /// /// A list of string * obj tuples to be used to fill the Metadata object. - static member FromList(l : (string * obj) list) = - let rec add (m : Metadata) (l : (string * obj) list) = + static member FromList(l: (string * obj) list) = + let rec add (m: Metadata) (l: (string * obj) list) = match l with | [] -> m | (k, v) :: xs -> add (m.Add(k, v)) xs + add (Metadata()) l /// Creates an empty Metadata object. - static member Empty = Metadata.FromList [ ] + static member Empty = Metadata.FromList [] /// /// Tries to find an value inside the metadata by it's key. /// /// The key to be used to search information for. - member __.TryFind<'Value>(key : string) = - if data.ContainsKey key then data.Item key :?> 'Value |> Some else None + member __.TryFind<'Value>(key: string) = if data.ContainsKey key then data.Item key :?> 'Value |> Some else None override __.ToString() = sprintf "%A" data @@ -1773,19 +1924,23 @@ and Metadata(data : Map) = /// The map of types is used to plan and execute queries. and TypeMap() = let map = Dictionary() + let isDefaultType name = let defaultTypes = - [ "__Schema" - "__Directive" - "__InputValue" - "__Type" - "__EnumValue" - "__Field" - "__TypeKind" - "__DirectiveLocation" ] + [ + "__Schema" + "__Directive" + "__InputValue" + "__Type" + "__EnumValue" + "__Field" + "__TypeKind" + "__DirectiveLocation" + ] + defaultTypes |> List.exists (fun x -> x = name) - let rec named (tdef : TypeDef) = + let rec named (tdef: TypeDef) = match tdef with | :? NamedDef as n -> Some n | :? NullableDef as n -> named n.OfType @@ -1797,51 +1952,56 @@ and TypeMap() = /// /// The NamedDef to be added to the type map. It's name will be used as the key. /// If set to true, and another NamedDef exists with the same name, it will be overwritten. - member __.AddType(def : NamedDef, ?overwrite : bool) = + member __.AddType(def: NamedDef, ?overwrite: bool) = let overwrite = defaultArg overwrite false + let add name def overwrite = - if not (map.ContainsKey(name)) - then map.Add(name, def) - elif overwrite - then map.[name] <- def + if not (map.ContainsKey(name)) then map.Add(name, def) + elif overwrite then map.[name] <- def + let asNamed x = match named x with | Some n -> n | _ -> failwith "Expected a Named type!" - let rec insert (def : NamedDef) = + + let rec insert (def: NamedDef) = match def with | :? ScalarDef as sdef -> add sdef.Name def overwrite | :? EnumDef as edef -> add edef.Name def overwrite | :? SubscriptionObjectDef as sdef -> add sdef.Name def overwrite + sdef.Fields |> Map.toSeq |> Seq.map snd - |> Seq.collect (fun x -> Array.append [| x.OutputTypeDef :> TypeDef |] (x.Args |> Array.map (fun a -> upcast a.TypeDef))) + |> Seq.collect (fun x -> + Array.append [| x.OutputTypeDef :> TypeDef |] (x.Args |> Array.map (fun a -> upcast a.TypeDef))) |> Seq.map asNamed |> Seq.filter (fun x -> not (map.ContainsKey(x.Name))) |> Seq.iter insert | :? ObjectDef as odef -> add odef.Name def overwrite + odef.Fields |> Map.toSeq |> Seq.map snd - |> Seq.collect (fun x -> Seq.append (x.TypeDef :> TypeDef |> Seq.singleton) (x.Args |> Seq.map (fun a -> upcast a.TypeDef))) + |> Seq.collect (fun x -> + Seq.append (x.TypeDef :> TypeDef |> Seq.singleton) (x.Args |> Seq.map (fun a -> upcast a.TypeDef))) |> Seq.map asNamed |> Seq.filter (fun x -> not (map.ContainsKey(x.Name))) |> Seq.iter insert - odef.Implements - |> Seq.iter insert + + odef.Implements |> Seq.iter insert | :? InterfaceDef as idef -> add idef.Name def overwrite + idef.Fields |> Seq.map (fun x -> asNamed x.TypeDef) |> Seq.filter (fun x -> not (map.ContainsKey(x.Name))) |> Seq.iter insert | :? UnionDef as udef -> add udef.Name def overwrite - udef.Options - |> Seq.iter insert + udef.Options |> Seq.iter insert | :? ListOfDef as ldef -> match named ldef.OfType with | Some innerdef -> insert innerdef @@ -1852,12 +2012,17 @@ and TypeMap() = | None -> () | :? InputObjectDef as iodef -> add iodef.Name def overwrite + iodef.Fields |> Seq.collect (fun x -> (x.TypeDef :> TypeDef) |> Seq.singleton) - |> Seq.map (fun x -> match named x with Some n -> n | _ -> failwith "Expected a Named type!") + |> Seq.map (fun x -> + match named x with + | Some n -> n + | _ -> failwith "Expected a Named type!") |> Seq.filter (fun x -> not (map.ContainsKey(x.Name))) |> Seq.iter insert | _ -> failwith "Unexpected type!" + insert def /// @@ -1865,20 +2030,22 @@ and TypeMap() = /// /// The NamedDef sequence to be added to the type map. Their names will be used as keys. /// If set to true, and another NamedDef exists with the same name on the sequence, it will be overwritten. - member this.AddTypes(defs : NamedDef seq, ?overwrite : bool) = + member this.AddTypes(defs: NamedDef seq, ?overwrite: bool) = let overwrite = defaultArg overwrite false defs |> Seq.iter (fun def -> this.AddType(def, overwrite)) /// Converts this type map to a sequence of string * NamedDef values, with the first item being the key. - member __.ToSeq(?includeDefaultTypes : bool) = + member __.ToSeq(?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes true let result = map |> Seq.map (fun kvp -> (kvp.Key, kvp.Value)) - if not includeDefaultTypes - then result |> Seq.filter (fun (k, _) -> not (isDefaultType k)) - else result + + if not includeDefaultTypes then + result |> Seq.filter (fun (k, _) -> not (isDefaultType k)) + else + result /// Converts this type map to a list of string * NamedDef values, with the first item being the key. - member this.ToList(?includeDefaultTypes : bool) = + member this.ToList(?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes true this.ToSeq(includeDefaultTypes) |> List.ofSeq @@ -1887,10 +2054,10 @@ and TypeMap() = /// /// The name of the NamedDef to be searched for. /// If set to true, it will search for the NamedDef among the default types. - member __.TryFind(name : string, ?includeDefaultTypes : bool) = + member __.TryFind(name: string, ?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes false - if not includeDefaultTypes && isDefaultType name - then + + if not includeDefaultTypes && isDefaultType name then None else match map.TryGetValue(name) with @@ -1902,8 +2069,9 @@ and TypeMap() = /// /// The name of the NamedDef to be searched for. /// If set to true, it will search for the NamedDef among the default types. - member this.TryFind<'Type when 'Type :> NamedDef>(name : string, ?includeDefaultTypes : bool) = + member this.TryFind<'Type when 'Type :> NamedDef>(name: string, ?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes false + match this.TryFind(name, includeDefaultTypes) with | Some item -> match item with @@ -1915,11 +2083,18 @@ and TypeMap() = /// Gets all NamedDef's inside the map that are, or implements the specified type. /// /// If set to true, it will search for the NamedDef among the default types. - member this.OfType<'Type when 'Type :> NamedDef>(?includeDefaultTypes : bool) = + member this.OfType<'Type when 'Type :> NamedDef>(?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes false + this.ToSeq() |> Seq.filter (fun (name, _) -> not includeDefaultTypes && not (isDefaultType name)) - |> Seq.map (snd >> (fun x -> match x with :? 'Type as x -> Some x | _ -> None)) + |> Seq.map ( + snd + >> (fun x -> + match x with + | :? 'Type as x -> Some x + | _ -> None) + ) |> Seq.choose id |> List.ofSeq @@ -1929,7 +2104,7 @@ and TypeMap() = /// /// The name of the ObjectDef that has the field that are being searched. /// The name of the FieldDef to be searched for. - member this.TryFindField(objname : string, fname : string) = + member this.TryFindField(objname: string, fname: string) = match this.TryFind(objname) with | Some odef -> odef.Fields |> Map.tryFind fname | None -> None @@ -1939,7 +2114,7 @@ and TypeMap() = /// /// The name of the ObjectDef that has the field that are being searched. /// The name of the FieldDef to be searched for. - member this.TryFindField<'Type when 'Type :> OutputDef>(objname : string, fname : string) = + member this.TryFindField<'Type when 'Type :> OutputDef>(objname: string, fname: string) = match this.TryFindField(objname, fname) with | Some fdef -> match fdef.TypeDef with @@ -1951,16 +2126,19 @@ and TypeMap() = /// Tries to find ObjectDef<'Val> types inside the map, that have fields that are lists of 'Res type. /// /// If set to true, it will search for the NamedDef among the default types. - member this.GetTypesWithListFields<'Val, 'Res>(?includeDefaultTypes : bool) = + member this.GetTypesWithListFields<'Val, 'Res>(?includeDefaultTypes: bool) = let includeDefaultTypes = defaultArg includeDefaultTypes false let toSeq map = map |> Map.toSeq |> Seq.map snd - let map (f : FieldDef<'Val>) = - let rec isList (fieldTypeDef : TypeDef) = + + let map (f: FieldDef<'Val>) = + let rec isList (fieldTypeDef: TypeDef) = match fieldTypeDef with | :? NullableDef as x -> isList x.OfType | :? ListOfDef<'Res, 'Res seq> -> true | _ -> false + if isList f.TypeDef then Some f else None + this.OfType>(includeDefaultTypes) |> Seq.map (fun x -> x, (x.Fields |> toSeq |> Seq.map map |> Seq.choose id |> List.ofSeq)) |> List.ofSeq @@ -1969,76 +2147,81 @@ and TypeMap() = /// Creates a new TypeMap instance, using a sequence of NamedDef's to fill it. /// /// The NamedDef sequence that has the NamedDef's that will be filled into the TypeMap. - static member FromSeq(defs : NamedDef seq) = + static member FromSeq(defs: NamedDef seq) = let map = TypeMap() defs |> Seq.iter (fun def -> map.AddType(def)) map module Tags = - let from (x : #Tag) : Tag seq = Seq.singleton (upcast x) + let from (x: #Tag) : Tag seq = Seq.singleton (upcast x) let fromSeq x : Tag seq = x |> Seq.map (fun t -> upcast t) [] module SubscriptionExtensions = type ISubscriptionProvider with - member this.Register subscription = - this.AsyncRegister subscription |> Async.RunSynchronously + member this.Register subscription = this.AsyncRegister subscription |> Async.RunSynchronously - member this.Publish<'T> name subType = - this.AsyncPublish name subType |> Async.RunSynchronously + member this.Publish<'T> name subType = this.AsyncPublish name subType |> Async.RunSynchronously - member this.PublishTag<'T> name index subType = - this.AsyncPublishTag name index subType |> Async.RunSynchronously + member this.PublishTag<'T> name index subType = this.AsyncPublishTag name index subType |> Async.RunSynchronously type ILiveFieldSubscriptionProvider with - member this.Register subscription = - this.AsyncRegister subscription |> Async.RunSynchronously + member this.Register subscription = this.AsyncRegister subscription |> Async.RunSynchronously member this.Publish<'T> typeName fieldName subType = this.AsyncPublish typeName fieldName subType |> Async.RunSynchronously [] module Resolve = - type private Marker = class end + type private Marker = + class + end - let private (|FSharpFunc|_|) (typ : Type) = + let private (|FSharpFunc|_|) (typ: Type) = if FSharpType.IsFunction typ then - let d,c = FSharpType.GetFunctionElements typ - Some(d,c) - else None + let d, c = FSharpType.GetFunctionElements typ + Some(d, c) + else + None - let private (|FSharpOption|_|) (typ : Type) = + let private (|FSharpOption|_|) (typ: Type) = if typ.GetTypeInfo().IsGenericType && typ.GetGenericTypeDefinition() = typedefof> then Some(typ.GenericTypeArguments |> Array.head) - else None + else + None let private (|FSharpAsync|_|) (typ: Type) = if typ.GetTypeInfo().IsGenericType && typ.GetGenericTypeDefinition() = typedefof> then Some(typ.GenericTypeArguments |> Array.head) - else None + else + None - let private boxify<'T,'U>(f:ResolveFieldContext -> 'T -> 'U) : ResolveFieldContext -> obj -> obj = - <@@ fun ctx (x:obj) -> f ctx (x :?> 'T) |> box @@> + let private boxify<'T, 'U> (f: ResolveFieldContext -> 'T -> 'U) : ResolveFieldContext -> obj -> obj = + <@@ fun ctx (x: obj) -> f ctx (x :?> 'T) |> box @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox - let private boxifyAsync<'T, 'U>(f:ResolveFieldContext -> 'T -> Async<'U>): ResolveFieldContext -> obj -> Async = - <@@ fun ctx (x:obj) -> async.Bind(f ctx (x :?> 'T), async.Return << box) @@> + let private boxifyAsync<'T, 'U> (f: ResolveFieldContext -> 'T -> Async<'U>) : ResolveFieldContext -> obj -> Async = + <@@ fun ctx (x: obj) -> async.Bind(f ctx (x :?> 'T), async.Return << box) @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox - let private boxifyFilter<'Root, 'Input, 'Output>(f:ResolveFieldContext -> 'Root -> 'Input -> 'Output option): ResolveFieldContext -> obj -> obj -> obj option = - <@@ fun ctx (r:obj) (i:obj) -> f ctx (r :?> 'Root) (i :?> 'Input) |> Option.map(box)@@> + let private boxifyFilter<'Root, 'Input, 'Output> + (f: ResolveFieldContext -> 'Root -> 'Input -> 'Output option) + : ResolveFieldContext -> obj -> obj -> obj option = + <@@ fun ctx (r: obj) (i: obj) -> f ctx (r :?> 'Root) (i :?> 'Input) |> Option.map (box) @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox - let private boxifyAsyncFilter<'Root, 'Input, 'Output>(f:ResolveFieldContext -> 'Root -> 'Input -> Async<'Output option>): ResolveFieldContext -> obj -> obj -> Async = - <@@ fun ctx (r:obj) (i:obj) -> async.Bind(f ctx (r :?> 'Root) (i :?> 'Input), async.Return << Option.map(box))@@> + let private boxifyAsyncFilter<'Root, 'Input, 'Output> + (f: ResolveFieldContext -> 'Root -> 'Input -> Async<'Output option>) + : ResolveFieldContext -> obj -> obj -> Async = + <@@ fun ctx (r: obj) (i: obj) -> async.Bind(f ctx (r :?> 'Root) (i :?> 'Input), async.Return << Option.map (box)) @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox let private getRuntimeMethod name = - let methods = typeof.DeclaringType.GetRuntimeMethods() + let methods = typeof.DeclaringType.GetRuntimeMethods () methods |> Seq.find (fun m -> m.Name.Equals name) let private runtimeBoxify = getRuntimeMethod "boxify" @@ -2049,75 +2232,93 @@ module Resolve = let private runtimeBoxifyAsyncFilter = getRuntimeMethod "boxifyAsyncFilter" - let private unwrapExpr = function - | WithValue(resolver, _, _) -> (resolver, resolver.GetType()) + let private unwrapExpr = + function + | WithValue (resolver, _, _) -> (resolver, resolver.GetType()) | expr -> failwithf "Could not extract resolver from Expr: '%A'" expr - let inline private resolveUntyped f d c (methodInfo:MethodInfo) = - let result = methodInfo.GetGenericMethodDefinition().MakeGenericMethod(d,c).Invoke(null, [|f|]) + let inline private resolveUntyped f d c (methodInfo: MethodInfo) = + let result = + methodInfo + .GetGenericMethodDefinition() + .MakeGenericMethod(d, c) + .Invoke(null, [| f |]) + result |> unbox - let inline private resolveUntypedFilter f r i o (methodInfo:MethodInfo) = - let result = methodInfo.GetGenericMethodDefinition().MakeGenericMethod(r, i, o).Invoke(null, [|f|]) + let inline private resolveUntypedFilter f r i o (methodInfo: MethodInfo) = + let result = + methodInfo + .GetGenericMethodDefinition() + .MakeGenericMethod(r, i, o) + .Invoke(null, [| f |]) + result |> unbox let private boxifyExpr expr : ResolveFieldContext -> obj -> obj = match unwrapExpr expr with - | resolver, FSharpFunc(_,FSharpFunc(d,c)) -> - resolveUntyped resolver d c runtimeBoxify + | resolver, FSharpFunc (_, FSharpFunc (d, c)) -> resolveUntyped resolver d c runtimeBoxify | resolver, _ -> failwithf "Unsupported signature for Resolve %A" (resolver.GetType()) let private boxifyExprAsync expr : ResolveFieldContext -> obj -> Async = match unwrapExpr expr with - | resolver, FSharpFunc(_,FSharpFunc(d,FSharpAsync(c))) -> - resolveUntyped resolver d c runtimeBoxifyAsync + | resolver, FSharpFunc (_, FSharpFunc (d, FSharpAsync (c))) -> resolveUntyped resolver d c runtimeBoxifyAsync | resolver, _ -> failwithf "Unsupported signature for Async Resolve %A" (resolver.GetType()) - let private boxifyFilterExpr expr: ResolveFieldContext -> obj -> obj -> obj option = + let private boxifyFilterExpr expr : ResolveFieldContext -> obj -> obj -> obj option = match unwrapExpr expr with - | resolver, FSharpFunc(_,FSharpFunc(r,FSharpFunc(i,FSharpOption(o)))) -> + | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpOption (o)))) -> resolveUntypedFilter resolver r i o runtimeBoxifyFilter | resolver, _ -> failwithf "Unsupported signature for Subscription Filter Resolve %A" (resolver.GetType()) - let private boxifyAsyncFilterExpr expr: ResolveFieldContext -> obj -> obj -> Async = + let private boxifyAsyncFilterExpr expr : ResolveFieldContext -> obj -> obj -> Async = match unwrapExpr expr with - | resolver, FSharpFunc(_,FSharpFunc(r,FSharpFunc(i,FSharpAsync(FSharpOption(o))))) -> + | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpAsync (FSharpOption (o))))) -> resolveUntypedFilter resolver r i o runtimeBoxifyAsyncFilter | resolver, _ -> failwithf "Unsupported signature for Async Subscription Filter Resolve %A" (resolver.GetType()) - let (|BoxedSync|_|) = function - | Sync(d,c,expr) -> Some(d,c,boxifyExpr expr) + let (|BoxedSync|_|) = + function + | Sync (d, c, expr) -> Some(d, c, boxifyExpr expr) | _ -> None - let (|BoxedAsync|_|) = function - | Async(d,c,expr) -> Some(d,c,boxifyExprAsync expr) + let (|BoxedAsync|_|) = + function + | Async (d, c, expr) -> Some(d, c, boxifyExprAsync expr) | _ -> None - let (|BoxedExpr|_|) = function - | ResolveExpr(e) -> Some(boxifyExpr e) + let (|BoxedExpr|_|) = + function + | ResolveExpr (e) -> Some(boxifyExpr e) | _ -> None - let (|BoxedFilterExpr|_|) = function - | Filter(r,i,o,expr) -> Some(r,i,o,boxifyFilterExpr expr) + let (|BoxedFilterExpr|_|) = + function + | Filter (r, i, o, expr) -> Some(r, i, o, boxifyFilterExpr expr) | _ -> None - let (|BoxedAsyncFilterExpr|_|) = function - | AsyncFilter(r,i,o,expr) -> Some(r,i,o,boxifyAsyncFilterExpr expr) + let (|BoxedAsyncFilterExpr|_|) = + function + | AsyncFilter (r, i, o, expr) -> Some(r, i, o, boxifyAsyncFilterExpr expr) | _ -> None let private genMethodResolve<'Val, 'Res> (typeInfo: TypeInfo) (methodInfo: MethodInfo) = - let argInfo = typeof.GetTypeInfo().GetDeclaredMethod("Arg") + let argInfo = + typeof + .GetTypeInfo() + .GetDeclaredMethod("Arg") + let valueVar = Var("value", typeof<'Val>) let ctxVar = Var("ctx", typeof) - let argExpr (arg : ParameterInfo) = + + let argExpr (arg: ParameterInfo) = Expr.Call(Expr.Var(ctxVar), argInfo.MakeGenericMethod(arg.ParameterType), [ Expr.Value(arg.Name) ]) - let args = - methodInfo.GetParameters() - |> Array.map argExpr - |> Array.toList + + let args = methodInfo.GetParameters() |> Array.map argExpr |> Array.toList + let expr = - Expr.Lambda - (ctxVar, Expr<'Val -> 'Res>.Lambda(valueVar, Expr.Call(Expr.Var(valueVar), methodInfo, args))) + Expr.Lambda(ctxVar, Expr<'Val -> 'Res>.Lambda (valueVar, Expr.Call(Expr.Var(valueVar), methodInfo, args))) + let compiled = expr |> LeafExpressionConverter.EvaluateQuotation let exprWithVal = Expr.WithValue(compiled, typeof 'Val -> 'Res>, expr) Sync(typeof<'Val>, typeof<'Res>, exprWithVal) @@ -2125,17 +2326,18 @@ module Resolve = let private genPropertyResolve<'Val, 'Res> (typeInfo: TypeInfo) property = let valueVar = Var("value", typeof<'Val>) let ctxVar = Var("ctx", typeof) + let expr = - Expr.Lambda - (ctxVar, - Expr<'Val -> 'Res>.Lambda(valueVar, Expr.PropertyGet(Expr.Var(valueVar), property))) + Expr.Lambda(ctxVar, Expr<'Val -> 'Res>.Lambda (valueVar, Expr.PropertyGet(Expr.Var(valueVar), property))) + let compiled = expr |> LeafExpressionConverter.EvaluateQuotation let exprWithVal = Expr.WithValue(compiled, typeof 'Val -> 'Res>, expr) Sync(typeof<'Val>, typeof<'Res>, exprWithVal) - let internal defaultResolve<'Val, 'Res> (fieldName : string) : Resolve = - let typeInfo = typeof<'Val>.GetTypeInfo() + let internal defaultResolve<'Val, 'Res> (fieldName: string) : Resolve = + let typeInfo = typeof<'Val>.GetTypeInfo () let property = typeInfo.GetDeclaredProperty(fieldName, ignoreCase = true) + match property with | null -> let methodInfo = typeInfo.GetDeclaredMethod(fieldName, ignoreCase = true) @@ -2145,97 +2347,100 @@ module Resolve = module Patterns = /// Active pattern to match GraphQL type defintion with Scalar. - let (|Scalar|_|) (tdef : TypeDef) = + let (|Scalar|_|) (tdef: TypeDef) = match tdef with | :? ScalarDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with Object. - let (|Object|_|) (tdef : TypeDef) = + let (|Object|_|) (tdef: TypeDef) = match tdef with | :? ObjectDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with Interface. - let (|Interface|_|) (tdef : TypeDef) = + let (|Interface|_|) (tdef: TypeDef) = match tdef with | :? InterfaceDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with Union. - let (|Union|_|) (tdef : TypeDef) = + let (|Union|_|) (tdef: TypeDef) = match tdef with | :? UnionDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with Enum. - let (|Enum|_|) (tdef : TypeDef) = + let (|Enum|_|) (tdef: TypeDef) = match tdef with | :? EnumDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with input object. - let (|InputObject|_|) (tdef : TypeDef) = + let (|InputObject|_|) (tdef: TypeDef) = match tdef with | :? InputObjectDef as x -> Some x | _ -> None /// Active patter to match GraphQL subscription object definitions - let (|SubscriptionObject|_|) (tdef : TypeDef) = + let (|SubscriptionObject|_|) (tdef: TypeDef) = match tdef with | :? SubscriptionObjectDef as x -> Some x | _ -> None /// Active pattern to match GraphQL type defintion with List. - let (|List|_|) (tdef : TypeDef) = + let (|List|_|) (tdef: TypeDef) = match tdef with | :? ListOfDef as x -> Some x.OfType | _ -> None /// Active pattern to match GraphQL type defintion with nullable / optional types. - let (|Nullable|_|) (tdef : TypeDef) = + let (|Nullable|_|) (tdef: TypeDef) = match tdef with | :? NullableDef as x -> Some x.OfType | _ -> None /// Active pattern to match GraphQL type defintion with non-null types. - let (|NonNull|_|) (tdef : TypeDef) = + let (|NonNull|_|) (tdef: TypeDef) = match tdef with | :? NullableDef -> None | other -> Some other /// Active pattern to match GraphQL type defintion with valid input types. - let (|Input|_|) (tdef : TypeDef) = + let (|Input|_|) (tdef: TypeDef) = match tdef with | :? InputDef as i -> Some i | _ -> None /// Active pattern to match GraphQL type defintion with valid output types. - let (|Output|_|) (tdef : TypeDef) = + let (|Output|_|) (tdef: TypeDef) = match tdef with | :? OutputDef as o -> Some o | _ -> None /// Active pattern to match GraphQL type defintion with valid leaf types. - let (|Leaf|_|) (tdef : TypeDef) = + let (|Leaf|_|) (tdef: TypeDef) = match tdef with | :? LeafDef as ldef -> Some ldef | _ -> None /// Active pattern to match GraphQL type defintion with valid composite types. - let (|Composite|_|) (tdef : TypeDef) = + let (|Composite|_|) (tdef: TypeDef) = match tdef with - | :? ObjectDef | :? InterfaceDef | :? UnionDef -> Some tdef + | :? ObjectDef + | :? InterfaceDef + | :? UnionDef -> Some tdef | _ -> None /// Active pattern to match GraphQL type defintion with valid abstract types. - let (|Abstract|_|) (tdef : TypeDef) = + let (|Abstract|_|) (tdef: TypeDef) = match tdef with - | :? InterfaceDef | :? UnionDef -> Some(tdef :?> AbstractDef) + | :? InterfaceDef + | :? UnionDef -> Some(tdef :?> AbstractDef) | _ -> None - let rec private named (tdef : TypeDef) = + let rec private named (tdef: TypeDef) = match tdef with | :? NamedDef as n -> Some n | Nullable inner -> named inner @@ -2243,7 +2448,7 @@ module Patterns = | _ -> None /// Active pattern to match GraphQL type defintion with named types. - let rec (|Named|_|) (tdef : TypeDef) = named tdef + let rec (|Named|_|) (tdef: TypeDef) = named tdef [] module SchemaDefinitions = @@ -2251,7 +2456,7 @@ module SchemaDefinitions = open System.Reflection /// Tries to convert any value to int. - let private coerceIntValue (x : obj) : int option = + let private coerceIntValue (x: obj) : int option = match x with | null -> None | :? int as i -> Some i @@ -2261,34 +2466,33 @@ module SchemaDefinitions = match Int32.TryParse(s) with | true, i -> Some i | false, _ -> None - | :? bool as b -> - Some(if b then 1 - else 0) + | :? bool as b -> Some(if b then 1 else 0) | other -> try Some(System.Convert.ToInt32 other) - with _ -> None + with + | _ -> None /// Tries to convert any value to int64. - let private coerceLongValue (x : obj) : int64 option = + let private coerceLongValue (x: obj) : int64 option = match x with | null -> None - | :? int as i -> Some (int64 i) + | :? int as i -> Some(int64 i) | :? int64 as l -> Some(l) | :? double as d -> Some(int64 d) | :? string as s -> match Int64.TryParse(s) with | true, i -> Some i | false, _ -> None - | :? bool as b -> - Some(if b then 1L else 0L) + | :? bool as b -> Some(if b then 1L else 0L) | other -> try Some(System.Convert.ToInt64 other) - with _ -> None + with + | _ -> None /// Tries to convert any value to double. - let private coerceFloatValue (x : obj) : double option = + let private coerceFloatValue (x: obj) : double option = match x with | null -> None | :? int as i -> Some(double i) @@ -2298,16 +2502,15 @@ module SchemaDefinitions = match Double.TryParse(s) with | true, i -> Some i | false, _ -> None - | :? bool as b -> - Some(if b then 1. - else 0.) + | :? bool as b -> Some(if b then 1. else 0.) | other -> try Some(System.Convert.ToDouble other) - with _ -> None + with + | _ -> None /// Tries to convert any value to bool. - let private coerceBoolValue (x : obj) : bool option = + let private coerceBoolValue (x: obj) : bool option = match x with | null -> None | :? int as i -> Some(i <> 0) @@ -2321,10 +2524,11 @@ module SchemaDefinitions = | other -> try Some(System.Convert.ToBoolean other) - with _ -> None + with + | _ -> None /// Tries to convert any value to URI. - let private coerceUriValue (x : obj) : Uri option = + let private coerceUriValue (x: obj) : Uri option = match x with | null -> None | :? Uri as u -> Some u @@ -2335,7 +2539,7 @@ module SchemaDefinitions = | other -> None /// Tries to convert any value to DateTime. - let private coerceDateValue (x : obj) : DateTime option = + let private coerceDateValue (x: obj) : DateTime option = match x with | null -> None | :? DateTime as d -> Some d @@ -2346,7 +2550,7 @@ module SchemaDefinitions = | other -> None /// Tries to convert any value to Guid. - let private coerceGuidValue (x : obj) : Guid option = + let private coerceGuidValue (x: obj) : Guid option = match x with | null -> None | :? Guid as g -> Some g @@ -2357,58 +2561,56 @@ module SchemaDefinitions = | other -> None /// Check if provided obj value is an Option and extract its wrapped value as object if possible - let private (|Option|_|) (x : obj) = - if x = null then None + let private (|Option|_|) (x: obj) = + if x = null then + None else let t = x.GetType().GetTypeInfo() + if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> then t.GetDeclaredProperty("Value").GetValue(x) |> Some - else None + else + None /// Tries to convert any value to string. - let coerceStringValue (x : obj) : string option = + let coerceStringValue (x: obj) : string option = match x with | null -> None | :? string as s -> Some s - | :? bool as b -> - Some(if b then "true" - else "false") + | :? bool as b -> Some(if b then "true" else "false") | Option o -> Some(o.ToString()) | _ -> Some(x.ToString()) /// Tries to convert any value to generic type parameter. - let private coerceIDValue (x : obj) : 't option = + let private coerceIDValue (x: obj) : 't option = match x with | null -> None - | :? string as s -> Some (downcast Convert.ChangeType(s, typeof<'t>)) + | :? string as s -> Some(downcast Convert.ChangeType(s, typeof<'t>)) | Option o -> Some(downcast Convert.ChangeType(o, typeof<'t>)) | _ -> Some(downcast Convert.ChangeType(x, typeof<'t>)) /// Tries to resolve AST query input to int. let private coerceIntInput = function - | IntValue i -> Some (int i) + | IntValue i -> Some(int i) | FloatValue f -> Some(int f) | StringValue s -> match Int32.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with | true, i -> Some i | false, _ -> None - | BooleanValue b -> - Some(if b then 1 - else 0) + | BooleanValue b -> Some(if b then 1 else 0) | _ -> None /// Tries to resolve AST query input to int64. let private coerceLongInput = function - | IntValue i -> Some (int64 i) + | IntValue i -> Some(int64 i) | FloatValue f -> Some(int64 f) | StringValue s -> match Int64.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with | true, i -> Some i | false, _ -> None - | BooleanValue b -> - Some(if b then 1L else 0L) + | BooleanValue b -> Some(if b then 1L else 0L) | _ -> None /// Tries to resolve AST query input to double. @@ -2420,9 +2622,7 @@ module SchemaDefinitions = match Double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture) with | true, i -> Some i | false, _ -> None - | BooleanValue b -> - Some(if b then 1. - else 0.) + | BooleanValue b -> Some(if b then 1. else 0.) | _ -> None /// Tries to resolve AST query input to string. @@ -2431,9 +2631,7 @@ module SchemaDefinitions = | IntValue i -> Some(i.ToString(CultureInfo.InvariantCulture)) | FloatValue f -> Some(f.ToString(CultureInfo.InvariantCulture)) | StringValue s -> Some s - | BooleanValue b -> - Some(if b then "true" - else "false") + | BooleanValue b -> Some(if b then "true" else "false") | _ -> None let coerceEnumInput = @@ -2444,12 +2642,8 @@ module SchemaDefinitions = /// Tries to resolve AST query input to bool. let coerceBoolInput = function - | IntValue i -> - Some(if i = 0L then false - else true) - | FloatValue f -> - Some(if f = 0. then false - else true) + | IntValue i -> Some(if i = 0L then false else true) + | FloatValue f -> Some(if f = 0. then false else true) | StringValue s -> match Boolean.TryParse(s) with | true, i -> Some i @@ -2458,7 +2652,7 @@ module SchemaDefinitions = | _ -> None /// Tries to resolve AST query input to provided generic type. - let private coerceIdInput input : 't option= + let private coerceIdInput input : 't option = match input with | IntValue i -> Some(downcast Convert.ChangeType(i, typeof<'t>)) | StringValue s -> Some(downcast Convert.ChangeType(s, typeof<'t>)) @@ -2493,13 +2687,21 @@ module SchemaDefinitions = /// Wraps a GraphQL type definition, allowing defining field/argument /// to take option of provided value. - let Nullable(innerDef : #TypeDef<'Val>) : NullableDef<'Val> = upcast { NullableDefinition.OfType = innerDef } + let Nullable (innerDef: #TypeDef<'Val>) : NullableDef<'Val> = + upcast + { + NullableDefinition.OfType = innerDef + } /// Wraps a GraphQL type definition, allowing defining field/argument /// to take collection of provided value. - let ListOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Seq> = upcast { ListOfDefinition.OfType = innerDef } + let ListOf (innerDef: #TypeDef<'Val>) : ListOfDef<'Val, 'Seq> = + upcast + { + ListOfDefinition.OfType = innerDef + } - let private ignoreInputResolve (_ : unit) (input : 'T) = () + let private ignoreInputResolve (_: unit) (input: 'T) = () let variableOrElse other value variables = match value with @@ -2507,153 +2709,208 @@ module SchemaDefinitions = | v -> other v /// GraphQL type of int - let Int : ScalarDefinition = - { Name = "Int" - Description = - Some - "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." - CoerceInput = coerceIntInput - CoerceValue = coerceIntValue } + let Int: ScalarDefinition = + { + Name = "Int" + Description = + Some + "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." + CoerceInput = coerceIntInput + CoerceValue = coerceIntValue + } /// GraphQL type of long - let Long : ScalarDefinition = - { Name = "Long" - Description = - Some - "The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1." - CoerceInput = coerceLongInput - CoerceValue = coerceLongValue } + let Long: ScalarDefinition = + { + Name = "Long" + Description = + Some + "The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1." + CoerceInput = coerceLongInput + CoerceValue = coerceLongValue + } /// GraphQL type of boolean - let Boolean : ScalarDefinition = - { Name = "Boolean" - Description = Some "The `Boolean` scalar type represents `true` or `false`." - CoerceInput = coerceBoolInput - CoerceValue = coerceBoolValue } + let Boolean: ScalarDefinition = + { + Name = "Boolean" + Description = Some "The `Boolean` scalar type represents `true` or `false`." + CoerceInput = coerceBoolInput + CoerceValue = coerceBoolValue + } /// GraphQL type of float - let Float : ScalarDefinition = - { Name = "Float" - Description = - Some - "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." - CoerceInput = coerceFloatInput - CoerceValue = coerceFloatValue } + let Float: ScalarDefinition = + { + Name = "Float" + Description = + Some + "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." + CoerceInput = coerceFloatInput + CoerceValue = coerceFloatValue + } /// GraphQL type of string - let String : ScalarDefinition = - { Name = "String" - Description = - Some - "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." - CoerceInput = coerceStringInput - CoerceValue = coerceStringValue } + let String: ScalarDefinition = + { + Name = "String" + + Description = + Some + "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." + CoerceInput = coerceStringInput + CoerceValue = coerceStringValue + } /// GraphQL type for custom identifier let ID<'Val> : ScalarDefinition<'Val> = - { Name = "ID" - Description = - Some - "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID." - CoerceInput = coerceIdInput - CoerceValue = coerceIDValue } - - let Obj : ScalarDefinition = { + { + Name = "ID" + Description = + Some + "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID." + CoerceInput = coerceIdInput + CoerceValue = coerceIDValue + } + + let Obj: ScalarDefinition = + { Name = "Object" Description = - Some - "The `Object` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." - CoerceInput = (fun (o: Value) -> Some (o:>obj)) - CoerceValue = (fun (o: obj) -> Some (o)) + Some + "The `Object` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." + CoerceInput = (fun (o: Value) -> Some(o :> obj)) + CoerceValue = (fun (o: obj) -> Some(o)) } /// GraphQL type for System.Uri - let Uri : ScalarDefinition = - { Name = "URI" - Description = - Some - "The `URI` scalar type represents a string resource identifier compatible with URI standard. The URI type appears in a JSON response as a String." - CoerceInput = coerceUriInput - CoerceValue = coerceUriValue } + let Uri: ScalarDefinition = + { + Name = "URI" + Description = + Some + "The `URI` scalar type represents a string resource identifier compatible with URI standard. The URI type appears in a JSON response as a String." + CoerceInput = coerceUriInput + CoerceValue = coerceUriValue + } /// GraphQL type for System.DateTime - let Date : ScalarDefinition = - { Name = "Date" - Description = - Some - "The `Date` scalar type represents a Date value with Time component. The Date type appears in a JSON response as a String representation compatible with ISO-8601 format." - CoerceInput = coerceDateInput - CoerceValue = coerceDateValue } + let Date: ScalarDefinition = + { + Name = "Date" + Description = + Some + "The `Date` scalar type represents a Date value with Time component. The Date type appears in a JSON response as a String representation compatible with ISO-8601 format." + CoerceInput = coerceDateInput + CoerceValue = coerceDateValue + } /// GraphQL type for System.Guid - let Guid : ScalarDefinition = - { Name = "Guid" - Description = - Some - "The `Guid` scalar type represents a Globaly Unique Identifier value. It's a 128-bit long byte key, that can be serialized to string." - CoerceInput = coerceGuidInput - CoerceValue = coerceGuidValue } + let Guid: ScalarDefinition = + { + Name = "Guid" + Description = + Some + "The `Guid` scalar type represents a Globally Unique Identifier value. It's a 128-bit long byte key, that can be serialized to string." + CoerceInput = coerceGuidInput + CoerceValue = coerceGuidValue + } /// GraphQL @include directive. - let IncludeDirective : DirectiveDef = - { Name = "include" - Description = - Some "Directs the executor to include this field or fragment only when the `if` argument is true." - Locations = - DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT - Args = - [| { InputFieldDefinition.Name = "if" - Description = Some "Included when true." - TypeDef = Boolean - DefaultValue = None - ExecuteInput = - variableOrElse (coerceBoolInput - >> Option.map box - >> Option.toObj) } |] } + let IncludeDirective: DirectiveDefinition = + { + Name = "include" + Description = Some "Directs the executor to include this field or fragment only when the `if` argument is true." + Locations = + ExecutableDirectiveLocation( + ExecutableDirectiveLocation.FIELD + ||| ExecutableDirectiveLocation.FRAGMENT_SPREAD + ||| ExecutableDirectiveLocation.INLINE_FRAGMENT + ) + Arguments = + [ + { + InputFieldDefinition.Name = "if" + Description = Some "Included when true." + Type = Boolean + DefaultValue = None + ExecuteInput = variableOrElse (coerceBoolInput >> Option.map box >> Option.toObj) + } + ] + } /// GraphQL @skip directive. - let SkipDirective : DirectiveDef = - { Name = "skip" - Description = Some "Directs the executor to skip this field or fragment when the `if` argument is true." - Locations = - DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT - Args = - [| { InputFieldDefinition.Name = "if" - Description = Some "Skipped when true." - TypeDef = Boolean - DefaultValue = None - ExecuteInput = - variableOrElse (coerceBoolInput - >> Option.map box - >> Option.toObj) } |] } + let SkipDirective: DirectiveDefinition = + { + Name = "skip" + Description = Some "Directs the executor to skip this field or fragment when the `if` argument is true." + Locations = + ExecutableDirectiveLocation( + ExecutableDirectiveLocation.FIELD + ||| ExecutableDirectiveLocation.FRAGMENT_SPREAD + ||| ExecutableDirectiveLocation.INLINE_FRAGMENT + ) + Arguments = + [ + { + InputFieldDefinition.Name = "if" + Description = Some "Skipped when true." + Type = Boolean + DefaultValue = None + ExecuteInput = variableOrElse (coerceBoolInput >> Option.map box >> Option.toObj) + } + ] + } /// GraphQL @defer directive. - let DeferDirective : DirectiveDef = - { Name = "defer" - Description = Some "Defers the resolution of this field or fragment" - Locations = - DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT ||| DirectiveLocation.FRAGMENT_DEFINITION - Args = [||] } + let DeferDirective: DirectiveDefinition = + { + Name = "defer" + Description = Some "Defers the resolution of this field or fragment" + Locations = + ExecutableDirectiveLocation( + ExecutableDirectiveLocation.FIELD + ||| ExecutableDirectiveLocation.FRAGMENT_SPREAD + ||| ExecutableDirectiveLocation.INLINE_FRAGMENT + ||| ExecutableDirectiveLocation.FRAGMENT_DEFINITION + ) + Arguments = [] + } /// GraphQL @stream directive. - let StreamDirective : DirectiveDef = - { Name = "stream" - Description = Some "Streams the resolution of this field or fragment" - Locations = - DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT ||| DirectiveLocation.FRAGMENT_DEFINITION - Args = [||] } + let StreamDirective: DirectiveDefinition = + { + Name = "stream" + Description = Some "Streams the resolution of this field or fragment" + Locations = + ExecutableDirectiveLocation( + ExecutableDirectiveLocation.FIELD + ||| ExecutableDirectiveLocation.FRAGMENT_SPREAD + ||| ExecutableDirectiveLocation.INLINE_FRAGMENT + ||| ExecutableDirectiveLocation.FRAGMENT_DEFINITION + ) + Arguments = [] + } /// GraphQL @live directive. - let LiveDirective : DirectiveDef = - { Name = "live" - Description = Some "Subscribes for live updates of this field or fragment" - Locations = - DirectiveLocation.FIELD ||| DirectiveLocation.FRAGMENT_SPREAD ||| DirectiveLocation.INLINE_FRAGMENT ||| DirectiveLocation.FRAGMENT_DEFINITION - Args = [||] } - - let internal matchParameters (methodInfo : MethodInfo) (ctx : ResolveFieldContext) = + let LiveDirective: DirectiveDefinition = + { + Name = "live" + Description = Some "Subscribes for live updates of this field or fragment" + Locations = + ExecutableDirectiveLocation( + ExecutableDirectiveLocation.FIELD + ||| ExecutableDirectiveLocation.FRAGMENT_SPREAD + ||| ExecutableDirectiveLocation.INLINE_FRAGMENT + ||| ExecutableDirectiveLocation.FRAGMENT_DEFINITION + ) + Arguments = [] + } + + let internal matchParameters (methodInfo: MethodInfo) (ctx: ResolveFieldContext) = methodInfo.GetParameters() |> Array.map (fun param -> ctx.Arg(param.Name)) - let inline internal strip (fn : 'In -> 'Out) : obj -> obj = fun i -> upcast fn (i :?> 'In) + + let inline internal strip (fn: 'In -> 'Out) : obj -> obj = fun i -> upcast fn (i :?> 'In) /// Common space for all definition helper methods. [] @@ -2666,12 +2923,19 @@ module SchemaDefinitions = /// Function used to resolve .NET object from GraphQL query AST. /// Function used to cross cast to .NET types. /// Optional scalar description. Usefull for generating documentation. - static member Scalar(name : string, coerceInput : Value -> 'T option, - coerceValue : obj -> 'T option, ?description : string) : ScalarDefinition<'T> = - { Name = name - Description = description - CoerceInput = coerceInput - CoerceValue = coerceValue } + static member Scalar + ( + name: string, + coerceInput: Value -> 'T option, + coerceValue: obj -> 'T option, + ?description: string + ) : ScalarDefinition<'T> = + { + Name = name + Description = description + CoerceInput = coerceInput + CoerceValue = coerceValue + } /// /// Creates GraphQL type definition for user defined enums. @@ -2679,10 +2943,13 @@ module SchemaDefinitions = /// Type name. Must be unique in scope of the current schema. /// List of enum value cases. /// Optional enum description. Usefull for generating documentation. - static member Enum(name : string, options : EnumValue<'Val> list, ?description : string) : EnumDef<'Val> = - upcast { EnumDefinition.Name = name - Description = description - Options = options |> List.toArray } + static member Enum(name: string, options: EnumValue<'Val> list, ?description: string) : EnumDef<'Val> = + upcast + { + EnumDefinition.Name = name + Description = description + Options = options |> List.toArray + } /// /// Creates a single enum option to be used as argument in . @@ -2694,11 +2961,13 @@ module SchemaDefinitions = /// /// Optional enum value description. Usefull for generating documentation. /// If set, marks an enum value as deprecated. - static member EnumValue(name : string, value : 'Val, ?description : string, ?deprecationReason : string) : EnumValue<'Val> = - { Name = name - Description = description - Value = value - DeprecationReason = deprecationReason } + static member EnumValue(name: string, value: 'Val, ?description: string, ?deprecationReason: string) : EnumValue<'Val> = + { + Name = name + Description = description + Value = value + DeprecationReason = deprecationReason + } /// /// Creates GraphQL custom output object type. It can be used as a valid output but not an input object @@ -2715,16 +2984,22 @@ module SchemaDefinitions = /// /// Optional function used to determine if provided .NET object instance matches current object definition. /// - static member Object(name : string, fieldsFn : unit -> FieldDef<'Val> list, ?description : string, - ?interfaces : InterfaceDef list, ?isTypeOf : obj -> bool) : ObjectDef<'Val> = - upcast { ObjectDefinition.Name = name - Description = description - FieldsFn = - lazy (fieldsFn() - |> List.map (fun f -> f.Name, f) - |> Map.ofList) - Implements = defaultArg (Option.map List.toArray interfaces) [||] - IsTypeOf = isTypeOf } + static member Object + ( + name: string, + fieldsFn: unit -> FieldDef<'Val> list, + ?description: string, + ?interfaces: InterfaceDef list, + ?isTypeOf: obj -> bool + ) : ObjectDef<'Val> = + upcast + { + ObjectDefinition.Name = name + Description = description + FieldsFn = lazy (fieldsFn () |> List.map (fun f -> f.Name, f) |> Map.ofList) + Implements = defaultArg (Option.map List.toArray interfaces) [||] + IsTypeOf = isTypeOf + } /// /// Creates GraphQL custom output object type. It can be used as a valid output but not an input object @@ -2739,16 +3014,22 @@ module SchemaDefinitions = /// /// Optional function used to determine if provided .NET object instance matches current object definition. /// - static member Object(name : string, fields : FieldDef<'Val> list, ?description : string, - ?interfaces : InterfaceDef list, ?isTypeOf : obj -> bool) : ObjectDef<'Val> = - upcast { ObjectDefinition.Name = name - Description = description - FieldsFn = - lazy (fields - |> List.map (fun f -> f.Name, f) - |> Map.ofList) - Implements = defaultArg (Option.map List.toArray interfaces) [||] - IsTypeOf = isTypeOf } + static member Object + ( + name: string, + fields: FieldDef<'Val> list, + ?description: string, + ?interfaces: InterfaceDef list, + ?isTypeOf: obj -> bool + ) : ObjectDef<'Val> = + upcast + { + ObjectDefinition.Name = name + Description = description + FieldsFn = lazy (fields |> List.map (fun f -> f.Name, f) |> Map.ofList) + Implements = defaultArg (Option.map List.toArray interfaces) [||] + IsTypeOf = isTypeOf + } /// /// Creates a custom GraphQL input object type. Unlike GraphQL objects, input objects are valid input types, @@ -2760,10 +3041,17 @@ module SchemaDefinitions = /// Function which generates a list of input fields defined by the current input object. Usefull, when object defines recursive dependencies. /// /// Optional input object description. Usefull for generating documentation. - static member InputObject(name : string, fieldsFn : unit -> InputFieldDef list, ?description : string) : InputObjectDefinition<'Out> = - { Name = name - FieldsFn = fun () -> fieldsFn() |> List.toArray - Description = description } + static member InputObject + ( + name: string, + fieldsFn: unit -> InputFieldDef list, + ?description: string + ) : InputObjectDefinition<'Out> = + { + Name = name + FieldsFn = fun () -> fieldsFn () |> List.toArray + Description = description + } /// /// Creates a custom GraphQL input object type. Unlike GraphQL objects, input objects are valid input types, @@ -2773,10 +3061,12 @@ module SchemaDefinitions = /// Type name. Must be unique in scope of the current schema. /// List of input fields defined by the current input object. /// Optional input object description. Usefull for generating documentation. - static member InputObject(name : string, fields : InputFieldDef list, ?description : string) : InputObjectDefinition<'Out> = - { Name = name - Description = description - FieldsFn = fun () -> fields |> List.toArray } + static member InputObject(name: string, fields: InputFieldDef list, ?description: string) : InputObjectDefinition<'Out> = + { + Name = name + Description = description + FieldsFn = fun () -> fields |> List.toArray + } /// /// Creates the top level subscription object that holds all of the possible subscriptions as fields. @@ -2784,10 +3074,17 @@ module SchemaDefinitions = /// Top level name. Must be unique in scope of the current schema. /// List of subscription fields to be defined for the schema. /// Optional description. Usefull for generating documentation. - static member SubscriptionObject<'Val>(name: string, fields: SubscriptionFieldDef<'Val> list, ?description: string):SubscriptionObjectDefinition<'Val> = - { Name = name - Fields = (fields |> List.map (fun f -> f.Name, f) |> Map.ofList) - Description = description } + static member SubscriptionObject<'Val> + ( + name: string, + fields: SubscriptionFieldDef<'Val> list, + ?description: string + ) : SubscriptionObjectDefinition<'Val> = + { + Name = name + Fields = (fields |> List.map (fun f -> f.Name, f) |> Map.ofList) + Description = description + } /// /// Creates field defined inside object types with automatically generated field resolve function. @@ -2798,15 +3095,24 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// Optional list of arguments used to parametrize field resolution. /// If set, marks current field as deprecated. - static member AutoField(name : string, typedef : #OutputDef<'Res>, ?description: string, ?args: InputFieldDef list, ?deprecationReason: string) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = description - TypeDef = typedef - Resolve = Resolve.defaultResolve<'Val, 'Res> name - Args = defaultArg args [] |> Array.ofList - DeprecationReason = deprecationReason - Metadata = Metadata.Empty - } + static member AutoField + ( + name: string, + typedef: #OutputDef<'Res>, + ?description: string, + ?args: InputFieldDef list, + ?deprecationReason: string + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = description + TypeDef = typedef + Resolve = Resolve.defaultResolve<'Val, 'Res> name + Args = defaultArg args [] |> Array.ofList + DeprecationReason = deprecationReason + Metadata = Metadata.Empty + } /// /// Creates field defined inside interfaces. When used for objects may cause runtime exceptions due to @@ -2814,15 +3120,17 @@ module SchemaDefinitions = /// /// Field name. Must be unique in scope of the defining object. /// GraphQL type definition of the current field's type. - static member Field(name : string, typedef : #OutputDef<'Res>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = None - TypeDef = typedef - Resolve = Undefined - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty - } + static member Field(name: string, typedef: #OutputDef<'Res>) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = None + TypeDef = typedef + Resolve = Undefined + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type. @@ -2830,15 +3138,22 @@ module SchemaDefinitions = /// Field name. Must be unique in scope of the defining object. /// GraphQL type definition of the current field's type. /// Expression used to resolve value from defining object. - static member Field(name : string, typedef : #OutputDef<'Res>, - [] resolve : Expr 'Val -> 'Res>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = None - TypeDef = typedef - Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty } + static member Field + ( + name: string, + typedef: #OutputDef<'Res>, + [] resolve: Expr 'Val -> 'Res> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = None + TypeDef = typedef + Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type. @@ -2847,16 +3162,24 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// Optional field description. Usefull for generating documentation. /// Expression used to resolve value from defining object. - static member Field(name : string, typedef : #OutputDef<'Res>, description : string, - [] resolve : Expr 'Val -> 'Res>) : FieldDef<'Val> = - - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty } + static member Field + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + [] resolve: Expr 'Val -> 'Res> + ) : FieldDef<'Val> = + + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type. @@ -2865,15 +3188,23 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// List of field arguments used to parametrize resolve expression output. /// Expression used to resolve value from defining object. - static member Field(name : string, typedef : #OutputDef<'Res>, args : InputFieldDef list, - [] resolve : Expr 'Val -> 'Res>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = None - TypeDef = typedef - Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) - Args = args |> List.toArray - DeprecationReason = None - Metadata = Metadata.Empty } + static member Field + ( + name: string, + typedef: #OutputDef<'Res>, + args: InputFieldDef list, + [] resolve: Expr 'Val -> 'Res> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = None + TypeDef = typedef + Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) + Args = args |> List.toArray + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type. @@ -2883,15 +3214,24 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// List of field arguments used to parametrize resolve expression output. /// Expression used to resolve value from defining object. - static member Field(name : string, typedef : #OutputDef<'Res>, description : string, args : InputFieldDef list, - [] resolve : Expr 'Val -> 'Res>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) - Args = args |> List.toArray - DeprecationReason = None - Metadata = Metadata.Empty } + static member Field + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + args: InputFieldDef list, + [] resolve: Expr 'Val -> 'Res> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) + Args = args |> List.toArray + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type. Fields is marked as deprecated. @@ -2902,16 +3242,25 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// Expression used to resolve value from defining object. /// Deprecation reason. - static member Field(name : string, typedef : #OutputDef<'Res>, description : string, args : InputFieldDef list, - [] resolve : Expr 'Val -> 'Res>, - deprecationReason : string) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) - Args = args |> List.toArray - DeprecationReason = Some deprecationReason - Metadata = Metadata.Empty } + static member Field + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + args: InputFieldDef list, + [] resolve: Expr 'Val -> 'Res>, + deprecationReason: string + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Sync(typeof<'Val>, typeof<'Res>, resolve) + Args = args |> List.toArray + DeprecationReason = Some deprecationReason + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type with asynchronously resolved value. @@ -2919,15 +3268,22 @@ module SchemaDefinitions = /// Field name. Must be unique in scope of the defining object. /// GraphQL type definition of the current field's type. /// Expression used to resolve value from defining object. - static member AsyncField(name : string, typedef : #OutputDef<'Res>, - [] resolve : Expr 'Val -> Async<'Res>>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = None - TypeDef = typedef - Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty } + static member AsyncField + ( + name: string, + typedef: #OutputDef<'Res>, + [] resolve: Expr 'Val -> Async<'Res>> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = None + TypeDef = typedef + Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type with asynchronously resolved value. @@ -2936,15 +3292,23 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// Optional field description. Usefull for generating documentation. /// Expression used to resolve value from defining object. - static member AsyncField(name : string, typedef : #OutputDef<'Res>, description : string, - [] resolve : Expr 'Val -> Async<'Res>>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty } + static member AsyncField + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + [] resolve: Expr 'Val -> Async<'Res>> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type with asynchronously resolved value. @@ -2954,16 +3318,24 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// List of field arguments used to parametrize resolve expression output. /// Expression used to resolve value from defining object. - static member AsyncField(name : string, typedef : #OutputDef<'Res>, description : string, - args : InputFieldDef list, - [] resolve : Expr 'Val -> Async<'Res>>) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) - Args = args |> List.toArray - DeprecationReason = None - Metadata = Metadata.Empty } + static member AsyncField + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + args: InputFieldDef list, + [] resolve: Expr 'Val -> Async<'Res>> + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) + Args = args |> List.toArray + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates field defined inside object type with asynchronously resolved value. Fields is marked as deprecated. @@ -2974,31 +3346,42 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// Expression used to resolve value from defining object. /// Deprecation reason. - static member AsyncField(name : string, typedef : #OutputDef<'Res>, description : string, - args : InputFieldDef list, - [] resolve : Expr 'Val -> Async<'Res>>, - deprecationReason : string) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = Some description - TypeDef = typedef - Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) - Args = args |> List.toArray - DeprecationReason = Some deprecationReason - Metadata = Metadata.Empty } + static member AsyncField + ( + name: string, + typedef: #OutputDef<'Res>, + description: string, + args: InputFieldDef list, + [] resolve: Expr 'Val -> Async<'Res>>, + deprecationReason: string + ) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = Some description + TypeDef = typedef + Resolve = Async(typeof<'Val>, typeof<'Res>, resolve) + Args = args |> List.toArray + DeprecationReason = Some deprecationReason + Metadata = Metadata.Empty + } /// /// Creates a custom defined field using a custom field execution function. /// /// Field name. Must be unique in scope of the defining object. /// Expression used to execute the field. - static member CustomField(name : string, [] execField : Expr) : FieldDef<'Val> = - upcast { FieldDefinition.Name = name - Description = None - TypeDef = Obj - Resolve = ResolveExpr(execField) - Args = [||] - DeprecationReason = None - Metadata = Metadata.Empty } + static member CustomField(name: string, [] execField: Expr) : FieldDef<'Val> = + upcast + { + FieldDefinition.Name = name + Description = None + TypeDef = Obj + Resolve = ResolveExpr(execField) + Args = [||] + DeprecationReason = None + Metadata = Metadata.Empty + } /// /// Creates a subscription field inside object type. @@ -3007,17 +3390,25 @@ module SchemaDefinitions = /// GraphQL type definition of the root field's type. /// GraphQL type definition of the current field's type. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - [] filter: Expr 'Root -> 'Input -> 'Output option>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = None - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + [] filter: Expr 'Root -> 'Input -> 'Output option> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = None + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type. @@ -3027,18 +3418,26 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - [] filter: Expr 'Root -> 'Input -> 'Output option>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = None - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + [] filter: Expr 'Root -> 'Input -> 'Output option>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = None + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type. @@ -3048,18 +3447,26 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// Optional field description. Usefull for generating documentation. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - [] filter: Expr 'Root -> 'Input -> 'Output option>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + [] filter: Expr 'Root -> 'Input -> 'Output option> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type. @@ -3070,19 +3477,27 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - [] filter: Expr 'Root -> 'Input -> 'Output option>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + [] filter: Expr 'Root -> 'Input -> 'Output option>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type. @@ -3093,19 +3508,27 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> 'Output option>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = args |> List.toArray - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> 'Output option> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = args |> List.toArray + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type. @@ -3117,20 +3540,28 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> 'Output option>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = args |> List.toArray - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> 'Output option>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = args |> List.toArray + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type. Field is marked as deprecated. @@ -3142,20 +3573,28 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// Deprecation reason. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> 'Output option>, - deprecationReason : string): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = Some deprecationReason - Args = args |> List.toArray - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> 'Output option>, + deprecationReason: string + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = Some deprecationReason + Args = args |> List.toArray + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type. @@ -3168,21 +3607,29 @@ module SchemaDefinitions = /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. /// Deprecation reason. - static member SubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> 'Output option>, - tagsResolver : TagsResolver, - deprecationReason : string): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = Some deprecationReason - Args = args |> List.toArray - Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member SubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> 'Output option>, + tagsResolver: TagsResolver, + deprecationReason: string + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = Some deprecationReason + Args = args |> List.toArray + Filter = Resolve.Filter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3191,17 +3638,25 @@ module SchemaDefinitions = /// GraphQL type definition of the root field's type. /// GraphQL type definition of the current field's type. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = None - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = None + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3211,18 +3666,26 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = None - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = None + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3232,18 +3695,26 @@ module SchemaDefinitions = /// GraphQL type definition of the current field's type. /// Optional field description. Usefull for generating documentation. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3254,19 +3725,27 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = [||] - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = [||] + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3277,19 +3756,27 @@ module SchemaDefinitions = /// Optional field description. Usefull for generating documentation. /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = args |> List.toArray - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>> + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = args |> List.toArray + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3301,20 +3788,28 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, - tagsResolver : TagsResolver): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = None - Args = args |> List.toArray - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, + tagsResolver: TagsResolver + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = None + Args = args |> List.toArray + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. Field is marked as deprecated. @@ -3326,20 +3821,28 @@ module SchemaDefinitions = /// List of field arguments used to parametrize resolve expression output. /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// Deprecation reason. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, - deprecationReason : string): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = Some deprecationReason - Args = args |> List.toArray - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = fun _ -> Seq.empty } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, + deprecationReason: string + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = Some deprecationReason + Args = args |> List.toArray + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = fun _ -> Seq.empty + } /// /// Creates a subscription field inside object type, with asynchronously resolved value. @@ -3352,21 +3855,29 @@ module SchemaDefinitions = /// A filter function which decides if the field should be published to clients or not, by returning it as Some or None. /// A function that resolves subscription tags, used to choose which filter functions will be used when publishing to subscribers. /// Deprecation reason. - static member AsyncSubscriptionField(name: string, rootdef: #OutputDef<'Root>, outputdef: #OutputDef<'Output>, - description: string, - args: InputFieldDef list, - [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, - tagsResolver : TagsResolver, - deprecationReason : string): SubscriptionFieldDef<'Root, 'Input, 'Output> = - upcast { Name = name - Description = Some description - RootTypeDef = rootdef - OutputTypeDef = outputdef - DeprecationReason = Some deprecationReason - Args = args |> List.toArray - Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) - Metadata = Metadata.Empty - TagsResolver = tagsResolver } + static member AsyncSubscriptionField + ( + name: string, + rootdef: #OutputDef<'Root>, + outputdef: #OutputDef<'Output>, + description: string, + args: InputFieldDef list, + [] filter: Expr 'Root -> 'Input -> Async<'Output option>>, + tagsResolver: TagsResolver, + deprecationReason: string + ) : SubscriptionFieldDef<'Root, 'Input, 'Output> = + upcast + { + Name = name + Description = Some description + RootTypeDef = rootdef + OutputTypeDef = outputdef + DeprecationReason = Some deprecationReason + Args = args |> List.toArray + Filter = Resolve.AsyncFilter(typeof<'Root>, typeof<'Input>, typeof<'Output>, filter) + Metadata = Metadata.Empty + TagsResolver = tagsResolver + } /// @@ -3379,12 +3890,15 @@ module SchemaDefinitions = /// GraphQL type definition of the current input type /// If defined, this value will be used when no matching input has been provided by the requester. /// Optional input description. Usefull for generating documentation. - static member Input(name : string, typedef : #InputDef<'In>, ?defaultValue : 'In, ?description : string) : InputFieldDef = - upcast { InputFieldDefinition.Name = name - Description = description - TypeDef = typedef - DefaultValue = defaultValue - ExecuteInput = Unchecked.defaultof } + static member Input(name: string, typedef: #InputDef<'In>, ?defaultValue: 'In, ?description: string) : InputFieldDef = + upcast + { + InputFieldDefinition.Name = name + Description = description + TypeDef = typedef + DefaultValue = defaultValue + ExecuteInput = Unchecked.defaultof + } /// /// Creates a custom GraphQL interface type. It's needs to be implemented by object types and should not be used alone. @@ -3395,12 +3909,20 @@ module SchemaDefinitions = /// /// Optional input description. Usefull for generating documentation. /// Optional function used to resolve actual Object definition of the .NET object provided as an input. - static member Interface(name : string, fieldsFn : unit -> FieldDef<'Val> list, ?description : string, - ?resolveType : obj -> ObjectDef) : InterfaceDef<'Val> = - upcast { InterfaceDefinition.Name = name - Description = description - FieldsFn = fun () -> fieldsFn() |> List.toArray - ResolveType = resolveType } + static member Interface + ( + name: string, + fieldsFn: unit -> FieldDef<'Val> list, + ?description: string, + ?resolveType: obj -> ObjectDef + ) : InterfaceDef<'Val> = + upcast + { + InterfaceDefinition.Name = name + Description = description + FieldsFn = fun () -> fieldsFn () |> List.toArray + ResolveType = resolveType + } /// /// Creates a custom GraphQL interface type. It's needs to be implemented by object types and should not be used alone. @@ -3409,12 +3931,20 @@ module SchemaDefinitions = /// List of fields defined by the current interface. /// Optional input description. Usefull for generating documentation. /// Optional function used to resolve actual Object definition of the .NET object provided as an input. - static member Interface(name : string, fields : FieldDef<'Val> list, ?description : string, - ?resolveType : obj -> ObjectDef) : InterfaceDef<'Val> = - upcast { InterfaceDefinition.Name = name - Description = description - FieldsFn = fun () -> fields |> List.toArray - ResolveType = resolveType } + static member Interface + ( + name: string, + fields: FieldDef<'Val> list, + ?description: string, + ?resolveType: obj -> ObjectDef + ) : InterfaceDef<'Val> = + upcast + { + InterfaceDefinition.Name = name + Description = description + FieldsFn = fun () -> fields |> List.toArray + ResolveType = resolveType + } /// /// Creates a custom GraphQL union type, materialized as one of the types defined. It can be used as interface/object type field. @@ -3426,10 +3956,19 @@ module SchemaDefinitions = /// Given F# discriminated union as input, returns .NET object valid with one of the defined GraphQL union cases. /// Resolves an Object definition of one of possible types, give input object. /// Optional union description. Usefull for generating documentation. - static member Union(name : string, options : ObjectDef list, resolveValue : 'In -> 'Out, - ?resolveType : 'In -> ObjectDef, ?description : string) : UnionDef<'In> = - upcast { UnionDefinition.Name = name - Description = description - Options = options |> List.toArray - ResolveType = resolveType - ResolveValue = resolveValue } + static member Union + ( + name: string, + options: ObjectDef list, + resolveValue: 'In -> 'Out, + ?resolveType: 'In -> ObjectDef, + ?description: string + ) : UnionDef<'In> = + upcast + { + UnionDefinition.Name = name + Description = description + Options = options |> List.toArray + ResolveType = resolveType + ResolveValue = resolveValue + } From bb27afdb4a6476fca931850f32989317131df377 Mon Sep 17 00:00:00 2001 From: Henrik Date: Sat, 10 Sep 2022 21:14:52 +0200 Subject: [PATCH 9/9] Fixing all warnings --- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 68 +++++++++----------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index f168b1b71..9fdefc9fc 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -43,7 +43,7 @@ module Introspection = Args: IntrospectionInputVal [] } - /// Introspection descriptor of a GraphQL type defintion. + /// Introspection descriptor of a GraphQL type definition. and IntrospectionType = { /// Which kind category current type belongs to. @@ -55,7 +55,7 @@ module Introspection = /// Array of field descriptors defined within current type. /// Only present for Object and Interface types. Fields: IntrospectionField [] option - /// Array of interfaces implemented by output object type defintion. + /// Array of interfaces implemented by output object type definition. Interfaces: IntrospectionTypeRef [] option /// Array of type references being possible implementation of current type. /// Only present for Union types (list of union cases) and Interface types @@ -234,7 +234,7 @@ module Introspection = } /// - /// Constructs an introspection type reference for any named type defintion + /// Constructs an introspection type reference for any named type definition /// (any type other than List or NonNull) with unique name included. /// /// Introspection type descriptor to construct reference from. @@ -483,7 +483,7 @@ and FieldExecuteCompiler = FieldDef -> ExecuteField /// A field execute map object. /// Field execute maps are mutable objects built to compile fields at runtime. and FieldExecuteMap(compiler: FieldExecuteCompiler) = - let map = new Dictionary() + let map = Dictionary() let getKey typeName fieldName = if List.exists ((=) fieldName) [ "__schema"; "__type"; "__typename" ] then @@ -554,7 +554,6 @@ and FieldExecuteMap(compiler: FieldExecuteCompiler) = /// a common root. and TypeDef = interface - /// Return .NET CLR type associated with current type definition. abstract Type: Type @@ -921,7 +920,7 @@ and ExecutionPlan = ValidationResult: ValidationResult } member x.Item - with get (id) = x.Fields |> List.find (fun f -> f.Identifier = id) + with get id = x.Fields |> List.find (fun f -> f.Identifier = id) /// Execution context of the current GraphQL operation. It contains a full /// knowledge about which fields will be accessed, what types are associated @@ -984,12 +983,8 @@ and ResolveFieldContext = | Some found -> downcast found | None -> raise ( - System.Collections.Generic.KeyNotFoundException( - sprintf - "Argument '%s' was not provided within context of a field '%s'. Check if it was supplied within GraphQL query." - name - x.ExecutionInfo.Identifier - ) + KeyNotFoundException + $"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. @@ -1063,8 +1058,8 @@ and [] internal FieldDefinition<'Val, 'Res> = override x.GetHashCode() = let mutable hash = x.Name.GetHashCode() - hash <- (hash * 397) ^^^ (x.TypeDef.GetHashCode()) - hash <- (hash * 397) ^^^ (x.Args.GetHashCode()) + hash <- (hash * 397) ^^^ x.TypeDef.GetHashCode() + hash <- (hash * 397) ^^^ x.Args.GetHashCode() hash override x.ToString() = @@ -1522,7 +1517,7 @@ and [] internal UnionDefinition<'In, 'Out> = override x.GetHashCode() = let mutable hash = x.Name.GetHashCode() - hash <- (hash * 397) ^^^ (x.Options.GetHashCode()) + hash <- (hash * 397) ^^^ x.Options.GetHashCode() hash override x.ToString() = x.Name + "!" @@ -1739,12 +1734,12 @@ and [] InputFieldDefinition<'In> = override x.GetHashCode() = let mutable hash = x.Name.GetHashCode() - hash <- (hash * 397) ^^^ (x.TypeDef.GetHashCode()) + hash <- (hash * 397) ^^^ x.TypeDef.GetHashCode() hash override x.ToString() = x.Name + ": " + x.TypeDef.ToString() -and Tag = System.IComparable +and Tag = IComparable and TagsResolver = ResolveFieldContext -> Tag seq @@ -1811,8 +1806,8 @@ and [] SubscriptionFieldDefinition<'Root, 'Input, override x.GetHashCode() = let mutable hash = x.Name.GetHashCode() - hash <- (hash * 397) ^^^ (x.TypeDef.GetHashCode()) - hash <- (hash * 397) ^^^ (x.Args.GetHashCode()) + hash <- (hash * 397) ^^^ x.TypeDef.GetHashCode() + hash <- (hash * 397) ^^^ x.Args.GetHashCode() hash override x.ToString() = @@ -1918,7 +1913,7 @@ and Metadata(data: Map) = /// The key to be used to search information for. member __.TryFind<'Value>(key: string) = if data.ContainsKey key then data.Item key :?> 'Value |> Some else None - override __.ToString() = sprintf "%A" data + override __.ToString() = $"%A{data}" /// Map of types of an ISchema. /// The map of types is used to plan and execute queries. @@ -1957,7 +1952,7 @@ and TypeMap() = let add name def overwrite = if not (map.ContainsKey(name)) then map.Add(name, def) - elif overwrite then map.[name] <- def + elif overwrite then map[name] <- def let asNamed x = match named x with @@ -2061,7 +2056,7 @@ and TypeMap() = None else match map.TryGetValue(name) with - | (true, item) -> Some item + | true, item -> Some item | _ -> None /// @@ -2209,14 +2204,14 @@ module Resolve = let private boxifyFilter<'Root, 'Input, 'Output> (f: ResolveFieldContext -> 'Root -> 'Input -> 'Output option) : ResolveFieldContext -> obj -> obj -> obj option = - <@@ fun ctx (r: obj) (i: obj) -> f ctx (r :?> 'Root) (i :?> 'Input) |> Option.map (box) @@> + <@@ fun ctx (r: obj) (i: obj) -> f ctx (r :?> 'Root) (i :?> 'Input) |> Option.map box @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox let private boxifyAsyncFilter<'Root, 'Input, 'Output> (f: ResolveFieldContext -> 'Root -> 'Input -> Async<'Output option>) : ResolveFieldContext -> obj -> obj -> Async = - <@@ fun ctx (r: obj) (i: obj) -> async.Bind(f ctx (r :?> 'Root) (i :?> 'Input), async.Return << Option.map (box)) @@> + <@@ fun ctx (r: obj) (i: obj) -> async.Bind(f ctx (r :?> 'Root) (i :?> 'Input), async.Return << Option.map box) @@> |> LeafExpressionConverter.EvaluateQuotation |> unbox @@ -2258,24 +2253,24 @@ module Resolve = let private boxifyExpr expr : ResolveFieldContext -> obj -> obj = match unwrapExpr expr with | resolver, FSharpFunc (_, FSharpFunc (d, c)) -> resolveUntyped resolver d c runtimeBoxify - | resolver, _ -> failwithf "Unsupported signature for Resolve %A" (resolver.GetType()) + | resolver, _ -> failwith $"Unsupported signature for Resolve %A{resolver.GetType()}" let private boxifyExprAsync expr : ResolveFieldContext -> obj -> Async = match unwrapExpr expr with - | resolver, FSharpFunc (_, FSharpFunc (d, FSharpAsync (c))) -> resolveUntyped resolver d c runtimeBoxifyAsync - | resolver, _ -> failwithf "Unsupported signature for Async Resolve %A" (resolver.GetType()) + | resolver, FSharpFunc (_, FSharpFunc (d, FSharpAsync c)) -> resolveUntyped resolver d c runtimeBoxifyAsync + | resolver, _ -> failwith $"Unsupported signature for Async Resolve %A{resolver.GetType()}" let private boxifyFilterExpr expr : ResolveFieldContext -> obj -> obj -> obj option = match unwrapExpr expr with - | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpOption (o)))) -> + | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpOption o))) -> resolveUntypedFilter resolver r i o runtimeBoxifyFilter - | resolver, _ -> failwithf "Unsupported signature for Subscription Filter Resolve %A" (resolver.GetType()) + | resolver, _ -> failwith $"Unsupported signature for Subscription Filter Resolve %A{resolver.GetType()}" let private boxifyAsyncFilterExpr expr : ResolveFieldContext -> obj -> obj -> Async = match unwrapExpr expr with - | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpAsync (FSharpOption (o))))) -> + | resolver, FSharpFunc (_, FSharpFunc (r, FSharpFunc (i, FSharpAsync (FSharpOption o)))) -> resolveUntypedFilter resolver r i o runtimeBoxifyAsyncFilter - | resolver, _ -> failwithf "Unsupported signature for Async Subscription Filter Resolve %A" (resolver.GetType()) + | resolver, _ -> failwith $"Unsupported signature for Async Subscription Filter Resolve %A{resolver.GetType()}" let (|BoxedSync|_|) = function @@ -2289,7 +2284,7 @@ module Resolve = let (|BoxedExpr|_|) = function - | ResolveExpr (e) -> Some(boxifyExpr e) + | ResolveExpr e -> Some(boxifyExpr e) | _ -> None let (|BoxedFilterExpr|_|) = @@ -2453,7 +2448,6 @@ module Patterns = [] module SchemaDefinitions = open System.Globalization - open System.Reflection /// Tries to convert any value to int. let private coerceIntValue (x: obj) : int option = @@ -2469,7 +2463,7 @@ module SchemaDefinitions = | :? bool as b -> Some(if b then 1 else 0) | other -> try - Some(System.Convert.ToInt32 other) + Some(Convert.ToInt32 other) with | _ -> None @@ -2487,7 +2481,7 @@ module SchemaDefinitions = | :? bool as b -> Some(if b then 1L else 0L) | other -> try - Some(System.Convert.ToInt64 other) + Some(Convert.ToInt64 other) with | _ -> None @@ -2505,7 +2499,7 @@ module SchemaDefinitions = | :? bool as b -> Some(if b then 1. else 0.) | other -> try - Some(System.Convert.ToDouble other) + Some(Convert.ToDouble other) with | _ -> None @@ -2523,7 +2517,7 @@ module SchemaDefinitions = | :? bool as b -> Some b | other -> try - Some(System.Convert.ToBoolean other) + Some(Convert.ToBoolean other) with | _ -> None