diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs index 4f3ce016..f3977dfa 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs +++ b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs @@ -224,7 +224,7 @@ module ObjectListFilter = | OfTypes types -> types |> Seq.map (fun t -> buildTypeDiscriminatorCheck param t) - |> Seq.reduce (fun acc expr -> Expression.Or (acc, expr)) + |> Seq.reduce (fun acc expr -> Expression.OrElse (acc, expr)) | FilterField f -> let paramExpr = Expression.PropertyOrField (param, f.FieldName) buildFilterExpr (SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value diff --git a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs index 7d6f68ab..4de59cf5 100644 --- a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs @@ -1,6 +1,7 @@ module FSharp.Data.GraphQL.Tests.MiddlewareTests open System +open System.Linq open System.Collections.Generic open System.Collections.Immutable open System.Text.Json @@ -22,27 +23,49 @@ and Subject = | A of A | B of B -and A = { id : int; value : string; subjects : int list } +and A = { Id : int; Value : string; Subjects : int list } -and B = { id : int; value : string; subjects : int list } +and B = { Id : int; Value : string; Subjects : int list } + +type Complex = { + Id : int + Name : string + Discriminator : string + Communities : int list + Buildings : int list +} +and Building = { Id : int; Name : string; Discriminator : string } +and Community = { Id : int; Name : string; Discriminator : string } + +type Property = + | Complex of Complex + | Building of Building + | Community of Community let getExecutor (expectedFilter : ObjectListFilter voption) = - let a1 : A = { id = 1; value = "A1"; subjects = [ 2; 6 ] } - let a2 : A = { id = 2; value = "A2"; subjects = [ 1; 3; 5 ] } - let a3 : A = { id = 3; value = "A3"; subjects = [ 1; 2; 4 ] } - let b1 = { id = 4; value = "1000"; subjects = [ 1; 5 ] } - let b2 = { id = 5; value = "2000"; subjects = [ 3; 4; 6 ] } - let b3 = { id = 6; value = "3000"; subjects = [ 1; 3; 5 ] } + let a1 : A = { Id = 1; Value = "A1"; Subjects = [ 2; 6 ] } + let a2 : A = { Id = 2; Value = "A2"; Subjects = [ 1; 3; 5 ] } + let a3 : A = { Id = 3; Value = "A3"; Subjects = [ 1; 2; 4 ] } + let b1 = { Id = 4; Value = "1000"; Subjects = [ 1; 5 ] } + let b2 = { Id = 5; Value = "2000"; Subjects = [ 3; 4; 6 ] } + let b3 = { Id = 6; Value = "3000"; Subjects = [ 1; 3; 5 ] } let al = [ a1; a2; a3 ] let bl = [ b1; b2; b3 ] - let getA id = al |> List.tryFind (fun a -> a.id = id) - let getB id = bl |> List.tryFind (fun b -> b.id = id) + let p1 = Complex{ Id = 1; Name = "Complex 1"; Discriminator = "Complex"; Communities = [ 5 ]; Buildings = [ 3 ] } + let p2 = Complex{ Id = 2; Name = "Complex 2"; Discriminator = "Complex"; Communities = [ 6 ]; Buildings = [ 4 ] } + let p3 = Building { Id = 3; Name = "Building 1"; Discriminator = "Building" } + let p4 = Building { Id = 4; Name = "Building 2"; Discriminator = "Building" } + let p5 = Community { Id = 5; Name = "Community 1"; Discriminator = "Community" } + let p6 = Community { Id = 6; Name = "Community 2"; Discriminator = "Community" } + let pl = [ p1; p2; p3; p4; p5; p6 ] + let getA id = al |> List.tryFind (fun a -> a.Id = id) + let getB id = bl |> List.tryFind (fun b -> b.Id = id) let subjects = (al |> List.map A) @ (bl |> List.map B) let getSubject id = let matchesId id = function - | A a -> a.id = id - | B b -> b.id = id + | A a -> a.Id = id + | B b -> b.Id = id subjects |> List.tryFind (matchesId id) let rec SubjectType = Define.Union ( @@ -65,8 +88,8 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = isTypeOf = (fun o -> o :? A), fieldsFn = fun () -> [ - Define.Field ("id", IntType, resolve = (fun _ a -> a.id)) - Define.Field ("value", StringType, resolve = (fun _ a -> a.value)) + Define.Field ("id", IntType, resolve = (fun _ a -> a.Id)) + Define.Field ("value", StringType, resolve = (fun _ a -> a.Value)) Define .Field( "subjects", @@ -75,7 +98,7 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = fun ctx (a : A) -> expectedFilter |> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter) - a.subjects |> List.map getSubject |> List.toSeq |> Some + a.Subjects |> List.map getSubject |> List.toSeq |> Some ) .WithQueryWeight (1.0) ] @@ -86,8 +109,8 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = isTypeOf = (fun o -> o :? B), fieldsFn = fun () -> [ - Define.Field ("id", IntType, resolve = (fun _ b -> b.id)) - Define.Field ("value", StringType, resolve = (fun _ b -> b.value)) + Define.Field ("id", IntType, resolve = (fun _ b -> b.Id)) + Define.Field ("value", StringType, resolve = (fun _ b -> b.Value)) Define .Field( "subjects", @@ -96,17 +119,77 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = fun ctx (b : B) -> expectedFilter |> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter) - b.subjects |> List.map getSubject |> List.toSeq |> Some + b.Subjects |> List.map getSubject |> List.toSeq |> Some ) .WithQueryWeight (1.0) ] ) + and ComplexType = + DefineRec.Object ( + name = "Complex", + isTypeOf = (fun o -> o :? Complex), + fieldsFn = + fun () -> [ + Define.Field ("id", IntType, resolve = (fun _ c -> c.Id)) + Define.Field ("name", StringType, resolve = (fun _ c -> c.Name)) + Define.Field ("discriminator", StringType, resolve = (fun _ c -> c.Discriminator)) + Define.Field ("communities", ListOf IntType, resolve = (fun _ c -> c.Communities)) + Define.Field ("buildings", ListOf IntType, resolve = (fun _ c -> c.Buildings)) + ] + ) + and BuildingType = + Define.Object ( + name = "Building", + isTypeOf = (fun o -> o :? Building), + fields = [ + Define.Field ("id", IntType, resolve = (fun _ b -> b.Id)) + Define.Field ("name", StringType, resolve = (fun _ b -> b.Name)) + Define.Field ("discriminator", StringType, resolve = (fun _ b -> b.Discriminator)) + ] + ) + and CommunityType = + Define.Object ( + name = "Community", + isTypeOf = (fun o -> o :? Community), + fields = [ + Define.Field ("id", IntType, resolve = (fun _ c -> c.Id)) + Define.Field ("name", StringType, resolve = (fun _ c -> c.Name)) + Define.Field ("discriminator", StringType, resolve = (fun _ c -> c.Discriminator)) + ] + ) + and PropertyType = + Define.Union<_, _> ( + name = "Property", + options = [ ComplexType; BuildingType; CommunityType ], + resolveValue = + (function + | Complex c -> box c + | Building b -> box b + | Community c -> box c), + resolveType = + (function + | Complex _ -> upcast ComplexType + | Building _ -> upcast BuildingType + | Community _ -> upcast CommunityType) + ) let Query = Define.Object ( name = "Query", fields = [ Define.Field ("A", Nullable AType, "A Field", [ Define.Input ("id", IntType) ], resolve = (fun ctx _ -> getA (ctx.Arg ("id")))) Define.Field ("B", Nullable BType, "B Field", [ Define.Input ("id", IntType) ], resolve = (fun ctx _ -> getB (ctx.Arg ("id")))) + Define.Field ( + "Properties", + ListOf PropertyType, + description = "Properties Field", + resolve = + (fun ctx _ -> + // The main task here is to check if the filter is empty + // when all union cases are specified or no union case is specified + Assert.True (ctx.Filter.IsNone) + Assert.True (ctx.ExecutionInfo.ResolveAbstractionFilter(ctx.Schema.TypeMap).IsNone) + pl) + ) ] ) let schema = Schema (Query) @@ -125,14 +208,15 @@ let executeWithVariables (query : Document, variables : ImmutableDictionary sync -let executeWithCustomFilter (query : Document, variables : ImmutableDictionary, customFilter : ObjectListFilter) = - let ex = getExecutor (ValueSome customFilter) +let executeAndVerifyFilter (query : Document, variables : ImmutableDictionary, filterToVerify : ObjectListFilter) = + let ex = getExecutor (ValueSome filterToVerify) ex.AsyncExecute (ast = query, variables = variables) |> sync -let expectedErrors : GQLProblemDetails list = [ +let expectedThresholdErrors : GQLProblemDetails list = [ GQLProblemDetails.Create ("Query complexity exceeds maximum threshold. Please reduce query complexity and try again.") ] + [] let ``Simple query: Must pass when below threshold`` () = let query = @@ -171,11 +255,10 @@ let ``Simple query: Must pass when below threshold`` () = ] ] let result = execute query - match result with - | Direct (data, errors) -> + + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - | _ -> fail "Expected Direct GQLResponse" result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 1.0) @@ -232,7 +315,8 @@ let ``Simple query: Must not pass when above threshold`` () = ...on B { ...AllB } }""" let result = execute query - result |> ensureRequestError <| fun errors -> errors |> equals expectedErrors + + ensureRequestError result <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0) @@ -271,6 +355,7 @@ let ``Deferred queries : Must pass when below threshold`` () = [ "A"; "subjects" ] ) let result = execute query + ensureDeferred result <| fun data errors deferred -> empty errors data |> equals (upcast expected) @@ -311,6 +396,7 @@ let ``Streamed queries : Must pass when below threshold`` () = let expectedDeferred2 = DeferredResult ([| NameValueLookup.ofList [ "id", upcast 6; "value", upcast "3000" ] |], [ "A"; "subjects"; 1 ]) let result = execute query + ensureDeferred result <| fun data errors deferred -> empty errors data |> equals (upcast expected) @@ -379,7 +465,7 @@ let ``Deferred and Streamed queries : Must not pass when above threshold`` () = asts query |> Seq.map execute |> Seq.iter (fun result -> - ensureRequestError result <| fun errors -> errors |> equals expectedErrors + ensureRequestError result <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0)) @@ -418,6 +504,7 @@ let ``Inline fragment query : Must pass when below threshold`` () = ] ] let result = execute query + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) @@ -469,7 +556,8 @@ let ``Inline fragment query : Must not pass when above threshold`` () = } }""" let result = execute query - ensureRequestError result <| fun errors -> errors |> equals expectedErrors + + ensureRequestError result <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0) @@ -513,14 +601,13 @@ let ``Object list filter: must return filter information in Metadata`` () = let expectedFilter : KeyValuePair = kvp ([ "A"; "s" ]) (And (Equals { FieldName = "id"; Value = 2L }, StartsWith { FieldName = "value"; Value = "A" })) let result = execute query + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 1.0) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] [] let ``Object list filter: Must return AND filter information in Metadata`` () = @@ -562,12 +649,11 @@ let ``Object list filter: Must return AND filter information in Metadata`` () = let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (And (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L })) let result = execute query + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] [] let ``Object list filter: Must return OR filter information in Metadata`` () = @@ -609,12 +695,11 @@ let ``Object list filter: Must return OR filter information in Metadata`` () = let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (Or (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L })) let result = execute query + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] [] let ``Object list filter: Must return NOT filter information in Metadata`` () = @@ -656,12 +741,11 @@ let ``Object list filter: Must return NOT filter information in Metadata`` () = let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (Not (StartsWith { FieldName = "value"; Value = "3" })) let result = execute query + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] [] let ``Object list filter: Must return filter information in Metadata when supplied as variable and parse all filter operators`` () = @@ -705,181 +789,197 @@ let ``Object list filter: Must return filter information in Metadata when suppli let variables = ImmutableDictionary.Empty.Add ("filter", notStartsFilter) let filter = Not (StartsWith { FieldName = "value"; Value = "3" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notEndsFilter = """{ "not": { "value_ends_with": "2" } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notEndsFilter) let filter = Not (EndsWith { FieldName = "value"; Value = "2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notStartsFilter = """{ "not": { "value_sw": "3" } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notStartsFilter) let filter = Not (StartsWith { FieldName = "value"; Value = "3" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notEndsFilter = """{ "not": { "value_ew": "2" } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notEndsFilter) let filter = Not (EndsWith { FieldName = "value"; Value = "2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notGreaterThanOrEqualFilter = """{ "not": { "id_greater_than_or_equal": 2 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notGreaterThanOrEqualFilter) let filter = Not (GreaterThanOrEqual { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables,filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notLessThanOrEqualFilter = """{ "not": { "id_less_than_or_equal": 4 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notLessThanOrEqualFilter) let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notGreaterThanFilter = """{ "not": { "id_greater_than": 2 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notGreaterThanFilter) let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notLessThanFilter = """{ "not": { "id_less_than": 4 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notLessThanFilter) let filter = Not (LessThan { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) - ensureDirect result - <| fun data errors -> + let result = executeAndVerifyFilter (query, variables, filter) + + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notGreaterThanOrEqualFilter = """{ "not": { "id_gte": 2 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notGreaterThanOrEqualFilter) let filter = Not (GreaterThanOrEqual { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables,filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notLessThanOrEqualFilter = """{ "not": { "id_lte": 4 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notLessThanOrEqualFilter) let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notGreaterThanFilter = """{ "not": { "id_gt": 2 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notGreaterThanFilter) let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notLessThanFilter = """{ "not": { "id_lt": 4 } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notLessThanFilter) let filter = Not (LessThan { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) - ensureDirect result - <| fun data errors -> + let result = executeAndVerifyFilter (query, variables, filter) + + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notContainsFilter = """{ "not": { "value_contains": "A" } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notContainsFilter) let filter = Not (Contains { FieldName = "value"; Value = "A" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] do let notEqualsFilter = """{ "not": { "value": "A2" } }""" |> JsonDocument.Parse |> _.RootElement let variables = ImmutableDictionary.Empty.Add ("filter", notEqualsFilter) let filter = Not (Equals { FieldName = "value"; Value = "A2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) - let result = executeWithCustomFilter (query, variables, filter) + let result = executeAndVerifyFilter (query, variables, filter) + ensureDirect result <| fun data errors -> empty errors data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + result.Metadata.TryFind ("filters") |> wantValueSome |> seqEquals [ expectedFilter ] + +[] +let ``Object list filter: Must return empty filter when all discriminated union types are specified`` () = + let query = + parse + """query testQuery() { Properties { ...Value } } + + fragment Value on Property { + ...on Complex { + id + name + discriminator + } + ...on Building { + id + name + discriminator + } + ...on Community { + id + name + discriminator + } + }""" + let result = execute query + ensureDirect result <| fun _ errors -> empty errors + +[] +let ``Object list filter: Must return empty filter when no discriminated union types are specified`` () = + let query = parse """query testQuery() { Properties { __typename } }""" + let result = execute query + ensureDirect result <| fun _ errors -> empty errors diff --git a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs index 998aaf93..d5a6bf82 100644 --- a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs @@ -339,32 +339,33 @@ let ``ObjectListFilter works with getDiscriminator and getDiscriminatorValue for c.Name |> equals "Complex AA" | _ -> failwith "Expected Complex" -type Cow = { ID : int; Name : string; Discriminator : string; __typename : string } +// Dummy types to be used in OfType filter +type Cow = class end +type Horse = class end +type Hamster = class end -type Horse = { ID : int; Name : string; Discriminator : string; __typename : string } - -type Hamster = { ID : int; Name : string; Discriminator : string; __typename : string } +type Animal = { ID : int; Name : string; Discriminator : string; __typename : string } let animalData = [ - { ID = 1; Discriminator = "Cow"; Name = "Cow A"; __typename = typeof.Name } + { ID = 1; Discriminator = "Cow"; Name = "Cow A"; __typename = "Cow" } { ID = 2 Discriminator = "Horse" Name = "Horse B" - __typename = typeof.Name + __typename = "Horse" } - { ID = 3; Discriminator = "Cow"; Name = "Cow C"; __typename = typeof.Name } + { ID = 3; Discriminator = "Cow"; Name = "Cow C"; __typename = "Cow" } { ID = 4 Discriminator = "Horse" Name = "Horse D" - __typename = typeof.Name + __typename = "Horse" } { ID = 5 Discriminator = "Hamster" Name = "Hamster E" - __typename = typeof.Name + __typename = "Hamster" } ] @@ -383,17 +384,13 @@ let ``ObjectListFilter works with getDiscriminatorValue for Horse`` () = let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 2 do - let result = List.head filteredData - match result with - | h -> - h.ID |> equals 2 - h.Name |> equals "Horse B" + let animal = List.head filteredData + animal.ID |> equals 2 + animal.Name |> equals "Horse B" do - let result = List.last filteredData - match result with - | h -> - h.ID |> equals 4 - h.Name |> equals "Horse D" + let animal = List.last filteredData + animal.ID |> equals 4 + animal.Name |> equals "Horse D" [] let ``ObjectListFilter works with getDiscriminatorValue startsWith for Horse and Hamster`` () = @@ -406,23 +403,19 @@ let ``ObjectListFilter works with getDiscriminatorValue startsWith for Horse and (function | t when t = typeof -> t.Name | t when t = typeof -> t.Name - | t when t = typeof -> t.Name + | t when t = typeof -> t.Name | _ -> raise (NotSupportedException "Type not supported")) ) let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 3 do - let result = List.head filteredData - match result with - | c -> - c.ID |> equals 2 - c.Name |> equals "Horse B" + let animal = List.head filteredData + animal.ID |> equals 2 + animal.Name |> equals "Horse B" do - let result = List.last filteredData - match result with - | c -> - c.ID |> equals 5 - c.Name |> equals "Hamster E" + let animal = List.last filteredData + animal.ID |> equals 5 + animal.Name |> equals "Hamster E" type ListTagsProduct = { Name : string; Tags : string list } @@ -488,3 +481,27 @@ let ``ObjectListFilter works with Contains operator on set collection properties do let result = List.last filteredData result.Name |> equals "Product C" + +[] +let ``ObjectListFilter OfTypes works with two or more types`` () = + + let queryable = animalData.AsQueryable () + let filter = OfTypes [ typeof; typeof ] + let options = + ObjectListFilterLinqOptions ( + getDiscriminatorValue = + (function + | t when t = typeof -> t.Name + | t when t = typeof -> t.Name + | _ -> raise (NotSupportedException "Type not supported")) + ) + let filteredData = queryable.Apply (filter, options) |> Seq.toList + List.length filteredData |> equals 4 + do + let animal = List.head filteredData + animal.ID |> equals 1 + animal.Name |> equals "Cow A" + do + let animal = List.last filteredData + animal.ID |> equals 4 + animal.Name |> equals "Horse D"