Skip to content

Commit b096a3a

Browse files
VectorTetraViktor Tochonov
and
Viktor Tochonov
authored
Implemented missing In operator parsing and its tests for ObjectListFilter (#526)
--------- Co-authored-by: Viktor Tochonov <[email protected]>
1 parent 2a2ed41 commit b096a3a

File tree

2 files changed

+122
-7
lines changed

2 files changed

+122
-7
lines changed

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

+30-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ open System.Text.Json
99
open FSharp.Data.GraphQL
1010
open FSharp.Data.GraphQL.Types
1111
open FSharp.Data.GraphQL.Ast
12+
open FsToolkit.ErrorHandling
1213

1314
type private ComparisonOperator =
1415
| EndsWith of string
@@ -19,6 +20,7 @@ type private ComparisonOperator =
1920
| GreaterThanOrEqual of string
2021
| LessThan of string
2122
| LessThanOrEqual of string
23+
| In of string
2224

2325
let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption, IGQLError list> =
2426

@@ -39,24 +41,25 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption,
3941
| s when s.EndsWith ("_lt") && s.Length > "_lt".Length -> LessThan (prefix "_lt" s)
4042
| s when s.EndsWith ("_less_than_or_equal") && s.Length > "_less_than_or_equal".Length -> LessThanOrEqual (prefix "_less_than_or_equal" s)
4143
| s when s.EndsWith ("_lte") && s.Length > "_lte".Length -> LessThanOrEqual (prefix "_lte" s)
44+
| s when s.EndsWith ("_in") && s.Length > "_in".Length -> In (prefix "_in" s)
4245
| s -> Equals s
4346

44-
let (|EquatableValue|Other|) v =
47+
let (|EquatableValue|NonEquatableValue|) v =
4548
match v with
4649
| IntValue v -> EquatableValue (v :> System.IComparable)
4750
| FloatValue v -> EquatableValue (v :> System.IComparable)
4851
| BooleanValue v -> EquatableValue (v :> System.IComparable)
4952
| StringValue v -> EquatableValue (v :> System.IComparable)
5053
| EnumValue v -> EquatableValue (v :> System.IComparable)
51-
| v -> Other v
54+
| v -> NonEquatableValue v
5255

53-
let (|ComparableValue|Other|) v =
56+
let (|ComparableValue|NonComparableValue|) v =
5457
match v with
5558
| IntValue v -> ComparableValue (v :> System.IComparable)
5659
| FloatValue v -> ComparableValue (v :> System.IComparable)
5760
| BooleanValue v -> ComparableValue (v :> System.IComparable)
5861
| StringValue v -> ComparableValue (v :> System.IComparable)
59-
| v -> Other v
62+
| v -> NonComparableValue v
6063

6164
let buildAnd x =
6265
let rec build acc x =
@@ -98,7 +101,7 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption,
98101
| Ok (ValueSome filter) -> Ok (ValueSome (Not filter))
99102
| EndsWith fname, StringValue value -> Ok (ValueSome (ObjectListFilter.EndsWith { FieldName = fname; Value = value }))
100103
| StartsWith fname, StringValue value -> Ok (ValueSome (ObjectListFilter.StartsWith { FieldName = fname; Value = value }))
101-
| Contains fname, StringValue value -> Ok (ValueSome (ObjectListFilter.Contains { FieldName = fname; Value = value }))
104+
| Contains fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.Contains { FieldName = fname; Value = value }))
102105
| Equals fname, ObjectValue value ->
103106
match mapInput value with
104107
| Error errs -> Error errs
@@ -109,6 +112,20 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption,
109112
| GreaterThanOrEqual fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.GreaterThanOrEqual { FieldName = fname; Value = value }))
110113
| LessThan fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.LessThan { FieldName = fname; Value = value }))
111114
| LessThanOrEqual fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.LessThanOrEqual { FieldName = fname; Value = value }))
115+
| In fname, ListValue values -> result {
116+
let! parsedValues =
117+
values
118+
|> Seq.map (function
119+
| EquatableValue v -> Ok v
120+
| NonEquatableValue v ->
121+
Error
122+
{ new IGQLError with
123+
member _.Message = $"Cannot coerce '{v.GetType ()}' to 'System.IComparable'"
124+
})
125+
|> Seq.toList
126+
|> splitSeqErrors
127+
return ValueSome (ObjectListFilter.In { FieldName = fname; Value = parsedValues |> Array.toList })
128+
}
112129
| _ -> Ok ValueNone
113130

114131
and mapInput value =
@@ -172,7 +189,13 @@ let ObjectListFilterType : ScalarDefinition<ObjectListFilter> = {
172189
"The `Filter` scalar type represents a filter on one or more fields of an object in an object list. The filter is represented by a JSON object where the fields are the complemented by specific suffixes to represent a query."
173190
CoerceInput =
174191
(function
175-
| InlineConstant c -> coerceObjectListFilterInput c |> Result.map ValueOption.toObj
176-
| Variable json -> json |> jsonElementToInputValue |> coerceObjectListFilterInput |> Result.map ValueOption.toObj)
192+
| InlineConstant c ->
193+
coerceObjectListFilterInput c
194+
|> Result.map ValueOption.toObj
195+
| Variable json ->
196+
json
197+
|> jsonElementToInputValue
198+
|> coerceObjectListFilterInput
199+
|> Result.map ValueOption.toObj)
177200
CoerceOutput = coerceObjectListFilterValue
178201
}

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

+92
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,98 @@ let ``Object list filter: Must return OR filter information in Metadata`` () =
701701
data |> equals (upcast expected)
702702
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]
703703

704+
[<Fact>]
705+
let ``Object list filter: Must return IN filter information in Metadata`` () =
706+
let query =
707+
parse
708+
"""query testQuery {
709+
A (id : 1) {
710+
id
711+
value
712+
subjects (filter : { value_in : ["3000", "A2"] }) { ...Value }
713+
}
714+
}
715+
716+
fragment Value on Subject {
717+
...on A {
718+
id
719+
value
720+
}
721+
...on B {
722+
id
723+
value
724+
}
725+
}"""
726+
let expected =
727+
NameValueLookup.ofList [
728+
"A",
729+
upcast
730+
NameValueLookup.ofList [
731+
"id", upcast 1
732+
"value", upcast "A1"
733+
"subjects",
734+
upcast
735+
[
736+
NameValueLookup.ofList [ "id", upcast 2; "value", upcast "A2" ]
737+
NameValueLookup.ofList [ "id", upcast 6; "value", upcast "3000" ]
738+
]
739+
]
740+
]
741+
let expectedFilter : KeyValuePair<obj list, _> =
742+
kvp ([ "A"; "subjects" ]) (In { FieldName = "value"; Value = [ "3000"; "A2" ] })
743+
let result = execute query
744+
745+
ensureDirect result <| fun data errors ->
746+
empty errors
747+
data |> equals (upcast expected)
748+
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]
749+
750+
[<Fact>]
751+
let ``Object list filter: Must return Contains filter information in Metadata`` () =
752+
let query =
753+
parse
754+
"""query testQuery {
755+
A (id : 1) {
756+
id
757+
value
758+
subjects (filter : { value_contains : "3"}) { ...Value }
759+
}
760+
}
761+
762+
fragment Value on Subject {
763+
...on A {
764+
id
765+
value
766+
}
767+
...on B {
768+
id
769+
value
770+
}
771+
}"""
772+
let expected =
773+
NameValueLookup.ofList [
774+
"A",
775+
upcast
776+
NameValueLookup.ofList [
777+
"id", upcast 1
778+
"value", upcast "A1"
779+
"subjects",
780+
upcast
781+
[
782+
NameValueLookup.ofList [ "id", upcast 2; "value", upcast "A2" ]
783+
NameValueLookup.ofList [ "id", upcast 6; "value", upcast "3000" ]
784+
]
785+
]
786+
]
787+
let expectedFilter : KeyValuePair<obj list, _> =
788+
kvp ([ "A"; "subjects" ]) (Contains { FieldName = "value"; Value = "3" })
789+
let result = execute query
790+
791+
ensureDirect result <| fun data errors ->
792+
empty errors
793+
data |> equals (upcast expected)
794+
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]
795+
704796
[<Fact>]
705797
let ``Object list filter: Must return NOT filter information in Metadata`` () =
706798
let query =

0 commit comments

Comments
 (0)