Skip to content

Commit 6194694

Browse files
author
Viktor Tochonov
committed
Implemented support for valueOption properties in CosmosDB tests
1 parent 2fa3df2 commit 6194694

File tree

5 files changed

+234
-22
lines changed

5 files changed

+234
-22
lines changed

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

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace FSharp.Data.GraphQL.Server.Middleware
22

33
open System
4+
open FSharp.Data.GraphQL
45

56
/// A filter definition for a field value.
67
type FieldFilter<'Val> = { FieldName : string; Value : 'Val }
@@ -175,29 +176,89 @@ module ObjectListFilter =
175176
static member op_Implicit (parameter : ParameterExpression) = SourceExpression (parameter :> Expression)
176177
static member op_Implicit (``member`` : MemberExpression) = SourceExpression (``member`` :> Expression)
177178

178-
let rec buildFilterExpr (param : SourceExpression) buildTypeDiscriminatorCheck filter : Expression =
179-
let build = buildFilterExpr param buildTypeDiscriminatorCheck
179+
//let iequtableType = typedefof<IEquatable<_>>
180+
let equalsMethod =
181+
typeof<obj>
182+
|> _.GetMethods(BindingFlags.Instance ||| BindingFlags.Public)
183+
|> Seq.where (fun m -> m.Name = "Equals")
184+
|> Seq.head
185+
186+
let rec buildFilterExpr isEnumerableQuery (param : SourceExpression) buildTypeDiscriminatorCheck filter : Expression =
187+
let build = buildFilterExpr isEnumerableQuery param buildTypeDiscriminatorCheck
188+
let (|NoCast|Enumerable|NonEnumerableCast|) value =
189+
if obj.ReferenceEquals (value, null) then
190+
NoCast
191+
else
192+
if isEnumerableQuery then Enumerable
193+
else NonEnumerableCast (value.GetType ())
194+
let unsafeConvertTo ``type`` ``member`` = Expression.Convert (Expression.Convert(``member``, typeof<obj>), ``type``)
195+
180196
match filter with
197+
| Not (Equals f) ->
198+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
199+
let ``const`` = Expression.Constant (Values.normalizeOptional ``member``.Type f.Value)
200+
if isEnumerableQuery && f.Value <> null then
201+
Expression.Not (Expression.Call (``const``, equalsMethod, ``member``))
202+
else
203+
Expression.NotEqual (``member``, ``const``)
181204
| Not f -> f |> build |> Expression.Not :> Expression
182205
| And (f1, f2) -> Expression.AndAlso (build f1, build f2)
183206
| Or (f1, f2) -> Expression.OrElse (build f1, build f2)
184-
| Equals f -> Expression.Equal (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
185-
| GreaterThan f -> Expression.GreaterThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
186-
| LessThan f -> Expression.LessThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
187-
| GreaterThanOrEqual f -> Expression.GreaterThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
188-
| LessThanOrEqual f -> Expression.LessThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
207+
| Equals f ->
208+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
209+
match f.Value with
210+
| NoCast ->
211+
Expression.Equal (``member``, Expression.Constant f.Value)
212+
| Enumerable ->
213+
Expression.Equal (``member``, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
214+
| NonEnumerableCast ``type`` ->
215+
Expression.Equal ((unsafeConvertTo ``type`` ``member``), Expression.Constant f.Value)
216+
| GreaterThan f ->
217+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
218+
let ``const`` = Expression.Constant (Values.normalizeOptional ``member``.Type f.Value)
219+
if isEnumerableQuery then
220+
Expression.GreaterThan (``member``, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
221+
else
222+
Expression.GreaterThan (Expression.Convert (Expression.Convert(``member``, typeof<obj>), f.Value.GetType ()), Expression.Constant f.Value)
223+
| LessThan f ->
224+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
225+
match f.Value with
226+
| NoCast ->
227+
Expression.LessThan (``member``, Expression.Constant f.Value)
228+
| Enumerable ->
229+
Expression.LessThan (``member``, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
230+
| NonEnumerableCast ``type`` ->
231+
Expression.LessThan ((unsafeConvertTo ``type`` ``member``), Expression.Constant f.Value)
232+
| GreaterThanOrEqual f ->
233+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
234+
match f.Value with
235+
| NoCast ->
236+
Expression.GreaterThanOrEqual (``member``, Expression.Constant f.Value)
237+
| Enumerable ->
238+
Expression.GreaterThanOrEqual (``member``, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
239+
| NonEnumerableCast ``type`` ->
240+
Expression.GreaterThanOrEqual ((unsafeConvertTo ``type`` ``member``), Expression.Constant f.Value)
241+
| LessThanOrEqual f ->
242+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
243+
match f.Value with
244+
| NoCast ->
245+
Expression.LessThanOrEqual (``member``, Expression.Constant f.Value)
246+
| Enumerable ->
247+
Expression.LessThanOrEqual (``member``, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
248+
| NonEnumerableCast ``type`` ->
249+
Expression.LessThanOrEqual ((unsafeConvertTo ``type`` ``member``), Expression.Constant f.Value)
189250
| StartsWith f ->
190251
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
191252
if ``member``.Type = stringType then
192-
Expression.Call (``member``, StringStartsWithMethod, Expression.Constant (f.Value))
253+
Expression.Call (``member``, StringStartsWithMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
193254
else
194-
Expression.Call (Expression.Convert (``member``, stringType), StringStartsWithMethod, Expression.Constant (f.Value))
255+
Expression.Call (Expression.Convert (``member``, stringType), StringStartsWithMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
195256
| EndsWith f ->
196257
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
197258
if ``member``.Type = stringType then
198-
Expression.Call (``member``, StringEndsWithMethod, Expression.Constant (f.Value))
259+
Expression.Call (``member``, StringEndsWithMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
199260
else
200-
Expression.Call (Expression.Convert (``member``, stringType), StringEndsWithMethod, Expression.Constant (f.Value))
261+
Expression.Call (Expression.Convert (``member``, stringType), StringEndsWithMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
201262
| Contains f ->
202263
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
203264
let isEnumerable (memberType : Type) =
@@ -216,35 +277,38 @@ module ObjectListFilter =
216277
| value -> value.GetType()
217278
let castedMember =
218279
if itemType = valueType then ``member`` :> Expression
219-
else
280+
elif isEnumerableQuery then
220281
let castMethod = getEnumerableCastMethod valueType
221282
Expression.Call (castMethod, ``member``)
283+
else
284+
let castedEnumerableType = genericIEnumerableType.MakeGenericType ([| valueType |])
285+
unsafeConvertTo castedEnumerableType ``member``
222286
match getCollectionInstanceContainsMethod memberType with
223287
| ValueNone ->
224288
let enumerableContains = getEnumerableContainsMethod valueType
225-
Expression.Call (enumerableContains, castedMember, Expression.Constant (f.Value))
289+
Expression.Call (enumerableContains, castedMember, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
226290
| ValueSome instanceContainsMethod ->
227-
Expression.Call (castedMember, instanceContainsMethod, Expression.Constant (f.Value))
291+
Expression.Call (castedMember, instanceContainsMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
228292
match ``member``.Member with
229293
| :? PropertyInfo as prop when prop.PropertyType |> isEnumerable -> callContains prop.PropertyType
230294
| :? FieldInfo as field when field.FieldType |> isEnumerable -> callContains field.FieldType
231295
| _ ->
232296
if ``member``.Type = stringType then
233-
Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value))
297+
Expression.Call (``member``, StringContainsMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
234298
else
235-
Expression.Call (Expression.Convert (``member``, stringType), StringContainsMethod, Expression.Constant (f.Value))
299+
Expression.Call (Expression.Convert (``member``, stringType), StringContainsMethod, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value))
236300
| In f when not (f.Value.IsEmpty) ->
237301
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
238302
let enumerableContains = getEnumerableContainsMethod typeof<IComparable>
239-
Expression.Call (enumerableContains, Expression.Constant (f.Value), Expression.Convert (``member``, typeof<IComparable>))
303+
Expression.Call (enumerableContains, Expression.Constant (Values.normalizeOptional ``member``.Type f.Value), Expression.Convert (``member``, typeof<IComparable>))
240304
| In f -> Expression.Constant (true)
241305
| OfTypes types ->
242306
types
243307
|> Seq.map (fun t -> buildTypeDiscriminatorCheck param t)
244308
|> Seq.reduce (fun acc expr -> Expression.OrElse (acc, expr))
245309
| FilterField f ->
246310
let paramExpr = Expression.PropertyOrField (param, f.FieldName)
247-
buildFilterExpr (SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value
311+
buildFilterExpr isEnumerableQuery (SourceExpression paramExpr) buildTypeDiscriminatorCheck f.Value
248312

249313
type private CompareDiscriminatorExpressionVisitor<'T, 'D> (
250314
compareDiscriminator : CompareDiscriminatorExpression<'T, 'D>,
@@ -260,7 +324,10 @@ module ObjectListFilter =
260324
else
261325
node :> Expression
262326

327+
let enumerableQueryType = typedefof<EnumerableQuery<_>>
328+
263329
let apply (options : ObjectListFilterLinqOptions<'T, 'D>) (filter : ObjectListFilter) (query : IQueryable<'T>) =
330+
let isEnumerableQuery = query.GetType().GetGenericTypeDefinition() = enumerableQueryType
264331
// Helper for discriminator comparison
265332
let buildTypeDiscriminatorCheck (param : SourceExpression) (t : Type) =
266333
match options.CompareDiscriminator, options.GetDiscriminatorValue with
@@ -290,7 +357,7 @@ module ObjectListFilter =
290357
replacer.Visit discExpr.Body
291358
let queryExpr =
292359
let param = Expression.Parameter (typeof<'T>, "x")
293-
let body = buildFilterExpr (SourceExpression param) buildTypeDiscriminatorCheck filter
360+
let body = buildFilterExpr isEnumerableQuery (SourceExpression param) buildTypeDiscriminatorCheck filter
294361
whereExpr<'T> query param body
295362
// Create and execute the final expression
296363
query.Provider.CreateQuery<'T> (queryExpr)

src/FSharp.Data.GraphQL.Server/FSharp.Data.GraphQL.Server.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
2727
<_Parameter1>FSharp.Data.GraphQL.Server.AspNetCore</_Parameter1>
2828
</AssemblyAttribute>
29+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
30+
<_Parameter1>FSharp.Data.GraphQL.Server.Middleware</_Parameter1>
31+
</AssemblyAttribute>
2932
</ItemGroup>
3033

3134
<ItemGroup>

src/FSharp.Data.GraphQL.Server/Values.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ let private wrapOptionalNone (outputType : Type) (inputType : Type) =
3030
else
3131
null
3232

33-
let private normalizeOptional (outputType : Type) value =
33+
let normalizeOptional (outputType : Type) value =
3434
match value with
3535
| null -> wrapOptionalNone outputType typeof<obj>
3636
| value ->

src/FSharp.Data.GraphQL.Shared/Helpers/ObjAndStructConversions.fs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ module internal Seq =
3838
|> Seq.map ValueSome
3939
|> _.FirstOrDefault()
4040

41+
let vtryHead (source : 'T seq) =
42+
use enumerator = source.GetEnumerator ()
43+
if not (enumerator.MoveNext ()) then
44+
ValueNone
45+
else
46+
match enumerator.Current with
47+
| null -> ValueNone
48+
| head -> ValueSome head
49+
50+
let vtryLast (source : 'T seq) =
51+
use enumerator = source.GetEnumerator ()
52+
if not (enumerator.MoveNext ()) then
53+
ValueNone
54+
else
55+
let mutable last = enumerator.Current
56+
while enumerator.MoveNext () do
57+
last <- enumerator.Current
58+
match last with
59+
| null -> ValueNone
60+
| last -> ValueSome last
61+
4162
module internal List =
4263

4364
let vchoose mapping list = list |> Seq.ofList |> Seq.vchoose mapping |> List.ofSeq

0 commit comments

Comments
 (0)