Skip to content

Commit 3f566e8

Browse files
committed
fixed all failing tests
1 parent dc8fe1f commit 3f566e8

3 files changed

Lines changed: 88 additions & 63 deletions

File tree

src/FSharp.Data.GraphQL.Server/Execution.fs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ let rec createCompletion (possibleTypesFn: TypeDef -> ObjectDef []) (returnDef:
240240
if value = null then AsyncVal.empty
241241
elif t.IsGenericType && t.GetGenericTypeDefinition() = optionDef then
242242
let _, fields = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(value, t)
243-
AsyncVal.wrap (fields.[0])
243+
innerfn ctx fields.[0]
244244
else innerfn ctx value
245245

246246
| Interface idef ->
@@ -336,7 +336,9 @@ and private executeFields (objdef: ObjectDef) (ctx: ResolveFieldContext) (value:
336336
|> Array.map (fun (name, info) ->
337337
let innerCtx = createFieldContext objdef ctx info
338338
let res = info.Definition.Execute innerCtx value
339-
res |> AsyncVal.map (fun x -> KeyValuePair<_,_>(name, x)))
339+
res
340+
|> AsyncVal.map (fun x -> KeyValuePair<_,_>(name, x))
341+
|> AsyncVal.rescue (fun e -> ctx.AddError e; KeyValuePair<_,_>(name, null)))
340342
|> AsyncVal.collectParallel
341343
|> AsyncVal.map (fun x -> upcast NameValueLookup x)
342344

@@ -360,7 +362,9 @@ let internal executePlan (ctx: ExecutionContext) (plan: ExecutionPlan) (objdef:
360362
Args = args
361363
Variables = ctx.Variables }
362364
let res = info.Definition.Execute fieldCtx value
363-
res |> AsyncVal.map (fun r -> KeyValuePair<_,_>(name, r)))
365+
res
366+
|> AsyncVal.map (fun r -> KeyValuePair<_,_>(name, r))
367+
|> AsyncVal.rescue (fun e -> fieldCtx.AddError e; KeyValuePair<_,_>(name, null)))
364368
match plan.Strategy with
365369
| Parallel -> AsyncVal.collectParallel results
366370
| Serial -> AsyncVal.collectSequential results

src/FSharp.Data.GraphQL.Shared/AsyncVal.fs

Lines changed: 48 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace FSharp.Data.GraphQL
22

33
open System
4+
open System.Collections.Generic
45

56
[<Struct>]
67
type AsyncVal<'T> =
@@ -49,13 +50,21 @@ module AsyncVal =
4950

5051
/// Maps content of AsyncVal using provided mapping function, returning new
5152
/// AsyncVal as the result.
52-
let map (fn: 'T -> 'Res) (x: AsyncVal<'T>) =
53+
let map (fn: 'T -> 'U) (x: AsyncVal<'T>) =
5354
if x.IsSync
54-
then AsyncVal<'Res>(fn x.Value)
55-
else AsyncVal<'Res> (async {
55+
then AsyncVal<'U>(fn x.Value)
56+
else AsyncVal<'U> (async {
5657
let! result = x.Async
5758
return fn result })
5859

60+
/// Applies rescue fn in case when contained Async value throws an exception.
61+
let rescue (fn: exn -> 'T) (x: AsyncVal<'T>) =
62+
if x.IsSync // sync vals will never throw, as they contain ready value
63+
then x
64+
else ofAsync(async {
65+
try return! x.Async
66+
with e -> return fn e })
67+
5968
/// Folds content of AsyncVal over provided initial state zero using provided fn.
6069
/// Returns new AsyncVal as a result.
6170
let fold (fn: 'State -> 'T -> 'State) (zero: 'State) (x: AsyncVal<'T>) : AsyncVal<'State> =
@@ -79,67 +88,47 @@ module AsyncVal =
7988
/// Converts array of AsyncVals into AsyncVal with array results.
8089
/// In case when are non-immediate values in provided array, they are
8190
/// executed asynchronously, one by one with regard to their order in array.
91+
/// Returned array maintain order of values.
8292
let collectSequential (values: AsyncVal<'T> []) : AsyncVal<'T []> =
83-
let i, a = values |> Array.partition isSync
84-
match i, a with
85-
| [||], [||] -> AsyncVal<_> [||]
86-
| immediates, [||] ->
87-
let x = immediates |> Array.map (fun v -> v.Value)
88-
AsyncVal<_> x
89-
| [||], awaitings ->
90-
let asyncs = awaitings |> Array.map (fun v -> v.Async)
91-
let x = async {
92-
let results = Array.zeroCreate asyncs.Length
93-
let mutable i = 0
94-
for a in asyncs do
95-
let! res = a
96-
results.[i] <- res
97-
i <- i + 1
98-
return results
99-
}
100-
ofAsync x
101-
| immediates, awaitings ->
102-
//TODO: optimize
103-
let ready = immediates |> Array.map (fun v -> v.Value)
104-
let asyncs = awaitings |> Array.map (fun v -> v.Async)
105-
let x = async {
106-
let results = Array.zeroCreate (ready.Length + asyncs.Length)
107-
Array.Copy(ready, results, ready.Length)
108-
let mutable i = ready.Length
109-
for a in asyncs do
110-
let! res = a
111-
results.[i] <- res
112-
i <- i + 1
113-
return results
114-
}
115-
ofAsync x
93+
if values.Length = 0 then AsyncVal<_> [||]
94+
elif values |> Array.exists isAsync then
95+
ofAsync <| async {
96+
let results = Array.zeroCreate values.Length
97+
for i = 0 to values.Length - 1 do
98+
let v = values.[i]
99+
if v.IsSync
100+
then results.[i] <- v.Value
101+
else
102+
let! r = v.Async
103+
results.[i] <- r
104+
return results }
105+
else AsyncVal<_> (values |> Array.map (fun x -> x.Value))
106+
116107

117108
/// Converts array of AsyncVals into AsyncVal with array results.
118109
/// In case when are non-immediate values in provided array, they are
119-
/// executed all in parallel, in unordered fashion.
110+
/// executed all in parallel, in unordered fashion. Order of values
111+
/// inside returned array is maintained.
120112
let collectParallel (values: AsyncVal<'T> []) : AsyncVal<'T []> =
121-
let i, a = values |> Array.partition isSync
122-
match i, a with
123-
| [||], [||] -> AsyncVal<_> [||]
124-
| immediates, [||] ->
125-
let x = immediates |> Array.map (fun v -> v.Value)
126-
AsyncVal<_> x
127-
| [||], awaitings ->
128-
let x = awaitings |> Array.map (fun v -> v.Async) |> Async.Parallel
129-
ofAsync x
130-
| immediates, awaitings ->
131-
//TODO: optimize
132-
let len = immediates.Length
133-
let asyncs = awaitings |> Array.map (fun v -> v.Async)
134-
let results = Array.zeroCreate (len + asyncs.Length)
135-
for i = 0 to len - 1 do
136-
results.[i] <- immediates.[i].Value
137-
let x = async {
138-
let! asyncResults = asyncs |> Async.Parallel
139-
Array.Copy(asyncResults, 0, results, len, asyncResults.Length)
140-
return results
141-
}
142-
ofAsync x
113+
if values.Length = 0 then AsyncVal<_> [||]
114+
else
115+
let indexes = List<_>(0)
116+
let continuations = List<_>(0)
117+
let results = Array.zeroCreate values.Length
118+
for i = 0 to values.Length - 1 do
119+
let v = values.[i]
120+
if v.IsSync
121+
then results.[i] <- v.Value
122+
else
123+
indexes.Add i
124+
continuations.Add v.Async
125+
if indexes.Count = 0
126+
then AsyncVal<_> results
127+
else ofAsync (async {
128+
let! vals = continuations |> Async.Parallel
129+
for i = 0 to indexes.Count - 1 do
130+
results.[indexes.[i]] <- vals.[i]
131+
return results })
143132

144133
type AsyncValBuilder () =
145134
member x.Zero () = AsyncVal.empty

tests/FSharp.Data.GraphQL.Tests/AsyncValTests.fs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,36 @@ let ``AsyncVal can be bound inside Async computation`` () =
6565
let! v = asyncVal { return 1 }
6666
return v }
6767
let res = a |> sync
68-
res |> equals 1
68+
res |> equals 1
69+
70+
[<Fact>]
71+
let ``AsyncVal sequential collection resolves all values in order of execution`` () =
72+
let mutable flag = "none"
73+
let a = async {
74+
do! Async.Sleep 1000
75+
flag <- "a"
76+
return 2
77+
}
78+
let b = async {
79+
flag <- "b"
80+
return 4 }
81+
let array = [| AsyncVal.wrap 1; AsyncVal.ofAsync a; AsyncVal.wrap 3; AsyncVal.ofAsync b |]
82+
let v = array |> AsyncVal.collectSequential
83+
v |> AsyncVal.get |> equals [| 1; 2; 3; 4 |]
84+
flag |> equals "b"
85+
86+
[<Fact>]
87+
let ``AsyncVal parallel collection resolves all values with no order of execution`` () =
88+
let mutable flag = "none"
89+
let a = async {
90+
do! Async.Sleep 1000
91+
flag <- "a"
92+
return 2
93+
}
94+
let b = async {
95+
flag <- "b"
96+
return 4 }
97+
let array = [| AsyncVal.wrap 1; AsyncVal.ofAsync a; AsyncVal.wrap 3; AsyncVal.ofAsync b |]
98+
let v = array |> AsyncVal.collectParallel
99+
v |> AsyncVal.get |> equals [| 1; 2; 3; 4 |]
100+
flag |> equals "a"

0 commit comments

Comments
 (0)