diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs index 5be35c26..da0489d8 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs +++ b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs @@ -12,7 +12,10 @@ type ObjectListFilter = | Not of ObjectListFilter | Equals of FieldFilter | GreaterThan of FieldFilter + | GreaterThanOrEqual of FieldFilter | LessThan of FieldFilter + | LessThanOrEqual of FieldFilter + | In of FieldFilter | StartsWith of FieldFilter | EndsWith of FieldFilter | Contains of FieldFilter @@ -92,10 +95,16 @@ module ObjectListFilter = let ( === ) fname value = Equals { FieldName = fname; Value = value } /// Creates a new ObjectListFilter representing a GREATER THAN operation of a comparable value. - let ( ==> ) fname value = GreaterThan { FieldName = fname; Value = value } + let ( >>> ) fname value = GreaterThan { FieldName = fname; Value = value } + + /// Creates a new ObjectListFilter representing a GREATER THAN OR EQUAL operation of a comparable value. + let ( ==> ) fname value = GreaterThanOrEqual { FieldName = fname; Value = value } /// Creates a new ObjectListFilter representing a LESS THAN operation of a comparable value. - let ( <== ) fname value = LessThan { FieldName = fname; Value = value } + let ( <<< ) fname value = LessThan { FieldName = fname; Value = value } + + /// Creates a new ObjectListFilter representing a LESS THAN OR EQUAL operation of a comparable value. + let ( <== ) fname value = LessThanOrEqual { FieldName = fname; Value = value } /// Creates a new ObjectListFilter representing a STARTS WITH operation of a string value. let ( =@@ ) fname value = StartsWith { FieldName = fname; Value = value } @@ -106,6 +115,9 @@ module ObjectListFilter = /// Creates a new ObjectListFilter representing a CONTAINS operation. let ( @=@ ) fname value = Contains { FieldName = fname; Value = value } + /// Creates a new ObjectListFilter representing a IN operation. + let ( =~= ) fname value = In { FieldName = fname; Value = value } + /// Creates a new ObjectListFilter representing a field sub comparison. let ( --> ) fname filter = FilterField { FieldName = fname; Value = filter } @@ -169,6 +181,8 @@ module ObjectListFilter = | Equals f -> Expression.Equal (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value)) | GreaterThan f -> Expression.GreaterThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value)) | LessThan f -> Expression.LessThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value)) + | GreaterThanOrEqual f -> Expression.GreaterThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value)) + | LessThanOrEqual f -> Expression.LessThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value)) | StartsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringStartsWithMethod, Expression.Constant (f.Value)) | EndsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringEndsWithMethod, Expression.Constant (f.Value)) | Contains f -> @@ -201,6 +215,12 @@ module ObjectListFilter = Expression.Constant (f.Value) ) | _ -> Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value)) + | In f -> + let ``member`` = Expression.PropertyOrField (param, f.FieldName) + f.Value + |> Seq.map (fun v -> Expression.Equal (``member``, Expression.Constant (v))) + |> Seq.reduce (fun acc expr -> Expression.OrElse (acc, expr)) + :> Expression | OfTypes types -> types |> Seq.map (fun t -> buildTypeDiscriminatorCheck param t) @@ -222,17 +242,15 @@ module ObjectListFilter = Expression.PropertyOrField (param, "__typename"), // Default discriminator value Expression.Constant (t.FullName) - ) - :> Expression + ) :> Expression | ValueSome discExpr, ValueNone -> Expression.Invoke ( // Provided discriminator comparison discExpr, param, // Default discriminator value gathered from type - Expression.Constant (t.FullName) - ) - :> Expression + Expression.Constant(t.FullName) + ) :> Expression | ValueNone, ValueSome discValueFn -> let discriminatorValue = discValueFn t Expression.Equal ( @@ -240,8 +258,7 @@ module ObjectListFilter = Expression.PropertyOrField (param, "__typename"), // Provided discriminator value gathered from type Expression.Constant (discriminatorValue) - ) - :> Expression + ) :> Expression | ValueSome discExpr, ValueSome discValueFn -> let discriminatorValue = discValueFn t Expression.Invoke ( diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs b/src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs index 44966741..26ad838e 100644 --- a/src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs +++ b/src/FSharp.Data.GraphQL.Server.Middleware/SchemaDefinitions.fs @@ -12,9 +12,19 @@ open FSharp.Data.GraphQL.Ast let internal removeNoFilter = Seq.where (fun filter -> filter <> NoFilter) +type private ComparisonOperator = + | EndsWith of string + | StartsWith of string + | Contains of string + | Equals of string + | GreaterThan of string + | GreaterThanOrEqual of string + | LessThan of string + | LessThanOrEqual of string + let rec private coerceObjectListFilterInput x : Result = - let (|EndsWith|StartsWith|GreaterThan|LessThan|Contains|Equals|) (s : string) = + let parseFieldCondition (s : string) = let s = s.ToLowerInvariant () let prefix (suffix : string) (s : string) = s.Substring (0, s.Length - suffix.Length) match s with @@ -22,11 +32,15 @@ let rec private coerceObjectListFilterInput x : Result "_ew".Length -> EndsWith (prefix "_ew" s) | s when s.EndsWith ("_starts_with") && s.Length > "_starts_with".Length -> StartsWith (prefix "_starts_with" s) | s when s.EndsWith ("_sw") && s.Length > "_sw".Length -> StartsWith (prefix "_sw" s) + | s when s.EndsWith ("_contains") && s.Length > "_contains".Length -> Contains (prefix "_contains" s) | s when s.EndsWith ("_greater_than") && s.Length > "_greater_than".Length -> GreaterThan (prefix "_greater_than" s) | s when s.EndsWith ("_gt") && s.Length > "_gt".Length -> GreaterThan (prefix "_gt" s) + | s when s.EndsWith ("_greater_than_or_equal") && s.Length > "_greater_than_or_equal".Length -> GreaterThanOrEqual (prefix "_greater_than_or_equal" s) + | s when s.EndsWith ("_gte") && s.Length > "_gte".Length -> GreaterThanOrEqual (prefix "_gte" s) | s when s.EndsWith ("_less_than") && s.Length > "_less_than".Length -> LessThan (prefix "_less_than" s) | s when s.EndsWith ("_lt") && s.Length > "_lt".Length -> LessThan (prefix "_lt" s) - | s when s.EndsWith ("_contains") && s.Length > "_contains".Length -> Contains (prefix "_contains" 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 -> Equals s let (|EquatableValue|Other|) v = @@ -76,7 +90,7 @@ let rec private coerceObjectListFilterInput x : Result Error errs | Ok coerced -> coerced |> removeNoFilter |> Seq.toList |> Ok - match name, value with + match parseFieldCondition name, value with | Equals "and", ListValue fields -> fields |> mapFilters |> Result.map buildAnd | Equals "or", ListValue fields -> fields |> mapFilters |> Result.map buildOr | Equals "not", ObjectValue value -> @@ -84,17 +98,19 @@ let rec private coerceObjectListFilterInput x : Result Error errs | Ok NoFilter -> Ok NoFilter | Ok filter -> Ok (Not filter) - | EndsWith fname, StringValue value -> Ok (EndsWith { FieldName = fname; Value = value }) - | StartsWith fname, StringValue value -> Ok (StartsWith { FieldName = fname; Value = value }) - | Contains fname, StringValue value -> Ok (Contains { FieldName = fname; Value = value }) + | EndsWith fname, StringValue value -> Ok (ObjectListFilter.EndsWith { FieldName = fname; Value = value }) + | StartsWith fname, StringValue value -> Ok (ObjectListFilter.StartsWith { FieldName = fname; Value = value }) + | Contains fname, StringValue value -> Ok (ObjectListFilter.Contains { FieldName = fname; Value = value }) | Equals fname, ObjectValue value -> match mapInput value with | Error errs -> Error errs | Ok NoFilter -> Ok NoFilter | Ok filter -> Ok (FilterField { FieldName = fname; Value = filter }) - | Equals fname, EquatableValue value -> Ok (Equals { FieldName = fname; Value = value }) - | GreaterThan fname, ComparableValue value -> Ok (GreaterThan { FieldName = fname; Value = value }) - | LessThan fname, ComparableValue value -> Ok (LessThan { FieldName = fname; Value = value }) + | Equals fname, EquatableValue value -> Ok (ObjectListFilter.Equals { FieldName = fname; Value = value }) + | GreaterThan fname, ComparableValue value -> Ok (ObjectListFilter.GreaterThan { FieldName = fname; Value = value }) + | GreaterThanOrEqual fname, ComparableValue value -> Ok (ObjectListFilter.GreaterThanOrEqual { FieldName = fname; Value = value }) + | LessThan fname, ComparableValue value -> Ok (ObjectListFilter.LessThan { FieldName = fname; Value = value }) + | LessThanOrEqual fname, ComparableValue value -> Ok (ObjectListFilter.LessThanOrEqual { FieldName = fname; Value = value }) | _ -> Ok NoFilter and mapInput value = diff --git a/src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs b/src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs index dd78521d..948daf71 100644 --- a/src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs +++ b/src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs @@ -1,6 +1,7 @@ namespace rec FSharp.Data.GraphQL open System.Linq +open System.Collections.Generic open FsToolkit.ErrorHandling module internal ValueOption = @@ -11,6 +12,12 @@ module internal Option = let mapValueOption mapping voption = voption |> ValueOption.map mapping |> ValueOption.toOption +[] +module KeyValuePair = + + let inline kvp key value = KeyValuePair (key, value) + let inline kvpObj key (value : obj) = KeyValuePair (key, value) + [] module internal ValueTuple = diff --git a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs index b936b984..7d6f68ab 100644 --- a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs @@ -2,7 +2,10 @@ module FSharp.Data.GraphQL.Tests.MiddlewareTests open System open System.Collections.Generic +open System.Collections.Immutable +open System.Text.Json open Xunit +open FSharp open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Server.Middleware @@ -10,8 +13,6 @@ open FSharp.Data.GraphQL.Shared open FSharp.Data.GraphQL.Parser open FSharp.Data.GraphQL.Execution open FSharp.Data.GraphQL.Ast -open System.Collections.Immutable -open System.Text.Json #nowarn "40" @@ -25,7 +26,7 @@ and A = { id : int; value : string; subjects : int list } and B = { id : int; value : string; subjects : int list } -let executor = +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 ] } @@ -70,7 +71,11 @@ let executor = .Field( "subjects", Nullable (ListOf (Nullable SubjectType)), - resolve = fun _ (a : A) -> a.subjects |> List.map getSubject |> List.toSeq |> Some + resolve = + fun ctx (a : A) -> + expectedFilter + |> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter) + a.subjects |> List.map getSubject |> List.toSeq |> Some ) .WithQueryWeight (1.0) ] @@ -87,7 +92,11 @@ let executor = .Field( "subjects", Nullable (ListOf (Nullable SubjectType)), - resolve = fun _ (b : B) -> b.subjects |> List.map getSubject |> List.toSeq |> Some + resolve = + fun ctx (b : B) -> + expectedFilter + |> ValueOption.iter (fun _ -> equals expectedFilter ctx.Filter) + b.subjects |> List.map getSubject |> List.toSeq |> Some ) .WithQueryWeight (1.0) ] @@ -108,10 +117,17 @@ let executor = ] Executor (schema, middleware) +let executor = getExecutor (ValueNone) + let execute (query : Document) = executor.AsyncExecute (query) |> sync let executeWithVariables (query : Document, variables : ImmutableDictionary) = - executor.AsyncExecute (ast = query, variables = variables) |> sync + executor.AsyncExecute (ast = query, variables = variables) + |> sync + +let executeWithCustomFilter (query : Document, variables : ImmutableDictionary, customFilter : ObjectListFilter) = + let ex = getExecutor (ValueSome customFilter) + ex.AsyncExecute (ast = query, variables = variables) |> sync let expectedErrors : GQLProblemDetails list = [ 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`` () = ] ] ] - let expectedFilter : KeyValuePair = - KeyValuePair ([ "A"; "s" ], And (Equals { FieldName = "id"; Value = 2L }, StartsWith { FieldName = "value"; Value = "A" })) + 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 @@ -543,8 +559,8 @@ let ``Object list filter: Must return AND filter information in Metadata`` () = ] ] ] - let expectedFilter : KeyValuePair = - KeyValuePair ([ "A"; "subjects" ], And (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L })) + 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 @@ -590,8 +606,8 @@ let ``Object list filter: Must return OR filter information in Metadata`` () = ] ] ] - let expectedFilter : KeyValuePair = - KeyValuePair ([ "A"; "subjects" ], Or (StartsWith { FieldName = "value"; Value = "3" }, Equals { FieldName = "id"; Value = 6L })) + 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 @@ -637,8 +653,8 @@ let ``Object list filter: Must return NOT filter information in Metadata`` () = ] ] ] - let expectedFilter : KeyValuePair = - KeyValuePair ([ "A"; "subjects" ], Not (StartsWith { FieldName = "value"; Value = "3" })) + let expectedFilter : KeyValuePair = + kvp ([ "A"; "subjects" ]) (Not (StartsWith { FieldName = "value"; Value = "3" })) let result = execute query ensureDirect result <| fun data errors -> empty errors @@ -648,11 +664,7 @@ let ``Object list filter: Must return NOT filter information in Metadata`` () = |> seqEquals [ expectedFilter ] [] -let ``Object list filter: Must return filter information in Metadata when supplied as variable`` () = - let jsonString = """{ "not": { "value_starts_with": "3" } }""" - let jsonElement = JsonDocument.Parse(jsonString).RootElement - - let dict = ImmutableDictionary.Empty.Add ("filter", jsonElement) +let ``Object list filter: Must return filter information in Metadata when supplied as variable and parse all filter operators`` () = let query = parse """query testQuery($filter: ObjectListFilter!) { @@ -688,12 +700,186 @@ let ``Object list filter: Must return filter information in Metadata when suppli ] ] ] - let expectedFilter : KeyValuePair = - KeyValuePair ([ "A"; "subjects" ], Not (StartsWith { FieldName = "value"; Value = "3" })) - let result = executeWithVariables (query, dict) - ensureDirect result <| fun data errors -> - empty errors - data |> equals (upcast expected) - result.Metadata.TryFind ("filters") - |> wantValueSome - |> seqEquals [ expectedFilter ] + do + let notStartsFilter = """{ "not": { "value_starts_with": "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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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 -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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 -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + 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) + ensureDirect result <| fun data errors -> + empty errors + data |> equals (upcast expected) + result.Metadata.TryFind ("filters") + |> wantValueSome + |> seqEquals [ expectedFilter ] diff --git a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs index 8613dcb5..998aaf93 100644 --- a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs @@ -8,10 +8,10 @@ open FSharp.Data.GraphQL.Server.Middleware open FSharp.Data.GraphQL.Tests.LinqTests [] -let ``ObjectListFilter works with Equals operator``() = - let filter = Equals { FieldName = "firstName"; Value = "Jonathan" } // :> IComparable - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with Equals operator`` () = + let filter = Equals { FieldName = "firstName"; Value = "Jonathan" } // :> IComparable + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 1 let result = List.head filteredData result.ID |> equals 2 @@ -21,10 +21,10 @@ let ``ObjectListFilter works with Equals operator``() = result.Friends |> equals [] [] -let ``ObjectListFilter works with GreaterThan operator``() = - let filter = GreaterThan { FieldName = "id"; Value = 4 } // :> IComparable - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with GreaterThan operator`` () = + let filter = GreaterThan { FieldName = "id"; Value = 4 } // :> IComparable + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 1 let result = List.head filteredData result.ID |> equals 7 @@ -34,10 +34,31 @@ let ``ObjectListFilter works with GreaterThan operator``() = result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ] [] -let ``ObjectListFilter works with LessThan operator``() = - let filter = LessThan { FieldName = "id"; Value = 4 } // :> IComparable - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with GreaterThanOrEqual operator`` () = + let filter = GreaterThanOrEqual { FieldName = "id"; Value = 4 } // :> IComparable + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList + List.length filteredData |> equals 2 + do + let result = List.head filteredData + result.ID |> equals 4 + result.FirstName |> equals "Ben" + result.LastName |> equals "Adams" + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + do + let result = List.last filteredData + result.ID |> equals 7 + result.FirstName |> equals "Jeneffer" + result.LastName |> equals "Trif" + result.Contact |> equals { Email = "j.trif@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ] + +[] +let ``ObjectListFilter works with LessThan operator`` () = + let filter = LessThan { FieldName = "id"; Value = 4 } // :> IComparable + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 1 let result = List.head filteredData result.ID |> equals 2 @@ -47,10 +68,31 @@ let ``ObjectListFilter works with LessThan operator``() = result.Friends |> equals [] [] -let ``ObjectListFilter works with StartsWith operator``() = - let filter = StartsWith { FieldName = "firstName"; Value = "J" } - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with LessThanOrEqual operator`` () = + let filter = LessThanOrEqual { FieldName = "id"; Value = 4 } // :> IComparable + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList + List.length filteredData |> equals 2 + do + let result = List.head filteredData + result.ID |> equals 4 + result.FirstName |> equals "Ben" + result.LastName |> equals "Adams" + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + do + let result = List.last filteredData + result.ID |> equals 2 + result.FirstName |> equals "Jonathan" + result.LastName |> equals "Abrams" + result.Contact |> equals { Email = "j.abrams@gmail.com" } + result.Friends |> equals [] + +[] +let ``ObjectListFilter works with StartsWith operator`` () = + let filter = StartsWith { FieldName = "firstName"; Value = "J" } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 let result = List.head filteredData result.ID |> equals 2 @@ -60,109 +102,134 @@ let ``ObjectListFilter works with StartsWith operator``() = result.Friends |> equals [] [] -let ``ObjectListFilter works with Contains operator``() = - let filter = Contains { FieldName = "firstName"; Value = "en" } - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with Contains operator`` () = + let filter = Contains { FieldName = "firstName"; Value = "en" } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 let result = List.head filteredData result.ID |> equals 4 result.FirstName |> equals "Ben" result.LastName |> equals "Adams" - result.Contact |> equals { Email = "b.adams@gmail.com" } - result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] [] -let ``ObjectListFilter works with EndsWith operator``() = - let filter = EndsWith { FieldName = "lastName"; Value = "ams" } - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with EndsWith operator`` () = + let filter = EndsWith { FieldName = "lastName"; Value = "ams" } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 let result = List.head filteredData result.ID |> equals 4 result.FirstName |> equals "Ben" result.LastName |> equals "Adams" - result.Contact |> equals { Email = "b.adams@gmail.com" } - result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] [] -let ``ObjectListFilter works with AND operator``() = +let ``ObjectListFilter works with AND operator`` () = let filter = - And ( - Contains { FieldName = "firstName"; Value = "en" }, - Equals { FieldName = "lastName"; Value = "Adams" } - ) - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList + And (Contains { FieldName = "firstName"; Value = "en" }, Equals { FieldName = "lastName"; Value = "Adams" }) + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 1 let result = List.head filteredData result.ID |> equals 4 result.FirstName |> equals "Ben" result.LastName |> equals "Adams" - result.Contact |> equals { Email = "b.adams@gmail.com" } - result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] [] -let ``ObjectListFilter works with OR operator``() = +let ``ObjectListFilter works with OR operator`` () = let filter = - Or ( - GreaterThan { FieldName = "id"; Value = 4 }, - Equals { FieldName = "lastName"; Value = "Adams" } - ) - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList + Or (GreaterThan { FieldName = "id"; Value = 4 }, Equals { FieldName = "lastName"; Value = "Adams" }) + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList + List.length filteredData |> equals 2 + let result = List.head filteredData + result.ID |> equals 4 + result.FirstName |> equals "Ben" + result.LastName |> equals "Adams" + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + +[] +let ``ObjectListFilter works with IN operator for string type field`` () = + let filter = In { FieldName = "firstName"; Value = [ "Jeneffer"; "Ben" ] } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 + do + let result = List.head filteredData + result.ID |> equals 4 + result.FirstName |> equals "Ben" + result.LastName |> equals "Adams" + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + do + let result = List.last filteredData + result.ID |> equals 7 + result.FirstName |> equals "Jeneffer" + result.LastName |> equals "Trif" + result.Contact |> equals { Email = "j.trif@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ] + +[] +let ``ObjectListFilter works with IN operator for int type field`` () = + let filter = In { FieldName = "id"; Value = [ 4; 2; 7 ] } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList + List.length filteredData |> equals 3 let result = List.head filteredData result.ID |> equals 4 result.FirstName |> equals "Ben" result.LastName |> equals "Adams" - result.Contact |> equals { Email = "b.adams@gmail.com" } - result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] + result.Contact |> equals { Email = "b.adams@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ] [] -let ``ObjectListFilter works with FilterField operator``() = +let ``ObjectListFilter works with FilterField operator`` () = let filter = - FilterField { FieldName = "Contact"; Value = Contains { FieldName = "Email"; Value = "j.trif@gmail.com" } } - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList + FilterField { + FieldName = "Contact" + Value = Contains { FieldName = "Email"; Value = "j.trif@gmail.com" } + } + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 1 let result = List.head filteredData result.ID |> equals 7 result.FirstName |> equals "Jeneffer" result.LastName |> equals "Trif" - result.Contact |> equals { Email = "j.trif@gmail.com" } - result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ] + result.Contact |> equals { Email = "j.trif@gmail.com" } + result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ] [] -let ``ObjectListFilter works with NOT operator``() = - let filter = - Not (Equals { FieldName = "lastName"; Value = "Adams" }) - let queryable = data.AsQueryable() - let filteredData = queryable.Apply(filter) |> Seq.toList +let ``ObjectListFilter works with NOT operator`` () = + let filter = Not (Equals { FieldName = "lastName"; Value = "Adams" }) + let queryable = data.AsQueryable () + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - result1.ID |> equals 2 - result1.FirstName |> equals "Jonathan" - result1.LastName |> equals "Abrams" - result1.Contact |> equals { Email = "j.abrams@gmail.com" } - result1.Friends |> equals [] - -type Complex = - { ID : int - Name : string - Discriminator : string } - -type Building = - { ID : int - Name : string - Discriminator : string } - -type Community = - { ID : int - Name : string - Discriminator : string - Complexes : int list - Buildings : int list } + let result = List.head filteredData + result.ID |> equals 2 + result.FirstName |> equals "Jonathan" + result.LastName |> equals "Abrams" + result.Contact |> equals { Email = "j.abrams@gmail.com" } + result.Friends |> equals [] + +type Complex = { ID : int; Name : string; Discriminator : string } + +type Building = { ID : int; Name : string; Discriminator : string } + +type Community = { + ID : int + Name : string + Discriminator : string + Complexes : int list + Buildings : int list +} type Property = | Complex of Complex @@ -171,55 +238,80 @@ type Property = [] -let ``ObjectListFilter works with getDiscriminator for Complex``() = - let propertyData: Property list = - [ - Complex { ID = 1; Name = "Complex A"; Discriminator = typeof.FullName } - Building { ID = 2; Name = "Building B"; Discriminator = typeof.FullName } - Community { ID = 3; Name = "Community C"; Discriminator = typeof.FullName; Complexes = [1]; Buildings = [2] } - Complex { ID = 4; Name = "Complex AA"; Discriminator = typeof.FullName } - Building { ID = 5; Name = "Building BB"; Discriminator = typeof.FullName } - Community { ID = 6; Name = "Community CC"; Discriminator = typeof.FullName; Complexes = [4]; Buildings = [5] } - ] - let queryable = propertyData.AsQueryable() - let filter = OfTypes [typeof] +let ``ObjectListFilter works with getDiscriminator for Complex`` () = + let propertyData : Property list = [ + Complex { ID = 1; Name = "Complex A"; Discriminator = typeof.FullName } + Building { ID = 2; Name = "Building B"; Discriminator = typeof.FullName } + Community { + ID = 3 + Name = "Community C" + Discriminator = typeof.FullName + Complexes = [ 1 ] + Buildings = [ 2 ] + } + Complex { ID = 4; Name = "Complex AA"; Discriminator = typeof.FullName } + Building { ID = 5; Name = "Building BB"; Discriminator = typeof.FullName } + Community { + ID = 6 + Name = "Community CC" + Discriminator = typeof.FullName + Complexes = [ 4 ] + Buildings = [ 5 ] + } + ] + let queryable = propertyData.AsQueryable () + let filter = OfTypes [ typeof ] let options = - ObjectListFilterLinqOptions( + ObjectListFilterLinqOptions ( (function | Complex c -> c.Discriminator | Building b -> b.Discriminator - | Community c -> c.Discriminator)) - let filteredData = queryable.Apply(filter, options) |> Seq.toList + | Community c -> c.Discriminator) + ) + let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - match result1 with - | Complex c -> - c.ID |> equals 1 - c.Name |> equals "Complex A" - | _ -> failwith "Expected Complex" - let result2 = List.last filteredData - match result2 with - | Complex c -> - c.ID |> equals 4 - c.Name |> equals "Complex AA" - | _ -> failwith "Expected Complex" + do + let result = List.head filteredData + match result with + | Complex c -> + c.ID |> equals 1 + c.Name |> equals "Complex A" + | _ -> failwith "Expected Complex" + do + let result = List.last filteredData + match result with + | Complex c -> + c.ID |> equals 4 + c.Name |> equals "Complex AA" + | _ -> failwith "Expected Complex" [] -let ``ObjectListFilter works with getDiscriminator and getDiscriminatorValue for Complex``() = - let propertyData: Property list = - [ - Complex { ID = 1; Name = "Complex A"; Discriminator = typeof.Name} - Building { ID = 2; Name = "Building B"; Discriminator = typeof.Name } - Community { ID = 3; Name = "Community C"; Discriminator = typeof.Name; Complexes = [1]; Buildings = [2] } - Complex { ID = 4; Name = "Complex AA"; Discriminator = typeof.Name } - Building { ID = 5; Name = "Building BB"; Discriminator = typeof.Name } - Community { ID = 6; Name = "Community CC"; Discriminator = typeof.Name; Complexes = [4]; Buildings = [5] } - ] - let queryable = propertyData.AsQueryable() - let filter = OfTypes [typeof] +let ``ObjectListFilter works with getDiscriminator and getDiscriminatorValue for Complex`` () = + let propertyData : Property list = [ + Complex { ID = 1; Name = "Complex A"; Discriminator = typeof.Name } + Building { ID = 2; Name = "Building B"; Discriminator = typeof.Name } + Community { + ID = 3 + Name = "Community C" + Discriminator = typeof.Name + Complexes = [ 1 ] + Buildings = [ 2 ] + } + Complex { ID = 4; Name = "Complex AA"; Discriminator = typeof.Name } + Building { ID = 5; Name = "Building BB"; Discriminator = typeof.Name } + Community { + ID = 6 + Name = "Community CC" + Discriminator = typeof.Name + Complexes = [ 4 ] + Buildings = [ 5 ] + } + ] + let queryable = propertyData.AsQueryable () + let filter = OfTypes [ typeof ] let options = - ObjectListFilterLinqOptions( + ObjectListFilterLinqOptions ( (function | Complex c -> c.Discriminator | Building b -> b.Discriminator @@ -230,167 +322,169 @@ let ``ObjectListFilter works with getDiscriminator and getDiscriminatorValue for | t when t = typeof -> "Community" | _ -> raise (NotSupportedException "Type not supported")) ) - let filteredData = queryable.Apply(filter, options) |> Seq.toList + let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - match result1 with - | Complex c -> - c.ID |> equals 1 - c.Name |> equals "Complex A" - | _ -> failwith "Expected Complex" - let result2 = List.last filteredData - match result2 with - | Complex c -> - c.ID |> equals 4 - c.Name |> equals "Complex AA" - | _ -> failwith "Expected Complex" - -type Cow = - { ID : int - Name : string - Discriminator : string - __typename : string } - -type Horse = - { ID : int - Name : string - Discriminator : string - __typename : string } - -type Hamster = - { ID : int - Name : string - Discriminator : string - __typename : string } - -let animalData = - [ - { ID = 1; Discriminator="Cow"; Name = "Cow A"; __typename = typeof.Name } - { ID = 2; Discriminator="Horse"; Name = "Horse B"; __typename = typeof.Name } - { ID = 3; Discriminator="Cow"; Name = "Cow C"; __typename = typeof.Name } - { ID = 4; Discriminator="Horse"; Name = "Horse D"; __typename = typeof.Name } - { ID = 5; Discriminator="Hamster"; Name = "Hamster E"; __typename = typeof.Name } - ] + do + let result = List.head filteredData + match result with + | Complex c -> + c.ID |> equals 1 + c.Name |> equals "Complex A" + | _ -> failwith "Expected Complex" + do + let result = List.last filteredData + match result with + | Complex c -> + c.ID |> equals 4 + c.Name |> equals "Complex AA" + | _ -> failwith "Expected Complex" + +type Cow = { ID : int; Name : string; Discriminator : string; __typename : string } + +type Horse = { ID : int; Name : string; Discriminator : string; __typename : string } + +type Hamster = { ID : int; Name : string; Discriminator : string; __typename : string } + +let animalData = [ + { ID = 1; Discriminator = "Cow"; Name = "Cow A"; __typename = typeof.Name } + { + ID = 2 + Discriminator = "Horse" + Name = "Horse B" + __typename = typeof.Name + } + { ID = 3; Discriminator = "Cow"; Name = "Cow C"; __typename = typeof.Name } + { + ID = 4 + Discriminator = "Horse" + Name = "Horse D" + __typename = typeof.Name + } + { + ID = 5 + Discriminator = "Hamster" + Name = "Hamster E" + __typename = typeof.Name + } +] [] -let ``ObjectListFilter works with getDiscriminatorValue for Horse``() = - let queryable = animalData.AsQueryable() - let filter = OfTypes [typeof] +let ``ObjectListFilter works with getDiscriminatorValue for Horse`` () = + let queryable = animalData.AsQueryable () + let filter = OfTypes [ 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 + 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 2 - let result1 = List.head filteredData - match result1 with - | h -> - h.ID |> equals 2 - h.Name |> equals "Horse B" - let result2 = List.last filteredData - match result2 with - | h -> - h.ID |> equals 4 - h.Name |> equals "Horse D" + do + let result = List.head filteredData + match result with + | h -> + h.ID |> equals 2 + h.Name |> equals "Horse B" + do + let result = List.last filteredData + match result with + | h -> + h.ID |> equals 4 + h.Name |> equals "Horse D" [] -let ``ObjectListFilter works with getDiscriminatorValue startsWith for Horse and Hamster``() = - - let queryable = animalData.AsQueryable() +let ``ObjectListFilter works with getDiscriminatorValue startsWith for Horse and Hamster`` () = + let queryable = animalData.AsQueryable () let filter = StartsWith { FieldName = "Discriminator"; Value = "H" } let options = ObjectListFilterLinqOptions ( - (fun entity (discriminator: string) -> - entity.Discriminator.StartsWith discriminator), - getDiscriminatorValue = (function - | t when t = typeof -> t.Name - | t when t = typeof -> t.Name - | t when t = typeof -> t.Name - | _ -> raise (NotSupportedException "Type not supported")) + (fun entity (discriminator : string) -> entity.Discriminator.StartsWith discriminator), + getDiscriminatorValue = + (function + | 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 + let filteredData = queryable.Apply (filter, options) |> Seq.toList List.length filteredData |> equals 3 - let result1 = List.head filteredData - match result1 with - | c -> - c.ID |> equals 2 - c.Name |> equals "Horse B" - let result2 = List.last filteredData - match result2 with - | c -> - c.ID |> equals 5 - c.Name |> equals "Hamster E" - -type ListTagsProduct = { - Name : string - Tags : string list -} + do + let result = List.head filteredData + match result with + | c -> + c.ID |> equals 2 + c.Name |> equals "Horse B" + do + let result = List.last filteredData + match result with + | c -> + c.ID |> equals 5 + c.Name |> equals "Hamster E" + +type ListTagsProduct = { Name : string; Tags : string list } [] -let ``ObjectListFilter works with Contains operator on list collection properties``() = - let productList = - [ - { Name = "Product A"; Tags = ["Tag1"; "Tag2"] } - { Name = "Product B"; Tags = ["Tag2"; "Tag3"] } - { Name = "Product C"; Tags = ["Tag3"; "Tag4"] } - { Name = "Product D"; Tags = ["Tag4"; "Tag5"] } - ] - let queryable = productList.AsQueryable() +let ``ObjectListFilter works with Contains operator on list collection properties`` () = + let productList = [ + { Name = "Product A"; Tags = [ "Tag1"; "Tag2" ] } + { Name = "Product B"; Tags = [ "Tag2"; "Tag3" ] } + { Name = "Product C"; Tags = [ "Tag3"; "Tag4" ] } + { Name = "Product D"; Tags = [ "Tag4"; "Tag5" ] } + ] + let queryable = productList.AsQueryable () let filter = Contains { FieldName = "Tags"; Value = "Tag3" } - let filteredData = queryable.Apply(filter) |> Seq.toList + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - result1.Name |> equals "Product B" - let result2 = List.last filteredData - result2.Name |> equals "Product C" - -type ArrayTagsProduct = { - Name : string - Tags : string array -} + do + let result = List.head filteredData + result.Name |> equals "Product B" + do + let result = List.last filteredData + result.Name |> equals "Product C" + +type ArrayTagsProduct = { Name : string; Tags : string array } [] -let ``ObjectListFilter works with Contains operator on array collection properties``() = - - let productArray = - [ - { Name = "Product A"; Tags = [|"Tag1"; "Tag2"|] } - { Name = "Product B"; Tags = [|"Tag2"; "Tag3"|] } - { Name = "Product C"; Tags = [|"Tag3"; "Tag4"|] } - { Name = "Product D"; Tags = [|"Tag4"; "Tag5"|] } +let ``ObjectListFilter works with Contains operator on array collection properties`` () = + + let productArray = [ + { Name = "Product A"; Tags = [| "Tag1"; "Tag2" |] } + { Name = "Product B"; Tags = [| "Tag2"; "Tag3" |] } + { Name = "Product C"; Tags = [| "Tag3"; "Tag4" |] } + { Name = "Product D"; Tags = [| "Tag4"; "Tag5" |] } ] - let queryable = productArray.AsQueryable() + let queryable = productArray.AsQueryable () let filter = Contains { FieldName = "Tags"; Value = "Tag3" } - let filteredData = queryable.Apply(filter) |> Seq.toList + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - result1.Name |> equals "Product B" - let result2 = List.last filteredData - result2.Name |> equals "Product C" + do + let result = List.head filteredData + result.Name |> equals "Product B" + do + let result = List.last filteredData + result.Name |> equals "Product C" -type SetTagsProduct = { - Name : string - Tags : string Set -} +type SetTagsProduct = { Name : string; Tags : string Set } [] -let ``ObjectListFilter works with Contains operator on set collection properties``() = - - let productArray = - [ - { Name = "Product A"; Tags = [|"Tag1"; "Tag2"|] |> Set.ofArray} - { Name = "Product B"; Tags = [|"Tag2"; "Tag3"|] |> Set.ofArray} - { Name = "Product C"; Tags = [|"Tag3"; "Tag4"|] |> Set.ofArray} - { Name = "Product D"; Tags = [|"Tag4"; "Tag5"|] |> Set.ofArray} +let ``ObjectListFilter works with Contains operator on set collection properties`` () = + + let productArray = [ + { Name = "Product A"; Tags = [| "Tag1"; "Tag2" |] |> Set.ofArray } + { Name = "Product B"; Tags = [| "Tag2"; "Tag3" |] |> Set.ofArray } + { Name = "Product C"; Tags = [| "Tag3"; "Tag4" |] |> Set.ofArray } + { Name = "Product D"; Tags = [| "Tag4"; "Tag5" |] |> Set.ofArray } ] - let queryable = productArray.AsQueryable() + let queryable = productArray.AsQueryable () let filter = Contains { FieldName = "Tags"; Value = "Tag3" } - let filteredData = queryable.Apply(filter) |> Seq.toList + let filteredData = queryable.Apply (filter) |> Seq.toList List.length filteredData |> equals 2 - let result1 = List.head filteredData - result1.Name |> equals "Product B" - let result2 = List.last filteredData - result2.Name |> equals "Product C" + do + let result = List.head filteredData + result.Name |> equals "Product B" + do + let result = List.last filteredData + result.Name |> equals "Product C"