From ac6ef222256e742ea5bc7521c9c39e77e5f96ae8 Mon Sep 17 00:00:00 2001 From: Viktor Tochonov Date: Thu, 20 Mar 2025 15:47:43 +0200 Subject: [PATCH 1/3] Fix OfTypes case for ObjectListFilter and Implemented new tests for OfTypes filter with differeny types quantity --- .../ObjectListFilter.fs | 2 +- .../MiddlewareTests.fs | 200 ++++++++++++++---- .../ObjectListFilterLinqTests.fs | 28 +++ 3 files changed, 191 insertions(+), 39 deletions(-) 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..ad67d5a7 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,28 +23,57 @@ 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 getProperty id = + let matchesId id = + function + | Complex c -> c.Id = id + | Building b -> b.Id = id + | Community c -> c.Id = id + pl |> List.vtryFind (matchesId id) let rec SubjectType = Define.Union ( name = "Subject", @@ -65,8 +95,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 +105,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 +116,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 +126,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 = + (fun u -> + match u with + | Complex c -> box c + | Building b -> box b + | Community c -> box c), + resolveType = + (fun u -> + match u with + | 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 +215,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 = @@ -232,7 +323,7 @@ let ``Simple query: Must not pass when above threshold`` () = ...on B { ...AllB } }""" let result = execute query - result |> ensureRequestError <| fun errors -> errors |> equals expectedErrors + result |> ensureRequestError <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0) @@ -379,7 +470,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)) @@ -469,7 +560,7 @@ 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) @@ -705,7 +796,7 @@ 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) @@ -718,7 +809,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -731,7 +822,7 @@ 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) @@ -744,7 +835,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -757,7 +848,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -770,7 +861,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -783,7 +874,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -796,7 +887,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) + let result = executeAndVerifyFilter (query, variables, filter) ensureDirect result <| fun data errors -> empty errors @@ -810,7 +901,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -823,7 +914,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -836,7 +927,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -849,7 +940,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) + let result = executeAndVerifyFilter (query, variables, filter) ensureDirect result <| fun data errors -> empty errors @@ -863,7 +954,7 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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) @@ -876,10 +967,43 @@ let ``Object list filter: Must return filter information in Metadata when suppli 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 ] + +[] +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..6c4ac005 100644 --- a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs @@ -488,3 +488,31 @@ 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 result = List.head filteredData + match result with + | c -> + c.ID |> equals 1 + c.Name |> equals "Cow A" + do + let result = List.last filteredData + match result with + | h -> + h.ID |> equals 4 + h.Name |> equals "Horse D" From 6b2a1e06441a272434c3077eae861a98d3ae7dfd Mon Sep 17 00:00:00 2001 From: Viktor Tochonov Date: Thu, 20 Mar 2025 16:17:57 +0200 Subject: [PATCH 2/3] Format MiddlewareTests.fs --- .../MiddlewareTests.fs | 178 ++++++++---------- 1 file changed, 75 insertions(+), 103 deletions(-) diff --git a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs index ad67d5a7..4a773484 100644 --- a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs @@ -67,13 +67,6 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = | A a -> a.Id = id | B b -> b.Id = id subjects |> List.tryFind (matchesId id) - let getProperty id = - let matchesId id = - function - | Complex c -> c.Id = id - | Building b -> b.Id = id - | Community c -> c.Id = id - pl |> List.vtryFind (matchesId id) let rec SubjectType = Define.Union ( name = "Subject", @@ -149,37 +142,31 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = 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)) - ] + 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)) - ] + 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<_,_> ( + Define.Union<_, _> ( name = "Property", options = [ ComplexType; BuildingType; CommunityType ], - resolveValue = - (fun u -> - match u with - | Complex c -> box c - | Building b -> box b - | Community c -> box c), + resolveValue = id, resolveType = - (fun u -> - match u with - | Complex _ -> upcast ComplexType - | Building _ -> upcast BuildingType - | Community _ -> upcast CommunityType) + (function + | Complex _ -> ComplexType + | Building _ -> BuildingType + | Community _ -> CommunityType) ) let Query = Define.Object ( @@ -187,15 +174,17 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = 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 - ) + 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) ) ] ) @@ -262,11 +251,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) @@ -323,7 +311,8 @@ let ``Simple query: Must not pass when above threshold`` () = ...on B { ...AllB } }""" let result = execute query - result |> ensureRequestError <| fun errors -> errors |> equals expectedThresholdErrors + + ensureRequestError result <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0) @@ -362,6 +351,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) @@ -402,6 +392,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) @@ -509,6 +500,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) @@ -560,6 +552,7 @@ let ``Inline fragment query : Must not pass when above threshold`` () = } }""" let result = execute query + ensureRequestError result <| fun errors -> errors |> equals expectedThresholdErrors result.Metadata.TryFind ("queryWeightThreshold") |> equals (ValueSome 2.0) result.Metadata.TryFind ("queryWeight") |> equals (ValueSome 3.0) @@ -604,14 +597,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`` () = @@ -653,12 +645,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`` () = @@ -700,12 +691,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`` () = @@ -747,12 +737,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`` () = @@ -797,12 +786,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (StartsWith { FieldName = "value"; Value = "3" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -810,12 +798,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (EndsWith { FieldName = "value"; Value = "2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -823,12 +810,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (StartsWith { FieldName = "value"; Value = "3" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -836,25 +822,23 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (EndsWith { FieldName = "value"; Value = "2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 = executeAndVerifyFilter (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 @@ -862,12 +846,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -875,12 +858,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -888,26 +870,23 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (LessThan { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) let result = executeAndVerifyFilter (query, variables, filter) - ensureDirect result - <| fun data errors -> + + 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 = executeAndVerifyFilter (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 @@ -915,12 +894,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (LessThanOrEqual { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -928,12 +906,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (GreaterThan { FieldName = "id"; Value = 2.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -941,13 +918,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (LessThan { FieldName = "id"; Value = 4.0 }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (filter) let result = executeAndVerifyFilter (query, variables, filter) - ensureDirect result - <| fun data errors -> + + 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 @@ -955,12 +930,11 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (Contains { FieldName = "value"; Value = "A" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 @@ -968,15 +942,14 @@ let ``Object list filter: Must return filter information in Metadata when suppli let filter = Not (Equals { FieldName = "value"; Value = "A2" }) let expectedFilter : KeyValuePair = kvp ([ "A"; "subjects" ]) (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 ``Object list filter: Must return empty filter when all discriminated union types are specified`` () = let query = parse """query testQuery() { Properties { ...Value } } @@ -998,12 +971,11 @@ let ``Object list filter: Must return empty filter when all discriminated union 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 ``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 From f0723e58fa0dbe106c7ef79268170a2d7b202ba7 Mon Sep 17 00:00:00 2001 From: Viktor Tochonov Date: Thu, 20 Mar 2025 16:39:08 +0200 Subject: [PATCH 3/3] Remove unnecessary types of Animals --- .../MiddlewareTests.fs | 12 ++-- .../ObjectListFilterLinqTests.fs | 69 ++++++++----------- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs index 4a773484..4de59cf5 100644 --- a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs @@ -161,12 +161,16 @@ let getExecutor (expectedFilter : ObjectListFilter voption) = Define.Union<_, _> ( name = "Property", options = [ ComplexType; BuildingType; CommunityType ], - resolveValue = id, + resolveValue = + (function + | Complex c -> box c + | Building b -> box b + | Community c -> box c), resolveType = (function - | Complex _ -> ComplexType - | Building _ -> BuildingType - | Community _ -> CommunityType) + | Complex _ -> upcast ComplexType + | Building _ -> upcast BuildingType + | Community _ -> upcast CommunityType) ) let Query = Define.Object ( diff --git a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs index 6c4ac005..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 } @@ -505,14 +498,10 @@ let ``ObjectListFilter OfTypes works with two or more types`` () = let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 4 do - let result = List.head filteredData - match result with - | c -> - c.ID |> equals 1 - c.Name |> equals "Cow A" + let animal = List.head filteredData + animal.ID |> equals 1 + animal.Name |> equals "Cow A" 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"