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..e72d50225 100644 --- a/FSharp.Data.GraphQL.sln +++ b/FSharp.Data.GraphQL.sln @@ -1,16 +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}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{BF60BC93-E09B-4E5F-9D85-95A519479D54}" @@ -20,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}" @@ -97,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}" @@ -137,6 +104,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 @@ -249,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} @@ -260,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/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/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.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/Schema.fs b/src/FSharp.Data.GraphQL.Server/Schema.fs index ebde61e27..18bdba0bf 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.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.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/Ast.fs b/src/FSharp.Data.GraphQL.Shared/Ast.fs index e35f736a3..e4cf47dae 100644 --- a/src/FSharp.Data.GraphQL.Shared/Ast.fs +++ b/src/FSharp.Data.GraphQL.Shared/Ast.fs @@ -1,67 +1,268 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc +// The MIT License (MIT) +// Copyright (c) 2016 Bazinga Technologies Inc +// Copyright (c) 2019 Henrik Feldt + 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. +/// +/// Each operation is represented by an optional operation name and a selection set. +/// +/// 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 -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.1 Operations -and OperationDefinition = +/// 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 + /// 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 +/// +/// 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 = { - OperationType: OperationType - Name: string option - VariableDefinitions: VariableDefinition list + Name: string + Value: Value + } + +/// 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.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, 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 } - member x.IsShortHandQuery = - x.OperationType = Query && x.Name.IsNone && x.VariableDefinitions.IsEmpty && x.Directives.IsEmpty -and OperationType = - | Query - | Mutation - | Subscription +/// 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 -/// 2.2.2 Selection Sets + /// May be empty + Directives: Directive list + + /// May not be empty + SelectionSet: Selection list + } + + +/// +/// 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 = @@ -69,127 +270,840 @@ and Field = | 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 = +/// 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 + 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 -/// 2.2.10 Directives -and Directive = + + +/// 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 +/// https://spec.graphql.org/October2021/#VariableDefinition +type VariableDefinition = + { + /// https://spec.graphql.org/October2021/#Variable + VariableName: string + /// 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 + } + +/// 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`; 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 + +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 of the input field / argument. Name: string - Arguments: Argument list + /// 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 + } + +/// 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 = + { + /// https://spec.graphql.org/October2021/#OperationType + Operation: OperationType + /// https://spec.graphql.org/October2021/#NamedType + NamedType: string + } + +/// 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. +/// +/// 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 + + /// https://spec.graphql.org/October2021/#RootOperationTypeDefinition + 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 + + /// 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://spec.graphql.org/October2021/#FieldDefinition +type FieldDefinition = + { + /// https://spec.graphql.org/October2021/#Description + Description: string option + Name: string + /// 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 + } + +// TypeDefinitions: + +/// 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 + } + +/// 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 + /// https://spec.graphql.org/October2021/#ImplementsInterfaces + ImplementsInterfaces: string list + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives + Directives: Directive list + // "{" fields "}" + Fields: FieldDefinition list + } + +/// 3.6.3 Object Extensions +/// +/// 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. +/// +/// 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 list + /// May be empty. + /// https://spec.graphql.org/October2021/#Directives + Directives: Directive list + /// May be empty. + Fields: FieldDefinition list + } + +/// 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 + /// 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 + + /// 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 + +/// 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 + + /// 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 + + /// May not be empty. + /// https://spec.graphql.org/October2021/#UnionMemberTypes + Types: UnionMemberType list + } + +/// 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 + } + +/// 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 list + } + +/// 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 + + /// May not empty if directives is empty. + /// + /// May be empty if directives is not empty. + Values: EnumValueDefinition list + } + + +/// 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 "}" + /// + /// 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 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 + Directives: Directive list + Fields: InputValueDefinition [] } - member x.If = x.Arguments |> List.find (fun arg -> arg.Name = "if") -// Type System Definition - -and OperationTypeDefinition = { - Type: string - Operation: OperationType -} - -and SchemaDefintion = { - OperationTypes: OperationTypeDefinition -} - -and ObjectTypeDefinition = { - Name: string - Interfaces: string [] - Fields: FieldDefinition [] -} - -and FieldDefinition = { - Name: string - Arguments: InputValueDefinition [] - Type: InputType -} - -and InputValueDefinition = { - Name: string - Type: InputType - DefaultValue: Value option -} - -and InterfaceTypeDefinition = { - Name: string - Fields: FieldDefinition [] -} - -and UnionTypeDefinition = { - Name: string - Types: string [] -} - -and EnumTypeDefinition = { - Name: string - Values: string [] -} - -and InputObjectTypeDefinition = { - Name: string - Fields: InputValueDefinition [] -} - -and TypeDefinition = - | ScalarTypeDefinition of string +type TypeDefinition = + /// https://spec.graphql.org/October2021/#ScalarTypeExtension + | ScalarTypeDefinition of ScalarTypeDefinition | ObjectTypeDefinition of ObjectTypeDefinition | InterfaceTypeDefinition of InterfaceTypeDefinition | UnionTypeDefinition of UnionTypeDefinition | EnumTypeDefinition of EnumTypeDefinition | InputObjectTypeDefinition of InputObjectTypeDefinition + 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 ExecutableDirectiveLocation = + | QUERY = 1 + | MUTATION = 2 + | SUBSCRIPTION = 4 + | FIELD = 8 + | FRAGMENT_DEFINITION = 16 + | FRAGMENT_SPREAD = 32 + | INLINE_FRAGMENT = 64 + + +/// 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://spec.graphql.org/October2021/#sec-Type-System.Directives +/// https://spec.graphql.org/October2021/#DirectiveDefinition +type DirectiveDefinition = + { + /// Optional directive description. + Description: string option + + // "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 + } + + +/// 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 + + 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 if OperationTypes is not, else it may not be empty. + Directives: Directive list + /// May be empty if the Directives list is not, else it may not be empty. + OperationTypes: RootOperationTypeDefinition list + } + +// 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 = + /// 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 +/// 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 + + member x.Directives = + match x with + | SchemaExtension se -> se.Directives + | TypeExtension te -> te.Directives + + +/// 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 + | 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 -> Some frag.Name + | TypeSystemDefinition _ -> None + | TypeSystemExtension _ -> None + + member x.Directives = + 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 + | OperationDefinition op -> op.SelectionSet + | FragmentDefinition frag -> frag.SelectionSet + | _ -> [] + + +/// 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 + + 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 +/// +/// 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 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. +/// +/// GraphQL services which only seek to execute GraphQL requests and not construct a new GraphQL schema may choose to only permit +/// ExecutableDocument. +/// +/// 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. +/// +/// 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 + } + + member x.Directives = x.Definitions |> List.collect (fun y -> y.Directives) diff --git a/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs b/src/FSharp.Data.GraphQL.Shared/AstExtensions.fs index 1133d3543..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,25 +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)) @@ -114,93 +159,161 @@ type Document with | ListValue x -> printCompound "[]" printValue x | 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 + + 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) @@ -208,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() @@ -223,19 +344,37 @@ type Document with member this.GetInfoMap() : Map = let fragments = this.Definitions - |> List.choose (function | OperationDefinition _ -> None | FragmentDefinition def -> Some def) + |> List.choose (function + | 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 -> failwithf "Can not get information about fragment \"%s\". Fragment spread definition was not found in the query." name + | 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 | FragmentDefinition _ -> None | OperationDefinition def -> Some def) + |> List.choose (function + | 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 -> @@ -243,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/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/Introspection.fs b/src/FSharp.Data.GraphQL.Shared/Introspection.fs index 296d0cc2e..a07d03adb 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 be4051e91..05fc75071 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 '!' + |>> NonNullNameType + "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/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index df6dca4d3..9fdefc9fc 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 @@ -16,35 +17,12 @@ 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. type TypeKind = | SCALAR = 1 - | OBJECT = 2 + | OBJECT = 2 | INTERFACE = 3 | UNION = 4 | ENUM = 5 @@ -54,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 [] } - - /// Introspection descriptor of a GraphQL type defintion. + { + /// 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 definition. 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 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 + /// (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. @@ -110,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. @@ -128,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. @@ -145,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. @@ -162,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. @@ -180,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 + /// 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. - 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 @@ -315,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. @@ -329,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 @@ -352,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 @@ -373,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 @@ -393,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. @@ -454,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) = - let map = new Dictionary() +and FieldExecuteMap(compiler: FieldExecuteCompiler) = + let map = 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. @@ -471,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)) | _ -> () @@ -486,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) @@ -497,40 +529,39 @@ 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 /// a common root. 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 @@ -611,18 +642,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. @@ -632,28 +665,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 @@ -665,9 +700,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 @@ -675,49 +708,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() @@ -728,14 +801,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 @@ -747,10 +820,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 = @@ -761,37 +835,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. @@ -805,98 +879,113 @@ 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 ( + 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. and ExecuteField = ResolveFieldContext -> obj -> AsyncVal @@ -906,20 +995,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 @@ -932,21 +1020,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 @@ -969,27 +1058,30 @@ 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() = - 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 @@ -999,25 +1091,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 @@ -1049,26 +1143,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 @@ -1084,11 +1180,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 @@ -1102,7 +1198,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> @@ -1110,12 +1206,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 @@ -1123,11 +1221,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 @@ -1136,11 +1234,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 @@ -1153,16 +1247,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 @@ -1175,35 +1271,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 @@ -1237,16 +1335,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 @@ -1260,35 +1361,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 @@ -1321,18 +1425,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 @@ -1347,10 +1454,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> @@ -1358,29 +1467,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 @@ -1406,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 + "!" @@ -1416,7 +1527,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 @@ -1426,7 +1537,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> @@ -1434,13 +1545,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() = @@ -1464,7 +1578,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 @@ -1476,21 +1590,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 @@ -1512,11 +1629,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 @@ -1524,13 +1641,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 @@ -1548,11 +1667,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. @@ -1563,32 +1682,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 @@ -1610,19 +1734,19 @@ 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 and SubscriptionFieldDef = interface - abstract OutputTypeDef : OutputDef - abstract TagsResolver : TagsResolver + abstract OutputTypeDef: OutputDef + abstract TagsResolver: TagsResolver inherit FieldDef end @@ -1639,17 +1763,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 @@ -1659,18 +1783,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) @@ -1678,61 +1806,67 @@ 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() = - 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 @@ -1744,22 +1878,11 @@ and [] SubscriptionObjectDefinition<'Val> = override x.ToString() = x.Name + "!" -/// GraphQL directive defintion. -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) /// @@ -1767,48 +1890,52 @@ 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 + override __.ToString() = $"%A{data}" /// Map of types of an ISchema. /// 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 @@ -1820,51 +1947,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 @@ -1875,12 +2007,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 /// @@ -1888,20 +2025,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 @@ -1910,14 +2049,14 @@ 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 - | (true, item) -> Some item + | true, item -> Some item | _ -> None /// @@ -1925,8 +2064,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 @@ -1938,11 +2078,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 @@ -1952,7 +2099,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 @@ -1962,7 +2109,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 @@ -1974,16 +2121,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 @@ -1992,76 +2142,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" @@ -2072,75 +2227,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, _ -> failwithf "Unsupported signature for Resolve %A" (resolver.GetType()) + | resolver, FSharpFunc (_, FSharpFunc (d, c)) -> resolveUntyped resolver d c runtimeBoxify + | 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 = + 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 = + 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 - | 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) @@ -2148,17 +2321,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) @@ -2168,97 +2342,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 @@ -2266,15 +2443,14 @@ 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 = open System.Globalization - 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 @@ -2284,34 +2460,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 + Some(Convert.ToInt32 other) + 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 + Some(Convert.ToInt64 other) + 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) @@ -2321,16 +2496,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 + Some(Convert.ToDouble other) + 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) @@ -2343,11 +2517,12 @@ module SchemaDefinitions = | :? bool as b -> Some b | other -> try - Some(System.Convert.ToBoolean other) - with _ -> None + Some(Convert.ToBoolean other) + 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 @@ -2358,7 +2533,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 @@ -2369,7 +2544,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 @@ -2380,58 +2555,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. @@ -2443,9 +2616,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. @@ -2454,9 +2625,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 = @@ -2467,12 +2636,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 @@ -2481,7 +2646,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>)) @@ -2516,13 +2681,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 @@ -2530,153 +2703,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. [] @@ -2689,12 +2917,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. @@ -2702,10 +2937,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 . @@ -2717,11 +2955,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 @@ -2738,16 +2978,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 @@ -2762,16 +3008,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, @@ -2783,10 +3035,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, @@ -2796,10 +3055,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. @@ -2807,10 +3068,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. @@ -2821,15 +3089,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 @@ -2837,15 +3114,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. @@ -2853,15 +3132,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. @@ -2870,16 +3156,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. @@ -2888,15 +3182,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. @@ -2906,15 +3208,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. @@ -2925,16 +3236,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. @@ -2942,15 +3262,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. @@ -2959,15 +3286,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. @@ -2977,16 +3312,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. @@ -2997,31 +3340,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. @@ -3030,17 +3384,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. @@ -3050,18 +3412,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. @@ -3071,18 +3441,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. @@ -3093,19 +3471,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. @@ -3116,19 +3502,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. @@ -3140,20 +3534,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. @@ -3165,20 +3567,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. @@ -3191,21 +3601,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. @@ -3214,17 +3632,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. @@ -3234,18 +3660,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. @@ -3255,18 +3689,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. @@ -3277,19 +3719,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. @@ -3300,19 +3750,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. @@ -3324,20 +3782,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. @@ -3349,20 +3815,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. @@ -3375,21 +3849,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 + } /// @@ -3402,12 +3884,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. @@ -3418,12 +3903,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. @@ -3432,12 +3925,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. @@ -3449,10 +3950,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 + } diff --git a/src/FSharp.Data.GraphQL.Shared/Validation.fs b/src/FSharp.Data.GraphQL.Shared/Validation.fs index b0dfec196..f4324f5b9 100644 --- a/src/FSharp.Data.GraphQL.Shared/Validation.fs +++ b/src/FSharp.Data.GraphQL.Shared/Validation.fs @@ -1,5 +1,5 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc +// The MIT License (MIT) +// Copyright (c) 2016 Bazinga Technologies Inc namespace FSharp.Data.GraphQL.Validation @@ -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 -> (sprintf "'%s' field is defined by interface %s, but not implemented in object %s" f.Name idef.Name objdef.Name)::acc - | Some objf when objf = f -> acc - | Some _ -> (sprintf "'%s.%s' field signature does not match it's definition in interface %s" objdef.Name f.Name 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,113 +37,171 @@ 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 - | _ -> failwithf "Unexpected value of typedef: %O" typedef + | _ -> failwith $"Unexpected value of typedef: {typedef}" let validateTypeMap (namedTypes: TypeMap) : ValidationResult = namedTypes.ToSeq() |> Seq.fold (fun acc (_, namedDef) -> acc @@ validateType namedDef) Success 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 [] } - member x.TryGetTypeByRef(tref : IntrospectionTypeRef) = - tryGetSchemaTypeByRef x.SchemaTypes tref - static member FromIntrospectionSchema(schema : IntrospectionSchema) = + { + 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) = 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 - /// Contains the reference to tye field type in the schema. - FieldType : IntrospectionTypeRef option - /// Contains the reference to the parent type of the current field in the schema. - 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 - /// In case this field is part of a selection of a fragment spread, gets the name of the fragment spread. - FragmentSpreadName : string option - /// Contains the selection info of this field, if it is an Object, Interface or Union type. - 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 [] - /// Contains the path of the field in the document. - Path : Path } + { + /// Contains the information about the field inside the selection set of the parent type. + Field: Field + /// Contains the reference to tye field type in the schema. + FieldType: IntrospectionTypeRef option + /// Contains the reference to the parent type of the current field in the schema. + 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 + /// In case this field is part of a selection of a fragment spread, gets the name of the fragment spread. + FragmentSpreadName: string option + /// Contains the selection info of this field, if it is an Object, Interface or Union type. + 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 [] + /// Contains the path of the field in the document. + 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 /// Contains information about an operation definition in the document, with related GraphQL schema type information. type OperationDefinitionInfo = - { /// Returns the definition from the parsed document. - Definition : OperationDefinition - /// Returns the selection information about the operation, with related GraphQL schema type information. - SelectionSet : SelectionInfo list } + { + /// Returns the definition from the parsed document. + Definition: OperationDefinition + /// Returns the selection information about the operation, with related GraphQL schema type information. + SelectionSet: SelectionInfo list + } /// Returns the name of the operation definition, if it does have a name. member x.Name = x.Definition.Name /// Contains information about a fragment definition in the document, with related GraphQL schema type information. type FragmentDefinitionInfo = - { /// Returns the definition from the parsed document. - Definition : FragmentDefinition - /// Returns the selection information about the fragment, with related GraphQL schema type information. - SelectionSet : SelectionInfo list } + { + /// Returns the definition from the parsed document. + Definition: FragmentDefinition + /// Returns the selection information about the fragment, with related GraphQL schema type information. + SelectionSet: SelectionInfo list + } /// Returns the name of the fragment definition, if it does have a name. member x.Name = x.Definition.Name @@ -149,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 @@ -165,26 +233,38 @@ module Ast = /// The validation context used to run validations against a parsed document. /// It should have the schema type information, the original document and the definition information about the original document. type ValidationContext = - { /// Gets information about all definitions in the original document, including related GraphQL schema type information for them. - Definitions : DefinitionInfo list - /// Gets information about the schema which the document is validated against. - Schema : SchemaInfo - /// Gets the document that is being validated. - Document : Document } + { + /// Gets information about all definitions in the original document, including related GraphQL schema type information for them. + Definitions: DefinitionInfo list + /// Gets information about the schema which the document is validated against. + Schema: SchemaInfo + /// Gets the document that is being validated. + 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 @@ -192,52 +272,67 @@ module Ast = | Spread (name, _, _) -> Some name 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 @@ -247,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 @@ -273,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 -> @@ -295,146 +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(sprintf "Operation '%s' has %i definitions. Each operation name must be unique." name count)) + 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(sprintf "Subscription operations should have only one root field. Operation '%s' has %i fields (%s)." operationName fieldNames.Length fieldNamesAsString) - | None -> AstError.AsResult(sprintf "Subscription operations should have only one root field. Operation has %i fields (%s)." fieldNames.Length 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(sprintf "Field '%s' is not defined in schema type '%s'." selection.Field.Name selection.FragmentOrParentType.Name, selection.Path) - else Success) - - let private typesAreApplicable (parentType : IntrospectionType, fragmentType : IntrospectionType) = - let parentPossibleTypes = parentType.PossibleTypes |> Option.defaultValue [||] |> Array.choose (fun x -> x.Name) |> Array.append [|parentType.Name|] |> Set.ofArray - let fragmentPossibleTypes = fragmentType.PossibleTypes |> Option.defaultValue [||] |> Array.choose (fun x -> x.Name) |> Array.append [|fragmentType.Name|] |> Set.ofArray + 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 parentPossibleTypes = + parentType.PossibleTypes + |> Option.defaultValue [||] + |> Array.choose (fun x -> x.Name) + |> Array.append [| parentType.Name |] + |> Set.ofArray + + let fragmentPossibleTypes = + fragmentType.PossibleTypes + |> Option.defaultValue [||] + |> Array.choose (fun x -> x.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(sprintf "Field name or alias '%s' appears two times, but they do not have the same return types in the scope of the parent type." fieldA.AliasOrName, 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 - if fieldA.Field.Name <> fieldB.Field.Name then hasSameShape @@ AstError.AsResult(sprintf "Field name or alias '%s' is referring to fields '%s' and '%s', but they are different fields in the scope of the parent type." aliasOrName fieldA.Field.Name fieldB.Field.Name, fieldA.Path) - else if fieldA.Field.Arguments <> fieldB.Field.Arguments then hasSameShape @@ AstError.AsResult(sprintf "Field name or alias '%s' refers to field '%s' two times, but each reference has different argument sets." aliasOrName fieldA.Field.Name, fieldA.Path) + + if fieldA.FragmentOrParentType = fieldB.FragmentOrParentType + || 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 + ) + 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 + ) 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(sprintf "Field '%s' of '%s' type is of type kind %s, and therefore should not contain inner fields in its selection." selection.Field.Name selection.FragmentOrParentType.Name (fieldType.Kind.ToString()), selection.Path) - | TypeKind.INTERFACE | TypeKind.UNION | TypeKind.OBJECT when selectionSetLength = 0 -> - AstError.AsResult(sprintf "Field '%s' of '%s' type is of type kind %s, and therefore should have inner fields in its selection." selection.Field.Name selection.FragmentOrParentType.Name (fieldType.Kind.ToString()), 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 -> @@ -442,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(sprintf "Field '%s' of type '%s' does not have an input named '%s' in its definition." selection.Field.Name selection.FragmentOrParentType.Name arg.Name, 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 -> @@ -454,29 +651,42 @@ module Ast = |> collectResults (fun arg -> match directiveType.Args |> Array.tryFind (fun argt -> argt.Name = arg.Name) with | Some _ -> Success - | _ -> AstError.AsResult(sprintf "Directive '%s' of field '%s' of type '%s' does not have an argument named '%s' in its definition." directiveType.Name selection.Field.Name selection.FragmentOrParentType.Name arg.Name, 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(sprintf "There are %i arguments with name '%s' defined in %s. Field arguments must be unique." length name fieldOrDirective, path) - else Success) - let argsValid = validateArgs (sprintf "alias or field '%s'" selection.AliasOrName) selection.Path selection.Field.Arguments - let directiveArgsValid = selection.Field.Directives |> collectResults (fun directive -> validateArgs (sprintf "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 -> @@ -484,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(sprintf "Argument '%s' of field '%s' of type '%s' is required and does not have a default value." argDef.Name selection.Field.Name selection.FragmentOrParentType.Name, 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 -> @@ -497,121 +712,185 @@ 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(sprintf "Argument '%s' of directive '%s' of field '%s' of type '%s' is required and does not have a default value." argDef.Name directiveType.Name selection.Field.Name selection.FragmentOrParentType.Name, 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(sprintf "There are %i fragments with name '%s' in the document. Fragment definitions must have unique names." length name) - 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(sprintf "Fragment '%s' has type condition '%s', but that type does not exist in the schema." frag.Name.Value frag.TypeCondition.Value) - | None -> AstError.AsResult(sprintf "Inline fragment has type condition '%s', but that type does not exist in the schema." frag.TypeCondition.Value, 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) + //| TypeSystemDefinition _ -> + // () + //| TypeSystemExtension _ -> + // () | FragmentDefinition frag -> let path = frag.Name |> Option.toList 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(sprintf "Fragment '%s' has type kind %s, but fragments can only be defined in UNION, OBJECT or INTERFACE types." selection.FragmentSpreadName.Value (fragType.Kind.ToString()), selection.Path) - | _ -> AstError.AsResult(sprintf "Inline fragment has type kind %s, but fragments can only be defined in UNION, OBJECT or INTERFACE types." (fragType.Kind.ToString()), 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(sprintf "Fragment '%s' is not used in any operation in the document. Fragments must be used in at least one operation." def.Name.Value)) + 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(sprintf "Fragment spread '%s' refers to a non-existent fragment definition in the document." spread.Name, 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) + //| TypeSystemDefinition _ -> + // () + //| TypeSystemExtension _ -> + // () | 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 - AstError.AsResult(sprintf "Fragment '%s' is making a cyclic reference." fragName) + + 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 @@ -624,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(sprintf "Fragment type condition '%s' is not applicable to the parent type of the field '%s'." fragmentType.Name 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(sprintf "Argument field or value named '%s' can not be coerced. It does not match a valid literal representation for the type." argName, selection.Path) match value with - | NullValue when tref.Kind = TypeKind.NON_NULL -> AstError.AsResult(sprintf "Argument '%s' value can not be coerced. It's type is non-nullable but the argument has a null value." argName, 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 @@ -674,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 @@ -687,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 (sprintf "Can not coerce argument '%s'. Argument definition '%s' have a required field '%s', but that field does not exist in the literal value for the argument." argName tref.Name.Value kvp.Key, 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(sprintf "Can not coerce argument '%s'. The field '%s' is not a valid field in the argument definition." argName kvp.Key, 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 @@ -710,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(sprintf "Directive '%s' is not defined in the schema." name, 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 } - - let rec private checkDirectivesInValidLocationOnInlineFragment (ctx : InlineFragmentContext) = + { + Schema: SchemaInfo + FragmentDefinitions: FragmentDefinition list + Path: Path + Directives: Directive list + SelectionSet: Selection list + } + + let rec private checkDirectivesInValidLocationOnInlineFragment (ctx: InlineFragmentContext) = let directivesValid = ctx.Directives - |> collectResults (validateDirective ctx.Schema ctx.Path DirectiveLocation.INLINE_FRAGMENT (fun d -> - sprintf "An inline fragment has a directive '%s', but this directive location is not supported by the schema definition." d.Name)) + |> 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 -> - sprintf "Field or alias '%s' has a directive '%s', but this directive location is not supported by the schema definition." field.AliasOrName directiveDef.Name)) + |> 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 -> - sprintf "Fragment '%s' has a directive '%s', but this directive location is not supported by the schema definition." ctx.FragmentName d.Name)) + |> 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 -> sprintf "%s operation '%s' has a directive '%s', but this directive location is not supported by the schema definition." (operation.OperationType.ToString()) operationName directiveDef.Name - | None -> sprintf "This %s operation has a directive '%s', but this directive location is not supported by the schema definition." (operation.OperationType.ToString()) directiveDef.Name)) + |> 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 @@ -870,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(sprintf "Directive '%s' appears %i times in the location it is used. Directives must be unique in their locations." name count, 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 -> @@ -899,11 +1294,15 @@ module Ast = |> collectResults (fun (var, count) -> match def.Name with | _ when count < 2 -> Success - | Some operationName -> AstError.AsResult(sprintf "Variable '%s' in operation '%s' is declared %i times. Variables must be unique in their operations." var.VariableName operationName count) - | None -> AstError.AsResult(sprintf "Variable '%s' is declared %i times in the operation. Variables must be unique in their operations." var.VariableName count)) + | 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 -> @@ -911,147 +1310,224 @@ module Ast = |> collectResults (fun var -> match def.Name, ctx.Schema.TryGetInputType(var.Type) with | Some operationName, None -> - AstError.AsResult(sprintf "Variable '%s' in operation '%s' has a type that is not an input type defined by the schema (%s)." var.VariableName operationName (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(sprintf "Variable '%s' has a type is not an input type defined by the schema (%s)." var.VariableName (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(sprintf "A variable '%s' is referenced in an argument '%s' of directive '%s' of field with alias or name '%s', but that variable is not defined in the operation." varName arg.Name directive.Name path.Head, 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(sprintf "A variable '%s' is referenced in argument '%s' of field with alias or name '%s', but that variable is not defined in the operation." varName arg.Name field.AliasOrName) + 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 []) - match def.Name, isUsed with - | _, true -> Success - | Some operationName, _ -> AstError.AsResult(sprintf "Variable definition '%s' is not used in operation '%s'. Every variable must be used." varDef.VariableName operationName) - | None, _ -> AstError.AsResult(sprintf "Variable definition '%s' is not used in operation. Every variable must be used." varDef.VariableName)) + 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.") | _ -> 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(sprintf "Variable '%s' can not be used in its reference. The type of the variable definition is not compatible with the type of its reference." varName, 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 | _ -> @@ -1059,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) 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..49cba5a6d 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, _) -> @@ -104,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 @@ -139,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 a05d75bdc..d67c2a3d6 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 @@ -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 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/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 diff --git a/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs b/tests/FSharp.Data.GraphQL.Tests/AstValidationTests.fs index eeff0575f..ef6a30935 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 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