Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions FSharp.Data.GraphQL.Integration.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/samples/">
<Project Path="samples/star-wars-api/star-wars-api.fsproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path="Directory.Build.props" />
<File Path="Directory.Build.targets" />
Expand All @@ -12,7 +15,9 @@
<Folder Name="/src/">
<Project Path="src/FSharp.Data.GraphQL.Server.AspNetCore/FSharp.Data.GraphQL.Server.AspNetCore.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Server.Giraffe/FSharp.Data.GraphQL.Server.Giraffe.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Server.Oxpecker/FSharp.Data.GraphQL.Server.Oxpecker.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Server.Relay/FSharp.Data.GraphQL.Server.Relay.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj" />
<Project Path="src/FSharp.Data.GraphQL.Shared/FSharp.Data.GraphQL.Shared.fsproj" />
</Folder>
Expand Down
1 change: 1 addition & 0 deletions Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<PackageReference Update="GraphQL.Server.Ui.Voyager" Version="8.*" />
<PackageReference Update="HotChocolate.AspNetCore" Version="15.*" />
<PackageReference Update="Iced" Version="1.17.*" />
<PackageReference Update="Microsoft.AspNetCore.Mvc.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Update="Microsoft.Azure.Cosmos" Version="3.*" />
<PackageReference Update="Microsoft.CodeCoverage" Version="17.3.*" />
<PackageReference Update="Microsoft.Data.Sqlite" Version="$(MicrosoftExtensionsVersion)" />
Expand Down
72 changes: 11 additions & 61 deletions build/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module Program

open System
open System.IO
open System.Net.Http
open System.Text.Json

open Fake.Core
open Fake.Core.TargetOperators
Expand Down Expand Up @@ -123,26 +121,6 @@ let runTests (project : string) (args : string) =
|> _.WithCommon(DotNetCli.setVersion))
project

let starWarsServerStream = StreamRef.Empty

let [<Literal>] StartStarWarsServerTarget = "StartStarWarsServer"
Target.create StartStarWarsServerTarget <| fun _ ->
Target.activateFinal "StopStarWarsServer"

let project =
"samples"
</> "star-wars-api"
</> "star-wars-api.fsproj"

startGraphQLServer project 8086 starWarsServerStream

let [<Literal>] StopStarWarsServerTarget = "StopStarWarsServer"
Target.createFinal StopStarWarsServerTarget <| fun _ ->
try
starWarsServerStream.Value.Write ([| 0uy |], 0, 1)
with e ->
printfn "%s" e.Message

let integrationTestServerProjectPath =
"tests"
</> "FSharp.Data.GraphQL.IntegrationTests.Server"
Expand Down Expand Up @@ -179,58 +157,35 @@ Target.createFinal StopIntegrationServerTarget <| fun _ ->
with e ->
printfn "%s" e.Message

let [<Literal>] UpdateIntrospectionFileTarget = "UpdateIntrospectionFile"
Target.create UpdateIntrospectionFileTarget <| fun _ ->
let client = new HttpClient ()
(task {
let! result = client.GetAsync ("http://localhost:8086")
let! contentStream = result.Content.ReadAsStreamAsync ()
let! jsonDocument = JsonDocument.ParseAsync contentStream
let file =
new FileStream ("tests/FSharp.Data.GraphQL.IntegrationTests/introspection.json", FileMode.Create, FileAccess.Write, FileShare.None)
let encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
let jsonWriterOptions = JsonWriterOptions (Indented = true, Encoder = encoder)
let writer = new Utf8JsonWriter (file, jsonWriterOptions)
jsonDocument.WriteTo writer
do! writer.FlushAsync ()
do! writer.DisposeAsync ()
do! file.DisposeAsync ()
result.Dispose ()
})
.Wait ()
client.Dispose ()

let unitTestsProjectPath =
"tests"
</> "FSharp.Data.GraphQL.Tests"
</> "FSharp.Data.GraphQL.Tests.fsproj"

let integrationTestsProjectPath =
"tests"
</> "FSharp.Data.GraphQL.IntegrationTests"
</> "FSharp.Data.GraphQL.IntegrationTests.fsproj"

let [<Literal>] BuildIntegrationTestsTarget = "BuildIntegrationTests"
Target.create BuildIntegrationTestsTarget <| fun _ ->
let [<Literal>] UpdateIntrospectionFileTarget = "UpdateIntrospectionFile"
Target.create UpdateIntrospectionFileTarget <| fun _ ->
integrationTestsProjectPath
|> DotNet.build (fun options -> {
|> DotNet.test (fun options -> {
options with
Framework = Some DotNetMoniker
Configuration = configuration
Common = { DotNetCli.setVersion options.Common with CustomParams = Some "--filter FullyQualifiedName~IntrospectionUpdateTests" }
MSBuildParams = {
options.MSBuildParams with
DisableInternalBinLog = true
Verbosity = Some Normal
}
Common = DotNetCli.setVersion options.Common
})

let unitTestsProjectPath =
"tests"
</> "FSharp.Data.GraphQL.Tests"
</> "FSharp.Data.GraphQL.Tests.fsproj"

let [<Literal>] RunUnitTestsTarget = "RunUnitTests"
Target.create RunUnitTestsTarget <| fun _ ->
runTests unitTestsProjectPath ""

let [<Literal>] RunIntegrationTestsTarget = "RunIntegrationTests"
Target.create RunIntegrationTestsTarget <| fun _ ->
runTests integrationTestsProjectPath "" //"--filter Execution=Sync"

let prepareDocGen () =
Shell.rm "docs/release-notes.md"
Shell.cp "RELEASE_NOTES.md" "docs/RELEASE_NOTES.md"
Expand Down Expand Up @@ -406,12 +361,7 @@ Target.create "PackAndPush" ignore
==> RestoreTarget
==> BuildTarget
==> RunUnitTestsTarget
==> StartStarWarsServerTarget
==> BuildIntegrationTestServerTarget
==> StartIntegrationServerTarget
==> UpdateIntrospectionFileTarget
==> BuildIntegrationTestsTarget
==> RunIntegrationTestsTarget
==> "All"
=?> (GenerateDocsTarget, Environment.environVar "GITHUB_ACTIONS" = "True")
|> ignore
Expand Down
6 changes: 6 additions & 0 deletions samples/star-wars-fabulous-client/StarWars.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
<Project Path="StarWars.Android/StarWars.Android.fsproj">
<BuildType Solution="Ad-Hoc|*" Project="Release" />
<BuildType Solution="AppStore|*" Project="Release" />
<Build Solution="*|Any CPU" Project="false" />
<Build Solution="*|ARM" Project="false" />
<Build Solution="*|iPhone" Project="false" />
<Build Solution="*|iPhoneSimulator" Project="false" />
<Build Solution="*|x64" Project="false" />
<Build Solution="*|x86" Project="false" />
<Deploy Solution="Debug|Any CPU" />
<Deploy Solution="Release|Any CPU" />
</Project>
Expand Down
5 changes: 4 additions & 1 deletion samples/star-wars-fabulous-client/StarWars/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ open FSharp.Data.GraphQL

module Commands =

type GraphQLApi = GraphQLProvider<"http://localhost:8086">
[<Literal>]
let IntrospectionPath = "../../../tests/FSharp.Data.GraphQL.IntegrationTests/introspection.json"

type GraphQLApi = GraphQLProvider<IntrospectionPath>
let GetCharactersData = GraphQLApi.Operation<"queries/FetchCharacters.graphql">()

type Character = GraphQLApi.Operations.FetchCharacters.Types.CharactersFields.Character
20 changes: 10 additions & 10 deletions samples/star-wars-fabulous-client/StarWars/StarWars.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
</PropertyGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="4.5.2" />
<PackageReference Update="FSharp.Core" VersionOverride="4.5.2" />
<!-- workaround for VSMac bug https://github.com/mono/monodevelop/pull/5137 -->
</ItemGroup>
<ItemGroup>
Expand All @@ -18,16 +18,16 @@
<Compile Include="Style.fs" />
<Compile Include="Common.fs" />
<None Include="queries\FetchCharacters.graphql" />
<PackageReference Include="Xamarin.Forms" Version="4.0.0.425677" />
<PackageReference Include="Xamarin.Essentials" Version="1.0.0" />
<PackageReference Include="Fabulous.Core" Version="0.35.0" />
<PackageReference Include="Fabulous.CustomControls" Version="0.35.0" />
<PackageReference Include="Fabulous.LiveUpdate" Version="0.35.0" />
<PackageReference Include="FSharp.Core" Version="4.6.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="FSharp.Data.GraphQL.Client" Version="1.0.1" />
<PackageReference Include="Xamarin.Forms" VersionOverride="4.0.0.425677" />
<PackageReference Include="Xamarin.Essentials" VersionOverride="1.0.0" />
<PackageReference Include="Fabulous.Core" VersionOverride="0.35.0" />
<PackageReference Include="Fabulous.CustomControls" VersionOverride="0.35.0" />
<PackageReference Include="Fabulous.LiveUpdate" VersionOverride="0.35.0" />
<PackageReference Include="FSharp.Core" VersionOverride="4.6.2" />
<PackageReference Include="Newtonsoft.Json" VersionOverride="13.0.1" />
<PackageReference Include="FSharp.Data.GraphQL.Client" VersionOverride="1.0.1" />
<Compile Include="CharacterDetailPage.fs" />
<Compile Include="MainPage.fs" />
<Compile Include="StarWars.fs" />
</ItemGroup>
</Project>
</Project>
86 changes: 71 additions & 15 deletions src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,73 @@ module SchemaDefinitions =
| false, _ -> getParseError destinationType s
| InlineConstant value -> value.GetCoerceError destinationType

/// 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 }

/// Wraps a GraphQL type definition, allowing defining field/argument
/// to take voption of provided value.
let StructNullable(innerDef : #TypeDef<'Val>) : StructNullableDef<'Val> = upcast { StructNullableDefinition.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 }
type TypeWrapperStaticDispatch =

static member Nullable<'Val>(innerDef : InputOutputDef<'Val>) : NullableDef<'Val> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { NullableDefinition.OfType = ofType }

static member Nullable<'Val>(innerDef : InputDef<'Val>) : InputDef<'Val option> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { NullableDefinition.OfType = ofType }

static member Nullable<'Val>(innerDef : OutputDef<'Val>) : OutputDef<'Val option> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { NullableDefinition.OfType = ofType }

static member StructNullable<'Val>(innerDef : InputOutputDef<'Val>) : StructNullableDef<'Val> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { StructNullableDefinition.OfType = ofType }

static member StructNullable<'Val>(innerDef : InputDef<'Val>) : InputDef<'Val voption> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { StructNullableDefinition.OfType = ofType }

static member StructNullable<'Val>(innerDef : OutputDef<'Val>) : OutputDef<'Val voption> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { StructNullableDefinition.OfType = ofType }

static member ListOf<'Val, 'Seq when 'Seq :> 'Val seq>(innerDef : InputOutputDef<'Val>) : ListOfDef<'Val, 'Seq> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { ListOfDefinition.OfType = ofType }

static member ListOf<'Val, 'Seq when 'Seq :> 'Val seq>(innerDef : InputDef<'Val>) : InputDef<'Seq> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { ListOfDefinition.OfType = ofType }

static member ListOf<'Val, 'Seq when 'Seq :> 'Val seq>(innerDef : OutputDef<'Val>) : OutputDef<'Seq> =
let ofType : TypeDef<'Val> = upcast innerDef
upcast { ListOfDefinition.OfType = ofType }

/// Wraps a GraphQL input or output type definition, allowing defining field/argument
/// to take option of provided value while preserving input/output kind of wrapped type.
/// Input wrappers produce input definitions, output wrappers produce output definitions,
/// and wrappers over types implementing both kinds keep both capabilities.
/// Dispatch is selected at compile time via SRTP.
let inline Nullable< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member Nullable : ^Def -> ^Wrapped) >
(innerDef : ^Def)
: ^Wrapped =
((^Def or TypeWrapperStaticDispatch) : (static member Nullable : ^Def -> ^Wrapped) innerDef)

/// Wraps a GraphQL input or output type definition, allowing defining field/argument
/// to take voption of provided value while preserving input/output kind of wrapped type.
/// Input wrappers produce input definitions, output wrappers produce output definitions,
/// and wrappers over types implementing both kinds keep both capabilities.
/// Dispatch is selected at compile time via SRTP.
let inline StructNullable< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member StructNullable : ^Def -> ^Wrapped) >
(innerDef : ^Def)
: ^Wrapped =
((^Def or TypeWrapperStaticDispatch) : (static member StructNullable : ^Def -> ^Wrapped) innerDef)

/// Wraps a GraphQL input or output type definition, allowing defining field/argument
/// to take collection of provided value while preserving input/output kind of wrapped type.
/// Input wrappers produce input definitions, output wrappers produce output definitions,
/// and wrappers over types implementing both kinds keep both capabilities.
/// Dispatch is selected at compile time via SRTP.
let inline ListOf< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member ListOf : ^Def -> ^Wrapped) >
(innerDef : ^Def)
: ^Wrapped =
((^Def or TypeWrapperStaticDispatch) : (static member ListOf : ^Def -> ^Wrapped) innerDef)

let internal variableOrElse other (_ : InputExecutionContextProvider) value (variables : IReadOnlyDictionary<string, obj>) =
match value with
Expand Down Expand Up @@ -1415,13 +1471,14 @@ module SchemaDefinitions =
/// <param name="defaultValue">If defined, this value will be used when no matching input has been provided by the requester.</param>
/// <param name="description">Optional input description. Usefull for generating documentation.</param>
static member SkippableInput(name : string, typedef : #InputDef<'In>, ?description : string) : InputFieldDef =
let typedef : InputDef<'In> = upcast typedef
upcast { InputFieldDefinition.Name = name
Description = description |> Option.map (fun s -> s + " Skip this field if you want to avoid saving it")
IsSkippable = true
TypeDef =
match (box typedef) with
| :? NullableDef<'In> as n -> n
| _ -> Nullable typedef
match (box typedef) with
| :? NullableDef<'In> as n -> (n :> InputDef<'In option>)
| _ -> Nullable typedef
DefaultValue = None
ExecuteInput = Unchecked.defaultof<ExecuteInput> }

Expand Down Expand Up @@ -1539,4 +1596,3 @@ module SchemaDefinitions =
Description = description
FieldsFn = fun () -> fieldsFn() |> List.toArray
ResolveType = resolveType }

6 changes: 4 additions & 2 deletions src/FSharp.Data.GraphQL.Shared/SchemaDefinitionsExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ type internal CustomFieldsObjectDefinition<'Val> (source : ObjectDef<'Val>, fiel
member _.Implements = source.Implements
member _.IsTypeOf = source.IsTypeOf
interface TypeDef with
member this.MakeList () = upcast (ListOf this)
member this.MakeNullable () = upcast (Nullable this)
// We construct wrappers directly here because this API works with untyped TypeDef values.
// The public ListOf/Nullable helpers use SRTP dispatch and require statically known direction.
member this.MakeList () = upcast { ListOfDefinition.OfType = this }
member this.MakeNullable () = upcast { NullableDefinition.OfType = this }
member _.Type = (source :> TypeDef).Type
interface NamedDef with
member _.Name = (source :> NamedDef).Name
Expand Down
Loading
Loading