Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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