Skip to content

Implemented missing In operator parsing and its tests for ObjectListFilter #526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
37 changes: 30 additions & 7 deletions src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ open System.Text.Json
open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types
open FSharp.Data.GraphQL.Ast
open FsToolkit.ErrorHandling

type private ComparisonOperator =
| EndsWith of string
Expand All @@ -19,6 +20,7 @@ type private ComparisonOperator =
| GreaterThanOrEqual of string
| LessThan of string
| LessThanOrEqual of string
| In of string

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

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

let (|EquatableValue|Other|) v =
let (|EquatableValue|NonEquatableValue|) v =
match v with
| IntValue v -> EquatableValue (v :> System.IComparable)
| FloatValue v -> EquatableValue (v :> System.IComparable)
| BooleanValue v -> EquatableValue (v :> System.IComparable)
| StringValue v -> EquatableValue (v :> System.IComparable)
| EnumValue v -> EquatableValue (v :> System.IComparable)
| v -> Other v
| v -> NonEquatableValue v

let (|ComparableValue|Other|) v =
let (|ComparableValue|NonComparableValue|) v =
match v with
| IntValue v -> ComparableValue (v :> System.IComparable)
| FloatValue v -> ComparableValue (v :> System.IComparable)
| BooleanValue v -> ComparableValue (v :> System.IComparable)
| StringValue v -> ComparableValue (v :> System.IComparable)
| v -> Other v
| v -> NonComparableValue v

let buildAnd x =
let rec build acc x =
Expand Down Expand Up @@ -98,7 +101,7 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption,
| Ok (ValueSome filter) -> Ok (ValueSome (Not filter))
| EndsWith fname, StringValue value -> Ok (ValueSome (ObjectListFilter.EndsWith { FieldName = fname; Value = value }))
| StartsWith fname, StringValue value -> Ok (ValueSome (ObjectListFilter.StartsWith { FieldName = fname; Value = value }))
| Contains fname, StringValue value -> Ok (ValueSome (ObjectListFilter.Contains { FieldName = fname; Value = value }))
| Contains fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.Contains { FieldName = fname; Value = value }))
| Equals fname, ObjectValue value ->
match mapInput value with
| Error errs -> Error errs
Expand All @@ -109,6 +112,20 @@ let rec private coerceObjectListFilterInput x : Result<ObjectListFilter voption,
| GreaterThanOrEqual fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.GreaterThanOrEqual { FieldName = fname; Value = value }))
| LessThan fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.LessThan { FieldName = fname; Value = value }))
| LessThanOrEqual fname, ComparableValue value -> Ok (ValueSome (ObjectListFilter.LessThanOrEqual { FieldName = fname; Value = value }))
| In fname, ListValue values -> result {
let! parsedValues =
values
|> Seq.map (function
| EquatableValue v -> Ok v
| NonEquatableValue v ->
Error
{ new IGQLError with
member _.Message = $"Cannot coerce '{v.GetType ()}' to 'System.IComparable'"
})
|> Seq.toList
|> splitSeqErrors
return ValueSome (ObjectListFilter.In { FieldName = fname; Value = parsedValues |> Array.toList })
}
| _ -> Ok ValueNone

and mapInput value =
Expand Down Expand Up @@ -172,7 +189,13 @@ let ObjectListFilterType : ScalarDefinition<ObjectListFilter> = {
"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."
CoerceInput =
(function
| InlineConstant c -> coerceObjectListFilterInput c |> Result.map ValueOption.toObj
| Variable json -> json |> jsonElementToInputValue |> coerceObjectListFilterInput |> Result.map ValueOption.toObj)
| InlineConstant c ->
coerceObjectListFilterInput c
|> Result.map ValueOption.toObj
| Variable json ->
json
|> jsonElementToInputValue
|> coerceObjectListFilterInput
|> Result.map ValueOption.toObj)
CoerceOutput = coerceObjectListFilterValue
}
92 changes: 92 additions & 0 deletions tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,98 @@ let ``Object list filter: Must return OR filter information in Metadata`` () =
data |> equals (upcast expected)
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]

[<Fact>]
let ``Object list filter: Must return IN filter information in Metadata`` () =
let query =
parse
"""query testQuery {
A (id : 1) {
id
value
subjects (filter : { value_in : ["3000", "A2"] }) { ...Value }
}
}

fragment Value on Subject {
...on A {
id
value
}
...on B {
id
value
}
}"""
let expected =
NameValueLookup.ofList [
"A",
upcast
NameValueLookup.ofList [
"id", upcast 1
"value", upcast "A1"
"subjects",
upcast
[
NameValueLookup.ofList [ "id", upcast 2; "value", upcast "A2" ]
NameValueLookup.ofList [ "id", upcast 6; "value", upcast "3000" ]
]
]
]
let expectedFilter : KeyValuePair<obj list, _> =
kvp ([ "A"; "subjects" ]) (In { FieldName = "value"; Value = [ "3000"; "A2" ] })
let result = execute query

ensureDirect result <| fun data errors ->
empty errors
data |> equals (upcast expected)
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]

[<Fact>]
let ``Object list filter: Must return Contains filter information in Metadata`` () =
let query =
parse
"""query testQuery {
A (id : 1) {
id
value
subjects (filter : { value_contains : "3"}) { ...Value }
}
}

fragment Value on Subject {
...on A {
id
value
}
...on B {
id
value
}
}"""
let expected =
NameValueLookup.ofList [
"A",
upcast
NameValueLookup.ofList [
"id", upcast 1
"value", upcast "A1"
"subjects",
upcast
[
NameValueLookup.ofList [ "id", upcast 2; "value", upcast "A2" ]
NameValueLookup.ofList [ "id", upcast 6; "value", upcast "3000" ]
]
]
]
let expectedFilter : KeyValuePair<obj list, _> =
kvp ([ "A"; "subjects" ]) (Contains { FieldName = "value"; Value = "3" })
let result = execute query

ensureDirect result <| fun data errors ->
empty errors
data |> equals (upcast expected)
result.Metadata.TryFind<ObjectListFilters> ("filters") |> wantValueSome |> seqEquals [ expectedFilter ]

[<Fact>]
let ``Object list filter: Must return NOT filter information in Metadata`` () =
let query =
Expand Down