Skip to content

Commit a7ba150

Browse files
VectorTetraViktor Tochonov
and
Viktor Tochonov
authoredMar 18, 2025··
Additional list filter operators (#518)
* Implemented new `ObjectListFilter` operators: `In`, `GreaterThanOrEqual`, `LessThanOrEqual` * Changed comments in buildTypeDiscriminatorCheck --------- Co-authored-by: Viktor Tochonov <viktor@ecierge.com>
1 parent 8dd7d03 commit a7ba150

File tree

5 files changed

+623
-303
lines changed

5 files changed

+623
-303
lines changed
 

‎src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs

+26-9
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ type ObjectListFilter =
1212
| Not of ObjectListFilter
1313
| Equals of FieldFilter<System.IComparable>
1414
| GreaterThan of FieldFilter<System.IComparable>
15+
| GreaterThanOrEqual of FieldFilter<System.IComparable>
1516
| LessThan of FieldFilter<System.IComparable>
17+
| LessThanOrEqual of FieldFilter<System.IComparable>
18+
| In of FieldFilter<System.IComparable list>
1619
| StartsWith of FieldFilter<string>
1720
| EndsWith of FieldFilter<string>
1821
| Contains of FieldFilter<string>
@@ -92,10 +95,16 @@ module ObjectListFilter =
9295
let ( === ) fname value = Equals { FieldName = fname; Value = value }
9396

9497
/// Creates a new ObjectListFilter representing a GREATER THAN operation of a comparable value.
95-
let ( ==> ) fname value = GreaterThan { FieldName = fname; Value = value }
98+
let ( >>> ) fname value = GreaterThan { FieldName = fname; Value = value }
99+
100+
/// Creates a new ObjectListFilter representing a GREATER THAN OR EQUAL operation of a comparable value.
101+
let ( ==> ) fname value = GreaterThanOrEqual { FieldName = fname; Value = value }
96102

97103
/// Creates a new ObjectListFilter representing a LESS THAN operation of a comparable value.
98-
let ( <== ) fname value = LessThan { FieldName = fname; Value = value }
104+
let ( <<< ) fname value = LessThan { FieldName = fname; Value = value }
105+
106+
/// Creates a new ObjectListFilter representing a LESS THAN OR EQUAL operation of a comparable value.
107+
let ( <== ) fname value = LessThanOrEqual { FieldName = fname; Value = value }
99108

100109
/// Creates a new ObjectListFilter representing a STARTS WITH operation of a string value.
101110
let ( =@@ ) fname value = StartsWith { FieldName = fname; Value = value }
@@ -106,6 +115,9 @@ module ObjectListFilter =
106115
/// Creates a new ObjectListFilter representing a CONTAINS operation.
107116
let ( @=@ ) fname value = Contains { FieldName = fname; Value = value }
108117

118+
/// Creates a new ObjectListFilter representing a IN operation.
119+
let ( =~= ) fname value = In { FieldName = fname; Value = value }
120+
109121
/// Creates a new ObjectListFilter representing a field sub comparison.
110122
let ( --> ) fname filter = FilterField { FieldName = fname; Value = filter }
111123

@@ -169,6 +181,8 @@ module ObjectListFilter =
169181
| Equals f -> Expression.Equal (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
170182
| GreaterThan f -> Expression.GreaterThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
171183
| LessThan f -> Expression.LessThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
184+
| GreaterThanOrEqual f -> Expression.GreaterThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
185+
| LessThanOrEqual f -> Expression.LessThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
172186
| StartsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringStartsWithMethod, Expression.Constant (f.Value))
173187
| EndsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringEndsWithMethod, Expression.Constant (f.Value))
174188
| Contains f ->
@@ -201,6 +215,12 @@ module ObjectListFilter =
201215
Expression.Constant (f.Value)
202216
)
203217
| _ -> Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value))
218+
| In f ->
219+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
220+
f.Value
221+
|> Seq.map (fun v -> Expression.Equal (``member``, Expression.Constant (v)))
222+
|> Seq.reduce (fun acc expr -> Expression.OrElse (acc, expr))
223+
:> Expression
204224
| OfTypes types ->
205225
types
206226
|> Seq.map (fun t -> buildTypeDiscriminatorCheck param t)
@@ -222,26 +242,23 @@ module ObjectListFilter =
222242
Expression.PropertyOrField (param, "__typename"),
223243
// Default discriminator value
224244
Expression.Constant (t.FullName)
225-
)
226-
:> Expression
245+
) :> Expression
227246
| ValueSome discExpr, ValueNone ->
228247
Expression.Invoke (
229248
// Provided discriminator comparison
230249
discExpr,
231250
param,
232251
// Default discriminator value gathered from type
233-
Expression.Constant (t.FullName)
234-
)
235-
:> Expression
252+
Expression.Constant(t.FullName)
253+
) :> Expression
236254
| ValueNone, ValueSome discValueFn ->
237255
let discriminatorValue = discValueFn t
238256
Expression.Equal (
239257
// Default discriminator property
240258
Expression.PropertyOrField (param, "__typename"),
241259
// Provided discriminator value gathered from type
242260
Expression.Constant (discriminatorValue)
243-
)
244-
:> Expression
261+
) :> Expression
245262
| ValueSome discExpr, ValueSome discValueFn ->
246263
let discriminatorValue = discValueFn t
247264
Expression.Invoke (

‎src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs

+25-9
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,35 @@ open FSharp.Data.GraphQL.Ast
1212

1313
let internal removeNoFilter = Seq.where (fun filter -> filter <> NoFilter)
1414

15+
type private ComparisonOperator =
16+
| EndsWith of string
17+
| StartsWith of string
18+
| Contains of string
19+
| Equals of string
20+
| GreaterThan of string
21+
| GreaterThanOrEqual of string
22+
| LessThan of string
23+
| LessThanOrEqual of string
24+
1525
let rec private coerceObjectListFilterInput x : Result<ObjectListFilter, IGQLError list> =
1626

17-
let (|EndsWith|StartsWith|GreaterThan|LessThan|Contains|Equals|) (s : string) =
27+
let parseFieldCondition (s : string) =
1828
let s = s.ToLowerInvariant ()
1929
let prefix (suffix : string) (s : string) = s.Substring (0, s.Length - suffix.Length)
2030
match s with
2131
| s when s.EndsWith ("_ends_with") && s.Length > "_ends_with".Length -> EndsWith (prefix "_ends_with" s)
2232
| s when s.EndsWith ("_ew") && s.Length > "_ew".Length -> EndsWith (prefix "_ew" s)
2333
| s when s.EndsWith ("_starts_with") && s.Length > "_starts_with".Length -> StartsWith (prefix "_starts_with" s)
2434
| s when s.EndsWith ("_sw") && s.Length > "_sw".Length -> StartsWith (prefix "_sw" s)
35+
| s when s.EndsWith ("_contains") && s.Length > "_contains".Length -> Contains (prefix "_contains" s)
2536
| s when s.EndsWith ("_greater_than") && s.Length > "_greater_than".Length -> GreaterThan (prefix "_greater_than" s)
2637
| s when s.EndsWith ("_gt") && s.Length > "_gt".Length -> GreaterThan (prefix "_gt" s)
38+
| s when s.EndsWith ("_greater_than_or_equal") && s.Length > "_greater_than_or_equal".Length -> GreaterThanOrEqual (prefix "_greater_than_or_equal" s)
39+
| s when s.EndsWith ("_gte") && s.Length > "_gte".Length -> GreaterThanOrEqual (prefix "_gte" s)
2740
| s when s.EndsWith ("_less_than") && s.Length > "_less_than".Length -> LessThan (prefix "_less_than" s)
2841
| s when s.EndsWith ("_lt") && s.Length > "_lt".Length -> LessThan (prefix "_lt" s)
29-
| s when s.EndsWith ("_contains") && s.Length > "_contains".Length -> Contains (prefix "_contains" s)
42+
| s when s.EndsWith ("_less_than_or_equal") && s.Length > "_less_than_or_equal".Length -> LessThanOrEqual (prefix "_less_than_or_equal" s)
43+
| s when s.EndsWith ("_lte") && s.Length > "_lte".Length -> LessThanOrEqual (prefix "_lte" s)
3044
| s -> Equals s
3145

3246
let (|EquatableValue|Other|) v =
@@ -76,25 +90,27 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter, IGQLErr
7690
match coerceResults with
7791
| Error errs -> Error errs
7892
| Ok coerced -> coerced |> removeNoFilter |> Seq.toList |> Ok
79-
match name, value with
93+
match parseFieldCondition name, value with
8094
| Equals "and", ListValue fields -> fields |> mapFilters |> Result.map buildAnd
8195
| Equals "or", ListValue fields -> fields |> mapFilters |> Result.map buildOr
8296
| Equals "not", ObjectValue value ->
8397
match mapInput value with
8498
| Error errs -> Error errs
8599
| Ok NoFilter -> Ok NoFilter
86100
| Ok filter -> Ok (Not filter)
87-
| EndsWith fname, StringValue value -> Ok (EndsWith { FieldName = fname; Value = value })
88-
| StartsWith fname, StringValue value -> Ok (StartsWith { FieldName = fname; Value = value })
89-
| Contains fname, StringValue value -> Ok (Contains { FieldName = fname; Value = value })
101+
| EndsWith fname, StringValue value -> Ok (ObjectListFilter.EndsWith { FieldName = fname; Value = value })
102+
| StartsWith fname, StringValue value -> Ok (ObjectListFilter.StartsWith { FieldName = fname; Value = value })
103+
| Contains fname, StringValue value -> Ok (ObjectListFilter.Contains { FieldName = fname; Value = value })
90104
| Equals fname, ObjectValue value ->
91105
match mapInput value with
92106
| Error errs -> Error errs
93107
| Ok NoFilter -> Ok NoFilter
94108
| Ok filter -> Ok (FilterField { FieldName = fname; Value = filter })
95-
| Equals fname, EquatableValue value -> Ok (Equals { FieldName = fname; Value = value })
96-
| GreaterThan fname, ComparableValue value -> Ok (GreaterThan { FieldName = fname; Value = value })
97-
| LessThan fname, ComparableValue value -> Ok (LessThan { FieldName = fname; Value = value })
109+
| Equals fname, EquatableValue value -> Ok (ObjectListFilter.Equals { FieldName = fname; Value = value })
110+
| GreaterThan fname, ComparableValue value -> Ok (ObjectListFilter.GreaterThan { FieldName = fname; Value = value })
111+
| GreaterThanOrEqual fname, ComparableValue value -> Ok (ObjectListFilter.GreaterThanOrEqual { FieldName = fname; Value = value })
112+
| LessThan fname, ComparableValue value -> Ok (ObjectListFilter.LessThan { FieldName = fname; Value = value })
113+
| LessThanOrEqual fname, ComparableValue value -> Ok (ObjectListFilter.LessThanOrEqual { FieldName = fname; Value = value })
98114
| _ -> Ok NoFilter
99115

100116
and mapInput value =

‎src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace rec FSharp.Data.GraphQL
22

33
open System.Linq
4+
open System.Collections.Generic
45
open FsToolkit.ErrorHandling
56

67
module internal ValueOption =
@@ -11,6 +12,12 @@ module internal Option =
1112

1213
let mapValueOption mapping voption = voption |> ValueOption.map mapping |> ValueOption.toOption
1314

15+
[<AutoOpen>]
16+
module KeyValuePair =
17+
18+
let inline kvp key value = KeyValuePair (key, value)
19+
let inline kvpObj key (value : obj) = KeyValuePair (key, value)
20+
1421
[<AutoOpen>]
1522
module internal ValueTuple =
1623

‎tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs

+214-28
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ module FSharp.Data.GraphQL.Tests.MiddlewareTests
22

33
open System
44
open System.Collections.Generic
5+
open System.Collections.Immutable
6+
open System.Text.Json
57
open Xunit
8+
open FSharp
69
open FSharp.Data.GraphQL
710
open FSharp.Data.GraphQL.Types
811
open FSharp.Data.GraphQL.Server.Middleware
912
open FSharp.Data.GraphQL.Shared
1013
open FSharp.Data.GraphQL.Parser
1114
open FSharp.Data.GraphQL.Execution
1215
open FSharp.Data.GraphQL.Ast
13-
open System.Collections.Immutable
14-
open System.Text.Json
1516

1617
#nowarn "40"
1718

@@ -25,7 +26,7 @@ and A = { id : int; value : string; subjects : int list }
2526

2627
and B = { id : int; value : string; subjects : int list }
2728

28-
let executor =
29+
let getExecutor (expectedFilter : ObjectListFilter voption) =
2930
let a1 : A = { id = 1; value = "A1"; subjects = [ 2; 6 ] }
3031
let a2 : A = { id = 2; value = "A2"; subjects = [ 1; 3; 5 ] }
3132
let a3 : A = { id = 3; value = "A3"; subjects = [ 1; 2; 4 ] }
@@ -70,7 +71,11 @@ let executor =
7071
.Field(
7172
"subjects",
7273
Nullable (ListOf (Nullable SubjectType)),
73-
resolve = fun _ (a : A) -> a.subjects |> List.map getSubject |> List.toSeq |> Some
74+
resolve =
75+
fun ctx (a : A) ->
76+
expectedFilter
77+
|> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter)
78+
a.subjects |> List.map getSubject |> List.toSeq |> Some
7479
)
7580
.WithQueryWeight (1.0)
7681
]
@@ -87,7 +92,11 @@ let executor =
8792
.Field(
8893
"subjects",
8994
Nullable (ListOf (Nullable SubjectType)),
90-
resolve = fun _ (b : B) -> b.subjects |> List.map getSubject |> List.toSeq |> Some
95+
resolve =
96+
fun ctx (b : B) ->
97+
expectedFilter
98+
|> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter)
99+
b.subjects |> List.map getSubject |> List.toSeq |> Some
91100
)
92101
.WithQueryWeight (1.0)
93102
]
@@ -108,10 +117,17 @@ let executor =
108117
]
109118
Executor (schema, middleware)
110119

120+
let executor = getExecutor (ValueNone)
121+
111122
let execute (query : Document) = executor.AsyncExecute (query) |> sync
112123

113124
let executeWithVariables (query : Document, variables : ImmutableDictionary<string, JsonElement>) =
114-
executor.AsyncExecute (ast = query, variables = variables) |> sync
125+
executor.AsyncExecute (ast = query, variables = variables)
126+
|> sync
127+
128+
let executeWithCustomFilter (query : Document, variables : ImmutableDictionary<string, JsonElement>, customFilter : ObjectListFilter) =
129+
let ex = getExecutor (ValueSome customFilter)
130+
ex.AsyncExecute (ast = query, variables = variables) |> sync
115131

116132
let expectedErrors : GQLProblemDetails list = [
117133
GQLProblemDetails.Create ("Query complexity exceeds maximum threshold. Please reduce query complexity and try again.")
@@ -494,8 +510,8 @@ let ``Object list filter: must return filter information in Metadata`` () =
494510
]
495511
]
496512
]
497-
let expectedFilter : KeyValuePair<obj list, ObjectListFilter> =
498-
KeyValuePair ([ "A"; "s" ], And (Equals { FieldName = "id"; Value = 2L }, StartsWith { FieldName = "value"; Value = "A" }))
513+
let expectedFilter : KeyValuePair<obj list, _> =
514+
kvp ([ "A"; "s" ]) (And (Equals { FieldName = "id"; Value = 2L }, StartsWith { FieldName = "value"; Value = "A" }))
499515
let result = execute query
500516
ensureDirect result <| fun data errors ->
501517
empty errors
@@ -543,8 +559,8 @@ let ``Object list filter: Must return AND filter information in Metadata`` () =
543559
]
544560
]
545561
]
546-
let expectedFilter : KeyValuePair<obj list, ObjectListFilter> =
547-
KeyValuePair ([ "A"; "subjects" ], And (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L }))
562+
let expectedFilter : KeyValuePair<obj list, _> =
563+
kvp ([ "A"; "subjects" ]) (And (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L }))
548564
let result = execute query
549565
ensureDirect result <| fun data errors ->
550566
empty errors
@@ -590,8 +606,8 @@ let ``Object list filter: Must return OR filter information in Metadata`` () =
590606
]
591607
]
592608
]
593-
let expectedFilter : KeyValuePair<obj list, ObjectListFilter> =
594-
KeyValuePair ([ "A"; "subjects" ], Or (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L }))
609+
let expectedFilter : KeyValuePair<obj list, _> =
610+
kvp ([ "A"; "subjects" ]) (Or (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L }))
595611
let result = execute query
596612
ensureDirect result <| fun data errors ->
597613
empty errors
@@ -637,8 +653,8 @@ let ``Object list filter: Must return NOT filter information in Metadata`` () =
637653
]
638654
]
639655
]
640-
let expectedFilter : KeyValuePair<obj list, ObjectListFilter> =
641-
KeyValuePair ([ "A"; "subjects" ], Not (StartsWith { FieldName = "value"; Value = "3" }))
656+
let expectedFilter : KeyValuePair<obj list, _> =
657+
kvp ([ "A"; "subjects" ]) (Not (StartsWith { FieldName = "value"; Value = "3" }))
642658
let result = execute query
643659
ensureDirect result <| fun data errors ->
644660
empty errors
@@ -648,11 +664,7 @@ let ``Object list filter: Must return NOT filter information in Metadata`` () =
648664
|> seqEquals [ expectedFilter ]
649665

650666
[<Fact>]
651-
let ``Object list filter: Must return filter information in Metadata when supplied as variable`` () =
652-
let jsonString = """{ "not": { "value_starts_with": "3" } }"""
653-
let jsonElement = JsonDocument.Parse(jsonString).RootElement
654-
655-
let dict = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", jsonElement)
667+
let ``Object list filter: Must return filter information in Metadata when supplied as variable and parse all filter operators`` () =
656668
let query =
657669
parse
658670
"""query testQuery($filter: ObjectListFilter!) {
@@ -688,12 +700,186 @@ let ``Object list filter: Must return filter information in Metadata when suppli
688700
]
689701
]
690702
]
691-
let expectedFilter : KeyValuePair<obj list, ObjectListFilter> =
692-
KeyValuePair ([ "A"; "subjects" ], Not (StartsWith { FieldName = "value"; Value = "3" }))
693-
let result = executeWithVariables (query, dict)
694-
ensureDirect result <| fun data errors ->
695-
empty errors
696-
data |> equals (upcast expected)
697-
result.Metadata.TryFind<ObjectListFilters> ("filters")
698-
|> wantValueSome
699-
|> seqEquals [ expectedFilter ]
703+
do
704+
let notStartsFilter = """{ "not": { "value_starts_with": "3" } }""" |> JsonDocument.Parse |> _.RootElement
705+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notStartsFilter)
706+
let filter = Not (StartsWith { FieldName = "value"; Value = "3" })
707+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
708+
let result = executeWithCustomFilter (query, variables, filter)
709+
ensureDirect result <| fun data errors ->
710+
empty errors
711+
data |> equals (upcast expected)
712+
result.Metadata.TryFind<ObjectListFilters> ("filters")
713+
|> wantValueSome
714+
|> seqEquals [ expectedFilter ]
715+
716+
do
717+
let notEndsFilter = """{ "not": { "value_ends_with": "2" } }""" |> JsonDocument.Parse |> _.RootElement
718+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notEndsFilter)
719+
let filter = Not (EndsWith { FieldName = "value"; Value = "2" })
720+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
721+
let result = executeWithCustomFilter (query, variables, filter)
722+
ensureDirect result <| fun data errors ->
723+
empty errors
724+
data |> equals (upcast expected)
725+
result.Metadata.TryFind<ObjectListFilters> ("filters")
726+
|> wantValueSome
727+
|> seqEquals [ expectedFilter ]
728+
729+
do
730+
let notStartsFilter = """{ "not": { "value_sw": "3" } }""" |> JsonDocument.Parse |> _.RootElement
731+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notStartsFilter)
732+
let filter = Not (StartsWith { FieldName = "value"; Value = "3" })
733+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
734+
let result = executeWithCustomFilter (query, variables, filter)
735+
ensureDirect result <| fun data errors ->
736+
empty errors
737+
data |> equals (upcast expected)
738+
result.Metadata.TryFind<ObjectListFilters> ("filters")
739+
|> wantValueSome
740+
|> seqEquals [ expectedFilter ]
741+
742+
do
743+
let notEndsFilter = """{ "not": { "value_ew": "2" } }""" |> JsonDocument.Parse |> _.RootElement
744+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notEndsFilter)
745+
let filter = Not (EndsWith { FieldName = "value"; Value = "2" })
746+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
747+
let result = executeWithCustomFilter (query, variables, filter)
748+
ensureDirect result <| fun data errors ->
749+
empty errors
750+
data |> equals (upcast expected)
751+
result.Metadata.TryFind<ObjectListFilters> ("filters")
752+
|> wantValueSome
753+
|> seqEquals [ expectedFilter ]
754+
755+
do
756+
let notGreaterThanOrEqualFilter = """{ "not": { "id_greater_than_or_equal": 2 } }""" |> JsonDocument.Parse |> _.RootElement
757+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notGreaterThanOrEqualFilter)
758+
let filter = Not (GreaterThanOrEqual { FieldName = "id"; Value = 2.0 })
759+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
760+
let result = executeWithCustomFilter (query, variables,filter)
761+
ensureDirect result <| fun data errors ->
762+
empty errors
763+
data |> equals (upcast expected)
764+
result.Metadata.TryFind<ObjectListFilters> ("filters")
765+
|> wantValueSome
766+
|> seqEquals [ expectedFilter ]
767+
768+
do
769+
let notLessThanOrEqualFilter = """{ "not": { "id_less_than_or_equal": 4 } }""" |> JsonDocument.Parse |> _.RootElement
770+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notLessThanOrEqualFilter)
771+
let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 })
772+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
773+
let result = executeWithCustomFilter (query, variables, filter)
774+
ensureDirect result <| fun data errors ->
775+
empty errors
776+
data |> equals (upcast expected)
777+
result.Metadata.TryFind<ObjectListFilters> ("filters")
778+
|> wantValueSome
779+
|> seqEquals [ expectedFilter ]
780+
781+
do
782+
let notGreaterThanFilter = """{ "not": { "id_greater_than": 2 } }""" |> JsonDocument.Parse |> _.RootElement
783+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notGreaterThanFilter)
784+
let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 })
785+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
786+
let result = executeWithCustomFilter (query, variables, filter)
787+
ensureDirect result <| fun data errors ->
788+
empty errors
789+
data |> equals (upcast expected)
790+
result.Metadata.TryFind<ObjectListFilters> ("filters")
791+
|> wantValueSome
792+
|> seqEquals [ expectedFilter ]
793+
794+
do
795+
let notLessThanFilter = """{ "not": { "id_less_than": 4 } }""" |> JsonDocument.Parse |> _.RootElement
796+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notLessThanFilter)
797+
let filter = Not (LessThan { FieldName = "id"; Value = 4.0 })
798+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
799+
let result = executeWithCustomFilter (query, variables, filter)
800+
ensureDirect result
801+
<| fun data errors ->
802+
empty errors
803+
data |> equals (upcast expected)
804+
result.Metadata.TryFind<ObjectListFilters> ("filters")
805+
|> wantValueSome
806+
|> seqEquals [ expectedFilter ]
807+
808+
do
809+
let notGreaterThanOrEqualFilter = """{ "not": { "id_gte": 2 } }""" |> JsonDocument.Parse |> _.RootElement
810+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notGreaterThanOrEqualFilter)
811+
let filter = Not (GreaterThanOrEqual { FieldName = "id"; Value = 2.0 })
812+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
813+
let result = executeWithCustomFilter (query, variables,filter)
814+
ensureDirect result <| fun data errors ->
815+
empty errors
816+
data |> equals (upcast expected)
817+
result.Metadata.TryFind<ObjectListFilters> ("filters")
818+
|> wantValueSome
819+
|> seqEquals [ expectedFilter ]
820+
821+
do
822+
let notLessThanOrEqualFilter = """{ "not": { "id_lte": 4 } }""" |> JsonDocument.Parse |> _.RootElement
823+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notLessThanOrEqualFilter)
824+
let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 })
825+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
826+
let result = executeWithCustomFilter (query, variables, filter)
827+
ensureDirect result <| fun data errors ->
828+
empty errors
829+
data |> equals (upcast expected)
830+
result.Metadata.TryFind<ObjectListFilters> ("filters")
831+
|> wantValueSome
832+
|> seqEquals [ expectedFilter ]
833+
834+
do
835+
let notGreaterThanFilter = """{ "not": { "id_gt": 2 } }""" |> JsonDocument.Parse |> _.RootElement
836+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notGreaterThanFilter)
837+
let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 })
838+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
839+
let result = executeWithCustomFilter (query, variables, filter)
840+
ensureDirect result <| fun data errors ->
841+
empty errors
842+
data |> equals (upcast expected)
843+
result.Metadata.TryFind<ObjectListFilters> ("filters")
844+
|> wantValueSome
845+
|> seqEquals [ expectedFilter ]
846+
847+
do
848+
let notLessThanFilter = """{ "not": { "id_lt": 4 } }""" |> JsonDocument.Parse |> _.RootElement
849+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notLessThanFilter)
850+
let filter = Not (LessThan { FieldName = "id"; Value = 4.0 })
851+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
852+
let result = executeWithCustomFilter (query, variables, filter)
853+
ensureDirect result
854+
<| fun data errors ->
855+
empty errors
856+
data |> equals (upcast expected)
857+
result.Metadata.TryFind<ObjectListFilters> ("filters")
858+
|> wantValueSome
859+
|> seqEquals [ expectedFilter ]
860+
861+
do
862+
let notContainsFilter = """{ "not": { "value_contains": "A" } }""" |> JsonDocument.Parse |> _.RootElement
863+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notContainsFilter)
864+
let filter = Not (Contains { FieldName = "value"; Value = "A" })
865+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
866+
let result = executeWithCustomFilter (query, variables, filter)
867+
ensureDirect result <| fun data errors ->
868+
empty errors
869+
data |> equals (upcast expected)
870+
result.Metadata.TryFind<ObjectListFilters> ("filters")
871+
|> wantValueSome
872+
|> seqEquals [ expectedFilter ]
873+
874+
do
875+
let notEqualsFilter = """{ "not": { "value": "A2" } }""" |> JsonDocument.Parse |> _.RootElement
876+
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("filter", notEqualsFilter)
877+
let filter = Not (Equals { FieldName = "value"; Value = "A2" })
878+
let expectedFilter : KeyValuePair<obj list, _> = kvp ([ "A"; "subjects" ]) (filter)
879+
let result = executeWithCustomFilter (query, variables, filter)
880+
ensureDirect result <| fun data errors ->
881+
empty errors
882+
data |> equals (upcast expected)
883+
result.Metadata.TryFind<ObjectListFilters> ("filters")
884+
|> wantValueSome
885+
|> seqEquals [ expectedFilter ]

‎tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs

+351-257
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.