Skip to content

Commit 8b05b5b

Browse files
Fix for guid filter in GraphQL (#1644)
## Why make this change? Fix #1598 to ensure GQL filter works with guid types. ## What is this change? Correctly resolved guid type to Hotchocolate type of `UUID` (per: https://chillicream.com/docs/hotchocolate/v12/defining-a-schema/scalars#uuid-type) which was earlier resolved to string. Accordingly parsed the guid values using `Guid.Parse()` method instead of parsing it as string. ## How was this tested? - [x] Unit Tests - `SchemaConverterTests` - [X] Integration Tests - Added filter tests with GUID to `GraphQLSupportedTypesTestsBase.QueryTypeColumnFilterAndOrderBy` --------- Co-authored-by: Aniruddh Munde <[email protected]>
1 parent ace4bff commit 8b05b5b

14 files changed

Lines changed: 94 additions & 32 deletions

File tree

src/Core/Services/ResolverMiddleware.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ protected static bool IsInnerObject(IMiddlewareContext context)
265265
SINGLE_TYPE => ((FloatValueNode)value).ToSingle(),
266266
FLOAT_TYPE => ((FloatValueNode)value).ToDouble(),
267267
DECIMAL_TYPE => ((FloatValueNode)value).ToDecimal(),
268+
// If we reach here, we can be sure that the value will not be null.
269+
UUID_TYPE => Guid.TryParse(value.Value!.ToString(), out Guid guidValue) ? guidValue : value.Value,
268270
_ => value.Value
269271
};
270272
}

src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes
55
{
66
/// <summary>
7-
/// Only used to group the supported type names under a class with a relevant name
7+
/// Only used to group the supported type names under a class with a relevant name.
8+
/// The type names mentioned here are Hotchocolate scalar built in types.
9+
/// The corresponding SQL type name may be different for e.g. UUID maps to Guid as the SQL type.
810
/// </summary>
911
public static class SupportedTypes
1012
{
13+
public const string UUID_TYPE = "UUID";
1114
public const string BYTE_TYPE = "Byte";
1215
public const string SHORT_TYPE = "Short";
1316
public const string INT_TYPE = "Int";
@@ -18,9 +21,8 @@ public static class SupportedTypes
1821
public const string STRING_TYPE = "String";
1922
public const string BOOLEAN_TYPE = "Boolean";
2023
public const string DATETIME_TYPE = "DateTime";
21-
public const string DATETIMEOFFSET_TYPE = "DateTimeOffset";
2224
public const string BYTEARRAY_TYPE = "ByteArray";
23-
public const string GUID_TYPE = "Guid";
25+
public const string DATETIMEOFFSET_TYPE = "DateTimeOffset";
2426
public const string LOCALTIME_TYPE = "LocalTime";
2527
public const string TIME_TYPE = "Time";
2628
}

src/Service.GraphQLBuilder/GraphQLUtils.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static bool IsBuiltInType(ITypeNode typeNode)
4545
HashSet<string> inBuiltTypes = new()
4646
{
4747
"ID",
48+
UUID_TYPE,
4849
BYTE_TYPE,
4950
SHORT_TYPE,
5051
INT_TYPE,

src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public static InputObjectTypeDefinitionNode StringInputType() =>
170170
new InputValueDefinitionNode(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new StringType().ToTypeNode(), null, new List<DirectiveNode>()),
171171
new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new StringType().ToTypeNode(), null, new List<DirectiveNode>()),
172172
new InputValueDefinitionNode(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List<DirectiveNode>()),
173-
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
173+
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
174174
}
175175
);
176176

@@ -198,7 +198,7 @@ public static InputObjectTypeDefinitionNode ByteArrayInputType() =>
198198
new StringValueNode("Input type for adding ByteArray filters"),
199199
new List<DirectiveNode>(),
200200
new List<InputValueDefinitionNode> {
201-
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
201+
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
202202
}
203203
);
204204

@@ -215,13 +215,32 @@ public static InputObjectTypeDefinitionNode LocalTimeInputType() =>
215215
new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new LocalTimeType().ToTypeNode(), null, new List<DirectiveNode>()),
216216
new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List<DirectiveNode>()),
217217
new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LocalTimeType().ToTypeNode(), null, new List<DirectiveNode>()),
218-
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("is null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
218+
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
219+
}
220+
);
221+
222+
public static InputObjectTypeDefinitionNode UuidInputType() =>
223+
new(
224+
location: null,
225+
new NameNode("UuidFilterInput"),
226+
new StringValueNode("Input type for adding Uuid filters"),
227+
new List<DirectiveNode>(),
228+
new List<InputValueDefinitionNode> {
229+
new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
230+
new InputValueDefinitionNode(null, new NameNode("contains"), new StringValueNode("Contains"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
231+
new InputValueDefinitionNode(null, new NameNode("notContains"), new StringValueNode("Not Contains"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
232+
new InputValueDefinitionNode(null, new NameNode("startsWith"), new StringValueNode("Starts With"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
233+
new InputValueDefinitionNode(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
234+
new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new UuidType().ToTypeNode(), null, new List<DirectiveNode>()),
235+
new InputValueDefinitionNode(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List<DirectiveNode>()),
236+
new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List<DirectiveNode>())
219237
}
220238
);
221239

222240
public static Dictionary<string, InputObjectTypeDefinitionNode> InputTypes = new()
223241
{
224242
{ "ID", IdInputType() },
243+
{ UUID_TYPE, UuidInputType() },
225244
{ BYTE_TYPE, ByteInputType() },
226245
{ SHORT_TYPE, ShortInputType() },
227246
{ INT_TYPE, IntInputType() },

src/Service.GraphQLBuilder/Sql/SchemaConverter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public static string GetGraphQLTypeFromSystemType(Type type)
220220
return type.Name switch
221221
{
222222
"String" => STRING_TYPE,
223-
"Guid" => STRING_TYPE,
223+
"Guid" => UUID_TYPE,
224224
"Byte" => BYTE_TYPE,
225225
"Int16" => SHORT_TYPE,
226226
"Int32" => INT_TYPE,
@@ -258,7 +258,7 @@ public static IValueNode CreateValueNodeFromDbObjectMetadata(object metadataValu
258258
short value => new ObjectValueNode(new ObjectFieldNode(SHORT_TYPE, new IntValueNode(value))),
259259
int value => new ObjectValueNode(new ObjectFieldNode(INT_TYPE, value)),
260260
long value => new ObjectValueNode(new ObjectFieldNode(LONG_TYPE, new IntValueNode(value))),
261-
Guid value => new ObjectValueNode(new ObjectFieldNode(GUID_TYPE, value.ToString())),
261+
Guid value => new ObjectValueNode(new ObjectFieldNode(UUID_TYPE, new UuidType().ParseValue(value))),
262262
string value => new ObjectValueNode(new ObjectFieldNode(STRING_TYPE, value)),
263263
bool value => new ObjectValueNode(new ObjectFieldNode(BOOLEAN_TYPE, value)),
264264
float value => new ObjectValueNode(new ObjectFieldNode(SINGLE_TYPE, new SingleType().ParseValue(value))),

src/Service.Tests/DatabaseSchema-MsSql.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ CREATE TABLE type_table(
164164
smalldatetime_types smalldatetime,
165165
time_types time,
166166
bytearray_types varbinary(max),
167-
guid_types uniqueidentifier DEFAULT newid()
167+
uuid_types uniqueidentifier DEFAULT newid()
168168
);
169169

170170
CREATE TABLE trees (
@@ -415,6 +415,7 @@ VALUES
415415
'9999-12-31', '9999-12-31 23:59:59', '9999-12-31 23:59:59.9999999', '9999-12-31 23:59:59.9999999+14:00', '2079-06-06', '23:59:59.9999999',
416416
0xFFFFFFFF),
417417
(5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
418+
INSERT INTO type_table(id, uuid_types) values(10, 'D1D021A8-47B4-4AE4-B718-98E89C41A161');
418419
SET IDENTITY_INSERT type_table OFF
419420

420421
SET IDENTITY_INSERT sales ON

src/Service.Tests/DatabaseSchema-PostgreSql.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ CREATE TABLE type_table(
143143
boolean_types boolean,
144144
datetime_types timestamp,
145145
bytearray_types bytea,
146-
guid_types uuid DEFAULT gen_random_uuid ()
146+
uuid_types uuid DEFAULT gen_random_uuid ()
147147
);
148148

149149
CREATE TABLE trees (
@@ -328,6 +328,7 @@ INSERT INTO type_table(id, short_types, int_types, long_types, string_types, sin
328328
(3, -32768, -2147483648, -9223372036854775808, '', -3.4E38, -1.7E308, 2.929292E-19, true, '1753-01-01 00:00:00.000', '\x00000000'),
329329
(4, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9999-12-31 23:59:59.997', '\xFFFFFFFF'),
330330
(5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
331+
INSERT INTO type_table(id, uuid_types) values(10, 'D1D021A8-47B4-4AE4-B718-98E89C41A161');
331332
INSERT INTO trees("treeId", species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m');
332333
INSERT INTO fungi(speciesid, region) VALUES (1, 'northeast'), (2, 'southwest');
333334
INSERT INTO notebooks(id, noteBookName, color, ownerName) VALUES (1, 'Notebook1', 'red', 'Sean'), (2, 'Notebook2', 'green', 'Ani'), (3, 'Notebook3', 'blue', 'Jarupat'), (4, 'Notebook4', 'yellow', 'Aaron');

src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public void MultipleColumnsAllMapped()
229229
[DataRow(typeof(DateTime), DATETIME_TYPE)]
230230
[DataRow(typeof(DateTimeOffset), DATETIME_TYPE)]
231231
[DataRow(typeof(byte[]), BYTEARRAY_TYPE)]
232-
[DataRow(typeof(Guid), STRING_TYPE)]
232+
[DataRow(typeof(Guid), UUID_TYPE)]
233233
[DataRow(typeof(TimeOnly), LOCALTIME_TYPE)]
234234
public void SystemTypeMapsToCorrectGraphQLType(Type systemType, string graphQLType)
235235
{

src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase
7070
[DataRow(BYTEARRAY_TYPE, 2)]
7171
[DataRow(BYTEARRAY_TYPE, 3)]
7272
[DataRow(BYTEARRAY_TYPE, 4)]
73-
[DataRow(GUID_TYPE, 1)]
74-
[DataRow(GUID_TYPE, 2)]
75-
[DataRow(GUID_TYPE, 3)]
76-
[DataRow(GUID_TYPE, 4)]
73+
[DataRow(UUID_TYPE, 1)]
74+
[DataRow(UUID_TYPE, 2)]
75+
[DataRow(UUID_TYPE, 3)]
76+
[DataRow(UUID_TYPE, 4)]
7777
public async Task QueryTypeColumn(string type, int id)
7878
{
7979
if (!IsSupportedType(type))
@@ -140,6 +140,8 @@ public async Task QueryTypeColumn(string type, int id)
140140
[DataRow(DECIMAL_TYPE, "eq", "-9.292929", "-9.292929", "=")]
141141
[DataRow(BOOLEAN_TYPE, "neq", "\'false\'", "false", "!=")]
142142
[DataRow(BOOLEAN_TYPE, "eq", "\'false\'", "false", "=")]
143+
[DataRow(UUID_TYPE, "eq", "'D1D021A8-47B4-4AE4-B718-98E89C41A161'", "\"D1D021A8-47B4-4AE4-B718-98E89C41A161\"", "=")]
144+
[DataRow(UUID_TYPE, "neq", "'D1D021A8-47B4-4AE4-B718-98E89C41A161'", "\"D1D021A8-47B4-4AE4-B718-98E89C41A161\"", "!=")]
143145
public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator)
144146
{
145147
if (!IsSupportedType(type))
@@ -429,8 +431,8 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value)
429431
[DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")]
430432
[DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")]
431433
[DataRow(BYTEARRAY_TYPE, "null")]
432-
[DataRow(GUID_TYPE, "\"3a1483a5-9ac2-4998-bcf3-78a28078c6ac\"")]
433-
[DataRow(GUID_TYPE, "null")]
434+
[DataRow(UUID_TYPE, "\"3a1483a5-9ac2-4998-bcf3-78a28078c6ac\"")]
435+
[DataRow(UUID_TYPE, "null")]
434436
public async Task UpdateTypeColumn(string type, string value)
435437
{
436438
if (!IsSupportedType(type))
@@ -464,8 +466,8 @@ public async Task UpdateTypeColumn(string type, string value)
464466
[DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")]
465467
[DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")]
466468
[DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")]
467-
[DataRow(GUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")]
468-
[DataRow(GUID_TYPE, null)]
469+
[DataRow(UUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")]
470+
[DataRow(UUID_TYPE, null)]
469471
public async Task UpdateTypeColumnWithArgument(string type, object value)
470472
{
471473
if (!IsSupportedType(type))
@@ -511,12 +513,43 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect
511513
{
512514
CompareTimeResults(actual.ToString(), expected);
513515
}
516+
else if (type == UUID_TYPE)
517+
{
518+
CompareUuidResults(actual.ToString(), expected);
519+
}
514520
else
515521
{
516522
SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString());
517523
}
518524
}
519525

526+
private static void CompareUuidResults(string actual, string expected)
527+
{
528+
string fieldName = "uuid_types";
529+
530+
using JsonDocument actualJsonDoc = JsonDocument.Parse(actual);
531+
using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected);
532+
533+
if (actualJsonDoc.RootElement.ValueKind is JsonValueKind.Array)
534+
{
535+
ValidateArrayResults(actualJsonDoc, expectedJsonDoc, fieldName);
536+
return;
537+
}
538+
539+
string actualUuidString = actualJsonDoc.RootElement.GetProperty(fieldName).ToString();
540+
string expectedUuidString = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString();
541+
542+
// handles cases when one of the values is null
543+
if (string.IsNullOrEmpty(actualUuidString) || string.IsNullOrEmpty(expectedUuidString))
544+
{
545+
Assert.AreEqual(expectedUuidString, actualUuidString);
546+
}
547+
else
548+
{
549+
AssertOnFields(fieldName, actualUuidString, expectedUuidString);
550+
}
551+
}
552+
520553
/// <summary>
521554
/// HotChocolate will parse large floats to exponential notation
522555
/// while the db will return the number fully printed out. Because
@@ -701,6 +734,12 @@ private static void AssertOnFields(string field, string actualElement, string ex
701734
TimeOnly expectedTime = TimeOnly.Parse(expectedElement.ToString());
702735
Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString());
703736
}
737+
else if (field.StartsWith(UUID_TYPE.ToLower()))
738+
{
739+
Guid actualValue = Guid.Parse(actualElement.ToString());
740+
Guid expectedValue = Guid.Parse(expectedElement.ToString());
741+
Assert.AreEqual(actualValue, expectedValue);
742+
}
704743
else
705744
{
706745
Assert.AreEqual(double.Parse(expectedElement), double.Parse(actualElement));
@@ -713,11 +752,7 @@ private static void AssertOnFields(string field, string actualElement, string ex
713752
/// </summary>
714753
private static string TypeNameToGraphQLType(string typeName)
715754
{
716-
if (typeName is GUID_TYPE)
717-
{
718-
return STRING_TYPE;
719-
}
720-
else if (typeName is DATETIMEOFFSET_TYPE)
755+
if (typeName is DATETIMEOFFSET_TYPE)
721756
{
722757
return DATETIME_TYPE;
723758
}

src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ protected override bool IsSupportedType(string type)
5656
{
5757
return type switch
5858
{
59-
GUID_TYPE => false,
59+
UUID_TYPE => false,
6060
DATETIMEOFFSET_TYPE => false,
6161
TIME_TYPE => false,
6262
_ => true

0 commit comments

Comments
 (0)