From 800de2bbbeed0d7fef7d61ccb6de162ac599a74c Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 31 Mar 2023 16:24:08 -0700 Subject: [PATCH 01/85] Add support for timespan --- .../GraphQLTypes/DefaultValueType.cs | 1 + .../GraphQLTypes/SupportedTypes.cs | 3 + src/Service.GraphQLBuilder/GraphQLUtils.cs | 3 +- .../Queries/StandardQueryInputs.cs | 20 ++++- .../Sql/SchemaConverter.cs | 2 + src/Service.Tests/DatabaseSchema-MsSql.sql | 32 +++++-- .../Sql/SchemaConverterTests.cs | 1 + src/Service/Parsers/EdmModelBuilder.cs | 87 ++++++++----------- 8 files changed, 92 insertions(+), 57 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index 2ce9d6b95e..1a97f76a81 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -24,6 +24,7 @@ protected override void Configure(IInputObjectTypeDescriptor descriptor) descriptor.Field(DECIMAL_TYPE).Type(); descriptor.Field(DATETIME_TYPE).Type(); descriptor.Field(BYTEARRAY_TYPE).Type(); + descriptor.Field(TIMESPAN_TYPE).Type(); } } } diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index 52c1b25ad4..54b87936b5 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -18,8 +18,11 @@ public static class SupportedTypes public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; public const string DATETIME_TYPE = "DateTime"; + // The DATETIME_NONUTC_TYPE constant is only used in testing + // since PostgreSQL doesn't support datetime values with a non-UTC time zone. public const string DATETIME_NONUTC_TYPE = "DateTimeNonUTC"; public const string BYTEARRAY_TYPE = "ByteArray"; + public const string TIMESPAN_TYPE = "TimeSpan"; public const string GUID_TYPE = "Guid"; } } diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 29b361d688..836c63004d 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -48,7 +48,8 @@ public static bool IsBuiltInType(ITypeNode typeNode) STRING_TYPE, BOOLEAN_TYPE, DATETIME_TYPE, - BYTEARRAY_TYPE + BYTEARRAY_TYPE, + TIMESPAN_TYPE }; string name = typeNode.NamedType().Name.Value; return inBuiltTypes.Contains(name); diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index 69ead89827..190c0d2c52 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -190,6 +190,23 @@ public static InputObjectTypeDefinitionNode DateTimeInputType() => } ); + public static InputObjectTypeDefinitionNode TimeSpanInputType() => + new( + location: null, + new NameNode("TimeSpanFilterInput"), + new StringValueNode("Input type for adding TimeSpan filters"), + new List(), + new List { + new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("is null test"), new BooleanType().ToTypeNode(), null, new List()) + } + ); + public static InputObjectTypeDefinitionNode ByteArrayInputType() => new( location: null, @@ -214,7 +231,8 @@ public static InputObjectTypeDefinitionNode ByteArrayInputType() => { BOOLEAN_TYPE, BooleanInputType() }, { STRING_TYPE, StringInputType() }, { DATETIME_TYPE, DateTimeInputType() }, - { BYTEARRAY_TYPE, ByteArrayInputType() } + { BYTEARRAY_TYPE, ByteArrayInputType() }, + { TIMESPAN_TYPE, TimeSpanInputType() }, }; /// diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 521e59616a..f5dc37e10f 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -82,6 +82,7 @@ public static ObjectTypeDefinitionNode FromDatabaseObject( DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseResult(value))), DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseValue(value))), byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(value))), + TimeSpan value => new ObjectValueNode(new ObjectFieldNode(TIMESPAN_TYPE, new TimeSpanType().ParseValue(value))), _ => throw new DataApiBuilderException( message: $"The type {column.DefaultValue.GetType()} is not supported as a GraphQL default value", statusCode: HttpStatusCode.InternalServerError, @@ -244,6 +245,7 @@ public static string GetGraphQLTypeForColumnType(Type type) "Boolean" => BOOLEAN_TYPE, "DateTime" => DATETIME_TYPE, "Byte[]" => BYTEARRAY_TYPE, + "TimeSpan" => TIMESPAN_TYPE, _ => throw new DataApiBuilderException( message: $"Column type {type} not handled by case. Please add a case resolving {type} to the appropriate GraphQL type", statusCode: HttpStatusCode.InternalServerError, diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index f8dfb7a171..4488fb080b 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -143,7 +143,12 @@ CREATE TABLE type_table( float_types float, decimal_types decimal(38, 19), boolean_types bit, + date_types date, datetime_types datetime, + datetime2_types datetime2, + datetimeoffset_types datetimeoffset, + smalldatetime_types smalldatetime, + time_types time, bytearray_types varbinary(max), guid_types uniqueidentifier DEFAULT newid() ); @@ -359,12 +364,27 @@ INSERT INTO reviews(id, book_id, content) VALUES (567, 1, 'Indeed a great book') SET IDENTITY_INSERT reviews OFF SET IDENTITY_INSERT type_table ON -INSERT INTO type_table(id, byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, datetime_types, bytearray_types) VALUES - (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, '1999-01-08 10:23:54', 0xABCDEF0123), - (2, 0, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, 0, '1999-01-08 10:23:00', 0x98AB7511AABB1234), - (3, 0, -32768, -2147483648, -9223372036854775808, 'null', -3.4E38, -1.7E308, 2.929292E-19, 1, '1753-01-01 00:00:00.000', 0x00000000), - (4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, 1, '9999-12-31 23:59:59', 0xFFFFFFFF), - (5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INSERT INTO type_table(id, +byte_types, short_types, int_types, long_types, +string_types, +single_types, float_types, decimal_types, +boolean_types, +date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, time_types, +bytearray_types) +VALUES + (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, + '1999-01-08', '1999-01-08 10:23:54', '1999-01-08 10:23:54.9999999', '1999-01-08 10:23:54.9999999-14:00', '1999-01-08 10:23:54', '10:23:54.9999999', + 0xABCDEF0123), + (2, 0, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, 0, + '1999-01-08', '1999-01-08 10:23:00', '1999-01-08 10:23:00.9999999', '1999-01-08 10:23:00.9999999+13:00', '1999-01-08 10:23:00', '10:23:00.9999999', + 0x98AB7511AABB1234), + (3, 0, -32768, -2147483648, -9223372036854775808, 'null', -3.4E38, -1.7E308, 2.929292E-19, 1, + '0001-01-01', '1753-01-01 00:00:00.000', '0001-01-01 00:00:00.0000000', '0001-01-01 00:00:00.0000000+0:00', '1900-01-01 00:00:00', '00:00:00.0000000', + 0x00000000), + (4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, 1, + '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', + 0xFFFFFFFF), + (5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); SET IDENTITY_INSERT type_table OFF SET IDENTITY_INSERT sales ON diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index 20bf8d158a..b94f6e1116 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -227,6 +227,7 @@ public void MultipleColumnsAllMapped() [DataRow(typeof(bool), BOOLEAN_TYPE)] [DataRow(typeof(DateTime), DATETIME_TYPE)] [DataRow(typeof(byte[]), BYTEARRAY_TYPE)] + [DataRow(typeof(TimeSpan), TIMESPAN_TYPE)] public void SystemTypeMapsToCorrectGraphQLType(Type systemType, string graphQLType) { SourceDefinition table = new(); diff --git a/src/Service/Parsers/EdmModelBuilder.cs b/src/Service/Parsers/EdmModelBuilder.cs index 1279c4817a..28ab5669f3 100644 --- a/src/Service/Parsers/EdmModelBuilder.cs +++ b/src/Service/Parsers/EdmModelBuilder.cs @@ -69,56 +69,9 @@ SourceDefinition sourceDefinition // each column represents a property of the current entity we are adding foreach (string column in sourceDefinition.Columns.Keys) { - // need to convert our column system type to an Edm type Type columnSystemType = sourceDefinition.Columns[column].SystemType; - EdmPrimitiveTypeKind type = EdmPrimitiveTypeKind.None; - if (columnSystemType.IsArray) - { - columnSystemType = columnSystemType.GetElementType()!; - } - - switch (columnSystemType.Name) - { - case "String": - type = EdmPrimitiveTypeKind.String; - break; - case "Guid": - type = EdmPrimitiveTypeKind.Guid; - break; - case "Byte": - type = EdmPrimitiveTypeKind.Byte; - break; - case "Int16": - type = EdmPrimitiveTypeKind.Int16; - break; - case "Int32": - type = EdmPrimitiveTypeKind.Int32; - break; - case "Int64": - type = EdmPrimitiveTypeKind.Int64; - break; - case "Single": - type = EdmPrimitiveTypeKind.Single; - break; - case "Double": - type = EdmPrimitiveTypeKind.Double; - break; - case "Decimal": - type = EdmPrimitiveTypeKind.Decimal; - break; - case "Boolean": - type = EdmPrimitiveTypeKind.Boolean; - break; - case "DateTime": - type = EdmPrimitiveTypeKind.DateTimeOffset; - break; - case "Date": - type = EdmPrimitiveTypeKind.Date; - break; - default: - throw new ArgumentException($"Column type" + - $" {columnSystemType.Name} not yet supported."); - } + // need to convert our column system type to an Edm type + EdmPrimitiveTypeKind type = GetEdmPrimitiveTypeFromSystemType(columnSystemType); // here we must use the correct aliasing for the column name // which is on a per entity basis. @@ -149,6 +102,42 @@ SourceDefinition sourceDefinition return this; } + /// + /// Given the system type, returns the corresponding primitive type kind. + /// + /// Type of the column. + /// EdmPrimitiveTypeKind + /// Throws when the column + private static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type columnSystemType) + { + if (columnSystemType.IsArray) + { + columnSystemType = columnSystemType.GetElementType()!; + } + + EdmPrimitiveTypeKind type = columnSystemType.Name switch + { + "String" => EdmPrimitiveTypeKind.String, + "Guid" => EdmPrimitiveTypeKind.Guid, + "Byte" => EdmPrimitiveTypeKind.Byte, + "Int16" => EdmPrimitiveTypeKind.Int16, + "Int32" => EdmPrimitiveTypeKind.Int32, + "Int64" => EdmPrimitiveTypeKind.Int64, + "Single" => EdmPrimitiveTypeKind.Single, + "Double" => EdmPrimitiveTypeKind.Double, + "Decimal" => EdmPrimitiveTypeKind.Decimal, + "Boolean" => EdmPrimitiveTypeKind.Boolean, + "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, + "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, + "Date" => EdmPrimitiveTypeKind.Date, + "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, + _ => throw new ArgumentException($"Column type" + + $" {columnSystemType.Name} not yet supported.") + }; + + return type; + } + /// /// Add the entity sets contained within the schema to container. /// From 0f12bd1cc62208ba0e2897352a44d52eb51663c0 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 09:57:36 -0700 Subject: [PATCH 02/85] remove unused using --- src/Service.GraphQLBuilder/Sql/SchemaConverter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 055d659822..47fccc9033 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Net; From 736724440aed672ce77febfb6a941b43e0a57b75 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 13:51:01 -0700 Subject: [PATCH 03/85] Add a case for TimeSpanType in ResolverMiddleware --- src/Service/Services/DbTypeHelper.cs | 2 ++ src/Service/Services/ResolverMiddleware.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Service/Services/DbTypeHelper.cs b/src/Service/Services/DbTypeHelper.cs index 57a63b6fd1..e1f7a868a5 100644 --- a/src/Service/Services/DbTypeHelper.cs +++ b/src/Service/Services/DbTypeHelper.cs @@ -30,6 +30,7 @@ public static class DbTypeHelper [typeof(char)] = DbType.StringFixedLength, [typeof(Guid)] = DbType.Guid, [typeof(byte[])] = DbType.Binary, + [typeof(TimeSpan)] = DbType.Time, [typeof(byte?)] = DbType.Byte, [typeof(sbyte?)] = DbType.SByte, [typeof(short?)] = DbType.Int16, @@ -44,6 +45,7 @@ public static class DbTypeHelper [typeof(bool?)] = DbType.Boolean, [typeof(char?)] = DbType.StringFixedLength, [typeof(Guid?)] = DbType.Guid, + [typeof(TimeSpan?)] = DbType.Time, [typeof(object)] = DbType.Object }; diff --git a/src/Service/Services/ResolverMiddleware.cs b/src/Service/Services/ResolverMiddleware.cs index 39e91c8352..afdd82e822 100644 --- a/src/Service/Services/ResolverMiddleware.cs +++ b/src/Service/Services/ResolverMiddleware.cs @@ -202,6 +202,7 @@ private static object PreParseLeaf(IMiddlewareContext context, string leafJson) SingleType => Single.Parse(leafJson), DateTimeType => DateTimeOffset.Parse(leafJson), ByteArrayType => Convert.FromBase64String(leafJson), + TimeSpanType => TimeSpan.Parse(leafJson), _ => leafJson }; } From 221baadc092cf83048f63961d724c7c89fdbb923 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 13:52:30 -0700 Subject: [PATCH 04/85] enclose name in [] --- src/Service.Tests/DatabaseSchema-MsSql.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 6bc0044714..4768723a12 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -66,7 +66,7 @@ CREATE TABLE books( CREATE TABLE players( id int IDENTITY(5001, 1) PRIMARY KEY, - name varchar(max) NOT NULL, + [name] varchar(max) NOT NULL, current_club_id int NOT NULL, new_club_id int NOT NULL ); @@ -378,7 +378,7 @@ VALUES (1, 'Awesome book', 1234), SET IDENTITY_INSERT books OFF SET IDENTITY_INSERT players ON -INSERT INTO players(id, name, current_club_id, new_club_id) +INSERT INTO players(id, [name], current_club_id, new_club_id) VALUES (1, 'Cristiano Ronaldo', 1113, 1111), (2, 'Leonel Messi', 1112, 1113); SET IDENTITY_INSERT players OFF From d210db2f922e2401d4368efdddbef278f38e4980 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 18:26:11 -0700 Subject: [PATCH 05/85] Support timespan as stored procedure parameter --- src/Service.GraphQLBuilder/GraphQLUtils.cs | 1 + .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index d3271edf51..ffe4be1a87 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -240,6 +240,7 @@ public static Tuple ConvertValueToGraphQLType(string default FLOAT_TYPE => new(FLOAT_TYPE, new FloatValueNode(double.Parse(defaultValueFromConfig))), DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult(DateTime.Parse(defaultValueFromConfig))), + TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseValue(defaultValueFromConfig)), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 81f3bd32f7..71dd280d41 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -53,6 +53,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] + [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10:57:11 AM -08:00", false, DisplayName = "TimeSpan")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, From 296511f0e8ce597f71e173c10bfced2dbe6d3527 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 20:53:50 -0700 Subject: [PATCH 06/85] Parse with invariant culture and assume UTC --- src/Service.GraphQLBuilder/GraphQLUtils.cs | 6 ++++-- src/Service/Services/ResolverMiddleware.cs | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index ffe4be1a87..53928ea3c4 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Net; using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Service.Exceptions; @@ -239,8 +240,9 @@ public static Tuple ConvertValueToGraphQLType(string default SINGLE_TYPE => new(SINGLE_TYPE, new SingleType().ParseValue(float.Parse(defaultValueFromConfig))), FLOAT_TYPE => new(FLOAT_TYPE, new FloatValueNode(double.Parse(defaultValueFromConfig))), DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), - DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult(DateTime.Parse(defaultValueFromConfig))), - TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseValue(defaultValueFromConfig)), + DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult( + DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))), + TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseResult(defaultValueFromConfig)), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service/Services/ResolverMiddleware.cs b/src/Service/Services/ResolverMiddleware.cs index afdd82e822..f63eadd3e0 100644 --- a/src/Service/Services/ResolverMiddleware.cs +++ b/src/Service/Services/ResolverMiddleware.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text.Json; using System.Threading.Tasks; using Azure.DataApiBuilder.Service.Authorization; @@ -200,9 +201,9 @@ private static object PreParseLeaf(IMiddlewareContext context, string leafJson) { ByteType => byte.Parse(leafJson), SingleType => Single.Parse(leafJson), - DateTimeType => DateTimeOffset.Parse(leafJson), + DateTimeType => DateTimeOffset.Parse(leafJson, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), ByteArrayType => Convert.FromBase64String(leafJson), - TimeSpanType => TimeSpan.Parse(leafJson), + TimeSpanType => TimeSpan.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), _ => leafJson }; } From 8c23ffe500062ceea0ecbc5b186c9934b5fc562b Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Fri, 5 May 2023 20:54:36 -0700 Subject: [PATCH 07/85] StoredProc default paramvalue --- .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 71dd280d41..13642ffa72 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -53,7 +53,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10:57:11 AM -08:00", false, DisplayName = "TimeSpan")] + [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10-57-11", false, DisplayName = "TimeSpan")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, From b0001688e2fe38ddc503b4ed0ea674a0cc0c5821 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Sat, 6 May 2023 18:06:32 -0700 Subject: [PATCH 08/85] TimeSpan Iso8601 format is PT23H59M59.999999S --- .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 2 +- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 13642ffa72..9c936652ac 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -53,7 +53,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10-57-11", false, DisplayName = "TimeSpan")] + [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "PT23H59M59.9999999S", false, DisplayName = "TimeSpan")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 39fe1d9066..8a2cc1d9d5 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -364,9 +365,10 @@ protected object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTimeOffset.Parse(param), + "DateTime" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), + "TimeSpan" => TimeSpan.Parse(param, DateTimeFormatInfo.InvariantInfo), "Guid" => Guid.Parse(param), _ => throw new NotSupportedException($"{systemType.Name} is not supported") }; From 1c35eb5105bd0f30aea36a37f37515ad7120c65a Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 8 May 2023 09:52:17 +0530 Subject: [PATCH 09/85] fixing tests --- src/Service.GraphQLBuilder/GraphQLUtils.cs | 2 +- .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 53928ea3c4..5dcbfb052b 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -242,7 +242,7 @@ public static Tuple ConvertValueToGraphQLType(string default DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult( DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))), - TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseResult(defaultValueFromConfig)), + TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseResult(TimeSpan.Parse(defaultValueFromConfig))), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 13642ffa72..7e1861e4ea 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -53,7 +53,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10-57-11", false, DisplayName = "TimeSpan")] + [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10:57:11.0000", false, DisplayName = "TimeSpan")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, From f9ab1d5627c416a763a1c22ef797d8e33069fabd Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 8 May 2023 11:21:21 +0530 Subject: [PATCH 10/85] fixing MsSql tests --- .../SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs index 024e22f028..6a27c33046 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs @@ -31,7 +31,7 @@ public class MsSqlInsertApiTests : InsertApiTestBase "InsertOneInSupportedTypes", $"SELECT [id] as [typeid], [byte_types], [short_types], [int_types], [long_types],string_types, [single_types], [float_types], " + $"[decimal_types], [boolean_types], [date_types], [datetime_types], [datetime2_types], [datetimeoffset_types], [smalldatetime_types], " + - $"[bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + + $"[time_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + $"WHERE [id] = { STARTING_ID_FOR_TEST_INSERTS } AND [bytearray_types] is NULL " + $"FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, From 1dd0f471192b0eefe4b9c1178de8a32bc4e53fbc Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Tue, 9 May 2023 01:12:24 -0700 Subject: [PATCH 11/85] Add timespan test in supported types --- .../GraphQLSupportedTypesTestsBase.cs | 11 +++++++++++ .../MySqlGQLSupportedTypesTests.cs | 1 + .../PostgreSqlGQLSupportedTypesTests.cs | 1 + 3 files changed, 13 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 4da1fcaaf4..fb4021d322 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -59,6 +59,11 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase [DataRow(DATETIME_TYPE, 2)] [DataRow(DATETIME_TYPE, 3)] [DataRow(DATETIME_TYPE, 4)] + [DataRow(TIMESPAN_TYPE, 1)] + [DataRow(TIMESPAN_TYPE, 2)] + [DataRow(TIMESPAN_TYPE, 3)] + [DataRow(TIMESPAN_TYPE, 4)] + [DataRow(TIMESPAN_TYPE, 5)] [DataRow(BYTEARRAY_TYPE, 1)] [DataRow(BYTEARRAY_TYPE, 2)] [DataRow(BYTEARRAY_TYPE, 3)] @@ -121,6 +126,8 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] + [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] + [DataRow(TIMESPAN_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -163,6 +170,7 @@ public async Task InsertIntoTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] + [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] public async Task InsertIntoTypeColumnWithArgument(string type, object value) { @@ -227,6 +235,8 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] + [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] + [DataRow(TIMESPAN_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -271,6 +281,7 @@ public async Task UpdateTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] + [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] [DataRow(GUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")] [DataRow(GUID_TYPE, null)] diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 18d6d886a0..0b312a8633 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -42,6 +42,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, + TIMESPAN_TYPE => true, _ => true }; } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index 714cea5b89..aee31417b6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -42,6 +42,7 @@ protected override bool IsSupportedType(string type) { BYTE_TYPE => false, DATETIME_NONUTC_TYPE => false, + TIMESPAN_TYPE => false, _ => true }; } From cd9ec5a1986f1a80b826e396549205ff59e70610 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Tue, 9 May 2023 01:31:42 -0700 Subject: [PATCH 12/85] Fix timespan_types column name --- src/Service.GraphQLBuilder/Sql/SchemaConverter.cs | 2 +- src/Service.Tests/DatabaseSchema-MsSql.sql | 4 ++-- .../SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 47fccc9033..9447688a2b 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -263,7 +263,7 @@ public static IValueNode CreateValueNodeFromDbObjectMetadata(object metadataValu decimal value => new ObjectValueNode(new ObjectFieldNode(DECIMAL_TYPE, new FloatValueNode(value))), DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseValue(value))), DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseResult(value))), - TimeSpan value => new ObjectValueNode(new ObjectFieldNode(TIMESPAN_TYPE, new TimeSpanType().ParseValue(value))), + TimeSpan value => new ObjectValueNode(new ObjectFieldNode(TIMESPAN_TYPE, new TimeSpanType().ParseResult(value))), byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(value))), _ => throw new DataApiBuilderException( message: $"The type {metadataValue.GetType()} is not supported as a GraphQL default value", diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 4768723a12..1d911c27ce 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -162,7 +162,7 @@ CREATE TABLE type_table( datetime2_types datetime2, datetimeoffset_types datetimeoffset, smalldatetime_types smalldatetime, - time_types time, + timespan_types time, bytearray_types varbinary(max), guid_types uniqueidentifier DEFAULT newid() ); @@ -399,7 +399,7 @@ byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, -date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, time_types, +date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, timespan_types, bytearray_types) VALUES (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs index 6a27c33046..ee3e6baa0b 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs @@ -31,7 +31,7 @@ public class MsSqlInsertApiTests : InsertApiTestBase "InsertOneInSupportedTypes", $"SELECT [id] as [typeid], [byte_types], [short_types], [int_types], [long_types],string_types, [single_types], [float_types], " + $"[decimal_types], [boolean_types], [date_types], [datetime_types], [datetime2_types], [datetimeoffset_types], [smalldatetime_types], " + - $"[time_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + + $"[timespan_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + $"WHERE [id] = { STARTING_ID_FOR_TEST_INSERTS } AND [bytearray_types] is NULL " + $"FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, From e80ef05ca67f058797117c726260f3080cefaae4 Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Thu, 11 May 2023 14:27:41 -0700 Subject: [PATCH 13/85] Fix comparison of timespan data type --- .../GraphQLSupportedTypesTestsBase.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index fb4021d322..b1eca5190a 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -329,7 +329,10 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareDateTimeResults(actual.ToString(), expected); } - else + else if (type == TIMESPAN_TYPE) + { + CompareTimeSpanResults(actual.ToString(), expected); + } { SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } @@ -400,6 +403,30 @@ private static void CompareDateTimeResults(string actual, string expected) } } + /// + /// Required due to different format between SQL time and HotChocolate TimeSpan time(ISO-8601) result. + /// + private static void CompareTimeSpanResults(string actual, string expected) + { + string fieldName = "timespan_types"; + + using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); + using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + + string actualDateTime = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedDateTime = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + + // handles cases when one of the values is null + if (string.IsNullOrEmpty(actualDateTime) || string.IsNullOrEmpty(expectedDateTime)) + { + Assert.AreEqual(expectedDateTime, actualDateTime); + } + else + { + Assert.AreEqual(TimeSpan.Parse(expectedDateTime), TimeSpan.Parse(actualDateTime)); + } + } + /// /// Needed to map the type name to a graphql type in argument tests /// where the argument type need to be specified. From 1aa7b654a8e6abeea8e775037e3ca227746dd33c Mon Sep 17 00:00:00 2001 From: Aniruddh Munde Date: Thu, 11 May 2023 14:45:18 -0700 Subject: [PATCH 14/85] Missing else --- .../GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index b1eca5190a..3ded865007 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -333,6 +333,7 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareTimeSpanResults(actual.ToString(), expected); } + else { SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } From d5d97fdf67ef3507c50bc10939020c0852d1a9ad Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 6 Jul 2023 12:36:46 +0530 Subject: [PATCH 15/85] adding timespan type --- src/Service/Services/TypeHelper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Service/Services/TypeHelper.cs b/src/Service/Services/TypeHelper.cs index 7f3d8498f9..b586e64379 100644 --- a/src/Service/Services/TypeHelper.cs +++ b/src/Service/Services/TypeHelper.cs @@ -31,6 +31,7 @@ public static class TypeHelper [typeof(char)] = DbType.StringFixedLength, [typeof(Guid)] = DbType.Guid, [typeof(byte[])] = DbType.Binary, + [typeof(TimeSpan)] = DbType.Time, [typeof(byte?)] = DbType.Byte, [typeof(sbyte?)] = DbType.SByte, [typeof(short?)] = DbType.Int16, @@ -45,6 +46,7 @@ public static class TypeHelper [typeof(bool?)] = DbType.Boolean, [typeof(char?)] = DbType.StringFixedLength, [typeof(Guid?)] = DbType.Guid, + [typeof(TimeSpan?)] = DbType.Time, [typeof(object)] = DbType.Object }; From 09e85b36cd04bc95aebd5ac00ddae7123226880d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 6 Jul 2023 14:12:04 +0530 Subject: [PATCH 16/85] fix formatting --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 2 -- src/Core/Services/ResolverMiddleware.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 1cced2ab41..b8b2e341c6 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -3,8 +3,6 @@ using System.Data; using System.Globalization; -using System.IO; -using System.Linq; using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config.DatabasePrimitives; diff --git a/src/Core/Services/ResolverMiddleware.cs b/src/Core/Services/ResolverMiddleware.cs index ca29abbea1..51be5ad48f 100644 --- a/src/Core/Services/ResolverMiddleware.cs +++ b/src/Core/Services/ResolverMiddleware.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; -using System.Collections.Generic; using System.Globalization; using System.Text.Json; using Azure.DataApiBuilder.Core.Authorization; From 3c4eba3fe9607277e6812012c65008ab1bc8ca86 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 17 Jul 2023 10:00:54 +0530 Subject: [PATCH 17/85] handling nullable types --- src/Core/Services/TypeHelper.cs | 26 ++++++++----------- .../CLRtoJsonValueTypeUnitTests.cs | 5 ++-- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 2f15a15dd4..d6ab299637 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -35,22 +35,9 @@ public static class TypeHelper [typeof(char)] = DbType.StringFixedLength, [typeof(Guid)] = DbType.Guid, [typeof(byte[])] = DbType.Binary, + [typeof(DateTime)] = DbType.DateTime, + [typeof(DateTimeOffset)] = DbType.DateTimeOffset, [typeof(TimeSpan)] = DbType.Time, - [typeof(byte?)] = DbType.Byte, - [typeof(sbyte?)] = DbType.SByte, - [typeof(short?)] = DbType.Int16, - [typeof(ushort?)] = DbType.UInt16, - [typeof(int?)] = DbType.Int32, - [typeof(uint?)] = DbType.UInt32, - [typeof(long?)] = DbType.Int64, - [typeof(ulong?)] = DbType.UInt64, - [typeof(float?)] = DbType.Single, - [typeof(double?)] = DbType.Double, - [typeof(decimal?)] = DbType.Decimal, - [typeof(bool?)] = DbType.Boolean, - [typeof(char?)] = DbType.StringFixedLength, - [typeof(Guid?)] = DbType.Guid, - [typeof(TimeSpan?)] = DbType.Time, [typeof(object)] = DbType.Object }; @@ -153,6 +140,15 @@ public static JsonDataType GetJsonDataTypeFromSystemType(Type type) /// DbType for the given system type. Null when no mapping exists. public static DbType? GetDbTypeFromSystemType(Type systemType) { + // Get the underlying type argument if the 'systemType' argument is a nullable type. + Type? nullableUnderlyingType = Nullable.GetUnderlyingType(systemType); + + // Will not be null when the input argument 'systemType' is a closed generic nullable type. + if (nullableUnderlyingType is not null) + { + systemType = nullableUnderlyingType; + } + if (!_systemTypeToDbTypeMap.TryGetValue(systemType, out DbType dbType)) { return null; diff --git a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs index 7d497ed8ca..9027ba39ba 100644 --- a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs +++ b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs @@ -75,8 +75,8 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa /// /// Validates the behavior of TypeHelper.GetJsonDataTypeFromSystemType(Type type) by /// ensuring that a nullable value type like int? is resolved to its underlying type int. - /// Consequently, the lookup in the _systemTypeToJsonDataTypeMap dictionary succeeds without - /// requiring nullable value type be defined as keys. + /// Consequently, the lookup in the _systemTypeToJsonDataTypeMap and _systemTypeToDbTypeMap + /// dictionary succeeds without requiring nullable value type be defined as keys. /// Nullable value types are represented in runtime as Nullable. Whereas /// nullable reference types do no have a standalone runtime representation. /// See csharplang discussion on why typeof(string?) (nullable reference type) is not valid, @@ -106,5 +106,6 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa public void ResolveUnderlyingTypeForNullableValueType(Type nullableType) { Assert.AreNotEqual(notExpected: JsonDataType.Undefined, actual: TypeHelper.GetJsonDataTypeFromSystemType(nullableType)); + Assert.IsNotNull(TypeHelper.GetDbTypeFromSystemType(nullableType)); } } From 507802e01b5c3fd3c646201f9b885a1f1169780d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 17 Jul 2023 11:10:56 +0530 Subject: [PATCH 18/85] resolving nits --- src/Core/Parsers/EdmModelBuilder.cs | 38 +---------------------------- src/Core/Services/TypeHelper.cs | 37 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/Core/Parsers/EdmModelBuilder.cs b/src/Core/Parsers/EdmModelBuilder.cs index 664fed928c..d895c8391b 100644 --- a/src/Core/Parsers/EdmModelBuilder.cs +++ b/src/Core/Parsers/EdmModelBuilder.cs @@ -69,7 +69,7 @@ SourceDefinition sourceDefinition { Type columnSystemType = sourceDefinition.Columns[column].SystemType; // need to convert our column system type to an Edm type - EdmPrimitiveTypeKind type = GetEdmPrimitiveTypeFromSystemType(columnSystemType); + EdmPrimitiveTypeKind type = TypeHelper.GetEdmPrimitiveTypeFromSystemType(columnSystemType); // The mapped (aliased) field name defined in the runtime config is used to create a representative // OData StructuralProperty. The created property is then added to the EdmEntityType. @@ -96,42 +96,6 @@ SourceDefinition sourceDefinition return this; } - /// - /// Given the system type, returns the corresponding primitive type kind. - /// - /// Type of the column. - /// EdmPrimitiveTypeKind - /// Throws when the column - private static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type columnSystemType) - { - if (columnSystemType.IsArray) - { - columnSystemType = columnSystemType.GetElementType()!; - } - - EdmPrimitiveTypeKind type = columnSystemType.Name switch - { - "String" => EdmPrimitiveTypeKind.String, - "Guid" => EdmPrimitiveTypeKind.Guid, - "Byte" => EdmPrimitiveTypeKind.Byte, - "Int16" => EdmPrimitiveTypeKind.Int16, - "Int32" => EdmPrimitiveTypeKind.Int32, - "Int64" => EdmPrimitiveTypeKind.Int64, - "Single" => EdmPrimitiveTypeKind.Single, - "Double" => EdmPrimitiveTypeKind.Double, - "Decimal" => EdmPrimitiveTypeKind.Decimal, - "Boolean" => EdmPrimitiveTypeKind.Boolean, - "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, - "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, - "Date" => EdmPrimitiveTypeKind.Date, - "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, - _ => throw new ArgumentException($"Column type" + - $" {columnSystemType.Name} not yet supported.") - }; - - return type; - } - /// /// Add the entity sets contained within the schema to container. /// diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index d6ab299637..aecf8df4ad 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -5,6 +5,7 @@ using System.Net; using Azure.DataApiBuilder.Core.Services.OpenAPI; using Azure.DataApiBuilder.Service.Exceptions; +using Microsoft.OData.Edm; namespace Azure.DataApiBuilder.Core.Services { @@ -105,6 +106,42 @@ public static class TypeHelper [SqlDbType.VarChar] = typeof(string) }; + /// + /// Given the system type, returns the corresponding primitive type kind. + /// + /// Type of the column. + /// EdmPrimitiveTypeKind + /// Throws when the column + public static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type columnSystemType) + { + if (columnSystemType.IsArray) + { + columnSystemType = columnSystemType.GetElementType()!; + } + + EdmPrimitiveTypeKind type = columnSystemType.Name switch + { + "String" => EdmPrimitiveTypeKind.String, + "Guid" => EdmPrimitiveTypeKind.Guid, + "Byte" => EdmPrimitiveTypeKind.Byte, + "Int16" => EdmPrimitiveTypeKind.Int16, + "Int32" => EdmPrimitiveTypeKind.Int32, + "Int64" => EdmPrimitiveTypeKind.Int64, + "Single" => EdmPrimitiveTypeKind.Single, + "Double" => EdmPrimitiveTypeKind.Double, + "Decimal" => EdmPrimitiveTypeKind.Decimal, + "Boolean" => EdmPrimitiveTypeKind.Boolean, + "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, + "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, + "Date" => EdmPrimitiveTypeKind.Date, + "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, + _ => throw new ArgumentException($"Column type" + + $" {columnSystemType.Name} not yet supported.") + }; + + return type; + } + /// /// Converts the .NET Framework (System/CLR) type to JsonDataType. /// Primitive data types in the OpenAPI standard (OAS) are based on the types supported From d852138fc24e7298306c42c0992a8c2d858fb6aa Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 18 Jul 2023 15:56:06 +0530 Subject: [PATCH 19/85] fixing sql type --- .../Sql Query Structures/BaseSqlQueryStructure.cs | 2 +- src/Core/Services/ResolverMiddleware.cs | 2 +- src/Core/Services/TypeHelper.cs | 7 ++++--- src/Service.GraphQLBuilder/Sql/SchemaConverter.cs | 2 +- src/Service.Tests/DatabaseSchema-MsSql.sql | 4 ++-- .../GraphQLBuilder/Sql/SchemaConverterTests.cs | 2 +- .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 2 +- .../OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 6 +++--- .../SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs | 2 +- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index b8b2e341c6..07cd74910c 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -364,7 +364,7 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "DateTime" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), - "TimeSpan" => TimeSpan.Parse(param, DateTimeFormatInfo.InvariantInfo), + "TimeOnly" => TimeOnly.Parse(param), "Guid" => Guid.Parse(param), _ => throw new NotSupportedException($"{systemType.Name} is not supported") }; diff --git a/src/Core/Services/ResolverMiddleware.cs b/src/Core/Services/ResolverMiddleware.cs index 51be5ad48f..3368d81489 100644 --- a/src/Core/Services/ResolverMiddleware.cs +++ b/src/Core/Services/ResolverMiddleware.cs @@ -199,7 +199,7 @@ private static object PreParseLeaf(IMiddlewareContext context, string leafJson) SingleType => Single.Parse(leafJson), DateTimeType => DateTimeOffset.Parse(leafJson, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), ByteArrayType => Convert.FromBase64String(leafJson), - TimeSpanType => TimeSpan.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), + TimeSpanType => TimeOnly.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), _ => leafJson }; } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index aecf8df4ad..cdfd99b37c 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -38,7 +38,7 @@ public static class TypeHelper [typeof(byte[])] = DbType.Binary, [typeof(DateTime)] = DbType.DateTime, [typeof(DateTimeOffset)] = DbType.DateTimeOffset, - [typeof(TimeSpan)] = DbType.Time, + [typeof(TimeOnly)] = DbType.Time, [typeof(object)] = DbType.Object }; @@ -67,6 +67,7 @@ public static class TypeHelper [typeof(Guid)] = JsonDataType.String, [typeof(byte[])] = JsonDataType.String, [typeof(TimeSpan)] = JsonDataType.String, + [typeof(TimeOnly)] = JsonDataType.String, [typeof(object)] = JsonDataType.Object, [typeof(DateTime)] = JsonDataType.String, [typeof(DateTimeOffset)] = JsonDataType.String @@ -98,7 +99,7 @@ public static class TypeHelper [SqlDbType.SmallInt] = typeof(short), [SqlDbType.SmallMoney] = typeof(decimal), [SqlDbType.Text] = typeof(string), - [SqlDbType.Time] = typeof(TimeSpan), + [SqlDbType.Time] = typeof(TimeOnly), [SqlDbType.Timestamp] = typeof(byte[]), [SqlDbType.TinyInt] = typeof(byte), [SqlDbType.UniqueIdentifier] = typeof(Guid), @@ -134,7 +135,7 @@ public static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type column "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, "Date" => EdmPrimitiveTypeKind.Date, - "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, + "TimeOnly" => EdmPrimitiveTypeKind.TimeOfDay, _ => throw new ArgumentException($"Column type" + $" {columnSystemType.Name} not yet supported.") }; diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 8117518a62..b710781eed 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -231,7 +231,7 @@ public static string GetGraphQLTypeFromSystemType(Type type) "DateTime" => DATETIME_TYPE, "DateTimeOffset" => DATETIME_TYPE, "Byte[]" => BYTEARRAY_TYPE, - "TimeSpan" => TIMESPAN_TYPE, + "TimeOnly" => TIMESPAN_TYPE, _ => throw new DataApiBuilderException( message: $"Column type {type} not handled by case. Please add a case resolving {type} to the appropriate GraphQL type", statusCode: HttpStatusCode.InternalServerError, diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 1d911c27ce..4768723a12 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -162,7 +162,7 @@ CREATE TABLE type_table( datetime2_types datetime2, datetimeoffset_types datetimeoffset, smalldatetime_types smalldatetime, - timespan_types time, + time_types time, bytearray_types varbinary(max), guid_types uniqueidentifier DEFAULT newid() ); @@ -399,7 +399,7 @@ byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, -date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, timespan_types, +date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, time_types, bytearray_types) VALUES (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index fbc3dde454..0ef7d929d4 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -230,7 +230,7 @@ public void MultipleColumnsAllMapped() [DataRow(typeof(DateTimeOffset), DATETIME_TYPE)] [DataRow(typeof(byte[]), BYTEARRAY_TYPE)] [DataRow(typeof(Guid), STRING_TYPE)] - [DataRow(typeof(TimeSpan), TIMESPAN_TYPE)] + [DataRow(typeof(TimeOnly), TIMESPAN_TYPE)] public void SystemTypeMapsToCorrectGraphQLType(Type systemType, string graphQLType) { SourceDefinition table = new(); diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 3e3846b8bd..211e0127c3 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -54,7 +54,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeSpan), TIMESPAN_TYPE, "10:57:11.0000", false, DisplayName = "TimeSpan")] + [DataRow(typeof(TimeOnly), TIMESPAN_TYPE, "10:57:11.0000", false, DisplayName = "TimeOnly")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, diff --git a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs index 9027ba39ba..fc68b4bb14 100644 --- a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs +++ b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs @@ -99,7 +99,7 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa [DataRow(typeof(bool?))] [DataRow(typeof(char?))] [DataRow(typeof(Guid?))] - [DataRow(typeof(TimeSpan?))] + [DataRow(typeof(TimeOnly?))] [DataRow(typeof(DateTime?))] [DataRow(typeof(DateTimeOffset?))] [DataTestMethod] diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 3ded865007..3f4457dab7 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -331,7 +331,7 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect } else if (type == TIMESPAN_TYPE) { - CompareTimeSpanResults(actual.ToString(), expected); + CompareTimeResults(actual.ToString(), expected); } else { @@ -407,9 +407,9 @@ private static void CompareDateTimeResults(string actual, string expected) /// /// Required due to different format between SQL time and HotChocolate TimeSpan time(ISO-8601) result. /// - private static void CompareTimeSpanResults(string actual, string expected) + private static void CompareTimeResults(string actual, string expected) { - string fieldName = "timespan_types"; + string fieldName = "time_types"; using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs index 44f165174e..ec15851c6e 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs @@ -32,7 +32,7 @@ public class MsSqlInsertApiTests : InsertApiTestBase "InsertOneInSupportedTypes", $"SELECT [id] as [typeid], [byte_types], [short_types], [int_types], [long_types],string_types, [single_types], [float_types], " + $"[decimal_types], [boolean_types], [date_types], [datetime_types], [datetime2_types], [datetimeoffset_types], [smalldatetime_types], " + - $"[timespan_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + + $"[time_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + $"WHERE [id] = { STARTING_ID_FOR_TEST_INSERTS } AND [bytearray_types] is NULL " + $"FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, From b84874f386cc3dace9d9f350ac8a30b2c721123f Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 20 Jul 2023 11:16:29 +0530 Subject: [PATCH 20/85] Add TimeOnly format for HotChocolate --- src/Core/Services/ResolverMiddleware.cs | 3 +- src/Core/Services/TypeHelper.cs | 1 + .../GraphQLTypes/DefaultValueType.cs | 2 +- .../GraphQLTypes/SupportedTypes.cs | 2 +- src/Service.GraphQLBuilder/GraphQLUtils.cs | 5 +- .../Queries/StandardQueryInputs.cs | 21 +++--- .../Sql/SchemaConverter.cs | 6 +- src/Service.Tests/DatabaseSchema-MsSql.sql | 4 +- .../Sql/SchemaConverterTests.cs | 2 +- .../Sql/StoredProcedureBuilderTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 72 ++++++++++++++----- .../MySqlGQLSupportedTypesTests.cs | 2 +- .../PostgreSqlGQLSupportedTypesTests.cs | 2 +- src/Service.Tests/dab-config.MsSql.json | 2 +- 14 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/Core/Services/ResolverMiddleware.cs b/src/Core/Services/ResolverMiddleware.cs index 3368d81489..2b115459c4 100644 --- a/src/Core/Services/ResolverMiddleware.cs +++ b/src/Core/Services/ResolverMiddleware.cs @@ -7,6 +7,7 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; +using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.Resolvers; @@ -199,7 +200,7 @@ private static object PreParseLeaf(IMiddlewareContext context, string leafJson) SingleType => Single.Parse(leafJson), DateTimeType => DateTimeOffset.Parse(leafJson, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), ByteArrayType => Convert.FromBase64String(leafJson), - TimeSpanType => TimeOnly.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), + TimeOnlyType => TimeOnly.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), _ => leafJson }; } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index cdfd99b37c..9f647c9523 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -136,6 +136,7 @@ public static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type column "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, "Date" => EdmPrimitiveTypeKind.Date, "TimeOnly" => EdmPrimitiveTypeKind.TimeOfDay, + "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, _ => throw new ArgumentException($"Column type" + $" {columnSystemType.Name} not yet supported.") }; diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index 1a97f76a81..f17628ef7d 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -24,7 +24,7 @@ protected override void Configure(IInputObjectTypeDescriptor descriptor) descriptor.Field(DECIMAL_TYPE).Type(); descriptor.Field(DATETIME_TYPE).Type(); descriptor.Field(BYTEARRAY_TYPE).Type(); - descriptor.Field(TIMESPAN_TYPE).Type(); + descriptor.Field(TIMEONLY_TYPE).Type(); } } } diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index 54b87936b5..fe50f4efe9 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -22,7 +22,7 @@ public static class SupportedTypes // since PostgreSQL doesn't support datetime values with a non-UTC time zone. public const string DATETIME_NONUTC_TYPE = "DateTimeNonUTC"; public const string BYTEARRAY_TYPE = "ByteArray"; - public const string TIMESPAN_TYPE = "TimeSpan"; + public const string TIMEONLY_TYPE = "TimeOnly"; public const string GUID_TYPE = "Guid"; } } diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 93dcc491bf..312a6689e9 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -9,6 +9,7 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; +using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.GraphQLBuilder.Sql; using HotChocolate.Language; using HotChocolate.Types; @@ -54,7 +55,7 @@ public static bool IsBuiltInType(ITypeNode typeNode) BOOLEAN_TYPE, DATETIME_TYPE, BYTEARRAY_TYPE, - TIMESPAN_TYPE + TIMEONLY_TYPE }; string name = typeNode.NamedType().Name.Value; return inBuiltTypes.Contains(name); @@ -243,7 +244,7 @@ public static Tuple ConvertValueToGraphQLType(string default DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult( DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))), - TIMESPAN_TYPE => new(TIMESPAN_TYPE, new TimeSpanType().ParseResult(TimeSpan.Parse(defaultValueFromConfig))), + TIMEONLY_TYPE => new(TIMEONLY_TYPE, new TimeOnlyType().ParseResult(TimeOnly.Parse(defaultValueFromConfig))), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index 190c0d2c52..5411a782a1 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; +using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using HotChocolate.Language; using HotChocolate.Types; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; @@ -190,19 +191,19 @@ public static InputObjectTypeDefinitionNode DateTimeInputType() => } ); - public static InputObjectTypeDefinitionNode TimeSpanInputType() => + public static InputObjectTypeDefinitionNode TimeOnlyInputType() => new( location: null, - new NameNode("TimeSpanFilterInput"), - new StringValueNode("Input type for adding TimeSpan filters"), + new NameNode("TimeOnlyFilterInput"), + new StringValueNode("Input type for adding TimeOnly filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new TimeSpanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new TimeSpanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new TimeSpanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new TimeSpanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new TimeSpanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new TimeSpanType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new TimeOnlyType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new TimeOnlyType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new TimeOnlyType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new TimeOnlyType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new TimeOnlyType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new TimeOnlyType().ToTypeNode(), null, new List()), new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -232,7 +233,7 @@ public static InputObjectTypeDefinitionNode ByteArrayInputType() => { STRING_TYPE, StringInputType() }, { DATETIME_TYPE, DateTimeInputType() }, { BYTEARRAY_TYPE, ByteArrayInputType() }, - { TIMESPAN_TYPE, TimeSpanInputType() }, + { TIMEONLY_TYPE, TimeOnlyInputType() }, }; /// diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index b710781eed..5e963a6e34 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -9,6 +9,7 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; +using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries; using HotChocolate.Language; using HotChocolate.Types; @@ -231,7 +232,8 @@ public static string GetGraphQLTypeFromSystemType(Type type) "DateTime" => DATETIME_TYPE, "DateTimeOffset" => DATETIME_TYPE, "Byte[]" => BYTEARRAY_TYPE, - "TimeOnly" => TIMESPAN_TYPE, + "TimeOnly" => TIMEONLY_TYPE, + "TimeSpan" => TIMEONLY_TYPE, _ => throw new DataApiBuilderException( message: $"Column type {type} not handled by case. Please add a case resolving {type} to the appropriate GraphQL type", statusCode: HttpStatusCode.InternalServerError, @@ -264,7 +266,7 @@ public static IValueNode CreateValueNodeFromDbObjectMetadata(object metadataValu decimal value => new ObjectValueNode(new ObjectFieldNode(DECIMAL_TYPE, new FloatValueNode(value))), DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseValue(value))), DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseResult(value))), - TimeSpan value => new ObjectValueNode(new ObjectFieldNode(TIMESPAN_TYPE, new TimeSpanType().ParseResult(value))), + TimeOnly value => new ObjectValueNode(new ObjectFieldNode(TIMEONLY_TYPE, new TimeOnlyType().ParseResult(value))), byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(value))), _ => throw new DataApiBuilderException( message: $"The type {metadataValue.GetType()} is not supported as a GraphQL default value", diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 4768723a12..22129ceeb0 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -162,7 +162,7 @@ CREATE TABLE type_table( datetime2_types datetime2, datetimeoffset_types datetimeoffset, smalldatetime_types smalldatetime, - time_types time, + timeonly_types time, bytearray_types varbinary(max), guid_types uniqueidentifier DEFAULT newid() ); @@ -399,7 +399,7 @@ byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, -date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, time_types, +date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, timeonly_types, bytearray_types) VALUES (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index 0ef7d929d4..c310bdd749 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -230,7 +230,7 @@ public void MultipleColumnsAllMapped() [DataRow(typeof(DateTimeOffset), DATETIME_TYPE)] [DataRow(typeof(byte[]), BYTEARRAY_TYPE)] [DataRow(typeof(Guid), STRING_TYPE)] - [DataRow(typeof(TimeOnly), TIMESPAN_TYPE)] + [DataRow(typeof(TimeOnly), TIMEONLY_TYPE)] public void SystemTypeMapsToCorrectGraphQLType(Type systemType, string graphQLType) { SourceDefinition table = new(); diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 211e0127c3..7ed46419e7 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -54,7 +54,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeOnly), TIMESPAN_TYPE, "10:57:11.0000", false, DisplayName = "TimeOnly")] + [DataRow(typeof(TimeOnly), TIMEONLY_TYPE, "10:57:11.0000", false, DisplayName = "TimeOnly")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 3f4457dab7..6f904908ef 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text.Json; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -59,11 +60,11 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase [DataRow(DATETIME_TYPE, 2)] [DataRow(DATETIME_TYPE, 3)] [DataRow(DATETIME_TYPE, 4)] - [DataRow(TIMESPAN_TYPE, 1)] - [DataRow(TIMESPAN_TYPE, 2)] - [DataRow(TIMESPAN_TYPE, 3)] - [DataRow(TIMESPAN_TYPE, 4)] - [DataRow(TIMESPAN_TYPE, 5)] + [DataRow(TIMEONLY_TYPE, 1)] + [DataRow(TIMEONLY_TYPE, 2)] + [DataRow(TIMEONLY_TYPE, 3)] + [DataRow(TIMEONLY_TYPE, 4)] + [DataRow(TIMEONLY_TYPE, 5)] [DataRow(BYTEARRAY_TYPE, 1)] [DataRow(BYTEARRAY_TYPE, 2)] [DataRow(BYTEARRAY_TYPE, 3)] @@ -126,8 +127,8 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] - [DataRow(TIMESPAN_TYPE, "null")] + [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -170,7 +171,7 @@ public async Task InsertIntoTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] - [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] public async Task InsertIntoTypeColumnWithArgument(string type, object value) { @@ -235,8 +236,8 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] - [DataRow(TIMESPAN_TYPE, "null")] + [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -281,7 +282,7 @@ public async Task UpdateTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] - [DataRow(TIMESPAN_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] [DataRow(GUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")] [DataRow(GUID_TYPE, null)] @@ -329,7 +330,7 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareDateTimeResults(actual.ToString(), expected); } - else if (type == TIMESPAN_TYPE) + else if (type == TIMEONLY_TYPE) { CompareTimeResults(actual.ToString(), expected); } @@ -400,31 +401,64 @@ private static void CompareDateTimeResults(string actual, string expected) } else { - Assert.AreEqual(DateTimeOffset.Parse(expectedDateTime), DateTimeOffset.Parse(actualDateTime)); + // Adjusting to universal, since DateTime doesn't account for TimeZone + DateTime expectedDateTimeUniversal = DateTime.Parse(expectedDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + DateTime actualDateTimeUniversal = DateTime.Parse(actualDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + Assert.AreEqual(expectedDateTimeUniversal, actualDateTimeUniversal); } } + /// + /// Required due to different format between mysql datetimeoffset and HotChocolate datetime + /// result + /// + //private static void CompareDateTimeOffsetResults(string actual, string expected) + //{ + // string fieldName = "datetimeoffset_types"; + + // using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); + // using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + + // string actualDateTime = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + // string expectedDateTime = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + + // // handles cases when one of the values is null + // if (string.IsNullOrEmpty(actualDateTime) || string.IsNullOrEmpty(expectedDateTime)) + // { + // Assert.AreEqual(expectedDateTime, actualDateTime); + // } + // else + // { + // // Adjusting to universal, since DateTime doesn't account for TimeZone + // DateTime expectedDateTimeUniversal = DateTimeOffset.Parse(expectedDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + // DateTime actualDateTimeUniversal = DateTimeOffset.Parse(actualDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + // Assert.AreEqual(expectedDateTimeUniversal, actualDateTimeUniversal); + // } + //} + /// /// Required due to different format between SQL time and HotChocolate TimeSpan time(ISO-8601) result. /// private static void CompareTimeResults(string actual, string expected) { - string fieldName = "time_types"; + string fieldName = "timeonly_types"; using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); - string actualDateTime = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); - string expectedDateTime = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string actualTimeString = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedTimeString = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); // handles cases when one of the values is null - if (string.IsNullOrEmpty(actualDateTime) || string.IsNullOrEmpty(expectedDateTime)) + if (string.IsNullOrEmpty(actualTimeString) || string.IsNullOrEmpty(expectedTimeString)) { - Assert.AreEqual(expectedDateTime, actualDateTime); + Assert.AreEqual(expectedTimeString, actualTimeString); } else { - Assert.AreEqual(TimeSpan.Parse(expectedDateTime), TimeSpan.Parse(actualDateTime)); + TimeOnly actualTime = TimeOnly.Parse(actualTimeString, CultureInfo.InvariantCulture); + TimeOnly expectedTime = TimeOnly.Parse(expectedTimeString, CultureInfo.InvariantCulture); + Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); } } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 0b312a8633..6a129a9cc2 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -42,7 +42,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, - TIMESPAN_TYPE => true, + TIMEONLY_TYPE => true, _ => true }; } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index aee31417b6..63c36ee5c5 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -42,7 +42,7 @@ protected override bool IsSupportedType(string type) { BYTE_TYPE => false, DATETIME_NONUTC_TYPE => false, - TIMESPAN_TYPE => false, + TIMEONLY_TYPE => false, _ => true }; } diff --git a/src/Service.Tests/dab-config.MsSql.json b/src/Service.Tests/dab-config.MsSql.json index 3f6db5db9b..6b8b717c62 100644 --- a/src/Service.Tests/dab-config.MsSql.json +++ b/src/Service.Tests/dab-config.MsSql.json @@ -2,7 +2,7 @@ "$schema": "https://github.com/Azure/data-api-builder/releases/download/vmajor.minor.patch/dab.draft.schema.json", "data-source": { "database-type": "mssql", - "connection-string": "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;", + "connection-string": "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=dabuser;Password=Adabpassword@1;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=5;", "options": { "set-session-context": true } From 9e1a93c521a69e2bbc8de8d9ba1a244719290096 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 20 Jul 2023 11:17:12 +0530 Subject: [PATCH 21/85] fix formatting --- .../GraphQLTypes/TimeOnlytype.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs new file mode 100644 index 0000000000..3de63e981a --- /dev/null +++ b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using HotChocolate.Language; +using HotChocolate.Types; + +namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; +public class TimeOnlyType : ScalarType +{ + /// + /// Initializes a new instance of the class. + /// + public TimeOnlyType() : base("TimeOnly") + { } + + /// + public override bool IsInstanceOfType(IValueNode valueSyntax) + { + return valueSyntax is StringValueNode; + } + + /// + public override object ParseLiteral(IValueNode literal) + { + if (literal is StringValueNode stringValueNode) + { + if (TimeSpan.TryParse(stringValueNode.Value, out TimeSpan timeSpan)) + { + return timeSpan; + } + } + + throw new ArgumentException("Invalid TimeOnly value."); + } + + /// + public override IValueNode ParseResult(object? resultValue) + { + if (resultValue is TimeOnly timeSpan) + { + return new StringValueNode(timeSpan.ToString(@"hh\:mm\:ss\.fff")); + } + + throw new ArgumentException("Invalid TimeOnly value."); + } + + /// + public override IValueNode ParseValue(object? value) + { + if (value is TimeOnly timeSpan) + { + return new StringValueNode(timeSpan.ToString(@"hh\:mm\:ss\.fff")); + } + + throw new ArgumentException("Invalid TimeOnly value."); + } + /// + public override object Serialize(object? value) + { + if (value is TimeSpan timeSpan) + { + return timeSpan.ToString(@"hh\:mm\:ss\.fff"); + } + + throw new ArgumentException("Invalid TimeOnly value."); + } +} From 84e11e657a5352caa65c646593ddcaca55ffcdbd Mon Sep 17 00:00:00 2001 From: abhishekkumams <102276754+abhishekkumams@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:18:33 +0530 Subject: [PATCH 22/85] Update dab-config.MsSql.json --- src/Service.Tests/dab-config.MsSql.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/dab-config.MsSql.json b/src/Service.Tests/dab-config.MsSql.json index 6b8b717c62..3f6db5db9b 100644 --- a/src/Service.Tests/dab-config.MsSql.json +++ b/src/Service.Tests/dab-config.MsSql.json @@ -2,7 +2,7 @@ "$schema": "https://github.com/Azure/data-api-builder/releases/download/vmajor.minor.patch/dab.draft.schema.json", "data-source": { "database-type": "mssql", - "connection-string": "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=dabuser;Password=Adabpassword@1;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=5;", + "connection-string": "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;", "options": { "set-session-context": true } From 38a9e24f264570f27b43b4d64eea35d6324646ac Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 20 Jul 2023 11:24:29 +0530 Subject: [PATCH 23/85] fix test --- .../SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs index ec15851c6e..b8d1bd6d4b 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs @@ -32,7 +32,7 @@ public class MsSqlInsertApiTests : InsertApiTestBase "InsertOneInSupportedTypes", $"SELECT [id] as [typeid], [byte_types], [short_types], [int_types], [long_types],string_types, [single_types], [float_types], " + $"[decimal_types], [boolean_types], [date_types], [datetime_types], [datetime2_types], [datetimeoffset_types], [smalldatetime_types], " + - $"[time_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + + $"[timeonly_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + $"WHERE [id] = { STARTING_ID_FOR_TEST_INSERTS } AND [bytearray_types] is NULL " + $"FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, From 718941b84ff29afdf902443c5d5ca18eb5b60b08 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 20 Jul 2023 12:20:28 +0530 Subject: [PATCH 24/85] fixing mysql tests --- .../GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 6a129a9cc2..9632690288 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -42,7 +42,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, - TIMEONLY_TYPE => true, + TIMEONLY_TYPE => false, _ => true }; } From 910c2d7dddcc189fbf7bd8c2bfc08f357fc52148 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 21 Jul 2023 11:45:10 +0530 Subject: [PATCH 25/85] fixing tests --- .../BaseSqlQueryStructure.cs | 3 +- src/Core/Services/GraphQLSchemaCreator.cs | 1 + .../GraphQLTypes/SupportedTypes.cs | 2 +- .../GraphQLTypes/TimeOnlytype.cs | 5 +- .../GraphQLSupportedTypesTestsBase.cs | 91 +++++++------------ .../MySqlGQLSupportedTypesTests.cs | 1 + .../PostgreSqlGQLSupportedTypesTests.cs | 2 +- 7 files changed, 43 insertions(+), 62 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 07cd74910c..637c84eb4f 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -361,10 +361,11 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), + "DateTime" => DateTime.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), "TimeOnly" => TimeOnly.Parse(param), + "TimeSpan" => TimeOnly.Parse(param), "Guid" => Guid.Parse(param), _ => throw new NotSupportedException($"{systemType.Name} is not supported") }; diff --git a/src/Core/Services/GraphQLSchemaCreator.cs b/src/Core/Services/GraphQLSchemaCreator.cs index c8fb481e2b..93e5d27330 100644 --- a/src/Core/Services/GraphQLSchemaCreator.cs +++ b/src/Core/Services/GraphQLSchemaCreator.cs @@ -91,6 +91,7 @@ private ISchemaBuilder Parse( .AddDirectiveType() // Add our custom scalar GraphQL types .AddType() + .AddType() .AddType() // Generate the GraphQL queries from the provided objects .AddDocument(QueryBuilder.Build(root, _databaseType, _entities, inputTypes, _authorizationResolver.EntityPermissionsMap, dbObjects)) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index fe50f4efe9..cd0160a045 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -20,7 +20,7 @@ public static class SupportedTypes public const string DATETIME_TYPE = "DateTime"; // The DATETIME_NONUTC_TYPE constant is only used in testing // since PostgreSQL doesn't support datetime values with a non-UTC time zone. - public const string DATETIME_NONUTC_TYPE = "DateTimeNonUTC"; + public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string BYTEARRAY_TYPE = "ByteArray"; public const string TIMEONLY_TYPE = "TimeOnly"; public const string GUID_TYPE = "Guid"; diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs index 3de63e981a..1ad7d218fa 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs @@ -36,9 +36,9 @@ public override object ParseLiteral(IValueNode literal) /// public override IValueNode ParseResult(object? resultValue) { - if (resultValue is TimeOnly timeSpan) + if (resultValue is TimeOnly timeOnly) { - return new StringValueNode(timeSpan.ToString(@"hh\:mm\:ss\.fff")); + return new StringValueNode(timeOnly.ToString(@"hh\:mm\:ss\.fff")); } throw new ArgumentException("Invalid TimeOnly value."); @@ -54,6 +54,7 @@ public override IValueNode ParseValue(object? value) throw new ArgumentException("Invalid TimeOnly value."); } + /// public override object Serialize(object? value) { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 6f904908ef..b52b1e5cce 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -123,11 +123,11 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(BOOLEAN_TYPE, "true")] [DataRow(BOOLEAN_TYPE, "false")] [DataRow(BOOLEAN_TYPE, "null")] - [DataRow(DATETIME_NONUTC_TYPE, "\"1999-01-08 10:23:54+8:00\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "\"23:59:59.9999999\"")] [DataRow(TIMEONLY_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] @@ -139,13 +139,6 @@ public async Task InsertIntoTypeColumn(string type, string value) Assert.Inconclusive("Type not supported"); } - // Datetime non utc type is a characterization of the value added to the datetime type, - // so before executing the query reset it to mean the actually underlying type. - if (DATETIME_NONUTC_TYPE.Equals(type)) - { - type = DATETIME_TYPE; - } - string field = $"{type.ToLowerInvariant()}_types"; string graphQLQueryName = "createSupportedType"; string gqlQuery = "mutation{ createSupportedType (item: {" + field + ": " + value + " }){ " + field + " } }"; @@ -169,7 +162,7 @@ public async Task InsertIntoTypeColumn(string type, string value) [DataRow(FLOAT_TYPE, -3.33)] [DataRow(DECIMAL_TYPE, 1222222.00000929292)] [DataRow(BOOLEAN_TYPE, true)] - [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] + [DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] @@ -180,16 +173,9 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) Assert.Inconclusive("Type not supported"); } - // Datetime non utc type is a characterization of the value added to the datetime type, - // so before executing the query reset it to mean the actually underlying type. - if (DATETIME_NONUTC_TYPE.Equals(type)) - { - type = DATETIME_TYPE; - } - string field = $"{type.ToLowerInvariant()}_types"; string graphQLQueryName = "createSupportedType"; - string gqlQuery = "mutation($param: " + type + "){ createSupportedType (item: {" + field + ": $param }){ " + field + " } }"; + string gqlQuery = "mutation($param: " + TypeNameToGraphQLType(type) + "){ createSupportedType (item: {" + field + ": $param }){ " + field + " } }"; string dbQuery = MakeQueryOnTypeTable(new List { field }, id: 5001); @@ -232,7 +218,7 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) [DataRow(BOOLEAN_TYPE, "true")] [DataRow(BOOLEAN_TYPE, "false")] [DataRow(BOOLEAN_TYPE, "null")] - [DataRow(DATETIME_NONUTC_TYPE, "\"1999-01-08 10:23:54+8:00\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] @@ -250,13 +236,6 @@ public async Task UpdateTypeColumn(string type, string value) Assert.Inconclusive("Type not supported"); } - // Datetime non utc type is a characterization of the value added to the datetime type, - // so before executing the query reset it to mean the actually underlying type. - if (DATETIME_NONUTC_TYPE.Equals(type)) - { - type = DATETIME_TYPE; - } - string field = $"{type.ToLowerInvariant()}_types"; string graphQLQueryName = "updateSupportedType"; string gqlQuery = "mutation{ updateSupportedType (typeid: 1, item: {" + field + ": " + value + " }){ " + field + " } }"; @@ -281,7 +260,7 @@ public async Task UpdateTypeColumn(string type, string value) [DataRow(DECIMAL_TYPE, 1222222.00000929292)] [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] - [DataRow(DATETIME_NONUTC_TYPE, "1999-01-08 10:23:54+8:00")] + [DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] [DataRow(GUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")] @@ -293,13 +272,6 @@ public async Task UpdateTypeColumnWithArgument(string type, object value) Assert.Inconclusive("Type not supported"); } - // Datetime non utc type is a characterization of the value added to the datetime type, - // so before executing the query reset it to mean the actually underlying type. - if (DATETIME_NONUTC_TYPE.Equals(type)) - { - type = DATETIME_TYPE; - } - string field = $"{type.ToLowerInvariant()}_types"; string graphQLQueryName = "updateSupportedType"; string gqlQuery = "mutation($param: " + TypeNameToGraphQLType(type) + "){ updateSupportedType (typeid: 1, item: {" + field + ": $param }){ " + field + " } }"; @@ -330,6 +302,10 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareDateTimeResults(actual.ToString(), expected); } + else if (type == DATETIMEOFFSET_TYPE) + { + CompareDateTimeOffsetResults(actual.ToString(), expected); + } else if (type == TIMEONLY_TYPE) { CompareTimeResults(actual.ToString(), expected); @@ -412,29 +388,26 @@ private static void CompareDateTimeResults(string actual, string expected) /// Required due to different format between mysql datetimeoffset and HotChocolate datetime /// result /// - //private static void CompareDateTimeOffsetResults(string actual, string expected) - //{ - // string fieldName = "datetimeoffset_types"; - - // using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); - // using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); - - // string actualDateTime = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); - // string expectedDateTime = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); - - // // handles cases when one of the values is null - // if (string.IsNullOrEmpty(actualDateTime) || string.IsNullOrEmpty(expectedDateTime)) - // { - // Assert.AreEqual(expectedDateTime, actualDateTime); - // } - // else - // { - // // Adjusting to universal, since DateTime doesn't account for TimeZone - // DateTime expectedDateTimeUniversal = DateTimeOffset.Parse(expectedDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - // DateTime actualDateTimeUniversal = DateTimeOffset.Parse(actualDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - // Assert.AreEqual(expectedDateTimeUniversal, actualDateTimeUniversal); - // } - //} + private static void CompareDateTimeOffsetResults(string actual, string expected) + { + string fieldName = "datetimeoffset_types"; + + using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); + using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + + string actualDateTimeOffset = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedDateTimeOffset = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + + // handles cases when one of the values is null + if (string.IsNullOrEmpty(actualDateTimeOffset) || string.IsNullOrEmpty(expectedDateTimeOffset)) + { + Assert.AreEqual(expectedDateTimeOffset, actualDateTimeOffset); + } + else + { + Assert.AreEqual(DateTimeOffset.Parse(expectedDateTimeOffset), DateTimeOffset.Parse(actualDateTimeOffset)); + } + } /// /// Required due to different format between SQL time and HotChocolate TimeSpan time(ISO-8601) result. @@ -472,6 +445,10 @@ private static string TypeNameToGraphQLType(string typeName) { return STRING_TYPE; } + else if (typeName is DATETIMEOFFSET_TYPE) + { + return DATETIME_TYPE; + } return typeName; } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 9632690288..b673e6350e 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -42,6 +42,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, + DATETIMEOFFSET_TYPE => false, TIMEONLY_TYPE => false, _ => true }; diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index 63c36ee5c5..8bfedb83d8 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -41,7 +41,7 @@ protected override bool IsSupportedType(string type) return type switch { BYTE_TYPE => false, - DATETIME_NONUTC_TYPE => false, + DATETIMEOFFSET_TYPE => false, TIMEONLY_TYPE => false, _ => true }; From 5e01c3cef0e57082c9262806ac4fcb293b2bf1be Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 21 Jul 2023 11:53:11 +0530 Subject: [PATCH 26/85] fix formatting --- .../GraphQLSupportedTypesTestsBase.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index b52b1e5cce..31a9250141 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -390,23 +390,23 @@ private static void CompareDateTimeResults(string actual, string expected) /// private static void CompareDateTimeOffsetResults(string actual, string expected) { - string fieldName = "datetimeoffset_types"; + string fieldName = "datetimeoffset_types"; - using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); - using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); + using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); - string actualDateTimeOffset = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); - string expectedDateTimeOffset = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string actualDateTimeOffset = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedDateTimeOffset = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); - // handles cases when one of the values is null - if (string.IsNullOrEmpty(actualDateTimeOffset) || string.IsNullOrEmpty(expectedDateTimeOffset)) - { - Assert.AreEqual(expectedDateTimeOffset, actualDateTimeOffset); - } - else - { + // handles cases when one of the values is null + if (string.IsNullOrEmpty(actualDateTimeOffset) || string.IsNullOrEmpty(expectedDateTimeOffset)) + { + Assert.AreEqual(expectedDateTimeOffset, actualDateTimeOffset); + } + else + { Assert.AreEqual(DateTimeOffset.Parse(expectedDateTimeOffset), DateTimeOffset.Parse(actualDateTimeOffset)); - } + } } /// From fc0be0417c4a3e64a47df099618a88f0c98b2f3a Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 24 Jul 2023 16:12:18 +0530 Subject: [PATCH 27/85] fixing tests --- src/Core/Services/GraphQLSchemaCreator.cs | 1 - src/Core/Services/TypeHelper.cs | 1 + src/Directory.Packages.props | 2 +- .../GraphQLTypes/TimeOnlytype.cs | 16 +++++++------- .../GraphQLFilterTestBase.cs | 22 +++++++++++++++++++ .../GraphQLFilterTests/MsSqlGQLFilterTests.cs | 14 ++++++++++++ 6 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/Core/Services/GraphQLSchemaCreator.cs b/src/Core/Services/GraphQLSchemaCreator.cs index 93e5d27330..c8fb481e2b 100644 --- a/src/Core/Services/GraphQLSchemaCreator.cs +++ b/src/Core/Services/GraphQLSchemaCreator.cs @@ -91,7 +91,6 @@ private ISchemaBuilder Parse( .AddDirectiveType() // Add our custom scalar GraphQL types .AddType() - .AddType() .AddType() // Generate the GraphQL queries from the provided objects .AddDocument(QueryBuilder.Build(root, _databaseType, _entities, inputTypes, _authorizationResolver.EntityPermissionsMap, dbObjects)) diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 9f647c9523..cf31bb2434 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -39,6 +39,7 @@ public static class TypeHelper [typeof(DateTime)] = DbType.DateTime, [typeof(DateTimeOffset)] = DbType.DateTimeOffset, [typeof(TimeOnly)] = DbType.Time, + [typeof(TimeSpan)] = DbType.Time, [typeof(object)] = DbType.Object }; diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index dde5860283..1e1c985184 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -19,7 +19,7 @@ - + diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs index 1ad7d218fa..e20b15be55 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs @@ -5,7 +5,7 @@ using HotChocolate.Types; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; -public class TimeOnlyType : ScalarType +public class TimeOnlyType : ScalarType { /// /// Initializes a new instance of the class. @@ -26,11 +26,11 @@ public override object ParseLiteral(IValueNode literal) { if (TimeSpan.TryParse(stringValueNode.Value, out TimeSpan timeSpan)) { - return timeSpan; + return new TimeOnly(timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds); } } - throw new ArgumentException("Invalid TimeOnly value."); + throw new ArgumentException("Invalid time value."); } /// @@ -38,7 +38,7 @@ public override IValueNode ParseResult(object? resultValue) { if (resultValue is TimeOnly timeOnly) { - return new StringValueNode(timeOnly.ToString(@"hh\:mm\:ss\.fff")); + return new StringValueNode(timeOnly.ToString(@"HH\:mm\:ss\.fff")); } throw new ArgumentException("Invalid TimeOnly value."); @@ -47,9 +47,9 @@ public override IValueNode ParseResult(object? resultValue) /// public override IValueNode ParseValue(object? value) { - if (value is TimeOnly timeSpan) + if (value is TimeOnly timeOnly) { - return new StringValueNode(timeSpan.ToString(@"hh\:mm\:ss\.fff")); + return new StringValueNode(timeOnly.ToString(@"HH\:mm\:ss\.fff")); } throw new ArgumentException("Invalid TimeOnly value."); @@ -58,9 +58,9 @@ public override IValueNode ParseValue(object? value) /// public override object Serialize(object? value) { - if (value is TimeSpan timeSpan) + if (value is TimeOnly timeOnly) { - return timeSpan.ToString(@"hh\:mm\:ss\.fff"); + return timeOnly.ToString(@"HH\:mm\:ss\.fff"); } throw new ArgumentException("Invalid TimeOnly value."); diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs index db0e904990..d761caa12b 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs @@ -71,6 +71,28 @@ public async Task TestStringFiltersEqWithMappings(string dbQuery) SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } + /// + /// Tests gte of TimeOnlyFilterInput where TimeOnly is a custom scalar Hotchocolate Type. + /// + [TestMethod] + public async Task TestTimeOnlyTypeFilter(string dbQuery) + { + string graphQLQueryName = "supportedTypes"; + string gqlQuery = @"{ + supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + @" : {timeonly_types: {gte: ""10:23:54.999""}}) + { + items { + typeid + timeonly_types + } + } + }"; + + JsonElement actual = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: false); + string expected = await GetDatabaseResultAsync(dbQuery); + SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); + } + /// /// Tests neq of StringFilterInput /// diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs index 7a6b18ec20..86ef16dd9a 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs @@ -58,6 +58,20 @@ ORDER BY [__column1] asc await TestStringFiltersEqWithMappings(msSqlQuery); } + [TestMethod] + public async Task TestTimeOnlyTypeFilter() + { + string msSqlQuery = @" + SELECT [id] AS [typeid], + CONVERT(VARCHAR(12), timeonly_types, 114) AS [timeonly_types] + FROM type_table + WHERE [timeonly_types] >= '10:23:54.999' + ORDER BY [typeid] asc + FOR JSON PATH, INCLUDE_NULL_VALUES"; + + await TestTimeOnlyTypeFilter(msSqlQuery); + } + /// /// Test Nested Filter for One-Many relationship /// From ebd7725ecbbf9eccf8d7fa61340adbf4546e2b3f Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 24 Jul 2023 17:15:15 +0530 Subject: [PATCH 28/85] fixing tests --- .../SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs | 7 +++++++ .../GraphQLFilterTests/PostgreSqlGQLFilterTests.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs index 8262a84497..307273a15a 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs @@ -73,6 +73,13 @@ public void TestNestedFilterWithOr() throw new System.NotImplementedException("Nested Filtering for MySQL is not yet implemented."); } + [Ignore] + [TestMethod] + public void TestTimeOnlyTypeFilter() + { + throw new System.NotImplementedException("Filtering TimeOnly Type for MySQL is not yet implemented."); + } + [TestMethod] public async Task TestStringFiltersEqWithMappings() { diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs index 1d2d2023ee..7b433033b7 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs @@ -168,6 +168,13 @@ public async Task TestStringFiltersEqWithMappings() await TestStringFiltersEqWithMappings(postgresQuery); } + [Ignore] + [TestMethod] + public void TestTimeOnlyTypeFilter() + { + throw new System.NotImplementedException("Filtering TimeOnly Type for PGSQL is not yet implemented."); + } + /// /// Gets the default schema for /// PostgreSql. From 0d0b26366a10c324a4d318c57c0eacc0da1b8254 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 24 Jul 2023 18:04:26 +0530 Subject: [PATCH 29/85] fixing tests --- .../GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs | 4 ++-- .../GraphQLSupportedTypesTestsBase.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index 1a958ce783..a05c8d9829 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -43,13 +43,13 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Test after token for decimal values.")] [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] - [DataRow("date_types", "\"0001-01-01\"", + [DataRow("date_types", "\"1753-01-01\"", "\"9999-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", "\"9999-12-31T23:59:59\"", 3, 4, DisplayName = "Test after token for datetime values.")] - [DataRow("datetime2_types", "\"0001-01-01 00:00:00.0000000\"", + [DataRow("datetime2_types", "\"1753-01-01 00:00:00.0000000\"", "\"9999-12-31T23:59:59.9999999\"", 3, 4, DisplayName = "Test after token for datetime2 values.")] [DataRow("datetimeoffset_types", "\"0001-01-01 00:00:00.0000000+0:00\"", diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 31a9250141..c33da1926b 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -222,7 +222,7 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] + [DataRow(TIMEONLY_TYPE, "\"23:59:59.9999999\"")] [DataRow(TIMEONLY_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] From 33edfaa667baa06b29f135c99aa150bbd34a9ef4 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 25 Jul 2023 22:44:07 +0530 Subject: [PATCH 30/85] using Hotchocolate.Types.NodaTime --- src/Core/Azure.DataApiBuilder.Core.csproj | 1 + .../BaseSqlQueryStructure.cs | 2 +- src/Core/Services/ResolverMiddleware.cs | 5 +- src/Directory.Packages.props | 1 + ...taApiBuilder.Service.GraphQLBuilder.csproj | 1 + .../GraphQLTypes/DefaultValueType.cs | 3 +- .../GraphQLTypes/SupportedTypes.cs | 5 +- .../GraphQLTypes/TimeOnlytype.cs | 68 ------------------- src/Service.GraphQLBuilder/GraphQLUtils.cs | 10 ++- .../Queries/StandardQueryInputs.cs | 32 ++++----- .../Sql/SchemaConverter.cs | 8 +-- src/Service.Tests/DatabaseSchema-MsSql.sql | 4 +- .../Sql/SchemaConverterTests.cs | 2 +- .../Sql/StoredProcedureBuilderTests.cs | 2 +- .../CLRtoJsonValueTypeUnitTests.cs | 1 + .../GraphQLFilterTestBase.cs | 8 +-- .../GraphQLFilterTests/MsSqlGQLFilterTests.cs | 8 +-- .../GraphQLFilterTests/MySqlGQLFilterTests.cs | 4 +- .../PostgreSqlGQLFilterTests.cs | 4 +- .../GraphQLSupportedTypesTestsBase.cs | 46 +++++++++---- .../MySqlGQLSupportedTypesTests.cs | 3 +- .../PostgreSqlGQLSupportedTypesTests.cs | 2 +- .../Azure.DataApiBuilder.Service.csproj | 1 + 23 files changed, 88 insertions(+), 133 deletions(-) delete mode 100644 src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs diff --git a/src/Core/Azure.DataApiBuilder.Core.csproj b/src/Core/Azure.DataApiBuilder.Core.csproj index 3850c5f587..a4ac48aeb5 100644 --- a/src/Core/Azure.DataApiBuilder.Core.csproj +++ b/src/Core/Azure.DataApiBuilder.Core.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 637c84eb4f..2cf9ca3ff8 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -364,9 +364,9 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "DateTime" => DateTime.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), + "Guid" => Guid.Parse(param), "TimeOnly" => TimeOnly.Parse(param), "TimeSpan" => TimeOnly.Parse(param), - "Guid" => Guid.Parse(param), _ => throw new NotSupportedException($"{systemType.Name} is not supported") }; } diff --git a/src/Core/Services/ResolverMiddleware.cs b/src/Core/Services/ResolverMiddleware.cs index 2b115459c4..d3941e7805 100644 --- a/src/Core/Services/ResolverMiddleware.cs +++ b/src/Core/Services/ResolverMiddleware.cs @@ -7,12 +7,13 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; -using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.Resolvers; +using HotChocolate.Types.NodaTime; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; +using NodaTime.Text; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Core.Services @@ -200,7 +201,7 @@ private static object PreParseLeaf(IMiddlewareContext context, string leafJson) SingleType => Single.Parse(leafJson), DateTimeType => DateTimeOffset.Parse(leafJson, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), ByteArrayType => Convert.FromBase64String(leafJson), - TimeOnlyType => TimeOnly.Parse(leafJson, DateTimeFormatInfo.InvariantInfo), + LocalTimeType => LocalTimePattern.ExtendedIso.Parse(leafJson).Value, _ => leafJson }; } diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 1e1c985184..ec66e68506 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -9,6 +9,7 @@ + diff --git a/src/Service.GraphQLBuilder/Azure.DataApiBuilder.Service.GraphQLBuilder.csproj b/src/Service.GraphQLBuilder/Azure.DataApiBuilder.Service.GraphQLBuilder.csproj index 71530f1eba..80fc4b94eb 100644 --- a/src/Service.GraphQLBuilder/Azure.DataApiBuilder.Service.GraphQLBuilder.csproj +++ b/src/Service.GraphQLBuilder/Azure.DataApiBuilder.Service.GraphQLBuilder.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index f17628ef7d..be27c13555 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -3,6 +3,7 @@ using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using HotChocolate.Types; +using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes @@ -24,7 +25,7 @@ protected override void Configure(IInputObjectTypeDescriptor descriptor) descriptor.Field(DECIMAL_TYPE).Type(); descriptor.Field(DATETIME_TYPE).Type(); descriptor.Field(BYTEARRAY_TYPE).Type(); - descriptor.Field(TIMEONLY_TYPE).Type(); + descriptor.Field(LOCALTIME_TYPE).Type(); } } } diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index cd0160a045..4470f8e2d1 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -18,11 +18,10 @@ public static class SupportedTypes public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; public const string DATETIME_TYPE = "DateTime"; - // The DATETIME_NONUTC_TYPE constant is only used in testing - // since PostgreSQL doesn't support datetime values with a non-UTC time zone. public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string BYTEARRAY_TYPE = "ByteArray"; - public const string TIMEONLY_TYPE = "TimeOnly"; public const string GUID_TYPE = "Guid"; + public const string LOCALTIME_TYPE = "LocalTime"; + public const string TIME_TYPE = "Time"; } } diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs b/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs deleted file mode 100644 index e20b15be55..0000000000 --- a/src/Service.GraphQLBuilder/GraphQLTypes/TimeOnlytype.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using HotChocolate.Language; -using HotChocolate.Types; - -namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; -public class TimeOnlyType : ScalarType -{ - /// - /// Initializes a new instance of the class. - /// - public TimeOnlyType() : base("TimeOnly") - { } - - /// - public override bool IsInstanceOfType(IValueNode valueSyntax) - { - return valueSyntax is StringValueNode; - } - - /// - public override object ParseLiteral(IValueNode literal) - { - if (literal is StringValueNode stringValueNode) - { - if (TimeSpan.TryParse(stringValueNode.Value, out TimeSpan timeSpan)) - { - return new TimeOnly(timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds); - } - } - - throw new ArgumentException("Invalid time value."); - } - - /// - public override IValueNode ParseResult(object? resultValue) - { - if (resultValue is TimeOnly timeOnly) - { - return new StringValueNode(timeOnly.ToString(@"HH\:mm\:ss\.fff")); - } - - throw new ArgumentException("Invalid TimeOnly value."); - } - - /// - public override IValueNode ParseValue(object? value) - { - if (value is TimeOnly timeOnly) - { - return new StringValueNode(timeOnly.ToString(@"HH\:mm\:ss\.fff")); - } - - throw new ArgumentException("Invalid TimeOnly value."); - } - - /// - public override object Serialize(object? value) - { - if (value is TimeOnly timeOnly) - { - return timeOnly.ToString(@"HH\:mm\:ss\.fff"); - } - - throw new ArgumentException("Invalid TimeOnly value."); - } -} diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 0d9f967e62..b537e46ba6 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -2,17 +2,16 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Net; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; -using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.GraphQLBuilder.Sql; using HotChocolate.Language; using HotChocolate.Types; +using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder @@ -55,7 +54,7 @@ public static bool IsBuiltInType(ITypeNode typeNode) BOOLEAN_TYPE, DATETIME_TYPE, BYTEARRAY_TYPE, - TIMEONLY_TYPE + LOCALTIME_TYPE }; string name = typeNode.NamedType().Name.Value; return inBuiltTypes.Contains(name); @@ -242,10 +241,9 @@ public static Tuple ConvertValueToGraphQLType(string default SINGLE_TYPE => new(SINGLE_TYPE, new SingleType().ParseValue(float.Parse(defaultValueFromConfig))), FLOAT_TYPE => new(FLOAT_TYPE, new FloatValueNode(double.Parse(defaultValueFromConfig))), DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), - DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult( - DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))), - TIMEONLY_TYPE => new(TIMEONLY_TYPE, new TimeOnlyType().ParseResult(TimeOnly.Parse(defaultValueFromConfig))), + DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult(DateTime.Parse(defaultValueFromConfig))), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), + LOCALTIME_TYPE => new(LOCALTIME_TYPE, new LocalTimeType().ParseResult(TimeOnly.Parse(defaultValueFromConfig))), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index 5411a782a1..365aca7f74 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; -using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using HotChocolate.Language; using HotChocolate.Types; +using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Queries @@ -191,31 +191,31 @@ public static InputObjectTypeDefinitionNode DateTimeInputType() => } ); - public static InputObjectTypeDefinitionNode TimeOnlyInputType() => + public static InputObjectTypeDefinitionNode ByteArrayInputType() => new( location: null, - new NameNode("TimeOnlyFilterInput"), - new StringValueNode("Input type for adding TimeOnly filters"), + new NameNode("ByteArrayFilterInput"), + new StringValueNode("Input type for adding ByteArray filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new TimeOnlyType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("is null test"), new BooleanType().ToTypeNode(), null, new List()) + new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); - public static InputObjectTypeDefinitionNode ByteArrayInputType() => + public static InputObjectTypeDefinitionNode LocalTimeInputType() => new( location: null, - new NameNode("ByteArrayFilterInput"), - new StringValueNode("Input type for adding ByteArray filters"), + new NameNode("LocalTimeFilterInput"), + new StringValueNode("Input type for adding LocalTime filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LocalTimeType().ToTypeNode(), null, new List()), + new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -233,7 +233,7 @@ public static InputObjectTypeDefinitionNode ByteArrayInputType() => { STRING_TYPE, StringInputType() }, { DATETIME_TYPE, DateTimeInputType() }, { BYTEARRAY_TYPE, ByteArrayInputType() }, - { TIMEONLY_TYPE, TimeOnlyInputType() }, + { LOCALTIME_TYPE, LocalTimeInputType() }, }; /// diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 5e963a6e34..d51d397a71 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -9,10 +9,10 @@ using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using Azure.DataApiBuilder.Service.GraphQLBuilder.Directives; -using Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes; using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries; using HotChocolate.Language; using HotChocolate.Types; +using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLStoredProcedureBuilder; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; @@ -232,8 +232,8 @@ public static string GetGraphQLTypeFromSystemType(Type type) "DateTime" => DATETIME_TYPE, "DateTimeOffset" => DATETIME_TYPE, "Byte[]" => BYTEARRAY_TYPE, - "TimeOnly" => TIMEONLY_TYPE, - "TimeSpan" => TIMEONLY_TYPE, + "TimeOnly" => LOCALTIME_TYPE, + "TimeSpan" => LOCALTIME_TYPE, _ => throw new DataApiBuilderException( message: $"Column type {type} not handled by case. Please add a case resolving {type} to the appropriate GraphQL type", statusCode: HttpStatusCode.InternalServerError, @@ -266,8 +266,8 @@ public static IValueNode CreateValueNodeFromDbObjectMetadata(object metadataValu decimal value => new ObjectValueNode(new ObjectFieldNode(DECIMAL_TYPE, new FloatValueNode(value))), DateTimeOffset value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseValue(value))), DateTime value => new ObjectValueNode(new ObjectFieldNode(DATETIME_TYPE, new DateTimeType().ParseResult(value))), - TimeOnly value => new ObjectValueNode(new ObjectFieldNode(TIMEONLY_TYPE, new TimeOnlyType().ParseResult(value))), byte[] value => new ObjectValueNode(new ObjectFieldNode(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(value))), + TimeOnly value => new ObjectValueNode(new ObjectFieldNode(LOCALTIME_TYPE, new LocalTimeType().ParseResult(value))), _ => throw new DataApiBuilderException( message: $"The type {metadataValue.GetType()} is not supported as a GraphQL default value", statusCode: HttpStatusCode.InternalServerError, diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 22129ceeb0..4768723a12 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -162,7 +162,7 @@ CREATE TABLE type_table( datetime2_types datetime2, datetimeoffset_types datetimeoffset, smalldatetime_types smalldatetime, - timeonly_types time, + time_types time, bytearray_types varbinary(max), guid_types uniqueidentifier DEFAULT newid() ); @@ -399,7 +399,7 @@ byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, -date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, timeonly_types, +date_types, datetime_types, datetime2_types, datetimeoffset_types, smalldatetime_types, time_types, bytearray_types) VALUES (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, 1, diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index c310bdd749..d6da633379 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -230,7 +230,7 @@ public void MultipleColumnsAllMapped() [DataRow(typeof(DateTimeOffset), DATETIME_TYPE)] [DataRow(typeof(byte[]), BYTEARRAY_TYPE)] [DataRow(typeof(Guid), STRING_TYPE)] - [DataRow(typeof(TimeOnly), TIMEONLY_TYPE)] + [DataRow(typeof(TimeOnly), LOCALTIME_TYPE)] public void SystemTypeMapsToCorrectGraphQLType(Type systemType, string graphQLType) { SourceDefinition table = new(); diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 7ed46419e7..be33d35d58 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -54,7 +54,7 @@ public class StoredProcedureBuilderTests [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12:00:00 AM", false, DisplayName = "DateTime")] [DataRow(typeof(DateTime), DATETIME_TYPE, "12/31/2030 12000 AM", true, DisplayName = "DateTime")] [DataRow(typeof(DateTimeOffset), DATETIME_TYPE, "11/19/2012 10:57:11 AM -08:00", false, DisplayName = "DateTimeOffset")] - [DataRow(typeof(TimeOnly), TIMEONLY_TYPE, "10:57:11.0000", false, DisplayName = "TimeOnly")] + [DataRow(typeof(TimeOnly), LOCALTIME_TYPE, "10:57:11.0000", false, DisplayName = "LocalTime")] [DataRow(typeof(byte[]), BYTEARRAY_TYPE, "AgQGCAoMDhASFA==", false, DisplayName = "Byte[]")] public void StoredProcedure_ParameterValueTypeResolution( Type systemType, diff --git a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs index fc68b4bb14..3dd726f952 100644 --- a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs +++ b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs @@ -100,6 +100,7 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa [DataRow(typeof(char?))] [DataRow(typeof(Guid?))] [DataRow(typeof(TimeOnly?))] + [DataRow(typeof(TimeSpan?))] [DataRow(typeof(DateTime?))] [DataRow(typeof(DateTimeOffset?))] [DataTestMethod] diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs index d761caa12b..99512cc50f 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs @@ -72,18 +72,18 @@ public async Task TestStringFiltersEqWithMappings(string dbQuery) } /// - /// Tests gte of TimeOnlyFilterInput where TimeOnly is a custom scalar Hotchocolate Type. + /// Tests gte of LocalTimeFilterInput where LocalTime is a NodatTime Hotchocolate Type. /// [TestMethod] - public async Task TestTimeOnlyTypeFilter(string dbQuery) + public async Task TestLocalTimeTypeFilter(string dbQuery) { string graphQLQueryName = "supportedTypes"; string gqlQuery = @"{ - supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + @" : {timeonly_types: {gte: ""10:23:54.999""}}) + supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + @" : {time_types: {gte: ""10:23:54.999""}}) { items { typeid - timeonly_types + time_types } } }"; diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs index 86ef16dd9a..d1d0bdfe65 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs @@ -59,17 +59,17 @@ ORDER BY [__column1] asc } [TestMethod] - public async Task TestTimeOnlyTypeFilter() + public async Task TestLocalTimeTypeFilter() { string msSqlQuery = @" SELECT [id] AS [typeid], - CONVERT(VARCHAR(12), timeonly_types, 114) AS [timeonly_types] + [time_types] FROM type_table - WHERE [timeonly_types] >= '10:23:54.999' + WHERE [time_types] >= '10:23:54.999' ORDER BY [typeid] asc FOR JSON PATH, INCLUDE_NULL_VALUES"; - await TestTimeOnlyTypeFilter(msSqlQuery); + await TestLocalTimeTypeFilter(msSqlQuery); } /// diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs index 307273a15a..9c7887f286 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs @@ -75,9 +75,9 @@ public void TestNestedFilterWithOr() [Ignore] [TestMethod] - public void TestTimeOnlyTypeFilter() + public void TestLocalTimeTypeFilter() { - throw new System.NotImplementedException("Filtering TimeOnly Type for MySQL is not yet implemented."); + throw new System.NotImplementedException("Filtering LocalTime Type for MySQL is not yet implemented."); } [TestMethod] diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs index 7b433033b7..46bf44720e 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs @@ -170,9 +170,9 @@ public async Task TestStringFiltersEqWithMappings() [Ignore] [TestMethod] - public void TestTimeOnlyTypeFilter() + public void TestLocalTimeTypeFilter() { - throw new System.NotImplementedException("Filtering TimeOnly Type for PGSQL is not yet implemented."); + throw new System.NotImplementedException("Filtering LocalTime Type for PGSQL is not yet implemented."); } /// diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index c33da1926b..817cda886b 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; @@ -60,11 +61,11 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase [DataRow(DATETIME_TYPE, 2)] [DataRow(DATETIME_TYPE, 3)] [DataRow(DATETIME_TYPE, 4)] - [DataRow(TIMEONLY_TYPE, 1)] - [DataRow(TIMEONLY_TYPE, 2)] - [DataRow(TIMEONLY_TYPE, 3)] - [DataRow(TIMEONLY_TYPE, 4)] - [DataRow(TIMEONLY_TYPE, 5)] + [DataRow(TIME_TYPE, 1)] + [DataRow(TIME_TYPE, 2)] + [DataRow(TIME_TYPE, 3)] + [DataRow(TIME_TYPE, 4)] + [DataRow(TIME_TYPE, 5)] [DataRow(BYTEARRAY_TYPE, 1)] [DataRow(BYTEARRAY_TYPE, 2)] [DataRow(BYTEARRAY_TYPE, 3)] @@ -127,9 +128,10 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMEONLY_TYPE, "\"23:59:59.9999999\"")] - [DataRow(TIMEONLY_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] + [DataRow(TIME_TYPE, "\"23:59:59.9999999\"")] + [DataRow(TIME_TYPE, "\"23:59:59\"")] + [DataRow(TIME_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] public async Task InsertIntoTypeColumn(string type, string value) @@ -153,6 +155,26 @@ public async Task InsertIntoTypeColumn(string type, string value) await ResetDbStateAsync(); } + [DataTestMethod] + [DataRow(TIME_TYPE, "\"32:59:59.9999999\"")] + [DataRow(TIME_TYPE, "\"22:67:59.9999999\"")] + public async Task InsertInvalidIntoTypeColumn(string type, string value) + { + if (!IsSupportedType(type)) + { + Assert.Inconclusive("Type not supported"); + } + + string field = $"{type.ToLowerInvariant()}_types"; + string graphQLQueryName = "createSupportedType"; + string gqlQuery = "mutation{ createSupportedType (item: {" + field + ": " + value + " }){ " + field + " } }"; + + JsonElement response = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: true); + string responseMessage = Regex.Unescape(JsonSerializer.Serialize(response)); + Assert.IsTrue(responseMessage.Contains($"{value} cannot be resolved as column \"{field}\" with type \"TimeSpan\".")); + await ResetDbStateAsync(); + } + [DataTestMethod] [DataRow(BYTE_TYPE, 255)] [DataRow(SHORT_TYPE, 30000)] @@ -164,7 +186,6 @@ public async Task InsertIntoTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] - [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] public async Task InsertIntoTypeColumnWithArgument(string type, object value) { @@ -222,8 +243,8 @@ public async Task InsertIntoTypeColumnWithArgument(string type, object value) [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] - [DataRow(TIMEONLY_TYPE, "\"23:59:59.9999999\"")] - [DataRow(TIMEONLY_TYPE, "null")] + [DataRow(TIME_TYPE, "\"23:59:59.9999999\"")] + [DataRow(TIME_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -261,7 +282,6 @@ public async Task UpdateTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] [DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")] - [DataRow(TIMEONLY_TYPE, "23:59:59.9999999")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] [DataRow(GUID_TYPE, "3a1483a5-9ac2-4998-bcf3-78a28078c6ac")] [DataRow(GUID_TYPE, null)] @@ -306,7 +326,7 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareDateTimeOffsetResults(actual.ToString(), expected); } - else if (type == TIMEONLY_TYPE) + else if (type == TIME_TYPE) { CompareTimeResults(actual.ToString(), expected); } @@ -414,7 +434,7 @@ private static void CompareDateTimeOffsetResults(string actual, string expected) /// private static void CompareTimeResults(string actual, string expected) { - string fieldName = "timeonly_types"; + string fieldName = "time_types"; using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index b673e6350e..135a6c41d6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -42,8 +42,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, - DATETIMEOFFSET_TYPE => false, - TIMEONLY_TYPE => false, + TIME_TYPE => false, _ => true }; } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index 8bfedb83d8..34e6a5dbd1 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -42,7 +42,7 @@ protected override bool IsSupportedType(string type) { BYTE_TYPE => false, DATETIMEOFFSET_TYPE => false, - TIMEONLY_TYPE => false, + TIME_TYPE => false, _ => true }; } diff --git a/src/Service/Azure.DataApiBuilder.Service.csproj b/src/Service/Azure.DataApiBuilder.Service.csproj index 8338c6c059..156dcc7436 100644 --- a/src/Service/Azure.DataApiBuilder.Service.csproj +++ b/src/Service/Azure.DataApiBuilder.Service.csproj @@ -51,6 +51,7 @@ + From 9883d8785189df52346d0e95fd6b5c57a6663915 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 26 Jul 2023 11:08:44 +0530 Subject: [PATCH 31/85] fix formatting --- .../MsSqlGraphQLPaginationTests.cs | 4 ++-- .../GraphQLSupportedTypesTestsBase.cs | 12 ++++++------ .../MySqlGQLSupportedTypesTests.cs | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index a55ee7fad6..0a5ce1938c 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -44,13 +44,13 @@ public static async Task SetupAsync(TestContext context) [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] [DataRow("date_types", "\"1753-01-01\"", - "\"9999-12-31\"", 3, 4, + "\"9998-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", "\"9998-12-31T23:59:59\"", 3, 4, DisplayName = "Test after token for datetime values.")] [DataRow("datetime2_types", "\"1753-01-01 00:00:00.0000000\"", - "\"9999-12-31T23:59:59.9999999\"", 3, 4, + "\"9998-12-31T23:59:59.9999999\"", 3, 4, DisplayName = "Test after token for datetime2 values.")] [DataRow("datetimeoffset_types", "\"0001-01-01 00:00:00.0000000+0:00\"", "\"9998-12-31T23:59:59.9999999+00:00\"", 3, 4, diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 5dfa0089c6..a6c5d4fa5c 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -173,10 +173,10 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataTestMethod] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08\'", "\"1999-01-08\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08\'", "\"1999-01-08\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'0001-01-01\'", "\"0001-01-01\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'0001-01-01\'", "\"0001-01-01\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'0001-01-01\'", "\"0001-01-01\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'0001-01-01\'", "\"0001-01-01T01:01:01\"", "=")] + [DataRow(DATETIME_TYPE, "lt", "\'1999-01-08\'", "\"1999-01-08\"", " < ")] + [DataRow(DATETIME_TYPE, "lte", "\'1999-01-08\'", "\"1999-01-08\"", " <= ")] + [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08\'", "\"1999-01-08\"", "!=")] + [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08\'", "\"1999-01-08T01:01:01\"", "=")] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " >= ")] [DataRow(DATETIME_TYPE, "lt", "\'9998-12-31 23:59:59\'", "\"9998-12-31 23:59:59\"", " < ")] @@ -591,8 +591,8 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen if (fieldName.StartsWith(DATETIME_TYPE.ToLower())) { // MySql returns a format that will not directly parse into DateTime type so we use string here for parsing - DateTime actualDateTime = DateTime.Parse(actualValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None); - DateTime expectedDateTime = DateTime.Parse(expectedValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None); + DateTime actualDateTime = DateTime.Parse(actualValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + DateTime expectedDateTime = DateTime.Parse(expectedValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); Assert.AreEqual(expectedDateTime, actualDateTime); } else if (fieldName.StartsWith(SINGLE_TYPE.ToLower())) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index d59ecafb50..a0a6863fa6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -57,6 +57,7 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, + DATETIMEOFFSET_TYPE => false, TIME_TYPE => false, _ => true }; From a3e968ef2ff723effe2b00cbd54ba82740eab063 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 26 Jul 2023 11:39:24 +0530 Subject: [PATCH 32/85] fixing tests --- src/Service.GraphQLBuilder/GraphQLUtils.cs | 7 +++++-- .../SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index b537e46ba6..972ec5f5b4 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Net; using Azure.DataApiBuilder.Config.DatabasePrimitives; using Azure.DataApiBuilder.Config.ObjectModel; @@ -12,6 +13,7 @@ using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Types.NodaTime; +using NodaTime.Text; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder @@ -241,9 +243,10 @@ public static Tuple ConvertValueToGraphQLType(string default SINGLE_TYPE => new(SINGLE_TYPE, new SingleType().ParseValue(float.Parse(defaultValueFromConfig))), FLOAT_TYPE => new(FLOAT_TYPE, new FloatValueNode(double.Parse(defaultValueFromConfig))), DECIMAL_TYPE => new(DECIMAL_TYPE, new FloatValueNode(decimal.Parse(defaultValueFromConfig))), - DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult(DateTime.Parse(defaultValueFromConfig))), + DATETIME_TYPE => new(DATETIME_TYPE, new DateTimeType().ParseResult( + DateTime.Parse(defaultValueFromConfig, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal))), BYTEARRAY_TYPE => new(BYTEARRAY_TYPE, new ByteArrayType().ParseValue(Convert.FromBase64String(defaultValueFromConfig))), - LOCALTIME_TYPE => new(LOCALTIME_TYPE, new LocalTimeType().ParseResult(TimeOnly.Parse(defaultValueFromConfig))), + LOCALTIME_TYPE => new(LOCALTIME_TYPE, new LocalTimeType().ParseResult(LocalTimePattern.ExtendedIso.Parse(defaultValueFromConfig).Value)), _ => throw new NotSupportedException(message: $"The {defaultValueFromConfig} parameter's value type [{paramValueType}] is not supported.") }; diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs index b8d1bd6d4b..ec15851c6e 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/MsSqlInsertApiTests.cs @@ -32,7 +32,7 @@ public class MsSqlInsertApiTests : InsertApiTestBase "InsertOneInSupportedTypes", $"SELECT [id] as [typeid], [byte_types], [short_types], [int_types], [long_types],string_types, [single_types], [float_types], " + $"[decimal_types], [boolean_types], [date_types], [datetime_types], [datetime2_types], [datetimeoffset_types], [smalldatetime_types], " + - $"[timeonly_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + + $"[time_types], [bytearray_types], LOWER([guid_types]) as [guid_types] FROM { _integrationTypeTable } " + $"WHERE [id] = { STARTING_ID_FOR_TEST_INSERTS } AND [bytearray_types] is NULL " + $"FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, From de932001873dcc9e5cc1cc9f6208ca6b8895a683 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 26 Jul 2023 14:44:36 +0530 Subject: [PATCH 33/85] adding tests --- .../GraphQLFilterTestBase.cs | 22 --------------- .../GraphQLFilterTests/MsSqlGQLFilterTests.cs | 14 ---------- .../GraphQLFilterTests/MySqlGQLFilterTests.cs | 7 ----- .../PostgreSqlGQLFilterTests.cs | 7 ----- .../GraphQLSupportedTypesTestsBase.cs | 27 +++++++++++++++++++ 5 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs index 99512cc50f..db0e904990 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/GraphQLFilterTestBase.cs @@ -71,28 +71,6 @@ public async Task TestStringFiltersEqWithMappings(string dbQuery) SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); } - /// - /// Tests gte of LocalTimeFilterInput where LocalTime is a NodatTime Hotchocolate Type. - /// - [TestMethod] - public async Task TestLocalTimeTypeFilter(string dbQuery) - { - string graphQLQueryName = "supportedTypes"; - string gqlQuery = @"{ - supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + @" : {time_types: {gte: ""10:23:54.999""}}) - { - items { - typeid - time_types - } - } - }"; - - JsonElement actual = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: false); - string expected = await GetDatabaseResultAsync(dbQuery); - SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); - } - /// /// Tests neq of StringFilterInput /// diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs index d1d0bdfe65..7a6b18ec20 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs @@ -58,20 +58,6 @@ ORDER BY [__column1] asc await TestStringFiltersEqWithMappings(msSqlQuery); } - [TestMethod] - public async Task TestLocalTimeTypeFilter() - { - string msSqlQuery = @" - SELECT [id] AS [typeid], - [time_types] - FROM type_table - WHERE [time_types] >= '10:23:54.999' - ORDER BY [typeid] asc - FOR JSON PATH, INCLUDE_NULL_VALUES"; - - await TestLocalTimeTypeFilter(msSqlQuery); - } - /// /// Test Nested Filter for One-Many relationship /// diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs index 9c7887f286..8262a84497 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MySqlGQLFilterTests.cs @@ -73,13 +73,6 @@ public void TestNestedFilterWithOr() throw new System.NotImplementedException("Nested Filtering for MySQL is not yet implemented."); } - [Ignore] - [TestMethod] - public void TestLocalTimeTypeFilter() - { - throw new System.NotImplementedException("Filtering LocalTime Type for MySQL is not yet implemented."); - } - [TestMethod] public async Task TestStringFiltersEqWithMappings() { diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs index 46bf44720e..1d2d2023ee 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/PostgreSqlGQLFilterTests.cs @@ -168,13 +168,6 @@ public async Task TestStringFiltersEqWithMappings() await TestStringFiltersEqWithMappings(postgresQuery); } - [Ignore] - [TestMethod] - public void TestLocalTimeTypeFilter() - { - throw new System.NotImplementedException("Filtering LocalTime Type for PGSQL is not yet implemented."); - } - /// /// Gets the default schema for /// PostgreSql. diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index a6c5d4fa5c..c3879627c8 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -202,6 +202,21 @@ public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string fi await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); } + /// + /// Test case for LocalTime filters. + /// + [DataTestMethod] + [DataRow(TIME_TYPE, "gt", "\'00:00:00.000\'", "\"00:00:00.000\"", " > ")] + [DataRow(TIME_TYPE, "gte", "\'10:13:14.123\'", "\"10:13:14.123\"", " >= ")] + [DataRow(TIME_TYPE, "lt", "\'23:59:59.999\'", "\"23:59:59.999\"", " < ")] + [DataRow(TIME_TYPE, "lte", "\'23:59:59.999\'", "\"23:59:59.999\"", " <= ")] + [DataRow(TIME_TYPE, "neq", "\'10:23:54.9999999\'", "\"10:23:54.9999999\"", "!=")] + [DataRow(TIME_TYPE, "eq", "\'10:23:54.9999999\'", "\"10:23:54.9999999\"", "=")] + public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) + { + await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); + } + [DataTestMethod] [DataRow(BYTE_TYPE, "255")] [DataRow(BYTE_TYPE, "0")] @@ -562,6 +577,12 @@ private static void CompareTimeResults(string actual, string expected) using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + if (actualJsonDoc.RootElement.ValueKind is JsonValueKind.Array) + { + ValidateArrayResults(actualJsonDoc, expectedJsonDoc, fieldName); + return; + } + string actualTimeString = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); string expectedTimeString = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); @@ -599,6 +620,12 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen { Assert.AreEqual(expectedValue.GetSingle(), actualValue.GetSingle()); } + else if (fieldName.StartsWith(TIME_TYPE.ToLower())) + { + TimeOnly actualTime = TimeOnly.Parse(actualValue.ToString(), CultureInfo.InvariantCulture); + TimeOnly expectedTime = TimeOnly.Parse(expectedValue.ToString(), CultureInfo.InvariantCulture); + Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); + } else { Assert.AreEqual(expectedValue.GetDouble(), actualValue.GetDouble()); From 184010587315114ea2152a1c4ed0ea4accf60cac Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 26 Jul 2023 17:25:58 +0530 Subject: [PATCH 34/85] adding tests for different time format --- .../GraphQLSupportedTypesTestsBase.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index c3879627c8..67c4f711d6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -255,6 +255,8 @@ public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string f [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] [DataRow(TIME_TYPE, "\"23:59:59.9999999\"")] [DataRow(TIME_TYPE, "\"23:59:59\"")] + [DataRow(TIME_TYPE, "\"23:59:59.9\"")] + [DataRow(TIME_TYPE, "\"23:59\"")] [DataRow(TIME_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] [DataRow(BYTEARRAY_TYPE, "null")] @@ -310,6 +312,8 @@ public async Task InsertInvalidIntoTypeColumn(string type, string value) [DataRow(BOOLEAN_TYPE, true)] [DataRow(DATETIMEOFFSET_TYPE, "1999-01-08 10:23:54+8:00")] [DataRow(DATETIME_TYPE, "1999-01-08 10:23:54")] + [DataRow(TIME_TYPE, "\"23:59:59.9999999\"")] + [DataRow(TIME_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=")] public async Task InsertIntoTypeColumnWithArgument(string type, object value) { From 4a1937832755f4294f5cda0483bb9eb82b4b5e3c Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jul 2023 11:58:45 +0530 Subject: [PATCH 35/85] using max dateTime in the test to check no overflow issue --- src/Service.Tests/DatabaseSchema-MsSql.sql | 2 +- src/Service.Tests/DatabaseSchema-MySql.sql | 2 +- src/Service.Tests/DatabaseSchema-PostgreSql.sql | 2 +- .../MsSqlGraphQLPaginationTests.cs | 8 ++++---- .../MySqlGraphQLPaginationTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 10 ++++------ 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index dbff3a2474..4768723a12 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -412,7 +412,7 @@ VALUES '0001-01-01', '1753-01-01 00:00:00.000', '0001-01-01 00:00:00.0000000', '0001-01-01 00:00:00.0000000+0:00', '1900-01-01 00:00:00', '00:00:00.0000000', 0x00000000), (4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, 1, - '9998-12-31', '9998-12-31 23:59:59', '9998-12-31 23:59:59.9999999', '9998-12-31 23:59:59.9999999+00:00', '2079-06-06', '23:59:59.9999999', + '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', 0xFFFFFFFF), (5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); SET IDENTITY_INSERT type_table OFF diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index 034041d70a..ecc27255c1 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -334,7 +334,7 @@ INSERT INTO type_table(id, byte_types, short_types, int_types, long_types, strin (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, true, '1999-01-08 10:23:54', 0xABCDEF0123), (2, 0, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, false, '1999-01-08 10:23:00', 0x98AB7511AABB1234), (3, 0, -32768, -2147483648, -9223372036854775808, '', -3.4E38, -1.7E308, 2.929292E-19, true, '1753-01-01 00:00:00.000', 0x00000000), - (4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9998-12-31 23:59:59', 0xFFFFFFFF), + (4, 255, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9999-12-31 23:59:59', 0xFFFFFFFF), (5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO trees(treeId, species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m'); INSERT INTO fungi(speciesid, region) VALUES (1, 'northeast'), (2, 'southwest'); diff --git a/src/Service.Tests/DatabaseSchema-PostgreSql.sql b/src/Service.Tests/DatabaseSchema-PostgreSql.sql index 091c5958b6..2a01af8c79 100644 --- a/src/Service.Tests/DatabaseSchema-PostgreSql.sql +++ b/src/Service.Tests/DatabaseSchema-PostgreSql.sql @@ -326,7 +326,7 @@ INSERT INTO type_table(id, short_types, int_types, long_types, string_types, sin (1, 1, 1, 1, '', 0.33, 0.33, 0.333333, true, '1999-01-08 10:23:54', '\xABCDEF0123'), (2, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, false, '19990108 10:23:00', '\x98AB7511AABB1234'), (3, -32768, -2147483648, -9223372036854775808, '', -3.4E38, -1.7E308, 2.929292E-19, true, '1753-01-01 00:00:00.000', '\x00000000'), - (4, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9998-12-31 23:59:59.997', '\xFFFFFFFF'), + (4, 32767, 2147483647, 9223372036854775807, 'null', 3.4E38, 1.7E308, 2.929292E-14, true, '9999-12-31 23:59:59.997', '\xFFFFFFFF'), (5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO trees("treeId", species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m'); INSERT INTO fungi(speciesid, region) VALUES (1, 'northeast'), (2, 'southwest'); diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index 0a5ce1938c..a05c8d9829 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -44,16 +44,16 @@ public static async Task SetupAsync(TestContext context) [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] [DataRow("date_types", "\"1753-01-01\"", - "\"9998-12-31\"", 3, 4, + "\"9999-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", - "\"9998-12-31T23:59:59\"", 3, 4, + "\"9999-12-31T23:59:59\"", 3, 4, DisplayName = "Test after token for datetime values.")] [DataRow("datetime2_types", "\"1753-01-01 00:00:00.0000000\"", - "\"9998-12-31T23:59:59.9999999\"", 3, 4, + "\"9999-12-31T23:59:59.9999999\"", 3, 4, DisplayName = "Test after token for datetime2 values.")] [DataRow("datetimeoffset_types", "\"0001-01-01 00:00:00.0000000+0:00\"", - "\"9998-12-31T23:59:59.9999999+00:00\"", 3, 4, + "\"9999-12-31T23:59:59.9999999+14:00\"", 3, 4, DisplayName = "Test after token for datetimeoffset values.")] [DataRow("smalldatetime_types", "\"1900-01-01 00:00:00\"", "\"2079-06-06T00:00:00\"", 3, 4, diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MySqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MySqlGraphQLPaginationTests.cs index d6aa33b291..3095265b60 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MySqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MySqlGraphQLPaginationTests.cs @@ -44,7 +44,7 @@ public static async Task SetupAsync(TestContext context) [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", - "\"9998-12-31 23:59:59.000000\"", 3, 4, + "\"9999-12-31 23:59:59.000000\"", 3, 4, DisplayName = "Test after token for datetime values.")] [DataRow("bytearray_types", "\"AAAAAA==\"", "\"/////w==\"", 3, 4, DisplayName = "Test after token for bytearray values.")] diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 67c4f711d6..44ad7aa5e1 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -167,8 +167,6 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper /// /// Separate test case for DateTime to allow overwrite for postgreSql. - /// Year 9998 used in test and data within test tables to avoid out of - /// date range error within GQL. /// [DataTestMethod] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08\'", "\"1999-01-08\"", " > ")] @@ -179,14 +177,14 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08\'", "\"1999-01-08T01:01:01\"", "=")] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'9998-12-31 23:59:59\'", "\"9998-12-31 23:59:59\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'9998-12-31 23:59:59\'", "\"9998-12-31 23:59:59\"", " <= ")] + [DataRow(DATETIME_TYPE, "lt", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", " < ")] + [DataRow(DATETIME_TYPE, "lte", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", " <= ")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", "!=")] [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", "=")] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'9998-12-31 23:59:59.9999999\'", "\"9998-12-31 23:59:59.9999999\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'9998-12-31 23:59:59.9999999\'", "\"9998-12-31 23:59:59.9999999\"", " <= ")] + [DataRow(DATETIME_TYPE, "lt", "\'9999-12-31 23:59:59.9999999\'", "\"9999-12-31 23:59:59.9999999\"", " < ")] + [DataRow(DATETIME_TYPE, "lte", "\'9999-12-31 23:59:59.9999999\'", "\"9999-12-31 23:59:59.9999999\"", " <= ")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "!=")] [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "=")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=")] From a6fa28f30ee940c86cc72e7f9bc2b254a25459fd Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jul 2023 12:24:39 +0530 Subject: [PATCH 36/85] fixing PGSQL tests --- .../PostgreSqlGraphQLPaginationTests.cs | 2 +- src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/PostgreSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/PostgreSqlGraphQLPaginationTests.cs index 60bbece479..ae58ed8f5c 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/PostgreSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/PostgreSqlGraphQLPaginationTests.cs @@ -43,7 +43,7 @@ public static async Task SetupAsync(TestContext context) [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", - "\"9998-12-31T23:59:59.997\"", 3, 4, + "\"9999-12-31T23:59:59.997\"", 3, 4, DisplayName = "Test after token for datetime values.")] [DataRow("bytearray_types", "\"AAAAAA==\"", "\"/////w==\"", 3, 4, DisplayName = "Test after token for bytearray values.")] diff --git a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs index 69722a3825..c58386f66e 100644 --- a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs +++ b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs @@ -59,9 +59,9 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Equate date types.")] [DataRow("datetime_types eq 2023-01-24T12:51:59Z", "([datetime_types] = @param1)", DisplayName = "Equate datetime types.")] - [DataRow("datetime2_types eq 9998-12-31T21:59:59.99999Z", "([datetime2_types] = @param1)", + [DataRow("datetime2_types eq 9999-12-31T21:59:59.99999Z", "([datetime2_types] = @param1)", DisplayName = "Equate datetime2 types.")] - [DataRow("datetimeoffset_types eq 9998-12-31T21:59:59.99999-14:00", + [DataRow("datetimeoffset_types eq 9999-12-31T21:59:59.99999-14:00", "([datetimeoffset_types] = @param1)", DisplayName = "Equate datetimeoffset types.")] [DataRow("smalldatetime_types eq 2079-06-06", "([smalldatetime_types] = @param1)", DisplayName = "Equate smalldatetime types.")] From a9c396df1960b1bd1f0e6dbff4c523d1827e6860 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jul 2023 14:24:44 +0530 Subject: [PATCH 37/85] fixing tests --- src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs index c58386f66e..69722a3825 100644 --- a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs +++ b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs @@ -59,9 +59,9 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Equate date types.")] [DataRow("datetime_types eq 2023-01-24T12:51:59Z", "([datetime_types] = @param1)", DisplayName = "Equate datetime types.")] - [DataRow("datetime2_types eq 9999-12-31T21:59:59.99999Z", "([datetime2_types] = @param1)", + [DataRow("datetime2_types eq 9998-12-31T21:59:59.99999Z", "([datetime2_types] = @param1)", DisplayName = "Equate datetime2 types.")] - [DataRow("datetimeoffset_types eq 9999-12-31T21:59:59.99999-14:00", + [DataRow("datetimeoffset_types eq 9998-12-31T21:59:59.99999-14:00", "([datetimeoffset_types] = @param1)", DisplayName = "Equate datetimeoffset types.")] [DataRow("smalldatetime_types eq 2079-06-06", "([smalldatetime_types] = @param1)", DisplayName = "Equate smalldatetime types.")] From bdb5fcceb9babec4d2b1c940085179c6b9c91e23 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jul 2023 17:50:18 +0530 Subject: [PATCH 38/85] fixing mysql tests --- .../GraphQLSupportedTypesTestsBase.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 44ad7aa5e1..473d9c68a0 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -197,6 +197,13 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "=")] public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { + // In MySQL, the DATETIME data type supports a range from '1000-01-01 00:00:00.0000000' to '9999-12-31 23:59:59.0000000' + if (DatabaseEngine is TestCategory.MYSQL && sqlValue is "\'9999-12-31 23:59:59.9999999\'") + { + sqlValue = "\'9999-12-31 23:59:59.0000000\'"; + gqlValue = "\"9999-12-31 23:59:59.0000000\""; + } + await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); } From a38db0ec6b41e3e363e6592a1e2d9e85c92cc788 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 27 Jul 2023 23:59:31 +0530 Subject: [PATCH 39/85] adding more tests --- .../GraphQLSupportedTypesTestsBase.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 473d9c68a0..9b3b7e9e00 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -289,6 +289,9 @@ public async Task InsertIntoTypeColumn(string type, string value) [DataTestMethod] [DataRow(TIME_TYPE, "\"32:59:59.9999999\"")] [DataRow(TIME_TYPE, "\"22:67:59.9999999\"")] + [DataRow(TIME_TYPE, "\"-22:67:59.9999999\"")] + [DataRow(TIME_TYPE, "\"22:-67:59.9999999\"")] + [DataRow(TIME_TYPE, "\"22:67:59.-9999999\"")] public async Task InsertInvalidIntoTypeColumn(string type, string value) { if (!IsSupportedType(type)) From dec4d154f8fdd060528b965a58c8a82b0884623e Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 31 Jul 2023 14:25:02 +0530 Subject: [PATCH 40/85] nit fixes --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 4 ++-- src/Core/Services/TypeHelper.cs | 2 -- .../GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 2cf9ca3ff8..b3c89b9004 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -361,8 +361,8 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTime.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), - "DateTimeOffset" => DateTimeOffset.Parse(param), + "DateTime" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), + "DateTimeOffset" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "Date" => DateOnly.Parse(param), "Guid" => Guid.Parse(param), "TimeOnly" => TimeOnly.Parse(param), diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index cf31bb2434..7abc229a34 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -36,8 +36,6 @@ public static class TypeHelper [typeof(char)] = DbType.StringFixedLength, [typeof(Guid)] = DbType.Guid, [typeof(byte[])] = DbType.Binary, - [typeof(DateTime)] = DbType.DateTime, - [typeof(DateTimeOffset)] = DbType.DateTimeOffset, [typeof(TimeOnly)] = DbType.Time, [typeof(TimeSpan)] = DbType.Time, [typeof(object)] = DbType.Object diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index a05c8d9829..1a958ce783 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -43,13 +43,13 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Test after token for decimal values.")] [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] - [DataRow("date_types", "\"1753-01-01\"", + [DataRow("date_types", "\"0001-01-01\"", "\"9999-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", "\"9999-12-31T23:59:59\"", 3, 4, DisplayName = "Test after token for datetime values.")] - [DataRow("datetime2_types", "\"1753-01-01 00:00:00.0000000\"", + [DataRow("datetime2_types", "\"0001-01-01 00:00:00.0000000\"", "\"9999-12-31T23:59:59.9999999\"", 3, 4, DisplayName = "Test after token for datetime2 values.")] [DataRow("datetimeoffset_types", "\"0001-01-01 00:00:00.0000000+0:00\"", From 127af62b2f234121b9f657ccfba21d460d94da02 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 31 Jul 2023 14:29:42 +0530 Subject: [PATCH 41/85] nit fix --- src/Service/Azure.DataApiBuilder.Service.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Service/Azure.DataApiBuilder.Service.csproj b/src/Service/Azure.DataApiBuilder.Service.csproj index 156dcc7436..8338c6c059 100644 --- a/src/Service/Azure.DataApiBuilder.Service.csproj +++ b/src/Service/Azure.DataApiBuilder.Service.csproj @@ -51,7 +51,6 @@ - From 7ff72b889ecd789c4783f608b551b4c61c312f37 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 31 Jul 2023 14:52:51 +0530 Subject: [PATCH 42/85] nit fix --- .../OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs index 3dd726f952..747b2649d0 100644 --- a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs +++ b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs @@ -101,8 +101,6 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa [DataRow(typeof(Guid?))] [DataRow(typeof(TimeOnly?))] [DataRow(typeof(TimeSpan?))] - [DataRow(typeof(DateTime?))] - [DataRow(typeof(DateTimeOffset?))] [DataTestMethod] public void ResolveUnderlyingTypeForNullableValueType(Type nullableType) { From 2b55cd416d1c400c185d50645c94f0ce1eacacce Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 31 Jul 2023 22:07:41 +0530 Subject: [PATCH 43/85] updating test --- .../GraphQLSupportedTypesTestsBase.cs | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 9b3b7e9e00..cf70d269f5 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -189,6 +189,8 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "=")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=")] [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=")] + [DataRow(DATETIMEOFFSET_TYPE, "neq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=")] + [DataRow(DATETIMEOFFSET_TYPE, "eq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=")] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:22:00\'", "\"1999-01-08 10:22:00\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", " >= ")] [DataRow(DATETIME_TYPE, "lt", "\'2079-06-06\'", "\"2079-06-06\"", " < ")] @@ -254,6 +256,7 @@ public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string f [DataRow(BOOLEAN_TYPE, "false")] [DataRow(BOOLEAN_TYPE, "null")] [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54.671287+8:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] [DataRow(DATETIME_TYPE, "null")] @@ -286,9 +289,13 @@ public async Task InsertIntoTypeColumn(string type, string value) await ResetDbStateAsync(); } + /// + /// Test case for invalid time, such as negative values or hours>24 or minutes/seconds>60. + /// [DataTestMethod] [DataRow(TIME_TYPE, "\"32:59:59.9999999\"")] [DataRow(TIME_TYPE, "\"22:67:59.9999999\"")] + [DataRow(TIME_TYPE, "\"14:12:99.9999999\"")] [DataRow(TIME_TYPE, "\"-22:67:59.9999999\"")] [DataRow(TIME_TYPE, "\"22:-67:59.9999999\"")] [DataRow(TIME_TYPE, "\"22:67:59.-9999999\"")] @@ -306,7 +313,6 @@ public async Task InsertInvalidIntoTypeColumn(string type, string value) JsonElement response = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: true); string responseMessage = Regex.Unescape(JsonSerializer.Serialize(response)); Assert.IsTrue(responseMessage.Contains($"{value} cannot be resolved as column \"{field}\" with type \"TimeSpan\".")); - await ResetDbStateAsync(); } [DataTestMethod] @@ -555,7 +561,7 @@ private static void CompareDateTimeResults(string actual, string expected) } /// - /// Required due to different format between mysql datetimeoffset and HotChocolate datetime + /// Required due to different format between sql datetimeoffset and HotChocolate datetime /// result /// private static void CompareDateTimeOffsetResults(string actual, string expected) @@ -565,22 +571,32 @@ private static void CompareDateTimeOffsetResults(string actual, string expected) using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); - string actualDateTimeOffset = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); - string expectedDateTimeOffset = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + if (actualJsonDoc.RootElement.ValueKind is JsonValueKind.Array) + { + ValidateArrayResults(actualJsonDoc, expectedJsonDoc, fieldName); + return; + } + + string actualDateTimeOffsetString = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedDateTimeOffsetString = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); // handles cases when one of the values is null - if (string.IsNullOrEmpty(actualDateTimeOffset) || string.IsNullOrEmpty(expectedDateTimeOffset)) + if (string.IsNullOrEmpty(actualDateTimeOffsetString) || string.IsNullOrEmpty(expectedDateTimeOffsetString)) { - Assert.AreEqual(expectedDateTimeOffset, actualDateTimeOffset); + Assert.AreEqual(expectedDateTimeOffsetString, actualDateTimeOffsetString); } else { - Assert.AreEqual(DateTimeOffset.Parse(expectedDateTimeOffset), DateTimeOffset.Parse(actualDateTimeOffset)); + DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); + // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. + Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); } } /// - /// Required due to different format between SQL time and HotChocolate TimeSpan time(ISO-8601) result. + /// Compares the value from SQL time and HotChocolate LocalTime. /// private static void CompareTimeResults(string actual, string expected) { @@ -605,8 +621,8 @@ private static void CompareTimeResults(string actual, string expected) } else { - TimeOnly actualTime = TimeOnly.Parse(actualTimeString, CultureInfo.InvariantCulture); - TimeOnly expectedTime = TimeOnly.Parse(expectedTimeString, CultureInfo.InvariantCulture); + TimeOnly actualTime = TimeOnly.Parse(actualTimeString); + TimeOnly expectedTime = TimeOnly.Parse(expectedTimeString); Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); } } @@ -621,7 +637,15 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen actualElement.TryGetProperty(fieldName, out JsonElement actualValue); expectedElement.TryGetProperty(fieldName, out JsonElement expectedValue); - if (fieldName.StartsWith(DATETIME_TYPE.ToLower())) + if (fieldName.StartsWith(DATETIMEOFFSET_TYPE.ToLower())) + { + DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); + // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. + Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); + } + else if (fieldName.StartsWith(DATETIME_TYPE.ToLower())) { // MySql returns a format that will not directly parse into DateTime type so we use string here for parsing DateTime actualDateTime = DateTime.Parse(actualValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); @@ -634,8 +658,8 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen } else if (fieldName.StartsWith(TIME_TYPE.ToLower())) { - TimeOnly actualTime = TimeOnly.Parse(actualValue.ToString(), CultureInfo.InvariantCulture); - TimeOnly expectedTime = TimeOnly.Parse(expectedValue.ToString(), CultureInfo.InvariantCulture); + TimeOnly actualTime = TimeOnly.Parse(actualValue.ToString()); + TimeOnly expectedTime = TimeOnly.Parse(expectedValue.ToString()); Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); } else From a05134b6db74a00e3b6bf517df102daaeb57f250 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 1 Aug 2023 11:25:05 +0530 Subject: [PATCH 44/85] added OData tests as well --- src/Core/Parsers/ODataASTVisitor.cs | 2 ++ src/Core/Services/TypeHelper.cs | 1 - src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Core/Parsers/ODataASTVisitor.cs b/src/Core/Parsers/ODataASTVisitor.cs index 829602f677..901de39935 100644 --- a/src/Core/Parsers/ODataASTVisitor.cs +++ b/src/Core/Parsers/ODataASTVisitor.cs @@ -143,6 +143,8 @@ private static object GetParamWithSystemType(string param, IEdmTypeReference edm return DateTimeOffset.Parse(param); case EdmPrimitiveTypeKind.String: return param; + case EdmPrimitiveTypeKind.TimeOfDay: + return TimeOnly.Parse(param); default: // should never happen due to the config being validated for correct types throw new NotSupportedException($"{edmType} is not supported"); diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 7abc229a34..02ed06aee2 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -134,7 +134,6 @@ public static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type column "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, "Date" => EdmPrimitiveTypeKind.Date, - "TimeOnly" => EdmPrimitiveTypeKind.TimeOfDay, "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, _ => throw new ArgumentException($"Column type" + $" {columnSystemType.Name} not yet supported.") diff --git a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs index 69722a3825..d34737f0ca 100644 --- a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs +++ b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs @@ -67,6 +67,8 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Equate smalldatetime types.")] [DataRow("bytearray_types eq 1000", "([bytearray_types] = @param1)", DisplayName = "Equate bytearray types.")] [DataRow("guid_types eq 9A19103F-16F7-4668-BE54-9A1E7A4F7556", "([guid_types] = @param1)", DisplayName = "Equate guid types.")] + [DataRow("time_types eq 10:23:54.9999999", "([time_types] = @param1)", DisplayName = "Equate time types.")] + [DataRow("time_types eq null", "([time_types] IS NULL)", DisplayName = "Equate time types for null.")] [TestMethod] public void VisitorLeftFieldRightConstantFilterTest(string filterExp, string expectedPredicate) { From de2f48ad773686b4953f3d91d3c77fd548ef4266 Mon Sep 17 00:00:00 2001 From: abhishekkumams <102276754+abhishekkumams@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:14:24 +0530 Subject: [PATCH 45/85] Update src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs Co-authored-by: Sean Leonard --- .../GraphQLSupportedTypesTestsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index cf70d269f5..81ea5d4a2e 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -590,7 +590,7 @@ private static void CompareDateTimeOffsetResults(string actual, string expected) DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); - // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. + // Comparing for milliseconds separately since HotChocolate time type is resolved only to 3 decimal places. Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); } } From 9676493cf94a84a244da2b25e21b3fac9a23defa Mon Sep 17 00:00:00 2001 From: abhishekkumams <102276754+abhishekkumams@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:14:44 +0530 Subject: [PATCH 46/85] Update src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs Co-authored-by: Sean Leonard --- .../GraphQLSupportedTypesTestsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 81ea5d4a2e..5978ef9659 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -642,7 +642,7 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); - // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. + // Comparing for milliseconds separately since HotChocolate time type is resolved only to 3 decimal places. Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); } else if (fieldName.StartsWith(DATETIME_TYPE.ToLower())) From ecec68e5b41d60373ed869c6760f13dc57b579b6 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sat, 5 Aug 2023 00:06:21 +0530 Subject: [PATCH 47/85] fix formatting and adding tests --- src/Core/Services/TypeHelper.cs | 5 + .../CLRtoJsonValueTypeUnitTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 112 +++++++++++------- .../Unittests/ODataASTVisitorUnitTests.cs | 18 +++ 4 files changed, 94 insertions(+), 43 deletions(-) diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 02ed06aee2..8ad1feb178 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -17,6 +17,10 @@ public static class TypeHelper { /// /// Maps .NET Framework types to DbType enum + /// Not Adding this hard mapping for System.DateTime/DateTimeOffset, + /// since System.DateTime is used for both SqldbType.DateTime and SqldbType.DateTime2. + /// So no change required here as seperate logic exist which does the mapping + /// and also Hotchocolate only has Hotchocolate.Types.DateTime for SqlDbType.DateTime/DateTime2/DateTimeOffset. /// private static Dictionary _systemTypeToDbTypeMap = new() { @@ -134,6 +138,7 @@ public static EdmPrimitiveTypeKind GetEdmPrimitiveTypeFromSystemType(Type column "DateTime" => EdmPrimitiveTypeKind.DateTimeOffset, "DateTimeOffset" => EdmPrimitiveTypeKind.DateTimeOffset, "Date" => EdmPrimitiveTypeKind.Date, + "TimeOnly" => EdmPrimitiveTypeKind.TimeOfDay, "TimeSpan" => EdmPrimitiveTypeKind.TimeOfDay, _ => throw new ArgumentException($"Column type" + $" {columnSystemType.Name} not yet supported.") diff --git a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs index 747b2649d0..b5964a04d2 100644 --- a/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs +++ b/src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs @@ -76,7 +76,7 @@ private static IEnumerable GetTestData_SupportedSystemTypesMapToJsonVa /// Validates the behavior of TypeHelper.GetJsonDataTypeFromSystemType(Type type) by /// ensuring that a nullable value type like int? is resolved to its underlying type int. /// Consequently, the lookup in the _systemTypeToJsonDataTypeMap and _systemTypeToDbTypeMap - /// dictionary succeeds without requiring nullable value type be defined as keys. + /// dictionary succeeds without requiring nullable value types be defined as keys. /// Nullable value types are represented in runtime as Nullable. Whereas /// nullable reference types do no have a standalone runtime representation. /// See csharplang discussion on why typeof(string?) (nullable reference type) is not valid, diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index cf70d269f5..e03ffabe5e 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -167,6 +167,8 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper /// /// Separate test case for DateTime to allow overwrite for postgreSql. + /// The method constructs a GraphQL query to filter and order the datetime column based on the given parameters. + /// The test checks various datetime data types such as datetime, datetimeoffset, and time. /// [DataTestMethod] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08\'", "\"1999-01-08\"", " > ")] @@ -210,7 +212,7 @@ public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string fi } /// - /// Test case for LocalTime filters. + /// Validates that usage of LocalTime values with comparison operators in GraphQL filters results in the expected filtered result set. /// [DataTestMethod] [DataRow(TIME_TYPE, "gt", "\'00:00:00.000\'", "\"00:00:00.000\"", " > ")] @@ -224,6 +226,33 @@ public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string f await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); } + /// + /// Validates that LocalTime values with X precision are handled correctly: precision of 7 decimal places used with eq (=) will + /// not return result with only 3 decimal places i.e. 10:23:54.999 != 10:23:54.9999999 + /// In the Database only one row exist with value 23:59:59.9999999 + /// + [DataTestMethod] + [DataRow("\"23:59:59.9999999\"", 1, DisplayName = "TimeType Precision Check with 7 decimal places")] + [DataRow("\"23:59:59.999\"", 0, DisplayName = "TimeType Precision Check with 3 decimal places")] + public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) + { + string graphQLQueryName = "supportedTypes"; + string gqlQuery = @"{ + supportedTypes(first: 100 orderBy: { " + "time_types" + ": ASC } filter: { " + "time_types" + ": {" + "eq" + ": " + gqlValue + @"} }) { + items { + " + "time_types" + @" + } + } + }"; + + JsonElement gqlResponse = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: false); + Assert.AreEqual(count, gqlResponse.GetProperty("items").GetArrayLength()); + } + + /// + /// the method constructs a GraphQL query to insert the value into the database table + /// and then executes the query and compares the expected result with the actual result to verify if different types are supported. + /// [DataTestMethod] [DataRow(BYTE_TYPE, "255")] [DataRow(BYTE_TYPE, "0")] @@ -553,10 +582,7 @@ private static void CompareDateTimeResults(string actual, string expected) } else { - // Adjusting to universal, since DateTime doesn't account for TimeZone - DateTime expectedDateTimeUniversal = DateTime.Parse(expectedDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - DateTime actualDateTimeUniversal = DateTime.Parse(actualDateTime, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - Assert.AreEqual(expectedDateTimeUniversal, actualDateTimeUniversal); + AssertOnFields(fieldName, actualDateTime, expectedDateTime); } } @@ -587,11 +613,7 @@ private static void CompareDateTimeOffsetResults(string actual, string expected) } else { - DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); - DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedDateTimeOffsetString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); - Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); - // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. - Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); + AssertOnFields(fieldName, actualDateTimeOffsetString, expectedDateTimeOffsetString); } } @@ -621,9 +643,7 @@ private static void CompareTimeResults(string actual, string expected) } else { - TimeOnly actualTime = TimeOnly.Parse(actualTimeString); - TimeOnly expectedTime = TimeOnly.Parse(expectedTimeString); - Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); + AssertOnFields(fieldName, actualTimeString, expectedTimeString); } } @@ -637,35 +657,43 @@ private static void ValidateArrayResults(JsonDocument actualJsonDoc, JsonDocumen actualElement.TryGetProperty(fieldName, out JsonElement actualValue); expectedElement.TryGetProperty(fieldName, out JsonElement expectedValue); - if (fieldName.StartsWith(DATETIMEOFFSET_TYPE.ToLower())) - { - DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); - DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedValue.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); - Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); - // Comparing for Miliseconds separetly since Hotcholate respond with only 3 decimal places. - Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); - } - else if (fieldName.StartsWith(DATETIME_TYPE.ToLower())) - { - // MySql returns a format that will not directly parse into DateTime type so we use string here for parsing - DateTime actualDateTime = DateTime.Parse(actualValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - DateTime expectedDateTime = DateTime.Parse(expectedValue.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); - Assert.AreEqual(expectedDateTime, actualDateTime); - } - else if (fieldName.StartsWith(SINGLE_TYPE.ToLower())) - { - Assert.AreEqual(expectedValue.GetSingle(), actualValue.GetSingle()); - } - else if (fieldName.StartsWith(TIME_TYPE.ToLower())) - { - TimeOnly actualTime = TimeOnly.Parse(actualValue.ToString()); - TimeOnly expectedTime = TimeOnly.Parse(expectedValue.ToString()); - Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); - } - else - { - Assert.AreEqual(expectedValue.GetDouble(), actualValue.GetDouble()); - } + AssertOnFields(fieldName, actualValue.ToString(), expectedValue.ToString()); + } + } + + /// + /// Compare given fields from actual and expected json. + /// + private static void AssertOnFields(string field, string actualElement, string expectedElement) + { + if (field.StartsWith(DATETIMEOFFSET_TYPE.ToLower())) + { + DateTimeOffset actualDateTimeOffset = DateTimeOffset.Parse(actualElement.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + DateTimeOffset expectedDateTimeOffset = DateTimeOffset.Parse(expectedElement.ToString(), DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal); + Assert.AreEqual(actualDateTimeOffset.ToString(), expectedDateTimeOffset.ToString()); + // Comparing for milliseconds separately since HotChocolate time type is resolved only to 3 decimal places. + Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); + } + else if (field.StartsWith(DATETIME_TYPE.ToLower())) + { + // Adjusting to universal, since DateTime doesn't account for TimeZone + DateTime actualDateTime = DateTime.Parse(actualElement.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + DateTime expectedDateTime = DateTime.Parse(expectedElement.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + Assert.AreEqual(expectedDateTime, actualDateTime); + } + else if (field.StartsWith(SINGLE_TYPE.ToLower())) + { + Assert.AreEqual(float.Parse(expectedElement), float.Parse(actualElement)); + } + else if (field.StartsWith(TIME_TYPE.ToLower())) + { + TimeOnly actualTime = TimeOnly.Parse(actualElement.ToString()); + TimeOnly expectedTime = TimeOnly.Parse(expectedElement.ToString()); + Assert.AreEqual(expectedTime.ToLongTimeString(), actualTime.ToLongTimeString()); + } + else + { + Assert.AreEqual(double.Parse(expectedElement), double.Parse(actualElement)); } } diff --git a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs index d34737f0ca..c440b5b572 100644 --- a/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs +++ b/src/Service.Tests/Unittests/ODataASTVisitorUnitTests.cs @@ -202,6 +202,24 @@ public void InvalidEdmTypeReferenceTest() Assert.ThrowsException(() => visitor.Visit(nodeIn)); } + /// + /// Tests that we throw an exception when trying to use an invalid + /// Time with negative value or time > 24 hours. + /// + [DataTestMethod] + [DataRow("time_types eq 25:23:54.9999999", DisplayName = "Exception thrown with invalid time>24 hrs.")] + [DataRow("time_types eq -13:23:54.9999999", DisplayName = "Exception thrown with invalid time>24 hrs.")] + public void InvalidTimeTypeODataFilterTest(string filterExp) + { + Assert.ThrowsException(() => PerformVisitorTest( + entityName: DEFAULT_ENTITY, + schemaName: DEFAULT_SCHEMA_NAME, + tableName: DEFAULT_TABLE_NAME, + filterString: $"?$filter={filterExp}", + expected: string.Empty + )); + } + /// /// Verifies that we throw an exception for values that can /// not be parsed into a valid Edm Type Kind. Create a constant From 30a1336c3396c1a02cf5ff3c758b60914744ccca Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sat, 5 Aug 2023 00:25:57 +0530 Subject: [PATCH 48/85] fixing tests --- .../GraphQLSupportedTypesTestsBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index e03ffabe5e..ac151a37a7 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -236,6 +236,11 @@ public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string f [DataRow("\"23:59:59.999\"", 0, DisplayName = "TimeType Precision Check with 3 decimal places")] public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) { + if (!IsSupportedType(TIME_TYPE)) + { + Assert.Inconclusive("Type not supported"); + } + string graphQLQueryName = "supportedTypes"; string gqlQuery = @"{ supportedTypes(first: 100 orderBy: { " + "time_types" + ": ASC } filter: { " + "time_types" + ": {" + "eq" + ": " + gqlValue + @"} }) { From f126b8362bfc9070a83ae708286ad5e6bec20ae4 Mon Sep 17 00:00:00 2001 From: abhishekkumams <102276754+abhishekkumams@users.noreply.github.com> Date: Mon, 14 Aug 2023 22:39:21 +0530 Subject: [PATCH 49/85] Update src/Core/Services/TypeHelper.cs Co-authored-by: Aniruddh Munde --- src/Core/Services/TypeHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 8ad1feb178..9e4c5dd456 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -19,7 +19,7 @@ public static class TypeHelper /// Maps .NET Framework types to DbType enum /// Not Adding this hard mapping for System.DateTime/DateTimeOffset, /// since System.DateTime is used for both SqldbType.DateTime and SqldbType.DateTime2. - /// So no change required here as seperate logic exist which does the mapping + /// So no change required here as separate logic exist which does the mapping /// and also Hotchocolate only has Hotchocolate.Types.DateTime for SqlDbType.DateTime/DateTime2/DateTimeOffset. /// private static Dictionary _systemTypeToDbTypeMap = new() From bd462fa9a8caea775fcd520e2b8f74ed62fc33a9 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 16 Aug 2023 19:49:33 +0530 Subject: [PATCH 50/85] Handling datetime types --- .../BaseSqlQueryStructure.cs | 2 +- .../Sql Query Structures/SqlQueryStructure.cs | 2 +- .../MsSqlMetadataProvider.cs | 9 ++++++ .../MetadataProviders/SqlMetadataProvider.cs | 30 +++++++++++++++---- .../MsSqlGraphQLPaginationTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 8 ++--- 6 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 30bcd60710..370338b87f 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -360,7 +360,7 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTimeOffset.Parse(param), + "DateTime" => DateTime.Parse(param), "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), "Guid" => Guid.Parse(param), diff --git a/src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs index 9174202dec..66a28d4c2c 100644 --- a/src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -475,7 +475,7 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues { column.TableAlias = SourceAlias; column.ParamName = column.Value is not null ? - MakeDbConnectionParam(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : + MakeDbConnectionParam(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName)), column.ColumnName) : MakeDbConnectionParam(null, column.ColumnName); } diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 15f080a783..aa30d04e43 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Data; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Resolvers; using Microsoft.Data.SqlClient; @@ -26,6 +27,8 @@ public MsSqlMetadataProvider( { } + public static readonly HashSet DateTimeTypes = new() { "datetime", "smalldatetime", "datetime2", "datetimeoffset" }; + public override string GetDefaultSchemaName() { return "dbo"; @@ -40,5 +43,11 @@ public override Type SqlToCLRType(string sqlType) { return TypeHelper.GetSystemTypeFromSqlDbType(sqlType); } + + /// + public override DbType DatetimeSqlToDbype(string sqlType) + { + return TypeHelper.GetDbTypeFromSqlDbDateTimeType(sqlType); + } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index fb4dcef251..5c89207201 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -349,6 +349,11 @@ private async Task FillSchemaForStoredProcedureAsync( /// public abstract Type SqlToCLRType(string sqlType); + /// + /// Takes a string version of a sql data type and returns its corresponding DbType. + /// + public abstract DbType DatetimeSqlToDbype(string sqlType); + /// /// Generates the map used to find a given entity based /// on the path that will be used for that entity. @@ -985,8 +990,7 @@ private async Task PopulateSourceDefinitionAsync( { IsNullable = (bool)columnInfoFromAdapter["AllowDBNull"], IsAutoGenerated = (bool)columnInfoFromAdapter["IsAutoIncrement"], - SystemType = systemTypeOfColumn, - DbType = TypeHelper.GetDbTypeFromSystemType(systemTypeOfColumn) + SystemType = systemTypeOfColumn }; // Tests may try to add the same column simultaneously @@ -998,7 +1002,7 @@ private async Task PopulateSourceDefinitionAsync( DataTable columnsInTable = await GetColumnsAsync(schemaName, tableName); - PopulateColumnDefinitionWithHasDefault( + PopulateColumnDefinitionWithHasDefaultAndDbType( sourceDefinition, columnsInTable); } @@ -1210,9 +1214,9 @@ protected virtual async Task GetColumnsAsync( } /// - /// Populates the column definition with HasDefault property. + /// Helper method to populate the column definition with HasDefault and DbType properties. /// - private static void PopulateColumnDefinitionWithHasDefault( + private void PopulateColumnDefinitionWithHasDefaultAndDbType( SourceDefinition sourceDefinition, DataTable allColumnsInTable) { @@ -1229,6 +1233,22 @@ private static void PopulateColumnDefinitionWithHasDefault( { columnDefinition.DefaultValue = columnInfo["COLUMN_DEFAULT"]; } + + columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); + + if (GetDatabaseType() is DatabaseType.MSSQL) + { + string sqlDataType = ((string)columnInfo["DATA_TYPE"]).ToLower(); + sqlDataType = sqlDataType.ToLower(); + if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) + { + // For MsSql, all the date time types i.e. smalldatetime, datetime, datetime2 map to System.DateTime system type. + // Hence we cannot directly determine the DbType from the system type. + // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly + // populate the DbTypes. + columnDefinition.DbType = DatetimeSqlToDbype(sqlDataType); + } + } } } } diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index 6e1353d640..153f179845 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -43,7 +43,7 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Test after token for decimal values.")] [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] - [DataRow("date_types", "\"0001-01-01\"", + [DataRow("date_types", "\"1753-01-01\"", "\"9998-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 7936566f72..51719d079a 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -167,10 +167,10 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataTestMethod] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08\'", "\"1999-01-08\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08\'", "\"1999-01-08\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'0001-01-01\'", "\"0001-01-01\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'0001-01-01\'", "\"0001-01-01\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'0001-01-01\'", "\"0001-01-01\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'0001-01-01\'", "\"0001-01-01T01:01:01\"", "=")] + [DataRow(DATETIME_TYPE, "lt", "\'1753-01-01\'", "\"1753-01-01\"", " < ")] + [DataRow(DATETIME_TYPE, "lte", "\'1753-01-01\'", "\"1753-01-01\"", " <= ")] + [DataRow(DATETIME_TYPE, "neq", "\'1753-01-01\'", "\"1753-01-01\"", "!=")] + [DataRow(DATETIME_TYPE, "eq", "\'1753-01-01\'", "\"1753-01-01\"", "=")] [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " > ")] [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " >= ")] [DataRow(DATETIME_TYPE, "lt", "\'9998-12-31 23:59:59\'", "\"9998-12-31 23:59:59\"", " < ")] From c8495915072b18cd3c23916d7b57c47ac169d1ad Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 16 Aug 2023 19:54:20 +0530 Subject: [PATCH 51/85] uncommitted changes --- .../MySqlMetadataProvider.cs | 5 ++++ .../PostgreSqlMetadataProvider.cs | 6 +++++ src/Core/Services/TypeHelper.cs | 27 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index c29f1e821f..a497be3ad5 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -117,5 +117,10 @@ public override Type SqlToCLRType(string sqlType) { throw new NotImplementedException(); } + + public override DbType DatetimeSqlToDbype(string sqlType) + { + throw new NotImplementedException(); + } } } diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index e4ebc6e919..0d7d33124d 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Data; using System.Net; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Resolvers; @@ -72,5 +73,10 @@ public override Type SqlToCLRType(string sqlType) { throw new NotImplementedException(); } + + public override DbType DatetimeSqlToDbype(string sqlType) + { + throw new NotImplementedException(); + } } } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 7c41909748..2bfacae7d4 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -116,6 +116,14 @@ public static class TypeHelper [SqlDbType.VarChar] = typeof(string) }; + private static Dictionary _timeSqlDbTypeToDbType = new() + { + ["datetime"] = DbType.DateTime, + ["smalldatetime"] = DbType.DateTime, + ["datetime2"] = DbType.DateTime2, + ["datetimeoffset"] = DbType.DateTimeOffset + }; + /// /// Converts the .NET Framework (System/CLR) type to JsonDataType. /// Primitive data types in the OpenAPI standard (OAS) are based on the types supported @@ -189,5 +197,24 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) statusCode: HttpStatusCode.ServiceUnavailable, subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError); } + + /// + /// Helper method to find the DbType for the given datetime data types in Sql Server. + /// + /// Data type of the datetime column in Sql Server. + /// Corresponding DbType for the column. + /// + public static DbType GetDbTypeFromSqlDbDateTimeType(string sqlDbTypeName) + { + if (_timeSqlDbTypeToDbType.TryGetValue(sqlDbTypeName, out DbType value)) + { + return value; + } + + throw new DataApiBuilderException( + message: $"Unexpected datetime data type: {sqlDbTypeName} encountered.", + statusCode: HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError); + } } } From 086968919324a49fea348912600a0f5a4b6763f2 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 16 Aug 2023 20:14:45 +0530 Subject: [PATCH 52/85] handling datetime --- .../MsSqlMetadataProvider.cs | 2 +- .../MySqlMetadataProvider.cs | 2 +- .../PostgreSqlMetadataProvider.cs | 2 +- .../MetadataProviders/SqlMetadataProvider.cs | 30 +++++++++++++------ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index aa30d04e43..8da36f55c6 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -45,7 +45,7 @@ public override Type SqlToCLRType(string sqlType) } /// - public override DbType DatetimeSqlToDbype(string sqlType) + public override DbType DatetimeSqlTypeToDbype(string sqlType) { return TypeHelper.GetDbTypeFromSqlDbDateTimeType(sqlType); } diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index a497be3ad5..5d926f814d 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -118,7 +118,7 @@ public override Type SqlToCLRType(string sqlType) throw new NotImplementedException(); } - public override DbType DatetimeSqlToDbype(string sqlType) + public override DbType DatetimeSqlTypeToDbype(string sqlType) { throw new NotImplementedException(); } diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index 0d7d33124d..1a1b5b1ca7 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -74,7 +74,7 @@ public override Type SqlToCLRType(string sqlType) throw new NotImplementedException(); } - public override DbType DatetimeSqlToDbype(string sqlType) + public override DbType DatetimeSqlTypeToDbype(string sqlType) { throw new NotImplementedException(); } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 5c89207201..d8d2835ace 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -307,14 +307,27 @@ private async Task FillSchemaForStoredProcedureAsync( // row["DATA_TYPE"] has value type string so a direct cast to System.Type is not supported. // See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings Type systemType = SqlToCLRType((string)row["DATA_TYPE"]); - // Add to parameters dictionary without the leading @ sign - storedProcedureDefinition.Parameters.TryAdd(((string)row["PARAMETER_NAME"])[1..], - new() + ParameterDefinition paramDefinition = new() + { + SystemType = systemType, + DbType = TypeHelper.GetDbTypeFromSystemType(systemType) + }; + + if (GetDatabaseType() is DatabaseType.MSSQL) + { + string sqlDataType = ((string)row["DATA_TYPE"]).ToLower(); + if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) { - SystemType = systemType, - DbType = TypeHelper.GetDbTypeFromSystemType(systemType) + // For MsSql, all the date time types i.e. smalldatetime, datetime, datetime2 map to System.DateTime system type. + // Hence we cannot directly determine the DbType from the system type. + // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly + // populate the DbTypes. + paramDefinition.DbType = DatetimeSqlTypeToDbype(sqlDataType); } - ); + } + + // Add to parameters dictionary without the leading @ sign + storedProcedureDefinition.Parameters.TryAdd(((string)row["PARAMETER_NAME"])[1..], paramDefinition); } // Loop through parameters specified in config, throw error if not found in schema @@ -352,7 +365,7 @@ private async Task FillSchemaForStoredProcedureAsync( /// /// Takes a string version of a sql data type and returns its corresponding DbType. /// - public abstract DbType DatetimeSqlToDbype(string sqlType); + public abstract DbType DatetimeSqlTypeToDbype(string sqlType); /// /// Generates the map used to find a given entity based @@ -1239,14 +1252,13 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( if (GetDatabaseType() is DatabaseType.MSSQL) { string sqlDataType = ((string)columnInfo["DATA_TYPE"]).ToLower(); - sqlDataType = sqlDataType.ToLower(); if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) { // For MsSql, all the date time types i.e. smalldatetime, datetime, datetime2 map to System.DateTime system type. // Hence we cannot directly determine the DbType from the system type. // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly // populate the DbTypes. - columnDefinition.DbType = DatetimeSqlToDbype(sqlDataType); + columnDefinition.DbType = DatetimeSqlTypeToDbype(sqlDataType); } } } From a4fbfedc54491399e63503c9d99ddabfa83d7206 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 16 Aug 2023 22:29:50 +0530 Subject: [PATCH 53/85] reverting a change --- src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs | 2 +- src/Core/Services/MetadataProviders/SqlMetadataProvider.cs | 2 +- src/Core/Services/TypeHelper.cs | 1 + .../GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 8da36f55c6..207a6e95d5 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -27,7 +27,7 @@ public MsSqlMetadataProvider( { } - public static readonly HashSet DateTimeTypes = new() { "datetime", "smalldatetime", "datetime2", "datetimeoffset" }; + public static readonly HashSet DateTimeTypes = new() { "date", "smalldatetime", "datetime", "datetime2", "datetimeoffset" }; public override string GetDefaultSchemaName() { diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index d8d2835ace..63b47f01b1 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -1254,7 +1254,7 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( string sqlDataType = ((string)columnInfo["DATA_TYPE"]).ToLower(); if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) { - // For MsSql, all the date time types i.e. smalldatetime, datetime, datetime2 map to System.DateTime system type. + // For MsSql, all the date time types i.e. date, smalldatetime, datetime, datetime2 map to System.DateTime system type. // Hence we cannot directly determine the DbType from the system type. // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly // populate the DbTypes. diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 2bfacae7d4..d202d5a20c 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -118,6 +118,7 @@ public static class TypeHelper private static Dictionary _timeSqlDbTypeToDbType = new() { + ["date"] = DbType.Date, ["datetime"] = DbType.DateTime, ["smalldatetime"] = DbType.DateTime, ["datetime2"] = DbType.DateTime2, diff --git a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs index 153f179845..6e1353d640 100644 --- a/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLPaginationTests/MsSqlGraphQLPaginationTests.cs @@ -43,7 +43,7 @@ public static async Task SetupAsync(TestContext context) DisplayName = "Test after token for decimal values.")] [DataRow("boolean_types", "false", "true", 2, 4, DisplayName = "Test after token for boolean values.")] - [DataRow("date_types", "\"1753-01-01\"", + [DataRow("date_types", "\"0001-01-01\"", "\"9998-12-31\"", 3, 4, DisplayName = "Test after token for date values.")] [DataRow("datetime_types", "\"1753-01-01T00:00:00.000\"", From bc5abb4719b60295d47631cd1008fcca6f4ec588 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 17 Aug 2023 02:34:40 +0530 Subject: [PATCH 54/85] tests for datetime2 --- .../GraphQLTypes/SupportedTypes.cs | 1 + .../GraphQLSupportedTypesTestsBase.cs | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index 4470f8e2d1..d78aa83e02 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -18,6 +18,7 @@ public static class SupportedTypes public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; public const string DATETIME_TYPE = "DateTime"; + public const string DATETIME2_TYPE = "DateTime2"; public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string BYTEARRAY_TYPE = "ByteArray"; public const string GUID_TYPE = "Guid"; diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 4969739104..14f032ecbf 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -199,6 +199,11 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataRow(DATETIME_TYPE, "lte", "\'2079-06-06\'", "\"2079-06-06\"", " <= ")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "!=")] [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "=")] + [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ")] + [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ")] + [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ")] + [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ")] + [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=")] public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { // In MySQL, the DATETIME data type supports a range from '1000-01-01 00:00:00.0000000' to '9999-12-31 23:59:59.0000000' @@ -502,6 +507,10 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect { CompareDateTimeOffsetResults(actual.ToString(), expected); } + else if(type == DATETIME2_TYPE) + { + CompareDateTime2Results(actual.ToString(), expected); + } else if (type == TIME_TYPE) { CompareTimeResults(actual.ToString(), expected); @@ -591,6 +600,37 @@ private static void CompareDateTimeResults(string actual, string expected) } } + /// + /// Required due to different format between mysql datetime and HotChocolate datetime + /// result + /// + private static void CompareDateTime2Results(string actual, string expected) + { + string fieldName = "datetime2_types"; + + using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); + using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); + + if (actualJsonDoc.RootElement.ValueKind is JsonValueKind.Array) + { + ValidateArrayResults(actualJsonDoc, expectedJsonDoc, fieldName); + return; + } + + string actualDateTime2 = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); + string expectedDateTime2 = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); + + // handles cases when one of the values is null + if (string.IsNullOrEmpty(actualDateTime2) || string.IsNullOrEmpty(expectedDateTime2)) + { + Assert.AreEqual(expectedDateTime2, actualDateTime2); + } + else + { + AssertOnFields(fieldName, actualDateTime2, expectedDateTime2); + } + } + /// /// Required due to different format between sql datetimeoffset and HotChocolate datetime /// result @@ -679,6 +719,15 @@ private static void AssertOnFields(string field, string actualElement, string ex // Comparing for milliseconds separately since HotChocolate time type is resolved only to 3 decimal places. Assert.AreEqual(actualDateTimeOffset.Millisecond, expectedDateTimeOffset.Millisecond); } + else if (field.StartsWith(DATETIME2_TYPE.ToLower())) + { + // Adjusting to universal, since DateTime doesn't account for TimeZone + DateTime actualDateTime = DateTime.Parse(actualElement.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + DateTime expectedDateTime = DateTime.Parse(expectedElement.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); + Assert.AreEqual(expectedDateTime.ToString(), actualDateTime.ToString()); + // Comparing for milliseconds separately since HotChocolate datetime2 type is resolved only to 3 decimal places. + Assert.AreEqual(expectedDateTime.Millisecond, actualDateTime.Millisecond); + } else if (field.StartsWith(DATETIME_TYPE.ToLower())) { // Adjusting to universal, since DateTime doesn't account for TimeZone From 5b4a99c91cb4e6391c3802d65621ad2faeff7d47 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 17 Aug 2023 02:57:33 +0530 Subject: [PATCH 55/85] Moving datetime2 tests to Mssql file --- .../GraphQLSupportedTypesTestsBase.cs | 48 ++++++++----------- .../MsSqlGQLSupportedTypesTests.cs | 21 ++++++++ 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 14f032ecbf..0cfa21c565 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -199,11 +199,6 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper [DataRow(DATETIME_TYPE, "lte", "\'2079-06-06\'", "\"2079-06-06\"", " <= ")] [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "!=")] [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "=")] - [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ")] - [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ")] - [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ")] - [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ")] - [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=")] public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { // In MySQL, the DATETIME data type supports a range from '1000-01-01 00:00:00.0000000' to '9999-12-31 23:59:59.0000000' @@ -495,29 +490,28 @@ public async Task UpdateTypeColumnWithArgument(string type, object value) /// private static void PerformTestEqualsForExtendedTypes(string type, string expected, string actual) { - if (type == SINGLE_TYPE || type == FLOAT_TYPE || type == DECIMAL_TYPE) + switch (type) { - CompareFloatResults(type, actual.ToString(), expected); - } - else if (type == DATETIME_TYPE) - { - CompareDateTimeResults(actual.ToString(), expected); - } - else if (type == DATETIMEOFFSET_TYPE) - { - CompareDateTimeOffsetResults(actual.ToString(), expected); - } - else if(type == DATETIME2_TYPE) - { - CompareDateTime2Results(actual.ToString(), expected); - } - else if (type == TIME_TYPE) - { - CompareTimeResults(actual.ToString(), expected); - } - else - { - SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); + case SINGLE_TYPE: + case FLOAT_TYPE: + case DECIMAL_TYPE: + CompareFloatResults(type, actual.ToString(), expected); + break; + case TIME_TYPE: + CompareTimeResults(actual.ToString(), expected); + break; + case DATETIME_TYPE: + CompareDateTimeResults(actual.ToString(), expected); + break; + case DATETIME2_TYPE: + CompareDateTime2Results(actual.ToString(), expected); + break; + case DATETIMEOFFSET_TYPE: + CompareDateTimeOffsetResults(actual.ToString(), expected); + break; + default: + SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString()); + break; } } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs index 2efbaff76c..d7d1b4ef21 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { @@ -26,6 +27,26 @@ protected override string MakeQueryOnTypeTable(List columnsToQuery, int return MakeQueryOnTypeTable(columnsToQuery, filterValue: id.ToString(), filterField: "id"); } + /// + /// Test to validate functioning of GraphQL query with filter and orderby with datetime2 column based on the given parameters. + /// + [DataTestMethod] + [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ")] + [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ")] + [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ")] + [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ")] + [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=")] + public async Task QueryTypeColumnFilterAndOrderByDateTime2(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) + { + if (DatabaseEngine is TestCategory.MYSQL && sqlValue is "\'9999-12-31 23:59:59.9999999\'") + { + sqlValue = "\'9999-12-31 23:59:59.0000000\'"; + gqlValue = "\"9999-12-31 23:59:59.0000000\""; + } + + await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); + } + protected override string MakeQueryOnTypeTable( List queriedColumns, string filterValue = "1", From 6783bdd5a4631795d3223c093548db39c64e9df9 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 17 Aug 2023 17:21:09 +0530 Subject: [PATCH 56/85] removing unintended change --- .../GraphQLSupportedTypesTestsBase.cs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index b1857df3cd..616f92c5fa 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -349,34 +349,6 @@ public async Task InsertInvalidTimeIntoTimeTypeColumn(string type, string value) Assert.IsTrue(responseMessage.Contains($"{value} cannot be resolved as column \"{field}\" with type \"TimeSpan\".")); } - /// - /// The code contains test rows that are used to test the insertion of various data types into a database table using GraphQL, - /// where the parameter values are passed as GraphQL request Variables. The test supports various data types such as byte, short, - /// int, long, string, float, decimal, boolean, datetimeoffset, datetime, time, and bytearray. - /// - [DataTestMethod] - [DataRow(TIME_TYPE, "\"32:59:59.9999999\"")] - [DataRow(TIME_TYPE, "\"22:67:59.9999999\"")] - [DataRow(TIME_TYPE, "\"14:12:99.9999999\"")] - [DataRow(TIME_TYPE, "\"-22:67:59.9999999\"")] - [DataRow(TIME_TYPE, "\"22:-67:59.9999999\"")] - [DataRow(TIME_TYPE, "\"22:67:59.-9999999\"")] - public async Task InsertInvalidIntoTypeColumn(string type, string value) - { - if (!IsSupportedType(type)) - { - Assert.Inconclusive("Type not supported"); - } - - string field = $"{type.ToLowerInvariant()}_types"; - string graphQLQueryName = "createSupportedType"; - string gqlQuery = "mutation{ createSupportedType (item: {" + field + ": " + value + " }){ " + field + " } }"; - - JsonElement response = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: true); - string responseMessage = Regex.Unescape(JsonSerializer.Serialize(response)); - Assert.IsTrue(responseMessage.Contains($"{value} cannot be resolved as column \"{field}\" with type \"TimeSpan\".")); - } - [DataTestMethod] [DataRow(BYTE_TYPE, 255)] [DataRow(SHORT_TYPE, 30000)] From f8e184e27ba4bd18768f9c4c9a11a9c080a23c33 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 12:33:48 +0530 Subject: [PATCH 57/85] undoing changes from time pr --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 5fe8581b7a..d5d258514d 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -361,8 +361,8 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTime.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), - "DateTimeOffset" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), + "DateTime" => DateTime.Parse(param), + "DateTimeOffset" => DateTimeOffset.Parse(param), "Date" => DateOnly.Parse(param), "Guid" => Guid.Parse(param), "TimeOnly" => TimeOnly.Parse(param), From 976f4e72560b5c5f12b617ce27d5680fb4f324ad Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 12:55:38 +0530 Subject: [PATCH 58/85] build fix --- src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index d5d258514d..5a513994d2 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Data; -using System.Globalization; using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config.DatabasePrimitives; From 4de8f1ee55fa209793c49cf54b7c9821ccf35b57 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 15:05:33 +0530 Subject: [PATCH 59/85] filter tests for datetime types specific to msssql --- .../GraphQLTypes/SupportedTypes.cs | 2 + .../GraphQLFilterTests/MsSqlGQLFilterTests.cs | 51 +++++++++++++++++++ .../GraphQLSupportedTypesTestsBase.cs | 14 ++--- .../MsSqlGQLSupportedTypesTests.cs | 6 --- .../MySqlGQLSupportedTypesTests.cs | 3 ++ .../PostgreSqlGQLSupportedTypesTests.cs | 3 ++ 6 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index d78aa83e02..a51dd60830 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -17,6 +17,8 @@ public static class SupportedTypes public const string DECIMAL_TYPE = "Decimal"; public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; + public const string DATE_TYPE = "Date"; + public const string SMALLDATETIME_TYPE = "SmallDateTime"; public const string DATETIME_TYPE = "DateTime"; public const string DATETIME2_TYPE = "DateTime2"; public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs index 7a6b18ec20..0b8d8aabd9 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs @@ -3,9 +3,13 @@ using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using Azure.DataApiBuilder.Service.Exceptions; +using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries; +using Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLFilterTests { @@ -25,6 +29,53 @@ public static async Task Setup(TestContext context) await InitializeTestFixture(context); } + /// + /// Test to validate that queries involing filters on datetime types that are specific to MsSql work fine. + /// + /// Type of datetime field. + /// Value of the field. + /// Filter operator. + /// Name of filter operator. + [DataTestMethod] + [DataRow(DATE_TYPE, "1999-01-08", "=", "eq", DisplayName = "date type filter test with eq operator")] + [DataRow(DATE_TYPE, "1999-01-08", ">=", "gte", DisplayName = "date type filter test with gte operator")] + [DataRow(DATE_TYPE, "9998-12-31", "!=", "neq", DisplayName = "date type filter test with ne operator")] + [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", "=", "eq", DisplayName = "smalldatetime type filter test with eq operator")] + [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", ">=", "gte", DisplayName = "smalldatetime type filter test with gte operator")] + [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", "!=", "neq", DisplayName = "smalldatetime type filter test with neq operator")] + [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", "=", "eq", DisplayName = "datetime2 type filter test with eq operator")] + [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", ">=", "gte", DisplayName = "datetime2 type filter test with gte operator")] + [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", "!=", "neq", DisplayName = "datetime2 type filter test with neq operator")] + public async Task TestDateTimeFilters( + string fieldType, + string fieldValue, + string filterOperator, + string filterOperatorName + ) + { + string graphQLQueryName = "supportedTypes"; + string fieldName = $"{fieldType.ToLower()}_types"; + string gqlQuery = @"{ + supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + " : { " + $"{fieldName}" + @": {" + $"{filterOperatorName}" + @": """ + $"{fieldValue}" + @"""}}) + { + items { " + + $"{fieldName}" + + @" + } + } + }"; + + string dbQuery = MakeQueryOn( + "type_table", + new List { fieldName }, + $"{fieldName} {filterOperator} '{fieldValue}'", + GetDefaultSchema()); + + JsonElement actual = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: false); + string expected = await GetDatabaseResultAsync(dbQuery); + GraphQLSupportedTypesTestBase.PerformTestEqualsForExtendedTypes(fieldType, expected, actual.ToString()); + } + /// /// Test Nested Filter for Many-One relationship. /// Also validates authorization failure behavior when the referenced nested entity diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 616f92c5fa..43c4f681e6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -488,7 +488,7 @@ public async Task UpdateTypeColumnWithArgument(string type, object value) /// Utility function to do special comparisons for some of the extended types /// if json compare doesn't suffice /// - private static void PerformTestEqualsForExtendedTypes(string type, string expected, string actual) + public static void PerformTestEqualsForExtendedTypes(string type, string expected, string actual) { switch (type) { @@ -500,8 +500,10 @@ private static void PerformTestEqualsForExtendedTypes(string type, string expect case TIME_TYPE: CompareTimeResults(actual.ToString(), expected); break; + case DATE_TYPE: + case SMALLDATETIME_TYPE: case DATETIME_TYPE: - CompareDateTimeResults(actual.ToString(), expected); + CompareDateTimeResults(actual.ToString(), expected, type); break; case DATETIME2_TYPE: CompareDateTime2Results(actual.ToString(), expected); @@ -564,12 +566,12 @@ private static void CompareFloatResults(string floatType, string actual, string } /// - /// Required due to different format between mysql datetime and HotChocolate datetime + /// Required due to different format between sql datetime and HotChocolate datetime /// result /// - private static void CompareDateTimeResults(string actual, string expected) + private static void CompareDateTimeResults(string actual, string expected, string fieldType) { - string fieldName = "datetime_types"; + string fieldName = $"{fieldType.ToLower()}_types"; using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); @@ -722,7 +724,7 @@ private static void AssertOnFields(string field, string actualElement, string ex // Comparing for milliseconds separately since HotChocolate datetime2 type is resolved only to 3 decimal places. Assert.AreEqual(expectedDateTime.Millisecond, actualDateTime.Millisecond); } - else if (field.StartsWith(DATETIME_TYPE.ToLower())) + else if (field.StartsWith(DATE_TYPE.ToLower()) || field.StartsWith(SMALLDATETIME_TYPE.ToLower()) || field.StartsWith(DATETIME_TYPE.ToLower())) { // Adjusting to universal, since DateTime doesn't account for TimeZone DateTime actualDateTime = DateTime.Parse(actualElement.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs index d7d1b4ef21..9f7538617a 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs @@ -38,12 +38,6 @@ protected override string MakeQueryOnTypeTable(List columnsToQuery, int [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=")] public async Task QueryTypeColumnFilterAndOrderByDateTime2(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { - if (DatabaseEngine is TestCategory.MYSQL && sqlValue is "\'9999-12-31 23:59:59.9999999\'") - { - sqlValue = "\'9999-12-31 23:59:59.0000000\'"; - gqlValue = "\"9999-12-31 23:59:59.0000000\""; - } - await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index a0a6863fa6..86675c2acf 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -57,6 +57,9 @@ protected override bool IsSupportedType(string type) return type switch { GUID_TYPE => false, + DATE_TYPE => false, + SMALLDATETIME_TYPE => false, + DATETIME2_TYPE => false, DATETIMEOFFSET_TYPE => false, TIME_TYPE => false, _ => true diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index e409510b92..ba2c572035 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -54,6 +54,9 @@ protected override bool IsSupportedType(string type) return type switch { BYTE_TYPE => false, + DATE_TYPE => false, + SMALLDATETIME_TYPE => false, + DATETIME2_TYPE => false, DATETIMEOFFSET_TYPE => false, TIME_TYPE => false, _ => true From 5ab415ec1f442a8609e121430746dbbcb4d3613b Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 16:33:46 +0530 Subject: [PATCH 60/85] get by pk for REST --- config-generators/mssql-commands.txt | 1 + config-generators/mysql-commands.txt | 2 +- config-generators/postgresql-commands.txt | 1 + src/Service.Tests/DatabaseSchema-MsSql.sql | 1 + src/Service.Tests/DatabaseSchema-MySql.sql | 1 + src/Service.Tests/DatabaseSchema-PostgreSql.sql | 1 + .../GraphQLSupportedTypesTestsBase.cs | 2 -- .../SqlTests/RestApiTests/Find/FindApiTestBase.cs | 11 +++++++++-- .../SqlTests/RestApiTests/Find/MsSqlFindApiTests.cs | 5 +++++ .../SqlTests/RestApiTests/Find/MySqlFindApiTests.cs | 12 ++++++++++++ .../RestApiTests/Find/PostgreSqlFindApiTests.cs | 11 +++++++++++ .../SqlTests/RestApiTests/RestApiTestBase.cs | 2 ++ 12 files changed, 45 insertions(+), 5 deletions(-) diff --git a/config-generators/mssql-commands.txt b/config-generators/mssql-commands.txt index f00278a166..196effce3d 100644 --- a/config-generators/mssql-commands.txt +++ b/config-generators/mssql-commands.txt @@ -11,6 +11,7 @@ add Broker --config "dab-config.MsSql.json" --source brokers --permissions "anon add WebsiteUser --config "dab-config.MsSql.json" --source website_users --permissions "anonymous:create,read,delete,update" add SupportedType --config "dab-config.MsSql.json" --source type_table --permissions "anonymous:create,read,delete,update" add stocks_price --config "dab-config.MsSql.json" --source stocks_price --permissions "authenticated:create,read,update,delete" +update stocks_price --config "dab-config.MsSql.json" --permissions "anonymous:read" update stocks_price --config "dab-config.MsSql.json" --permissions "TestNestedFilterFieldIsNull_ColumnForbidden:read" --fields.exclude "price" update stocks_price --config "dab-config.MsSql.json" --permissions "TestNestedFilterFieldIsNull_EntityReadForbidden:create" add Tree --config "dab-config.MsSql.json" --source trees --permissions "anonymous:create,read,update,delete" diff --git a/config-generators/mysql-commands.txt b/config-generators/mysql-commands.txt index 8b88694ca6..eae738faba 100644 --- a/config-generators/mysql-commands.txt +++ b/config-generators/mysql-commands.txt @@ -87,7 +87,7 @@ update BookWebsitePlacement --config "dab-config.MySql.json" --permissions "auth update BookWebsitePlacement --config "dab-config.MySql.json" --permissions "authenticated:delete" --fields.include "*" --policy-database "@claims.userId eq @item.id" update Author --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true update WebsiteUser --config "dab-config.MySql.json" --permissions "authenticated:create,read,delete,update" --rest false --graphql "websiteUser:websiteUsers" -update stocks_price --config "dab-config.MySql.json" --permissions "authenticated:create,read,delete,update" --rest false +update stocks_price --config "dab-config.MySql.json" --permissions "authenticated:create,read,delete,update" --rest true update Comic --config "dab-config.MySql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true --relationship myseries --target.entity series --cardinality one update series --config "dab-config.MySql.json" --relationship comics --target.entity Comic --cardinality many update Broker --config "dab-config.MySql.json" --permissions "authenticated:create,update,read,delete" --graphql false diff --git a/config-generators/postgresql-commands.txt b/config-generators/postgresql-commands.txt index e955d03378..4674e44599 100644 --- a/config-generators/postgresql-commands.txt +++ b/config-generators/postgresql-commands.txt @@ -10,6 +10,7 @@ add Broker --config "dab-config.PostgreSql.json" --source brokers --permissions add WebsiteUser --config "dab-config.PostgreSql.json" --source website_users --permissions "anonymous:create,read,delete,update" add SupportedType --config "dab-config.PostgreSql.json" --source type_table --permissions "anonymous:create,read,delete,update" add stocks_price --config "dab-config.PostgreSql.json" --source stocks_price --permissions "authenticated:create,read,update,delete" +update stocks_price --config "dab-config.PostgreSql.json" --permissions "anonymous:read" update stocks_price --config "dab-config.PostgreSql.json" --permissions "TestNestedFilterFieldIsNull_ColumnForbidden:read" --fields.exclude "price" update stocks_price --config "dab-config.PostgreSql.json" --permissions "TestNestedFilterFieldIsNull_EntityReadForbidden:create" add Tree --config "dab-config.PostgreSql.json" --source trees --permissions "anonymous:create,read,update,delete" diff --git a/src/Service.Tests/DatabaseSchema-MsSql.sql b/src/Service.Tests/DatabaseSchema-MsSql.sql index 4768723a12..af744805b9 100644 --- a/src/Service.Tests/DatabaseSchema-MsSql.sql +++ b/src/Service.Tests/DatabaseSchema-MsSql.sql @@ -437,6 +437,7 @@ VALUES (1, 'Star Trek', 'SciFi', NULL), (2, 'Cinderella', 'Tales', 3001),(3,'Ún (5, 'Snow White', 'AnotherTales', 3001); INSERT INTO stocks(categoryid, pieceid, categoryName) VALUES (1, 1, 'SciFi'), (2, 1, 'Tales'),(0,1,''),(100, 99, 'Historical'); INSERT INTO stocks_price(categoryid, pieceid, price, is_wholesale_price) VALUES (2, 1, 100.57, 1), (1, 1, 42.75, 0), (100, 99, NULL, NULL); +INSERT INTO stocks_price(categoryid, pieceid, instant, price, is_wholesale_price) VALUES (2, 1, '2023-08-21 15:11:04', 100.57, 1); INSERT INTO trees(treeId, species, region, height) VALUES (1, 'Tsuga terophylla', 'Pacific Northwest', '30m'), (2, 'Pseudotsuga menziesii', 'Pacific Northwest', '40m'); INSERT INTO aow(NoteNum, DetailAssessmentAndPlanning, WagingWar, StrategicAttack) VALUES (1, 'chapter one notes: ', 'chapter two notes: ', 'chapter three notes: '); INSERT INTO fungi(speciesid, region) VALUES (1, 'northeast'), (2, 'southwest'); diff --git a/src/Service.Tests/DatabaseSchema-MySql.sql b/src/Service.Tests/DatabaseSchema-MySql.sql index ecc27255c1..ecee72e670 100644 --- a/src/Service.Tests/DatabaseSchema-MySql.sql +++ b/src/Service.Tests/DatabaseSchema-MySql.sql @@ -330,6 +330,7 @@ VALUES (1, 'Star Trek', 'SciFi', NULL), (2, 'Cinderella', 'Tales', 3001), (3,'Ú INSERT INTO stocks(categoryid, pieceid, categoryName) VALUES (1, 1, 'SciFi'), (2, 1, 'Tales'),(0,1,''),(100, 99, 'Historical'); INSERT INTO brokers(`ID Number`, `First Name`, `Last Name`) VALUES (1, 'Michael', 'Burry'), (2, 'Jordan', 'Belfort'); INSERT INTO stocks_price(categoryid, pieceid, price, is_wholesale_price) VALUES (2, 1, 100.57, true), (1, 1, 42.75, false); +INSERT INTO stocks_price(categoryid, pieceid, instant, price, is_wholesale_price) VALUES (2, 1, '2023-08-21 15:11:04', 100.57, 1); INSERT INTO type_table(id, byte_types, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, datetime_types, bytearray_types) VALUES (1, 1, 1, 1, 1, '', 0.33, 0.33, 0.333333, true, '1999-01-08 10:23:54', 0xABCDEF0123), (2, 0, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, false, '1999-01-08 10:23:00', 0x98AB7511AABB1234), diff --git a/src/Service.Tests/DatabaseSchema-PostgreSql.sql b/src/Service.Tests/DatabaseSchema-PostgreSql.sql index 2a01af8c79..fc80fcd0b7 100644 --- a/src/Service.Tests/DatabaseSchema-PostgreSql.sql +++ b/src/Service.Tests/DatabaseSchema-PostgreSql.sql @@ -322,6 +322,7 @@ INSERT INTO comics(id, title, "categoryName", series_id) VALUES (1, 'Star Trek', 'SciFi', NULL), (2, 'Cinderella', 'Tales', 3001),(3,'Únknown','', 3002), (4, 'Alexander the Great', 'Historical', NULL);INSERT INTO stocks(categoryid, pieceid, "categoryName") VALUES (1, 1, 'SciFi'), (2, 1, 'Tales'),(0,1,''),(100, 99, 'Historical'); INSERT INTO brokers("ID Number", "First Name", "Last Name") VALUES (1, 'Michael', 'Burry'), (2, 'Jordan', 'Belfort'); INSERT INTO stocks_price(categoryid, pieceid, price, is_wholesale_price) VALUES (2, 1, 100.57, True), (1, 1, 42.75, False); +INSERT INTO stocks_price(categoryid, pieceid, instant, price, is_wholesale_price) VALUES (2, 1, '2023-08-21 15:11:04', 100.57, True); INSERT INTO type_table(id, short_types, int_types, long_types, string_types, single_types, float_types, decimal_types, boolean_types, datetime_types, bytearray_types) VALUES (1, 1, 1, 1, '', 0.33, 0.33, 0.333333, true, '1999-01-08 10:23:54', '\xABCDEF0123'), (2, -1, -1, -1, 'lksa;jdflasdf;alsdflksdfkldj', -9.2, -9.2, -9.292929, false, '19990108 10:23:00', '\x98AB7511AABB1234'), diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 43c4f681e6..fee2699a81 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -16,8 +16,6 @@ namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests [TestClass] public abstract class GraphQLSupportedTypesTestBase : SqlTestBase { - protected const string TYPE_TABLE = "TypeTable"; - #region Tests [DataTestMethod] diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs index f12307b2d3..8c41620673 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs @@ -29,13 +29,20 @@ public abstract class FindApiTestBase : RestApiTestBase /// Tests the REST Api for FindById operation without a query string. /// [TestMethod] - public async Task FindByIdTest() + public async Task FindByPKTest() { await SetupAndRunRestApiTest( primaryKeyRoute: "id/2", queryString: string.Empty, entityNameOrPath: _integrationEntityName, - sqlQuery: GetQuery(nameof(FindByIdTest)) + sqlQuery: GetQuery("FindByIdTest") + ); + + await SetupAndRunRestApiTest( + primaryKeyRoute: "categoryid/2/pieceid/1/instant/2023-08-21T15:11:04", + queryString: string.Empty, + entityNameOrPath: _entityWithDateTimePK, + sqlQuery: GetQuery("FindByDateTimePKTest") ); } diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/MsSqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/MsSqlFindApiTests.cs index ea1bae1bb6..6f22959825 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/MsSqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/MsSqlFindApiTests.cs @@ -21,6 +21,11 @@ public class MsSqlFindApiTests : FindApiTestBase $"SELECT * FROM { _integrationTableName } " + $"WHERE id = 2 FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" }, + { + "FindByDateTimePKTest", + $"SELECT * FROM { _tableWithDateTimePK } " + + $"WHERE categoryid = 2 AND pieceid = 1 AND instant = '2023-08-21 15:11:04' FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER" + }, { "FindEmptyTable", $"SELECT * FROM { _emptyTableTableName } " + diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/MySqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/MySqlFindApiTests.cs index 8330943650..92272406c2 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/MySqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/MySqlFindApiTests.cs @@ -26,6 +26,18 @@ ORDER BY id asc LIMIT 1 ) AS subq" }, + { + "FindByDateTimePKTest", + @" + SELECT JSON_OBJECT('categoryid', categoryid, 'pieceid', pieceid, 'instant', instant, + 'price', price, 'is_wholesale_price', is_wholesale_price) AS data + FROM ( + SELECT categoryid, pieceid, instant, price, if(is_wholesale_price = 1, cast(TRUE as json), cast(FALSE as json)) AS is_wholesale_price + FROM " + _tableWithDateTimePK + @" + WHERE categoryid = 2 AND pieceid = 1 AND instant = '2023-08-21 15:11:04' + LIMIT 1 + ) AS subq" + }, { "FindEmptyTable", @" diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs index 48a4ce1944..e1a0dfa289 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs @@ -26,6 +26,17 @@ ORDER BY id asc LIMIT 1 ) AS subq" }, + { + "FindByDateTimePKTest", + @" + SELECT to_jsonb(subq) AS data + FROM ( + SELECT * + FROM " + _tableWithDateTimePK + @" + WHERE categoryid = 2 AND pieceid = 1 AND instant = '2023-08-21 15:11:04' + LIMIT 1 + ) AS subq" + }, { "FindEmptyTable", @" diff --git a/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs index 0d7853f9c3..99694d59f7 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs @@ -57,6 +57,8 @@ public abstract class RestApiTestBase : SqlTestBase protected static readonly string _Composite_NonAutoGenPK_EntityPath = "commodities"; protected static readonly string _integrationTypeEntity = "SupportedType"; protected static readonly string _integrationTypeTable = "type_table"; + protected static readonly string _entityWithDateTimePK = "stocks_price"; + protected static readonly string _tableWithDateTimePK = "stocks_price"; public abstract string GetQuery(string key); } From 077a2657ba3816e3371723517ccb6c35fbda6af2 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 16:48:46 +0530 Subject: [PATCH 61/85] moving tests to one place --- .../GraphQLFilterTests/MsSqlGQLFilterTests.cs | 51 ------------------- .../MsSqlGQLSupportedTypesTests.cs | 19 ++++--- 2 files changed, 13 insertions(+), 57 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs index 0b8d8aabd9..7a6b18ec20 100644 --- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs @@ -3,13 +3,9 @@ using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; using Azure.DataApiBuilder.Service.Exceptions; -using Azure.DataApiBuilder.Service.GraphQLBuilder.Queries; -using Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLFilterTests { @@ -29,53 +25,6 @@ public static async Task Setup(TestContext context) await InitializeTestFixture(context); } - /// - /// Test to validate that queries involing filters on datetime types that are specific to MsSql work fine. - /// - /// Type of datetime field. - /// Value of the field. - /// Filter operator. - /// Name of filter operator. - [DataTestMethod] - [DataRow(DATE_TYPE, "1999-01-08", "=", "eq", DisplayName = "date type filter test with eq operator")] - [DataRow(DATE_TYPE, "1999-01-08", ">=", "gte", DisplayName = "date type filter test with gte operator")] - [DataRow(DATE_TYPE, "9998-12-31", "!=", "neq", DisplayName = "date type filter test with ne operator")] - [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", "=", "eq", DisplayName = "smalldatetime type filter test with eq operator")] - [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", ">=", "gte", DisplayName = "smalldatetime type filter test with gte operator")] - [DataRow(SMALLDATETIME_TYPE, "1999-01-08 10:24:00", "!=", "neq", DisplayName = "smalldatetime type filter test with neq operator")] - [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", "=", "eq", DisplayName = "datetime2 type filter test with eq operator")] - [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", ">=", "gte", DisplayName = "datetime2 type filter test with gte operator")] - [DataRow(DATETIME2_TYPE, "1999-01-08 10:23:00.9999999", "!=", "neq", DisplayName = "datetime2 type filter test with neq operator")] - public async Task TestDateTimeFilters( - string fieldType, - string fieldValue, - string filterOperator, - string filterOperatorName - ) - { - string graphQLQueryName = "supportedTypes"; - string fieldName = $"{fieldType.ToLower()}_types"; - string gqlQuery = @"{ - supportedTypes( " + QueryBuilder.FILTER_FIELD_NAME + " : { " + $"{fieldName}" + @": {" + $"{filterOperatorName}" + @": """ + $"{fieldValue}" + @"""}}) - { - items { " + - $"{fieldName}" + - @" - } - } - }"; - - string dbQuery = MakeQueryOn( - "type_table", - new List { fieldName }, - $"{fieldName} {filterOperator} '{fieldValue}'", - GetDefaultSchema()); - - JsonElement actual = await ExecuteGraphQLRequestAsync(gqlQuery, graphQLQueryName, isAuthenticated: false); - string expected = await GetDatabaseResultAsync(dbQuery); - GraphQLSupportedTypesTestBase.PerformTestEqualsForExtendedTypes(fieldType, expected, actual.ToString()); - } - /// /// Test Nested Filter for Many-One relationship. /// Also validates authorization failure behavior when the referenced nested entity diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs index 9f7538617a..150f5cbed5 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs @@ -31,12 +31,19 @@ protected override string MakeQueryOnTypeTable(List columnsToQuery, int /// Test to validate functioning of GraphQL query with filter and orderby with datetime2 column based on the given parameters. /// [DataTestMethod] - [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ")] - [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ")] - [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ")] - [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ")] - [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=")] - public async Task QueryTypeColumnFilterAndOrderByDateTime2(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) + [DataRow(DATE_TYPE, "eq", "\'1999-01-08\'", "\"1999-01-08\"", "=", DisplayName = "date type filter and orderby test with eq operator")] + [DataRow(DATE_TYPE, "gte", "\'1999-01-08\'", "\"1999-01-08\"", ">=", DisplayName = "date type filter test and orderby with gte operator")] + [DataRow(DATE_TYPE, "neq", "\'9998-12-31\'", "\"9998-12-31\"", "!=", DisplayName = "date type filter test and orderby with ne operator")] + [DataRow(SMALLDATETIME_TYPE, "eq", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", "=", DisplayName = "smalldatetime type filter and orderby test with eq operator")] + [DataRow(SMALLDATETIME_TYPE, "gte", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", ">=", DisplayName = "smalldatetime type filter and orderby test with gte operator")] + [DataRow(SMALLDATETIME_TYPE, "neq", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", "!=", DisplayName = "smalldatetime type filter and orderby test with neq operator")] + [DataRow(DATETIME2_TYPE, "eq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "=", DisplayName = "datetime2 type filter and orderby test with eq operator")] + [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ", DisplayName = "datetime2 type filter and orderby test with gt operator")] + [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ", DisplayName = "datetime2 type filter and orderby test with gte operator")] + [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ", DisplayName = "datetime2 type filter and orderby test with lt operator")] + [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ", DisplayName = "datetime2 type filter and orderby test with lte operator")] + [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=", DisplayName = "datetime2 type filter and orderby test with neq operator")] + public async Task QueryTypeColumnFilterAndOrderByDateTimeTypes(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); } From fce32e024707359b67164a446ff3a09ce8b7095c Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 17:00:54 +0530 Subject: [PATCH 62/85] fixing snapshots --- ...ionTests.TestReadingRuntimeConfigForMsSql.verified.txt | 8 ++++++++ ...ionTests.TestReadingRuntimeConfigForMySql.verified.txt | 2 +- ...sts.TestReadingRuntimeConfigForPostgreSql.verified.txt | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMsSql.verified.txt b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMsSql.verified.txt index d4b248e510..634c91c58a 100644 --- a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMsSql.verified.txt +++ b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMsSql.verified.txt @@ -1151,6 +1151,14 @@ } ] }, + { + Role: anonymous, + Actions: [ + { + Action: Read + } + ] + }, { Role: TestNestedFilterFieldIsNull_ColumnForbidden, Actions: [ diff --git a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMySql.verified.txt b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMySql.verified.txt index e701eb2699..e65f8882d2 100644 --- a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMySql.verified.txt +++ b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForMySql.verified.txt @@ -1007,7 +1007,7 @@ Enabled: true }, Rest: { - Enabled: false + Enabled: true }, Permissions: [ { diff --git a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForPostgreSql.verified.txt b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForPostgreSql.verified.txt index 4f127dee0a..8df8482cb8 100644 --- a/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForPostgreSql.verified.txt +++ b/src/Service.Tests/Snapshots/ConfigurationTests.TestReadingRuntimeConfigForPostgreSql.verified.txt @@ -1103,6 +1103,14 @@ } ] }, + { + Role: anonymous, + Actions: [ + { + Action: Read + } + ] + }, { Role: TestNestedFilterFieldIsNull_ColumnForbidden, Actions: [ From 8dd5b604973dfa1a7d8ffb1d000424abe4439102 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 21 Aug 2023 20:20:53 +0530 Subject: [PATCH 63/85] updating configs --- .../GraphQLSupportedTypesTestsBase.cs | 37 +------------------ .../MsSqlGQLSupportedTypesTests.cs | 3 +- src/Service.Tests/dab-config.MsSql.json | 8 ++++ src/Service.Tests/dab-config.MySql.json | 2 +- src/Service.Tests/dab-config.PostgreSql.json | 8 ++++ 5 files changed, 21 insertions(+), 37 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index fee2699a81..b0f62d7553 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -486,7 +486,7 @@ public async Task UpdateTypeColumnWithArgument(string type, object value) /// Utility function to do special comparisons for some of the extended types /// if json compare doesn't suffice /// - public static void PerformTestEqualsForExtendedTypes(string type, string expected, string actual) + private static void PerformTestEqualsForExtendedTypes(string type, string expected, string actual) { switch (type) { @@ -501,10 +501,8 @@ public static void PerformTestEqualsForExtendedTypes(string type, string expecte case DATE_TYPE: case SMALLDATETIME_TYPE: case DATETIME_TYPE: - CompareDateTimeResults(actual.ToString(), expected, type); - break; case DATETIME2_TYPE: - CompareDateTime2Results(actual.ToString(), expected); + CompareDateTimeResults(actual.ToString(), expected, type); break; case DATETIMEOFFSET_TYPE: CompareDateTimeOffsetResults(actual.ToString(), expected); @@ -594,37 +592,6 @@ private static void CompareDateTimeResults(string actual, string expected, strin } } - /// - /// Required due to different format between mysql datetime and HotChocolate datetime - /// result - /// - private static void CompareDateTime2Results(string actual, string expected) - { - string fieldName = "datetime2_types"; - - using JsonDocument actualJsonDoc = JsonDocument.Parse(actual); - using JsonDocument expectedJsonDoc = JsonDocument.Parse(expected); - - if (actualJsonDoc.RootElement.ValueKind is JsonValueKind.Array) - { - ValidateArrayResults(actualJsonDoc, expectedJsonDoc, fieldName); - return; - } - - string actualDateTime2 = actualJsonDoc.RootElement.GetProperty(fieldName).ToString(); - string expectedDateTime2 = expectedJsonDoc.RootElement.GetProperty(fieldName).ToString(); - - // handles cases when one of the values is null - if (string.IsNullOrEmpty(actualDateTime2) || string.IsNullOrEmpty(expectedDateTime2)) - { - Assert.AreEqual(expectedDateTime2, actualDateTime2); - } - else - { - AssertOnFields(fieldName, actualDateTime2, expectedDateTime2); - } - } - /// /// Required due to different format between sql datetimeoffset and HotChocolate datetime /// result diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs index 150f5cbed5..6106201ad6 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs @@ -28,7 +28,8 @@ protected override string MakeQueryOnTypeTable(List columnsToQuery, int } /// - /// Test to validate functioning of GraphQL query with filter and orderby with datetime2 column based on the given parameters. + /// Test to validate functioning of GraphQL query with filter and orderby with datetime column types specific to MsSql + /// i.e. smalldatetime, datetime2. /// [DataTestMethod] [DataRow(DATE_TYPE, "eq", "\'1999-01-08\'", "\"1999-01-08\"", "=", DisplayName = "date type filter and orderby test with eq operator")] diff --git a/src/Service.Tests/dab-config.MsSql.json b/src/Service.Tests/dab-config.MsSql.json index 2649ca2fcb..f91facdced 100644 --- a/src/Service.Tests/dab-config.MsSql.json +++ b/src/Service.Tests/dab-config.MsSql.json @@ -1209,6 +1209,14 @@ "enabled": true }, "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "read" + } + ] + }, { "role": "authenticated", "actions": [ diff --git a/src/Service.Tests/dab-config.MySql.json b/src/Service.Tests/dab-config.MySql.json index 3a780c9e7e..273c86c860 100644 --- a/src/Service.Tests/dab-config.MySql.json +++ b/src/Service.Tests/dab-config.MySql.json @@ -1084,7 +1084,7 @@ } }, "rest": { - "enabled": false + "enabled": true }, "permissions": [ { diff --git a/src/Service.Tests/dab-config.PostgreSql.json b/src/Service.Tests/dab-config.PostgreSql.json index dfb4668baa..ce2e4d6c03 100644 --- a/src/Service.Tests/dab-config.PostgreSql.json +++ b/src/Service.Tests/dab-config.PostgreSql.json @@ -1163,6 +1163,14 @@ "enabled": true }, "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "read" + } + ] + }, { "role": "authenticated", "actions": [ From 452f7f9fbb6cf79a72c2fa08ca6117efbf776b20 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 00:51:15 +0530 Subject: [PATCH 64/85] adding tests --- .../GraphQLSupportedTypesTestsBase.cs | 69 +++++-------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index b0f62d7553..fd15086709 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -19,59 +19,22 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase #region Tests [DataTestMethod] - [DataRow(BYTE_TYPE, 1)] - [DataRow(BYTE_TYPE, 2)] - [DataRow(BYTE_TYPE, 3)] - [DataRow(BYTE_TYPE, 4)] - [DataRow(SHORT_TYPE, 1)] - [DataRow(SHORT_TYPE, 2)] - [DataRow(SHORT_TYPE, 3)] - [DataRow(SHORT_TYPE, 4)] - [DataRow(INT_TYPE, 1)] - [DataRow(INT_TYPE, 2)] - [DataRow(INT_TYPE, 3)] - [DataRow(INT_TYPE, 4)] - [DataRow(LONG_TYPE, 1)] - [DataRow(LONG_TYPE, 2)] - [DataRow(LONG_TYPE, 3)] - [DataRow(LONG_TYPE, 4)] - [DataRow(SINGLE_TYPE, 1)] - [DataRow(SINGLE_TYPE, 2)] - [DataRow(SINGLE_TYPE, 3)] - [DataRow(SINGLE_TYPE, 4)] - [DataRow(FLOAT_TYPE, 1)] - [DataRow(FLOAT_TYPE, 2)] - [DataRow(FLOAT_TYPE, 3)] - [DataRow(FLOAT_TYPE, 4)] - [DataRow(DECIMAL_TYPE, 1)] - [DataRow(DECIMAL_TYPE, 2)] - [DataRow(DECIMAL_TYPE, 3)] - [DataRow(DECIMAL_TYPE, 4)] - [DataRow(STRING_TYPE, 1)] - [DataRow(STRING_TYPE, 2)] - [DataRow(STRING_TYPE, 3)] - [DataRow(STRING_TYPE, 4)] - [DataRow(BOOLEAN_TYPE, 1)] - [DataRow(BOOLEAN_TYPE, 2)] - [DataRow(BOOLEAN_TYPE, 3)] - [DataRow(BOOLEAN_TYPE, 4)] - [DataRow(DATETIME_TYPE, 1)] - [DataRow(DATETIME_TYPE, 2)] - [DataRow(DATETIME_TYPE, 3)] - [DataRow(DATETIME_TYPE, 4)] - [DataRow(TIME_TYPE, 1)] - [DataRow(TIME_TYPE, 2)] - [DataRow(TIME_TYPE, 3)] - [DataRow(TIME_TYPE, 4)] - [DataRow(TIME_TYPE, 5)] - [DataRow(BYTEARRAY_TYPE, 1)] - [DataRow(BYTEARRAY_TYPE, 2)] - [DataRow(BYTEARRAY_TYPE, 3)] - [DataRow(BYTEARRAY_TYPE, 4)] - [DataRow(GUID_TYPE, 1)] - [DataRow(GUID_TYPE, 2)] - [DataRow(GUID_TYPE, 3)] - [DataRow(GUID_TYPE, 4)] + [DataRow(BYTE_TYPE, 1, DisplayName = "Query by PK test selecting only byte_types.")] + [DataRow(SHORT_TYPE, 2, DisplayName = "Query by PK test selecting only short_types.")] + [DataRow(INT_TYPE, 3, DisplayName = "Query by PK test selecting only int_types.")] + [DataRow(LONG_TYPE, 4, DisplayName = "Query by PK test selecting only long_types.")] + [DataRow(SINGLE_TYPE, 1, DisplayName = "Query by PK test selecting only float_types.")] + [DataRow(FLOAT_TYPE, 2, DisplayName = "Query by PK test selecting only byte_type.")] + [DataRow(DECIMAL_TYPE, 3, DisplayName = "Query by PK test selecting only decimal_types")] + [DataRow(STRING_TYPE, 4, DisplayName = "Query by PK test selecting only string_types.")] + [DataRow(BOOLEAN_TYPE, 1, DisplayName = "Query by PK test selecting only boolean_types.")] + [DataRow(DATETIME_TYPE, 2, DisplayName = "Query by PK test selecting only datetime_types.")] + [DataRow(DATETIME2_TYPE, 3, DisplayName = "Query by PK test selecting only datetime2_types.")] + [DataRow(SMALLDATETIME_TYPE, 4, DisplayName = "Query by PK test selecting only smalldatetime_types.")] + [DataRow(DATE_TYPE, 1, DisplayName = "Query by PK test selecting only date_types.")] + [DataRow(TIME_TYPE, 2, DisplayName = "Query by PK test selecting only time_types.")] + [DataRow(BYTEARRAY_TYPE, 3, DisplayName = "Query by PK test selecting only bytearray_types.")] + [DataRow(GUID_TYPE, 4, DisplayName = "Query by PK test selecting only guid_types.")] public async Task QueryTypeColumn(string type, int id) { if (!IsSupportedType(type)) From 7e561264ca40eab3d4bfec77d776be40742989e6 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 01:20:10 +0530 Subject: [PATCH 65/85] adding tests --- .../GraphQLSupportedTypesTestsBase.cs | 83 ++++++++++++------- .../MsSqlGQLSupportedTypesTests.cs | 23 ----- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index fd15086709..0345b9326d 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -30,6 +30,7 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase [DataRow(BOOLEAN_TYPE, 1, DisplayName = "Query by PK test selecting only boolean_types.")] [DataRow(DATETIME_TYPE, 2, DisplayName = "Query by PK test selecting only datetime_types.")] [DataRow(DATETIME2_TYPE, 3, DisplayName = "Query by PK test selecting only datetime2_types.")] + [DataRow(DATETIMEOFFSET_TYPE, 4, DisplayName = "Query by PK test selecting only datetimeoffset_types.")] [DataRow(SMALLDATETIME_TYPE, 4, DisplayName = "Query by PK test selecting only smalldatetime_types.")] [DataRow(DATE_TYPE, 1, DisplayName = "Query by PK test selecting only date_types.")] [DataRow(TIME_TYPE, 2, DisplayName = "Query by PK test selecting only time_types.")] @@ -132,34 +133,60 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper /// The test checks various datetime data types such as datetime, datetimeoffset, and time. /// [DataTestMethod] - [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08\'", "\"1999-01-08\"", " > ")] - [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08\'", "\"1999-01-08\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'1753-01-01\'", "\"1753-01-01\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'1753-01-01\'", "\"1753-01-01\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'1753-01-01\'", "\"1753-01-01\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'1753-01-01\'", "\"1753-01-01\"", "=")] - [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " > ")] - [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:00\'", "\"1999-01-08 10:23:00\"", "=")] - [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", " > ")] - [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'9999-12-31 23:59:59.9999999\'", "\"9999-12-31 23:59:59.9999999\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'9999-12-31 23:59:59.9999999\'", "\"9999-12-31 23:59:59.9999999\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "=")] - [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=")] - [DataRow(DATETIMEOFFSET_TYPE, "neq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=")] - [DataRow(DATETIMEOFFSET_TYPE, "eq", "\'1999-01-08 10:23:54.9999999-14:00\'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=")] - [DataRow(DATETIME_TYPE, "gt", "\'1999-01-08 10:22:00\'", "\"1999-01-08 10:22:00\"", " > ")] - [DataRow(DATETIME_TYPE, "gte", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", " >= ")] - [DataRow(DATETIME_TYPE, "lt", "\'2079-06-06\'", "\"2079-06-06\"", " < ")] - [DataRow(DATETIME_TYPE, "lte", "\'2079-06-06\'", "\"2079-06-06\"", " <= ")] - [DataRow(DATETIME_TYPE, "neq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "!=")] - [DataRow(DATETIME_TYPE, "eq", "\'1999-01-08 10:23:54\'", "\"1999-01-08 10:23:54\"", "=")] + [DataRow(DATETIME_TYPE, "eq", "'1999-01-08'", "\"1999-01-08\"", " = ", + DisplayName = "datetime type filter and orderby test with eq operator and specific value '1999-01-08'.")] + [DataRow(DATETIME_TYPE, "lt", "'1753-01-01'", "\"1753-01-01\"", " < ", + DisplayName = "datetime type filter and orderby test with lt operator and specific value '1753-01-01'.")] + [DataRow(DATETIME_TYPE, "lte", "'1753-01-01'", "\"1753-01-01\"", " <= ", + DisplayName = "datetime type filter and orderby test with lte operator and specific value '1753-01-01'.")] + [DataRow(DATETIME_TYPE, "neq", "'1753-01-01'", "\"1753-01-01\"", "!=", + DisplayName = "datetime type filter and orderby test with neq operator and specific value '1753-01-01'.")] + [DataRow(DATETIME_TYPE, "eq", "'1753-01-01'", "\"1753-01-01\"", "=", + DisplayName = "datetime type filter and orderby test with eq operator and specific value '1753-01-01'.")] + [DataRow(DATETIME_TYPE, "gt", "'1999-01-08 10:23:00'", "\"1999-01-08 10:23:00\"", " > ", + DisplayName = "datetime type filter and orderby test with gt operator and specific value '1999-01-08 10:23:00'.")] + [DataRow(DATETIME_TYPE, "gte", "'1999-01-08 10:23:00'", "\"1999-01-08 10:23:00\"", " >= ", + DisplayName = "datetime type filter and orderby test with gte operator and specific value '1999-01-08 10:23:00'.")] + [DataRow(DATETIME_TYPE, "lt", "'9999-12-31 23:59:59'", "\"9999-12-31 23:59:59\"", " < ", + DisplayName = "datetime type filter and orderby test with lt operator and specific value '9999-12-31 23:59:59'.")] + [DataRow(DATETIME_TYPE, "lte", "'9999-12-31 23:59:59'", "\"9999-12-31 23:59:59\"", " <= ", + DisplayName = "datetime type filter and orderby test with lte operator and specific value '9999-12-31 23:59:59'.")] + [DataRow(DATETIME_TYPE, "neq", "'1999-01-08 10:23:00'", "\"1999-01-08 10:23:00\"", "!=", + DisplayName = "datetime type filter and orderby test with neq operator and specific value '1999-01-08 10:23:00'.")] + [DataRow(DATETIME_TYPE, "eq", "'1999-01-08 10:23:00'", "\"1999-01-08 10:23:00\"", "=", + DisplayName = "datetime type filter and orderby test with eq operator and specific value '1999-01-08 10:23:00'.")] + [DataRow(DATETIME_TYPE, "lte", "'2079-06-06'", "\"2079-06-06\"", " <= ", + DisplayName = "datetime type filter and orderby test with lte operator and specific value '2079-06-06'.")] + [DataRow(DATETIME_TYPE, "neq", "'1999-01-08 10:23:54'", "\"1999-01-08 10:23:54\"", "!=", + DisplayName = "datetime type filter and orderby test with neq operator and specific value '1999-01-08 10:23:54'.")] + [DataRow(DATETIMEOFFSET_TYPE, "neq", "'1999-01-08 10:23:54.9999999-14:00'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=", + DisplayName = "datetimeoffset type filter and orderby test with neq operator")] + [DataRow(DATETIMEOFFSET_TYPE, "eq", "'1999-01-08 10:23:54.9999999-14:00'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=", + DisplayName = "datetimeoffset type filter and orderby test with eq operator")] + [DataRow(DATE_TYPE, "eq", "'1999-01-08'", "\"1999-01-08\"", "=", + DisplayName = "date type filter and orderby test with eq operator")] + [DataRow(DATE_TYPE, "gte", "'1999-01-08'", "\"1999-01-08\"", ">=", + DisplayName = "date type filter test and orderby with gte operator")] + [DataRow(DATE_TYPE, "neq", "'9998-12-31'", "\"9998-12-31\"", "!=", + DisplayName = "date type filter test and orderby with ne operator")] + [DataRow(SMALLDATETIME_TYPE, "eq", "'1999-01-08 10:24:00'", "\"1999-01-08 10:24:00\"", "=", + DisplayName = "smalldatetime type filter and orderby test with eq operator")] + [DataRow(SMALLDATETIME_TYPE, "gte", "'1999-01-08 10:24:00'", "\"1999-01-08 10:24:00\"", ">=", + DisplayName = "smalldatetime type filter and orderby test with gte operator")] + [DataRow(SMALLDATETIME_TYPE, "neq", "'1999-01-08 10:24:00'", "\"1999-01-08 10:24:00\"", "!=", + DisplayName = "smalldatetime type filter and orderby test with neq operator")] + [DataRow(DATETIME2_TYPE, "eq", "'1999-01-08 10:23:00.9999999'", "\"1999-01-08 10:23:00.9999999\"", "=", + DisplayName = "datetime2 type filter and orderby test with eq operator")] + [DataRow(DATETIME2_TYPE, "gt", "'0001-01-08 10:23:00.9999999'", "\"0001-01-08 10:23:00.9999999\"", " > ", + DisplayName = "datetime2 type filter and orderby test with gt operator")] + [DataRow(DATETIME2_TYPE, "gte", "'0001-01-08 10:23:00.9999999'", "\"0001-01-08 10:23:00.9999999\"", " >= ", + DisplayName = "datetime2 type filter and orderby test with gte operator")] + [DataRow(DATETIME2_TYPE, "lt", "'0002-06-06'", "\"0002-06-06\"", " < ", + DisplayName = "datetime2 type filter and orderby test with lt operator")] + [DataRow(DATETIME2_TYPE, "lte", "'9999-12-31'", "\"9999-12-31\"", " <= ", + DisplayName = "datetime2 type filter and orderby test with lte operator")] + [DataRow(DATETIME2_TYPE, "neq", "'9999-12-31 23:59:59'", "\"9999-12-31 23:59:59\"", "!=", + DisplayName = "datetime2 type filter and orderby test with neq operator")] public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { // In MySQL, the DATETIME data type supports a range from '1000-01-01 00:00:00.0000000' to '9999-12-31 23:59:59.0000000' diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs index 6106201ad6..2efbaff76c 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MsSqlGQLSupportedTypesTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { @@ -27,28 +26,6 @@ protected override string MakeQueryOnTypeTable(List columnsToQuery, int return MakeQueryOnTypeTable(columnsToQuery, filterValue: id.ToString(), filterField: "id"); } - /// - /// Test to validate functioning of GraphQL query with filter and orderby with datetime column types specific to MsSql - /// i.e. smalldatetime, datetime2. - /// - [DataTestMethod] - [DataRow(DATE_TYPE, "eq", "\'1999-01-08\'", "\"1999-01-08\"", "=", DisplayName = "date type filter and orderby test with eq operator")] - [DataRow(DATE_TYPE, "gte", "\'1999-01-08\'", "\"1999-01-08\"", ">=", DisplayName = "date type filter test and orderby with gte operator")] - [DataRow(DATE_TYPE, "neq", "\'9998-12-31\'", "\"9998-12-31\"", "!=", DisplayName = "date type filter test and orderby with ne operator")] - [DataRow(SMALLDATETIME_TYPE, "eq", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", "=", DisplayName = "smalldatetime type filter and orderby test with eq operator")] - [DataRow(SMALLDATETIME_TYPE, "gte", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", ">=", DisplayName = "smalldatetime type filter and orderby test with gte operator")] - [DataRow(SMALLDATETIME_TYPE, "neq", "\'1999-01-08 10:24:00\'", "\"1999-01-08 10:24:00\"", "!=", DisplayName = "smalldatetime type filter and orderby test with neq operator")] - [DataRow(DATETIME2_TYPE, "eq", "\'1999-01-08 10:23:00.9999999\'", "\"1999-01-08 10:23:00.9999999\"", "=", DisplayName = "datetime2 type filter and orderby test with eq operator")] - [DataRow(DATETIME2_TYPE, "gt", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " > ", DisplayName = "datetime2 type filter and orderby test with gt operator")] - [DataRow(DATETIME2_TYPE, "gte", "\'0001-01-08 10:23:00.9999999\'", "\"0001-01-08 10:23:00.9999999\"", " >= ", DisplayName = "datetime2 type filter and orderby test with gte operator")] - [DataRow(DATETIME2_TYPE, "lt", "\'0002-06-06\'", "\"0002-06-06\"", " < ", DisplayName = "datetime2 type filter and orderby test with lt operator")] - [DataRow(DATETIME2_TYPE, "lte", "\'9999-12-31\'", "\"9999-12-31\"", " <= ", DisplayName = "datetime2 type filter and orderby test with lte operator")] - [DataRow(DATETIME2_TYPE, "neq", "\'9999-12-31 23:59:59\'", "\"9999-12-31 23:59:59\"", "!=", DisplayName = "datetime2 type filter and orderby test with neq operator")] - public async Task QueryTypeColumnFilterAndOrderByDateTimeTypes(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) - { - await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); - } - protected override string MakeQueryOnTypeTable( List queriedColumns, string filterValue = "1", From 59dba5c7dbc2503b665dd651ce815515e8f2875c Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 01:23:04 +0530 Subject: [PATCH 66/85] some cleanup --- .../GraphQLSupportedTypesTestsBase.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 0345b9326d..0d819fa004 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -80,14 +80,14 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(LONG_TYPE, "lte", "-9223372036854775808", "-9223372036854775808", " <= ")] [DataRow(LONG_TYPE, "neq", "1", "1", "!=")] [DataRow(LONG_TYPE, "eq", "-1", "-1", "=")] - [DataRow(STRING_TYPE, "neq", "\'foo\'", "\"foo\"", "!=")] - [DataRow(STRING_TYPE, "eq", "\'lksa;jdflasdf;alsdflksdfkldj\'", "\"lksa;jdflasdf;alsdflksdfkldj\"", "=")] + [DataRow(STRING_TYPE, "neq", "'foo'", "\"foo\"", "!=")] + [DataRow(STRING_TYPE, "eq", "'lksa;jdflasdf;alsdflksdfkldj'", "\"lksa;jdflasdf;alsdflksdfkldj\"", "=")] [DataRow(SINGLE_TYPE, "gt", "-9.3", "-9.3", ">")] [DataRow(SINGLE_TYPE, "gte", "-9.2", "-9.2", ">=")] [DataRow(SINGLE_TYPE, "lt", ".33", "0.33", "<")] [DataRow(SINGLE_TYPE, "lte", ".33", "0.33", "<=")] [DataRow(SINGLE_TYPE, "neq", "9.2", "9.2", "!=")] - [DataRow(SINGLE_TYPE, "eq", "\'0.33\'", "0.33", "=")] + [DataRow(SINGLE_TYPE, "eq", "'0.33'", "0.33", "=")] [DataRow(FLOAT_TYPE, "gt", "-9.2", "-9.2", ">")] [DataRow(FLOAT_TYPE, "gte", "-9.2", "-9.2", ">=")] [DataRow(FLOAT_TYPE, "lt", ".33", "0.33", "<")] @@ -100,8 +100,8 @@ public async Task QueryTypeColumn(string type, int id) [DataRow(DECIMAL_TYPE, "lte", "0.333333", "0.333333", " <= ")] [DataRow(DECIMAL_TYPE, "neq", "0.0", "0.0", "!=")] [DataRow(DECIMAL_TYPE, "eq", "-9.292929", "-9.292929", "=")] - [DataRow(BOOLEAN_TYPE, "neq", "\'false\'", "false", "!=")] - [DataRow(BOOLEAN_TYPE, "eq", "\'false\'", "false", "=")] + [DataRow(BOOLEAN_TYPE, "neq", "'false'", "false", "!=")] + [DataRow(BOOLEAN_TYPE, "eq", "'false'", "false", "=")] public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { if (!IsSupportedType(type)) @@ -190,9 +190,9 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { // In MySQL, the DATETIME data type supports a range from '1000-01-01 00:00:00.0000000' to '9999-12-31 23:59:59.0000000' - if (DatabaseEngine is TestCategory.MYSQL && sqlValue is "\'9999-12-31 23:59:59.9999999\'") + if (DatabaseEngine is TestCategory.MYSQL && sqlValue is "'9999-12-31 23:59:59.9999999'") { - sqlValue = "\'9999-12-31 23:59:59.0000000\'"; + sqlValue = "'9999-12-31 23:59:59.0000000'"; gqlValue = "\"9999-12-31 23:59:59.0000000\""; } @@ -203,12 +203,12 @@ public async Task QueryTypeColumnFilterAndOrderByDateTime(string type, string fi /// Validates that usage of LocalTime values with comparison operators in GraphQL filters results in the expected filtered result set. /// [DataTestMethod] - [DataRow(TIME_TYPE, "gt", "\'00:00:00.000\'", "\"00:00:00.000\"", " > ")] - [DataRow(TIME_TYPE, "gte", "\'10:13:14.123\'", "\"10:13:14.123\"", " >= ")] - [DataRow(TIME_TYPE, "lt", "\'23:59:59.999\'", "\"23:59:59.999\"", " < ")] - [DataRow(TIME_TYPE, "lte", "\'23:59:59.999\'", "\"23:59:59.999\"", " <= ")] - [DataRow(TIME_TYPE, "neq", "\'10:23:54.9999999\'", "\"10:23:54.9999999\"", "!=")] - [DataRow(TIME_TYPE, "eq", "\'10:23:54.9999999\'", "\"10:23:54.9999999\"", "=")] + [DataRow(TIME_TYPE, "gt", "'00:00:00.000'", "\"00:00:00.000\"", " > ")] + [DataRow(TIME_TYPE, "gte", "'10:13:14.123'", "\"10:13:14.123\"", " >= ")] + [DataRow(TIME_TYPE, "lt", "'23:59:59.999'", "\"23:59:59.999\"", " < ")] + [DataRow(TIME_TYPE, "lte", "'23:59:59.999'", "\"23:59:59.999\"", " <= ")] + [DataRow(TIME_TYPE, "neq", "'10:23:54.9999999'", "\"10:23:54.9999999\"", "!=")] + [DataRow(TIME_TYPE, "eq", "'10:23:54.9999999'", "\"10:23:54.9999999\"", "=")] public async Task QueryTypeColumnFilterAndOrderByLocalTime(string type, string filterOperator, string sqlValue, string gqlValue, string queryOperator) { await QueryTypeColumnFilterAndOrderBy(type, filterOperator, sqlValue, gqlValue, queryOperator); From 49d415ef835e2bd9bc382c344802af453c55e3cd Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 01:36:39 +0530 Subject: [PATCH 67/85] adding more tests --- .../GraphQLSupportedTypesTestsBase.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 0d819fa004..66f33f315c 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -243,7 +243,7 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) } /// - /// the method constructs a GraphQL query to insert the value into the database table + /// The method constructs a GraphQL query to insert the value into the database table /// and then executes the query and compares the expected result with the actual result to verify if different types are supported. /// [DataTestMethod] @@ -277,19 +277,25 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(BOOLEAN_TYPE, "true")] [DataRow(BOOLEAN_TYPE, "false")] [DataRow(BOOLEAN_TYPE, "null")] - [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] - [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54.671287+8:00\"")] - [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] - [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] - [DataRow(DATETIME_TYPE, "null")] [DataRow(BYTEARRAY_TYPE, "\"U3RyaW5neQ==\"")] + [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] + [DataRow(BYTEARRAY_TYPE, "null")] [DataRow(TIME_TYPE, "\"23:59:59.9999999\"")] [DataRow(TIME_TYPE, "\"23:59:59\"")] [DataRow(TIME_TYPE, "\"23:59:59.9\"")] [DataRow(TIME_TYPE, "\"23:59\"")] [DataRow(TIME_TYPE, "null")] - [DataRow(BYTEARRAY_TYPE, "\"V2hhdGNodSBkb2luZyBkZWNvZGluZyBvdXIgdGVzdCBiYXNlNjQgc3RyaW5ncz8=\"")] - [DataRow(BYTEARRAY_TYPE, "null")] + [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] + [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] + [DataRow(DATETIME_TYPE, "null")] + [DataRow(SMALLDATETIME_TYPE, "\"1900-01-01\"")] + [DataRow(SMALLDATETIME_TYPE, "\"2079-06-06\"")] + [DataRow(DATETIME2_TYPE, "\"0001-01-01 00:00:00.0000000\"")] + [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.9999999\"")] + [DataRow(DATE_TYPE, "\"0001-01-01\"")] + [DataRow(DATE_TYPE, "\"9999-12-31\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54.671287+8:00\"")] public async Task InsertIntoTypeColumn(string type, string value) { if (!IsSupportedType(type)) From 1eea2ddb8e34249f601a383a4c87479ba9b420f2 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 09:50:48 +0530 Subject: [PATCH 68/85] removing dup code --- .../MsSqlMetadataProvider.cs | 14 ++++++-- .../MySqlMetadataProvider.cs | 2 +- .../PostgreSqlMetadataProvider.cs | 2 +- .../MetadataProviders/SqlMetadataProvider.cs | 33 ++++++------------- src/Core/Services/TypeHelper.cs | 21 +----------- 5 files changed, 25 insertions(+), 47 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 207a6e95d5..52f02c8db2 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Data; +using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Resolvers; using Microsoft.Data.SqlClient; @@ -45,9 +46,18 @@ public override Type SqlToCLRType(string sqlType) } /// - public override DbType DatetimeSqlTypeToDbype(string sqlType) + public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) { - return TypeHelper.GetDbTypeFromSqlDbDateTimeType(sqlType); + if (TypeHelper.DateTimeMsSqlTypeToDbType.TryGetValue(sqlDateTimeType, out DbType dbType)) + { + // For MsSql, all the date time types i.e. date, smalldatetime, datetime, datetime2 map to System.DateTime system type. + // Hence we cannot directly determine the DbType from the system type. + // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly + // populate the DbTypes. + return dbType; + } + + return null; } } } diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index 5d926f814d..68c471c4e0 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -118,7 +118,7 @@ public override Type SqlToCLRType(string sqlType) throw new NotImplementedException(); } - public override DbType DatetimeSqlTypeToDbype(string sqlType) + public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) { throw new NotImplementedException(); } diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index 1a1b5b1ca7..3dd7e30746 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -74,7 +74,7 @@ public override Type SqlToCLRType(string sqlType) throw new NotImplementedException(); } - public override DbType DatetimeSqlTypeToDbype(string sqlType) + public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) { throw new NotImplementedException(); } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 63b47f01b1..a1f902509c 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -312,18 +312,10 @@ private async Task FillSchemaForStoredProcedureAsync( SystemType = systemType, DbType = TypeHelper.GetDbTypeFromSystemType(systemType) }; - - if (GetDatabaseType() is DatabaseType.MSSQL) + DbType? dbTypeForMsSqlDateTimeType = GetDbTypeForDateTimeTypes((string)row["DATA_TYPE"]); + if (dbTypeForMsSqlDateTimeType is not null) { - string sqlDataType = ((string)row["DATA_TYPE"]).ToLower(); - if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) - { - // For MsSql, all the date time types i.e. smalldatetime, datetime, datetime2 map to System.DateTime system type. - // Hence we cannot directly determine the DbType from the system type. - // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly - // populate the DbTypes. - paramDefinition.DbType = DatetimeSqlTypeToDbype(sqlDataType); - } + paramDefinition.DbType = dbTypeForMsSqlDateTimeType; } // Add to parameters dictionary without the leading @ sign @@ -363,9 +355,11 @@ private async Task FillSchemaForStoredProcedureAsync( public abstract Type SqlToCLRType(string sqlType); /// - /// Takes a string version of a sql data type and returns its corresponding DbType. + /// Takes a string version of a sql date/time type and returns its corresponding DbType. + /// Returns null when the provided sql type is not a date/time type. /// - public abstract DbType DatetimeSqlTypeToDbype(string sqlType); + /// sql date/time/datetime type. + public abstract DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType); /// /// Generates the map used to find a given entity based @@ -1248,18 +1242,11 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( } columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); + DbType? dbTypeForMsSqlDateTimeType = GetDbTypeForDateTimeTypes((string)columnInfo["DATA_TYPE"]); - if (GetDatabaseType() is DatabaseType.MSSQL) + if (dbTypeForMsSqlDateTimeType is not null) { - string sqlDataType = ((string)columnInfo["DATA_TYPE"]).ToLower(); - if (MsSqlMetadataProvider.DateTimeTypes.Contains(sqlDataType)) - { - // For MsSql, all the date time types i.e. date, smalldatetime, datetime, datetime2 map to System.DateTime system type. - // Hence we cannot directly determine the DbType from the system type. - // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly - // populate the DbTypes. - columnDefinition.DbType = DatetimeSqlTypeToDbype(sqlDataType); - } + columnDefinition.DbType = dbTypeForMsSqlDateTimeType; } } } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index e7ffad1814..20397b4c0a 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -111,7 +111,7 @@ public static class TypeHelper [SqlDbType.VarChar] = typeof(string) }; - private static Dictionary _timeSqlDbTypeToDbType = new() + public static Dictionary DateTimeMsSqlTypeToDbType = new() { ["date"] = DbType.Date, ["datetime"] = DbType.DateTime, @@ -239,24 +239,5 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) statusCode: HttpStatusCode.ServiceUnavailable, subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError); } - - /// - /// Helper method to find the DbType for the given datetime data types in Sql Server. - /// - /// Data type of the datetime column in Sql Server. - /// Corresponding DbType for the column. - /// - public static DbType GetDbTypeFromSqlDbDateTimeType(string sqlDbTypeName) - { - if (_timeSqlDbTypeToDbType.TryGetValue(sqlDbTypeName, out DbType value)) - { - return value; - } - - throw new DataApiBuilderException( - message: $"Unexpected datetime data type: {sqlDbTypeName} encountered.", - statusCode: HttpStatusCode.ServiceUnavailable, - subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError); - } } } From c259d69b371cedf11f1b7f95be9dcf12791d7c18 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 10:09:12 +0530 Subject: [PATCH 69/85] adding back tests --- .../MySqlMetadataProvider.cs | 2 +- .../PostgreSqlMetadataProvider.cs | 2 +- .../MetadataProviders/SqlMetadataProvider.cs | 1 + .../GraphQLSupportedTypesTestsBase.cs | 82 +++++++++++++++---- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index 68c471c4e0..6db6d92b06 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -120,7 +120,7 @@ public override Type SqlToCLRType(string sqlType) public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) { - throw new NotImplementedException(); + return null; } } } diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index 3dd7e30746..062a6a4668 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -76,7 +76,7 @@ public override Type SqlToCLRType(string sqlType) public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) { - throw new NotImplementedException(); + return null; } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index a1f902509c..0b6cc18e70 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -312,6 +312,7 @@ private async Task FillSchemaForStoredProcedureAsync( SystemType = systemType, DbType = TypeHelper.GetDbTypeFromSystemType(systemType) }; + DbType? dbTypeForMsSqlDateTimeType = GetDbTypeForDateTimeTypes((string)row["DATA_TYPE"]); if (dbTypeForMsSqlDateTimeType is not null) { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 66f33f315c..3f1185ccf9 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -19,23 +19,71 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase #region Tests [DataTestMethod] - [DataRow(BYTE_TYPE, 1, DisplayName = "Query by PK test selecting only byte_types.")] - [DataRow(SHORT_TYPE, 2, DisplayName = "Query by PK test selecting only short_types.")] - [DataRow(INT_TYPE, 3, DisplayName = "Query by PK test selecting only int_types.")] - [DataRow(LONG_TYPE, 4, DisplayName = "Query by PK test selecting only long_types.")] - [DataRow(SINGLE_TYPE, 1, DisplayName = "Query by PK test selecting only float_types.")] - [DataRow(FLOAT_TYPE, 2, DisplayName = "Query by PK test selecting only byte_type.")] - [DataRow(DECIMAL_TYPE, 3, DisplayName = "Query by PK test selecting only decimal_types")] - [DataRow(STRING_TYPE, 4, DisplayName = "Query by PK test selecting only string_types.")] - [DataRow(BOOLEAN_TYPE, 1, DisplayName = "Query by PK test selecting only boolean_types.")] - [DataRow(DATETIME_TYPE, 2, DisplayName = "Query by PK test selecting only datetime_types.")] - [DataRow(DATETIME2_TYPE, 3, DisplayName = "Query by PK test selecting only datetime2_types.")] - [DataRow(DATETIMEOFFSET_TYPE, 4, DisplayName = "Query by PK test selecting only datetimeoffset_types.")] - [DataRow(SMALLDATETIME_TYPE, 4, DisplayName = "Query by PK test selecting only smalldatetime_types.")] - [DataRow(DATE_TYPE, 1, DisplayName = "Query by PK test selecting only date_types.")] - [DataRow(TIME_TYPE, 2, DisplayName = "Query by PK test selecting only time_types.")] - [DataRow(BYTEARRAY_TYPE, 3, DisplayName = "Query by PK test selecting only bytearray_types.")] - [DataRow(GUID_TYPE, 4, DisplayName = "Query by PK test selecting only guid_types.")] + [DataRow(BYTE_TYPE, 1, DisplayName = "Query by PK test selecting only byte_types with typeid = 1.")] + [DataRow(BYTE_TYPE, 2, DisplayName = "Query by PK test selecting only byte_types with typeid = 2.")] + [DataRow(BYTE_TYPE, 3, DisplayName = "Query by PK test selecting only byte_types with typeid = 3.")] + [DataRow(BYTE_TYPE, 4, DisplayName = "Query by PK test selecting only byte_types with typeid = 4.")] + [DataRow(SHORT_TYPE, 1, DisplayName = "Query by PK test selecting only short_types with typeid = 1.")] + [DataRow(SHORT_TYPE, 2, DisplayName = "Query by PK test selecting only short_types with typeid = 2.")] + [DataRow(SHORT_TYPE, 3, DisplayName = "Query by PK test selecting only short_types with typeid = 3.")] + [DataRow(SHORT_TYPE, 4, DisplayName = "Query by PK test selecting only short_types with typeid = 4.")] + [DataRow(INT_TYPE, 1, DisplayName = "Query by PK test selecting only int_types with typeid = 1.")] + [DataRow(INT_TYPE, 2, DisplayName = "Query by PK test selecting only int_types with typeid = 2.")] + [DataRow(INT_TYPE, 3, DisplayName = "Query by PK test selecting only int_types with typeid = 3.")] + [DataRow(INT_TYPE, 4, DisplayName = "Query by PK test selecting only int_types with typeid = 4.")] + [DataRow(LONG_TYPE, 1, DisplayName = "Query by PK test selecting only long_types with typeid = 1.")] + [DataRow(LONG_TYPE, 2, DisplayName = "Query by PK test selecting only long_types with typeid = 2.")] + [DataRow(LONG_TYPE, 3, DisplayName = "Query by PK test selecting only long_types with typeid = 3.")] + [DataRow(LONG_TYPE, 4, DisplayName = "Query by PK test selecting only long_types with typeid = 4.")] + [DataRow(SINGLE_TYPE, 1, DisplayName = "Query by PK test selecting only single_types with typeid = 1.")] + [DataRow(SINGLE_TYPE, 2, DisplayName = "Query by PK test selecting only single_types with typeid = 2.")] + [DataRow(SINGLE_TYPE, 3, DisplayName = "Query by PK test selecting only single_types with typeid = 3.")] + [DataRow(SINGLE_TYPE, 4, DisplayName = "Query by PK test selecting only single_types with typeid = 4.")] + [DataRow(FLOAT_TYPE, 1, DisplayName = "Query by PK test selecting only byte_type with typeid = 1.")] + [DataRow(FLOAT_TYPE, 2, DisplayName = "Query by PK test selecting only byte_type with typeid = 2.")] + [DataRow(FLOAT_TYPE, 3, DisplayName = "Query by PK test selecting only byte_type with typeid = 3.")] + [DataRow(FLOAT_TYPE, 4, DisplayName = "Query by PK test selecting only byte_type with typeid = 4.")] + [DataRow(DECIMAL_TYPE, 1, DisplayName = "Query by PK test selecting only decimal_types with typeid = 1.")] + [DataRow(DECIMAL_TYPE, 2, DisplayName = "Query by PK test selecting only decimal_types with typeid = 2.")] + [DataRow(DECIMAL_TYPE, 3, DisplayName = "Query by PK test selecting only decimal_types with typeid = 3.")] + [DataRow(DECIMAL_TYPE, 4, DisplayName = "Query by PK test selecting only decimal_types with typeid = 4.")] + [DataRow(STRING_TYPE, 1, DisplayName = "Query by PK test selecting only string_types with typeid = 1.")] + [DataRow(STRING_TYPE, 2, DisplayName = "Query by PK test selecting only string_types with typeid = 2.")] + [DataRow(STRING_TYPE, 3, DisplayName = "Query by PK test selecting only string_types with typeid = 3.")] + [DataRow(STRING_TYPE, 4, DisplayName = "Query by PK test selecting only string_types with typeid = 4.")] + [DataRow(BOOLEAN_TYPE, 1, DisplayName = "Query by PK test selecting only boolean_types with typeid = 1.")] + [DataRow(BOOLEAN_TYPE, 2, DisplayName = "Query by PK test selecting only boolean_types with typeid = 2.")] + [DataRow(BOOLEAN_TYPE, 3, DisplayName = "Query by PK test selecting only boolean_types with typeid = 3.")] + [DataRow(BOOLEAN_TYPE, 4, DisplayName = "Query by PK test selecting only boolean_types with typeid = 4.")] + [DataRow(DATETIME_TYPE, 1, DisplayName = "Query by PK test selecting only datetime_types with typeid = 1.")] + [DataRow(DATETIME_TYPE, 2, DisplayName = "Query by PK test selecting only datetime_types with typeid = 2.")] + [DataRow(DATETIME_TYPE, 3, DisplayName = "Query by PK test selecting only datetime_types with typeid = 3.")] + [DataRow(DATETIME_TYPE, 4, DisplayName = "Query by PK test selecting only datetime_types with typeid = 4.")] + [DataRow(DATETIME2_TYPE, 1, DisplayName = "Query by PK test selecting only datetime2_types with typeid = 1.")] + [DataRow(DATETIME2_TYPE, 2, DisplayName = "Query by PK test selecting only datetime2_types with typeid = 2.")] + [DataRow(DATETIME2_TYPE, 3, DisplayName = "Query by PK test selecting only datetime2_types with typeid = 3.")] + [DataRow(DATETIME2_TYPE, 4, DisplayName = "Query by PK test selecting only datetime2_types with typeid = 4.")] + [DataRow(DATETIMEOFFSET_TYPE, 1, DisplayName = "Query by PK test selecting only datetimeoffset_types with typeid = 1.")] + [DataRow(DATETIMEOFFSET_TYPE, 2, DisplayName = "Query by PK test selecting only datetimeoffset_types with typeid = 2.")] + [DataRow(DATETIMEOFFSET_TYPE, 3, DisplayName = "Query by PK test selecting only datetimeoffset_types with typeid = 3.")] + [DataRow(DATETIMEOFFSET_TYPE, 4, DisplayName = "Query by PK test selecting only datetimeoffset_types with typeid = 4.")] + [DataRow(SMALLDATETIME_TYPE, 1, DisplayName = "Query by PK test selecting only smalldatetime_types with typeid = 1.")] + [DataRow(SMALLDATETIME_TYPE, 1, DisplayName = "Query by PK test selecting only smalldatetime_types with typeid = 1.")] + [DataRow(SMALLDATETIME_TYPE, 1, DisplayName = "Query by PK test selecting only smalldatetime_types with typeid = 1.")] + [DataRow(SMALLDATETIME_TYPE, 1, DisplayName = "Query by PK test selecting only smalldatetime_types with typeid = 1.")] + [DataRow(DATE_TYPE, 1, DisplayName = "Query by PK test selecting only date_types with typeid = 1.")] + [DataRow(DATE_TYPE, 2, DisplayName = "Query by PK test selecting only date_types with typeid = 2.")] + [DataRow(DATE_TYPE, 3, DisplayName = "Query by PK test selecting only date_types with typeid = 3.")] + [DataRow(DATE_TYPE, 4, DisplayName = "Query by PK test selecting only date_types with typeid = 4.")] + [DataRow(TIME_TYPE, 1, DisplayName = "Query by PK test selecting only time_types with typeid = 1.")] + [DataRow(TIME_TYPE, 2, DisplayName = "Query by PK test selecting only time_types with typeid = 2.")] + [DataRow(TIME_TYPE, 3, DisplayName = "Query by PK test selecting only time_types with typeid = 3.")] + [DataRow(TIME_TYPE, 4, DisplayName = "Query by PK test selecting only time_types with typeid = 4.")] + [DataRow(BYTEARRAY_TYPE, 1, DisplayName = "Query by PK test selecting only bytearray_types with typeid = 1.")] + [DataRow(BYTEARRAY_TYPE, 2, DisplayName = "Query by PK test selecting only bytearray_types with typeid = 2.")] + [DataRow(BYTEARRAY_TYPE, 3, DisplayName = "Query by PK test selecting only bytearray_types with typeid = 3.")] + [DataRow(BYTEARRAY_TYPE, 4, DisplayName = "Query by PK test selecting only bytearray_types with typeid = 4.")] + [DataRow(GUID_TYPE, 4, DisplayName = "Query by PK test selecting only guid_types with typeid = 4.")] public async Task QueryTypeColumn(string type, int id) { if (!IsSupportedType(type)) From cc6c61610cf21e791ff2fb6a721857613052aef8 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 22 Aug 2023 10:24:19 +0530 Subject: [PATCH 70/85] adding more tests --- .../MetadataProviders/MsSqlMetadataProvider.cs | 1 - .../GraphQLSupportedTypesTestsBase.cs | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 52f02c8db2..77ab17c89a 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Data; -using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Resolvers; using Microsoft.Data.SqlClient; diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 3f1185ccf9..b1e85e0f40 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -209,6 +209,8 @@ public async Task QueryTypeColumnFilterAndOrderBy(string type, string filterOper DisplayName = "datetime type filter and orderby test with neq operator and specific value '1999-01-08 10:23:54'.")] [DataRow(DATETIMEOFFSET_TYPE, "neq", "'1999-01-08 10:23:54.9999999-14:00'", "\"1999-01-08 10:23:54.9999999-14:00\"", "!=", DisplayName = "datetimeoffset type filter and orderby test with neq operator")] + [DataRow(DATETIMEOFFSET_TYPE, "lt", "'9999-12-31 23:59:59.9999999'", "\"9999-12-31 23:59:59.9999999\"", "<", + DisplayName = "datetimeoffset type filter and orderby test with lt operator and max value for datetimeoffset.")] [DataRow(DATETIMEOFFSET_TYPE, "eq", "'1999-01-08 10:23:54.9999999-14:00'", "\"1999-01-08 10:23:54.9999999-14:00\"", "=", DisplayName = "datetimeoffset type filter and orderby test with eq operator")] [DataRow(DATE_TYPE, "eq", "'1999-01-08'", "\"1999-01-08\"", "=", @@ -333,8 +335,8 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(TIME_TYPE, "\"23:59:59.9\"")] [DataRow(TIME_TYPE, "\"23:59\"")] [DataRow(TIME_TYPE, "null")] - [DataRow(DATETIME_TYPE, "\"1999-01-08 09:20:00\"")] - [DataRow(DATETIME_TYPE, "\"1999-01-08\"")] + [DataRow(DATETIME_TYPE, "\"1753-01-01 00:00:00.000\"")] + [DataRow(DATETIME_TYPE, "\"9999-12-31 23:59:59.997\"")] [DataRow(DATETIME_TYPE, "null")] [DataRow(SMALLDATETIME_TYPE, "\"1900-01-01\"")] [DataRow(SMALLDATETIME_TYPE, "\"2079-06-06\"")] @@ -342,8 +344,8 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.9999999\"")] [DataRow(DATE_TYPE, "\"0001-01-01\"")] [DataRow(DATE_TYPE, "\"9999-12-31\"")] - [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54+8:00\"")] - [DataRow(DATETIMEOFFSET_TYPE, "\"1999-01-08 10:23:54.671287+8:00\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"0001-01-01 00:00:00.0000000\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31 23:59:59.9999999\"")] public async Task InsertIntoTypeColumn(string type, string value) { if (!IsSupportedType(type)) From 7ebf6ba396b551eec65390637fddea862a127daf Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 24 Aug 2023 14:09:59 +0530 Subject: [PATCH 71/85] Addressing review + allowing z notation --- src/Core/Models/GraphQLFilterParsers.cs | 2 +- .../MsSqlMetadataProvider.cs | 7 +++--- .../MySqlMetadataProvider.cs | 5 ---- .../PostgreSqlMetadataProvider.cs | 5 ---- .../MetadataProviders/SqlMetadataProvider.cs | 8 +++--- src/Core/Services/TypeHelper.cs | 25 ++++++++++++++----- .../RestApiTests/Find/FindApiTestBase.cs | 2 +- .../SqlTests/RestApiTests/RestApiTestBase.cs | 1 - 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/Core/Models/GraphQLFilterParsers.cs b/src/Core/Models/GraphQLFilterParsers.cs index 61f80701e4..cda05f912d 100644 --- a/src/Core/Models/GraphQLFilterParsers.cs +++ b/src/Core/Models/GraphQLFilterParsers.cs @@ -551,7 +551,7 @@ public static Predicate Parse( predicates.Push(new PredicateOperand(new Predicate( new PredicateOperand(column), op, - new PredicateOperand(processLiteral ? $"{processLiterals(value, column.ColumnName)}" : value.ToString())) + new PredicateOperand(processLiteral ? $"{processLiterals(value, null)}" : value.ToString())) )); } diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 77ab17c89a..baafa440cc 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -45,17 +45,18 @@ public override Type SqlToCLRType(string sqlType) } /// - public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) + public override DbType? SqlDbTypeToDbType(string sqlDbTypeName) { - if (TypeHelper.DateTimeMsSqlTypeToDbType.TryGetValue(sqlDateTimeType, out DbType dbType)) + if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { // For MsSql, all the date time types i.e. date, smalldatetime, datetime, datetime2 map to System.DateTime system type. // Hence we cannot directly determine the DbType from the system type. // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly // populate the DbTypes. - return dbType; + return TypeHelper.GetDbTypeFromSqlDbType(sqlDbType); } + // This code should never be hit because every sqlDbTypeName must have a corresponding sqlDbType. return null; } } diff --git a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs index 6db6d92b06..c29f1e821f 100644 --- a/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MySqlMetadataProvider.cs @@ -117,10 +117,5 @@ public override Type SqlToCLRType(string sqlType) { throw new NotImplementedException(); } - - public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) - { - return null; - } } } diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index 062a6a4668..7205b67b10 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -73,10 +73,5 @@ public override Type SqlToCLRType(string sqlType) { throw new NotImplementedException(); } - - public override DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType) - { - return null; - } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 0b6cc18e70..3c93f3834f 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -313,7 +313,7 @@ private async Task FillSchemaForStoredProcedureAsync( DbType = TypeHelper.GetDbTypeFromSystemType(systemType) }; - DbType? dbTypeForMsSqlDateTimeType = GetDbTypeForDateTimeTypes((string)row["DATA_TYPE"]); + DbType? dbTypeForMsSqlDateTimeType = SqlDbTypeToDbType((string)row["DATA_TYPE"]); if (dbTypeForMsSqlDateTimeType is not null) { paramDefinition.DbType = dbTypeForMsSqlDateTimeType; @@ -359,8 +359,8 @@ private async Task FillSchemaForStoredProcedureAsync( /// Takes a string version of a sql date/time type and returns its corresponding DbType. /// Returns null when the provided sql type is not a date/time type. /// - /// sql date/time/datetime type. - public abstract DbType? GetDbTypeForDateTimeTypes(string sqlDateTimeType); + /// Name of the SqlDb type. + public virtual DbType? SqlDbTypeToDbType(string sqlDbTypeName) => null; /// /// Generates the map used to find a given entity based @@ -1243,7 +1243,7 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( } columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); - DbType? dbTypeForMsSqlDateTimeType = GetDbTypeForDateTimeTypes((string)columnInfo["DATA_TYPE"]); + DbType? dbTypeForMsSqlDateTimeType = SqlDbTypeToDbType((string)columnInfo["DATA_TYPE"]); if (dbTypeForMsSqlDateTimeType is not null) { diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 20397b4c0a..15fa7ce190 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -111,13 +111,13 @@ public static class TypeHelper [SqlDbType.VarChar] = typeof(string) }; - public static Dictionary DateTimeMsSqlTypeToDbType = new() + public static Dictionary _sqlDbTypeToDbType = new() { - ["date"] = DbType.Date, - ["datetime"] = DbType.DateTime, - ["smalldatetime"] = DbType.DateTime, - ["datetime2"] = DbType.DateTime2, - ["datetimeoffset"] = DbType.DateTimeOffset + [SqlDbType.Date] = DbType.Date, + [SqlDbType.DateTime] = DbType.DateTime, + [SqlDbType.SmallDateTime] = DbType.DateTime, + [SqlDbType.DateTime2] = DbType.DateTime2, + [SqlDbType.DateTimeOffset] = DbType.DateTimeOffset }; /// @@ -239,5 +239,18 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) statusCode: HttpStatusCode.ServiceUnavailable, subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError); } + + /// + /// Helper method to get the DbType corresponding to the given SqlDbType. + /// + public static DbType? GetDbTypeFromSqlDbType(SqlDbType sqlDbType) + { + if (_sqlDbTypeToDbType.TryGetValue(sqlDbType, out DbType dbType)) + { + return dbType; + } + + return null; + } } } diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs index 8c41620673..165b2f889f 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs @@ -41,7 +41,7 @@ await SetupAndRunRestApiTest( await SetupAndRunRestApiTest( primaryKeyRoute: "categoryid/2/pieceid/1/instant/2023-08-21T15:11:04", queryString: string.Empty, - entityNameOrPath: _entityWithDateTimePK, + entityNameOrPath: _tableWithDateTimePK, sqlQuery: GetQuery("FindByDateTimePKTest") ); } diff --git a/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs index 99694d59f7..4dfae42d86 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/RestApiTestBase.cs @@ -57,7 +57,6 @@ public abstract class RestApiTestBase : SqlTestBase protected static readonly string _Composite_NonAutoGenPK_EntityPath = "commodities"; protected static readonly string _integrationTypeEntity = "SupportedType"; protected static readonly string _integrationTypeTable = "type_table"; - protected static readonly string _entityWithDateTimePK = "stocks_price"; protected static readonly string _tableWithDateTimePK = "stocks_price"; public abstract string GetQuery(string key); From f29c8c9ff8ab351349e910bc69023e4f4e62ef36 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 24 Aug 2023 14:15:50 +0530 Subject: [PATCH 72/85] formatting fix --- .../Services/MetadataProviders/PostgreSqlMetadataProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs index 7205b67b10..e4ebc6e919 100644 --- a/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/PostgreSqlMetadataProvider.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Data; using System.Net; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Resolvers; From f9f5ee2ebb6bf96cc1f7c3f795f260fd29ff3635 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 10 Oct 2023 14:48:25 +0530 Subject: [PATCH 73/85] updating parsing logic --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 5a513994d2..bb6b63ae47 100644 --- a/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Core/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Data; +using System.Globalization; using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config.DatabasePrimitives; @@ -360,8 +361,8 @@ protected static object ParseParamAsSystemType(string param, Type systemType) "Double" => double.Parse(param), "Decimal" => decimal.Parse(param), "Boolean" => bool.Parse(param), - "DateTime" => DateTime.Parse(param), - "DateTimeOffset" => DateTimeOffset.Parse(param), + "DateTime" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal).DateTime, + "DateTimeOffset" => DateTimeOffset.Parse(param, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal), "Date" => DateOnly.Parse(param), "Guid" => Guid.Parse(param), "TimeOnly" => TimeOnly.Parse(param), From 9e3d009262cd1045a8f59c975f08feb9b22637bc Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 10 Oct 2023 15:39:38 +0530 Subject: [PATCH 74/85] formatting fix --- src/Core/Models/GraphQLFilterParsers.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core/Models/GraphQLFilterParsers.cs b/src/Core/Models/GraphQLFilterParsers.cs index af97e26fe3..9b57734e46 100644 --- a/src/Core/Models/GraphQLFilterParsers.cs +++ b/src/Core/Models/GraphQLFilterParsers.cs @@ -560,10 +560,10 @@ public static Predicate Parse( throw new NotSupportedException($"Operation {name} on int type not supported."); } - predicates.Push(new PredicateOperand(new Predicate( - new PredicateOperand(column), - op, - new PredicateOperand(processLiteral ? $"{processLiterals(value, null)}" : value.ToString())) + predicates.Push(new PredicateOperand(new Predicate( + new PredicateOperand(column), + op, + new PredicateOperand(processLiteral ? $"{processLiterals(value, column.ColumnName)}" : value.ToString())) )); } From d7f6c8702b89e24da49afd6bf54fba661371125b Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 10 Oct 2023 15:51:38 +0530 Subject: [PATCH 75/85] formatting fix --- src/Core/Models/GraphQLFilterParsers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Models/GraphQLFilterParsers.cs b/src/Core/Models/GraphQLFilterParsers.cs index 9b57734e46..d1b93ff950 100644 --- a/src/Core/Models/GraphQLFilterParsers.cs +++ b/src/Core/Models/GraphQLFilterParsers.cs @@ -565,7 +565,7 @@ public static Predicate Parse( op, new PredicateOperand(processLiteral ? $"{processLiterals(value, column.ColumnName)}" : value.ToString())) )); - } + } return GQLFilterParser.MakeChainPredicate(predicates, PredicateOperation.AND); } From c61e91a8cb123734786df42fc1348a57b199aeb0 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Mon, 30 Oct 2023 17:01:42 +0530 Subject: [PATCH 76/85] Adding conditional to populate dbtype for datetime/datetimeoffset types for MsSql only --- .../MsSqlMetadataProvider.cs | 4 +--- .../MetadataProviders/SqlMetadataProvider.cs | 20 ++++++++++--------- src/Core/Services/TypeHelper.cs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 279d41c61d..193842ec18 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -32,8 +32,6 @@ public MsSqlMetadataProvider( { } - public static readonly HashSet DateTimeTypes = new() { "date", "smalldatetime", "datetime", "datetime2", "datetimeoffset" }; - public override string GetDefaultSchemaName() { return "dbo"; @@ -83,7 +81,7 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st } /// - public override DbType? SqlDbTypeToDbType(string sqlDbTypeName) + public override DbType? SqlDbDateTimeTypeToDbType(string sqlDbTypeName) { if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 994c789b5d..18bd3fa4b3 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -332,10 +332,12 @@ private async Task FillSchemaForStoredProcedureAsync( DbType = TypeHelper.GetDbTypeFromSystemType(systemType) }; - DbType? dbTypeForMsSqlDateTimeType = SqlDbTypeToDbType((string)row["DATA_TYPE"]); - if (dbTypeForMsSqlDateTimeType is not null) + if (GetDatabaseType() is DatabaseType.MSSQL && + (paramDefinition.SystemType == typeof(DateTime) || paramDefinition.SystemType == typeof(DateTimeOffset))) { - paramDefinition.DbType = dbTypeForMsSqlDateTimeType; + // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. + // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. + paramDefinition.DbType = SqlDbDateTimeTypeToDbType((string)row["DATA_TYPE"]); } // Add to parameters dictionary without the leading @ sign @@ -392,7 +394,7 @@ public virtual Task PopulateTriggerMetadataForTable(string entityName, string sc /// Returns null when the provided sql type is not a date/time type. /// /// Name of the SqlDb type. - public virtual DbType? SqlDbTypeToDbType(string sqlDbTypeName) => null; + public virtual DbType? SqlDbDateTimeTypeToDbType(string sqlDbTypeName) => throw new NotImplementedException(); /// /// Generates the map used to find a given entity based @@ -1039,7 +1041,6 @@ private async Task PopulateSourceDefinitionAsync( IsNullable = (bool)columnInfoFromAdapter["AllowDBNull"], IsAutoGenerated = (bool)columnInfoFromAdapter["IsAutoIncrement"], SystemType = systemTypeOfColumn, - DbType = TypeHelper.GetDbTypeFromSystemType(systemTypeOfColumn), // An auto-increment column is also considered as a read-only column. For other types of read-only columns, // the flag is populated later via PopulateColumnDefinitionsWithReadOnlyFlag() method. IsReadOnly = (bool)columnInfoFromAdapter["IsAutoIncrement"] @@ -1331,11 +1332,12 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( } columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); - DbType? dbTypeForMsSqlDateTimeType = SqlDbTypeToDbType((string)columnInfo["DATA_TYPE"]); - - if (dbTypeForMsSqlDateTimeType is not null) + if (GetDatabaseType() is DatabaseType.MSSQL && + (columnDefinition.SystemType == typeof(DateTime) || columnDefinition.SystemType == typeof(DateTimeOffset))) { - columnDefinition.DbType = dbTypeForMsSqlDateTimeType; + // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. + // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. + columnDefinition.DbType = SqlDbDateTimeTypeToDbType((string)columnInfo["DATA_TYPE"]); } } } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 15fa7ce190..0e6ffcd5cc 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -111,7 +111,7 @@ public static class TypeHelper [SqlDbType.VarChar] = typeof(string) }; - public static Dictionary _sqlDbTypeToDbType = new() + private static Dictionary _sqlDbDateTimeTypeToDbType = new() { [SqlDbType.Date] = DbType.Date, [SqlDbType.DateTime] = DbType.DateTime, @@ -245,7 +245,7 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) /// public static DbType? GetDbTypeFromSqlDbType(SqlDbType sqlDbType) { - if (_sqlDbTypeToDbType.TryGetValue(sqlDbType, out DbType dbType)) + if (_sqlDbDateTimeTypeToDbType.TryGetValue(sqlDbType, out DbType dbType)) { return dbType; } From 93794d59f865d191975eaf2218ef43e34022fe6e Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 1 Nov 2023 08:40:10 +0530 Subject: [PATCH 77/85] adding test for z notation insertions --- .../Services/MetadataProviders/MsSqlMetadataProvider.cs | 2 +- src/Core/Services/MetadataProviders/SqlMetadataProvider.cs | 6 +++--- .../GraphQLSupportedTypesTestsBase.cs | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 193842ec18..e9a909cec4 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -81,7 +81,7 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st } /// - public override DbType? SqlDbDateTimeTypeToDbType(string sqlDbTypeName) + public override DbType? ResolveDbType(string sqlDbTypeName) { if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index cf664b6c5b..0ff5e24b6d 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -337,7 +337,7 @@ private async Task FillSchemaForStoredProcedureAsync( { // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - paramDefinition.DbType = SqlDbDateTimeTypeToDbType((string)row["DATA_TYPE"]); + paramDefinition.DbType = ResolveDbType((string)row["DATA_TYPE"]); } // Add to parameters dictionary without the leading @ sign @@ -394,7 +394,7 @@ public virtual Task PopulateTriggerMetadataForTable(string entityName, string sc /// Returns null when the provided sql type is not a date/time type. /// /// Name of the SqlDb type. - public virtual DbType? SqlDbDateTimeTypeToDbType(string sqlDbTypeName) => throw new NotImplementedException(); + public virtual DbType? ResolveDbType(string sqlDbTypeName) => throw new NotImplementedException(); /// /// Generates the map used to find a given entity based @@ -1337,7 +1337,7 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( { // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - columnDefinition.DbType = SqlDbDateTimeTypeToDbType((string)columnInfo["DATA_TYPE"]); + columnDefinition.DbType = ResolveDbType((string)columnInfo["DATA_TYPE"]); } } } diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 7098d2565c..7283efc262 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -342,15 +342,18 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(TIME_TYPE, "null")] [DataRow(DATETIME_TYPE, "\"1753-01-01 00:00:00.000\"")] [DataRow(DATETIME_TYPE, "\"9999-12-31 23:59:59.997\"")] + [DataRow(DATETIME_TYPE, "\"9999-12-31 23:59:59.997Z\"")] [DataRow(DATETIME_TYPE, "null")] [DataRow(SMALLDATETIME_TYPE, "\"1900-01-01\"")] [DataRow(SMALLDATETIME_TYPE, "\"2079-06-06\"")] [DataRow(DATETIME2_TYPE, "\"0001-01-01 00:00:00.0000000\"")] [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.9999999\"")] + [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.999Z\"")] [DataRow(DATE_TYPE, "\"0001-01-01\"")] [DataRow(DATE_TYPE, "\"9999-12-31\"")] [DataRow(DATETIMEOFFSET_TYPE, "\"0001-01-01 00:00:00.0000000\"")] [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31 23:59:59.9999999\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31 23:59:59.9999999Z\"")] public async Task InsertIntoTypeColumn(string type, string value) { if (!IsSupportedType(type)) From 661e271b6bd3ad043815cc41f9a30d31cb359b57 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 16 Nov 2023 17:27:19 +0530 Subject: [PATCH 78/85] addressing review --- .../MsSqlMetadataProvider.cs | 10 +++++--- .../MetadataProviders/SqlMetadataProvider.cs | 25 +++++++++++++------ src/Core/Services/TypeHelper.cs | 13 +++++----- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index e9a909cec4..7f0c28f1b4 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System.Data; +using System.Diagnostics.CodeAnalysis; +using System.Net; using System.Text.Json; using System.Text.Json.Nodes; using Azure.DataApiBuilder.Config.DatabasePrimitives; @@ -9,6 +11,7 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Core.Resolvers.Factories; +using Azure.DataApiBuilder.Service.Exceptions; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -81,7 +84,7 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st } /// - public override DbType? ResolveDbType(string sqlDbTypeName) + public override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) { if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { @@ -89,11 +92,12 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st // Hence we cannot directly determine the DbType from the system type. // However, to make sure that the database correctly interprets these datatypes, it is necessary to correctly // populate the DbTypes. - return TypeHelper.GetDbTypeFromSqlDbType(sqlDbType); + return TypeHelper.TryGetDbTypeFromSqlDbDateTimeType(sqlDbType, out dbType); } // This code should never be hit because every sqlDbTypeName must have a corresponding sqlDbType. - return null; + dbType = 0; + return false; } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 0ff5e24b6d..8b145d6a95 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -325,7 +325,8 @@ private async Task FillSchemaForStoredProcedureAsync( { // row["DATA_TYPE"] has value type string so a direct cast to System.Type is not supported. // See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings - Type systemType = SqlToCLRType((string)row["DATA_TYPE"]); + string sqlType = (string)row["DATA_TYPE"]; + Type systemType = SqlToCLRType(sqlType); ParameterDefinition paramDefinition = new() { SystemType = systemType, @@ -337,7 +338,11 @@ private async Task FillSchemaForStoredProcedureAsync( { // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - paramDefinition.DbType = ResolveDbType((string)row["DATA_TYPE"]); + DbType dbType; + if (TryResolveDbType(sqlType, out dbType)) + { + paramDefinition.DbType = dbType; + } } // Add to parameters dictionary without the leading @ sign @@ -391,10 +396,11 @@ public virtual Task PopulateTriggerMetadataForTable(string entityName, string sc /// /// Takes a string version of a sql date/time type and returns its corresponding DbType. - /// Returns null when the provided sql type is not a date/time type. /// - /// Name of the SqlDb type. - public virtual DbType? ResolveDbType(string sqlDbTypeName) => throw new NotImplementedException(); + /// Name of the sqlDbType.< + /// DbType of the parameter corresponding to its sqlDbTypeName. + /// Returns true when the given sqlDbTypeName datetime type is supported by DAB and resolve it to its corresponding DbType, else false. + public virtual bool TryResolveDbType(string sqlDbTypeName, [NotNullWhen(true)] out DbType dbType) => throw new NotImplementedException(); /// /// Generates the map used to find a given entity based @@ -1035,12 +1041,11 @@ private async Task PopulateSourceDefinitionAsync( subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); } - Type systemTypeOfColumn = (Type)columnInfoFromAdapter["DataType"]; ColumnDefinition column = new() { IsNullable = (bool)columnInfoFromAdapter["AllowDBNull"], IsAutoGenerated = (bool)columnInfoFromAdapter["IsAutoIncrement"], - SystemType = systemTypeOfColumn, + SystemType = (Type)columnInfoFromAdapter["DataType"], // An auto-increment column is also considered as a read-only column. For other types of read-only columns, // the flag is populated later via PopulateColumnDefinitionsWithReadOnlyFlag() method. IsReadOnly = (bool)columnInfoFromAdapter["IsAutoIncrement"] @@ -1337,7 +1342,11 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( { // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - columnDefinition.DbType = ResolveDbType((string)columnInfo["DATA_TYPE"]); + DbType dbType; + if (TryResolveDbType((string)columnInfo["DATA_TYPE"], out dbType)) + { + columnDefinition.DbType = dbType; + } } } } diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index 0e6ffcd5cc..a28704499c 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Data; +using System.Diagnostics.CodeAnalysis; using System.Net; using Azure.DataApiBuilder.Core.Services.OpenAPI; using Azure.DataApiBuilder.Service.Exceptions; @@ -243,14 +244,12 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) /// /// Helper method to get the DbType corresponding to the given SqlDbType. /// - public static DbType? GetDbTypeFromSqlDbType(SqlDbType sqlDbType) + /// Underlying sqlDbType of the parameter. + /// DbType of the parameter corresponding to its sqlDbType. + /// True if given DateTime sqlDbType is supported by DAB, else false. + public static bool TryGetDbTypeFromSqlDbDateTimeType(SqlDbType sqlDbType, [NotNullWhen(true)] out DbType dbType) { - if (_sqlDbDateTimeTypeToDbType.TryGetValue(sqlDbType, out DbType dbType)) - { - return dbType; - } - - return null; + return _sqlDbDateTimeTypeToDbType.TryGetValue(sqlDbType, out dbType); } } } From 8dc9b41fadb1c6ad82dd2900ded868b92f8be6f6 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 16 Nov 2023 17:56:05 +0530 Subject: [PATCH 79/85] Addressing review/ renaming class --- .../MetadataProviders/MsSqlMetadataProvider.cs | 5 ++--- .../MetadataProviders/SqlMetadataProvider.cs | 11 ++++++++++- src/Core/Services/ResolverMiddleware.cs | 2 +- src/Core/Services/TypeHelper.cs | 2 +- .../GraphQLTypes/DefaultValueType.cs | 2 +- .../GraphQLTypes/SupportedTypes.cs | 16 ++++++++++++---- src/Service.GraphQLBuilder/GraphQLUtils.cs | 2 +- .../Queries/StandardQueryInputs.cs | 2 +- .../Sql/SchemaConverter.cs | 2 +- .../GraphQLBuilder/Sql/SchemaConverterTests.cs | 2 +- .../Sql/StoredProcedureBuilderTests.cs | 2 +- .../GraphQLSupportedTypesTestsBase.cs | 3 ++- .../MySqlGQLSupportedTypesTests.cs | 3 ++- .../PostgreSqlGQLSupportedTypesTests.cs | 3 ++- 14 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 7f0c28f1b4..16a273cdd2 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -2,8 +2,6 @@ // Licensed under the MIT License. using System.Data; -using System.Diagnostics.CodeAnalysis; -using System.Net; using System.Text.Json; using System.Text.Json.Nodes; using Azure.DataApiBuilder.Config.DatabasePrimitives; @@ -11,7 +9,6 @@ using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Core.Resolvers.Factories; -using Azure.DataApiBuilder.Service.Exceptions; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; @@ -96,6 +93,8 @@ public override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) } // This code should never be hit because every sqlDbTypeName must have a corresponding sqlDbType. + // However, when a new data type is introduced in MsSql which maps to .NET type of DateTime, this code block + // will be hit. Returning false instead of throwing an exception in that case prevents the engine from crashing. dbType = 0; return false; } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 8b145d6a95..eea8bc14e3 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -343,6 +343,10 @@ private async Task FillSchemaForStoredProcedureAsync( { paramDefinition.DbType = dbType; } + else + { + _logger.LogWarning("Could not determine DbType for SqlDb type of {sqlType}", sqlType); + } } // Add to parameters dictionary without the leading @ sign @@ -1343,10 +1347,15 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. DbType dbType; - if (TryResolveDbType((string)columnInfo["DATA_TYPE"], out dbType)) + string sqlType = (string)columnInfo["DATA_TYPE"]; + if (TryResolveDbType(sqlType, out dbType)) { columnDefinition.DbType = dbType; } + else + { + _logger.LogWarning("Could not determine DbType for SqlDb type of {sqlType}", sqlType); + } } } } diff --git a/src/Core/Services/ResolverMiddleware.cs b/src/Core/Services/ResolverMiddleware.cs index ae615f1f68..f57195bc28 100644 --- a/src/Core/Services/ResolverMiddleware.cs +++ b/src/Core/Services/ResolverMiddleware.cs @@ -18,7 +18,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using NodaTime.Text; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Core.Services { diff --git a/src/Core/Services/TypeHelper.cs b/src/Core/Services/TypeHelper.cs index a28704499c..911d305ef0 100644 --- a/src/Core/Services/TypeHelper.cs +++ b/src/Core/Services/TypeHelper.cs @@ -242,7 +242,7 @@ public static Type GetSystemTypeFromSqlDbType(string sqlDbTypeName) } /// - /// Helper method to get the DbType corresponding to the given SqlDbType. + /// Helper method to get the DbType corresponding to the given SqlDb datetime type. /// /// Underlying sqlDbType of the parameter. /// DbType of the parameter corresponding to its sqlDbType. diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index be27c13555..7db253c240 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -4,7 +4,7 @@ using Azure.DataApiBuilder.Service.GraphQLBuilder.CustomScalars; using HotChocolate.Types; using HotChocolate.Types.NodaTime; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes { diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index e71f27644a..bf8f96ca32 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -8,7 +8,7 @@ namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes /// The type names mentioned here are Hotchocolate scalar built in types. /// The corresponding SQL type name may be different for e.g. UUID maps to Guid as the SQL type. /// - public static class SupportedTypes + public static class SupportedHotChocolateTypes { public const string UUID_TYPE = "UUID"; public const string BYTE_TYPE = "Byte"; @@ -20,13 +20,21 @@ public static class SupportedTypes public const string DECIMAL_TYPE = "Decimal"; public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; - public const string DATE_TYPE = "Date"; - public const string SMALLDATETIME_TYPE = "SmallDateTime"; public const string DATETIME_TYPE = "DateTime"; - public const string DATETIME2_TYPE = "DateTime2"; public const string BYTEARRAY_TYPE = "ByteArray"; public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string LOCALTIME_TYPE = "LocalTime"; public const string TIME_TYPE = "Time"; } + + /// + /// Class representing the sql data types supported by DAB which do not have a + /// corresponding identical representation in HotChocolate scalar built in types. + /// + public static class SupportedSqlTypes + { + public const string DATE_TYPE = "date"; + public const string SMALLDATETIME_TYPE = "smalldatetime"; + public const string DATETIME2_TYPE = "datetime2"; + } } diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 2b97ba7e46..cef0b6ed35 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -16,7 +16,7 @@ using HotChocolate.Types; using HotChocolate.Types.NodaTime; using NodaTime.Text; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder { diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index 9566a320a1..e30aa36c9c 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -5,7 +5,7 @@ using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Types.NodaTime; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Queries { diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index 43cf23dcee..add876de84 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -15,7 +15,7 @@ using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLStoredProcedureBuilder; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index 1dc3c1faef..23687524e1 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -12,7 +12,7 @@ using Azure.DataApiBuilder.Service.GraphQLBuilder.Sql; using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index c4627a0681..a77a5f9dfa 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -16,7 +16,7 @@ using Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Helpers; using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 7283efc262..35e35ccdc5 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -8,7 +8,8 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 3fa4457f45..1249ee2b93 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -5,7 +5,8 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index b34766a97f..634dc212a9 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -5,7 +5,8 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { From 2ac66081a020cbf2d9319a1bef7d26046c533909 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Thu, 16 Nov 2023 18:37:07 +0530 Subject: [PATCH 80/85] added more tests --- .../GraphQLSupportedTypesTestsBase.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 35e35ccdc5..07e2cecae3 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -343,6 +343,7 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(TIME_TYPE, "null")] [DataRow(DATETIME_TYPE, "\"1753-01-01 00:00:00.000\"")] [DataRow(DATETIME_TYPE, "\"9999-12-31 23:59:59.997\"")] + [DataRow(DATETIME_TYPE, "\"9999-12-31T23:59:59.997\"")] [DataRow(DATETIME_TYPE, "\"9999-12-31 23:59:59.997Z\"")] [DataRow(DATETIME_TYPE, "null")] [DataRow(SMALLDATETIME_TYPE, "\"1900-01-01\"")] @@ -350,10 +351,12 @@ public async Task TestTimeTypePrecisionCheck(string gqlValue, int count) [DataRow(DATETIME2_TYPE, "\"0001-01-01 00:00:00.0000000\"")] [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.9999999\"")] [DataRow(DATETIME2_TYPE, "\"9999-12-31 23:59:59.999Z\"")] + [DataRow(DATETIME2_TYPE, "\"9999-12-31T23:59:59.9999999\"")] [DataRow(DATE_TYPE, "\"0001-01-01\"")] [DataRow(DATE_TYPE, "\"9999-12-31\"")] [DataRow(DATETIMEOFFSET_TYPE, "\"0001-01-01 00:00:00.0000000\"")] [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31 23:59:59.9999999\"")] + [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31T23:59:59.9999999\"")] [DataRow(DATETIMEOFFSET_TYPE, "\"9999-12-31 23:59:59.9999999Z\"")] public async Task InsertIntoTypeColumn(string type, string value) { From 9cd00cfa3ff920a5511ea226bc79208b1b63c22e Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Wed, 29 Nov 2023 22:06:05 +0530 Subject: [PATCH 81/85] renaming class/fixing build failure due to extended new() declaration --- .../MsSqlMetadataProvider.cs | 2 +- .../MetadataProviders/SqlMetadataProvider.cs | 2 +- .../GraphQLTypes/DefaultValueType.cs | 1 + .../GraphQLTypes/SupportedTypes.cs | 8 +- src/Service.GraphQLBuilder/GraphQLUtils.cs | 1 + .../Queries/StandardQueryInputs.cs | 173 +++++++++--------- .../Sql/SchemaConverter.cs | 1 + .../Sql/SchemaConverterTests.cs | 1 + .../Sql/StoredProcedureBuilderTests.cs | 1 + .../GraphQLSupportedTypesTestsBase.cs | 2 +- .../MySqlGQLSupportedTypesTests.cs | 2 +- .../PostgreSqlGQLSupportedTypesTests.cs | 2 +- 12 files changed, 101 insertions(+), 95 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index 16a273cdd2..e726134eff 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -81,7 +81,7 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st } /// - public override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) + protected override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) { if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index eea8bc14e3..6efe9eeb9f 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -404,7 +404,7 @@ public virtual Task PopulateTriggerMetadataForTable(string entityName, string sc /// Name of the sqlDbType.< /// DbType of the parameter corresponding to its sqlDbTypeName. /// Returns true when the given sqlDbTypeName datetime type is supported by DAB and resolve it to its corresponding DbType, else false. - public virtual bool TryResolveDbType(string sqlDbTypeName, [NotNullWhen(true)] out DbType dbType) => throw new NotImplementedException(); + protected virtual bool TryResolveDbType(string sqlDbTypeName, [NotNullWhen(true)] out DbType dbType) => throw new NotImplementedException(); /// /// Generates the map used to find a given entity based diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index 7db253c240..bcc405e876 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -5,6 +5,7 @@ using HotChocolate.Types; using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes { diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index bf8f96ca32..6a96aa047c 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -20,7 +20,6 @@ public static class SupportedHotChocolateTypes public const string DECIMAL_TYPE = "Decimal"; public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; - public const string DATETIME_TYPE = "DateTime"; public const string BYTEARRAY_TYPE = "ByteArray"; public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string LOCALTIME_TYPE = "LocalTime"; @@ -28,11 +27,12 @@ public static class SupportedHotChocolateTypes } /// - /// Class representing the sql data types supported by DAB which do not have a - /// corresponding identical representation in HotChocolate scalar built in types. + /// Class representing the sql datetime types supported by DAB which all map to the same .NET type of DateTime + /// and Hotchocolate scalar type of DateTime. /// - public static class SupportedSqlTypes + public static class SupportedDateTimeTypes { + public const string DATETIME_TYPE = "DateTime"; public const string DATE_TYPE = "date"; public const string SMALLDATETIME_TYPE = "smalldatetime"; public const string DATETIME2_TYPE = "datetime2"; diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index cef0b6ed35..5eb54f2094 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -17,6 +17,7 @@ using HotChocolate.Types.NodaTime; using NodaTime.Text; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder { diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index e30aa36c9c..fd95d0f193 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -6,6 +6,7 @@ using HotChocolate.Types; using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Queries { @@ -18,9 +19,9 @@ public static InputObjectTypeDefinitionNode IdInputType() => new StringValueNode("Input type for adding ID filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new IdType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new IdType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new IdType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new IdType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -31,9 +32,9 @@ public static InputObjectTypeDefinitionNode BooleanInputType() => new StringValueNode("Input type for adding Boolean filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new BooleanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new BooleanType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new BooleanType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new BooleanType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -44,13 +45,13 @@ public static InputObjectTypeDefinitionNode ByteInputType() => new StringValueNode("Input type for adding Byte filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new ByteType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new ByteType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -61,13 +62,13 @@ public static InputObjectTypeDefinitionNode ShortInputType() => new StringValueNode("Input type for adding Short filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new ShortType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new ShortType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -78,13 +79,13 @@ public static InputObjectTypeDefinitionNode IntInputType() => new StringValueNode("Input type for adding Int filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new IntType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new IntType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -95,13 +96,13 @@ public static InputObjectTypeDefinitionNode LongInputType() => new StringValueNode("Input type for adding Long filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LongType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LongType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -112,13 +113,13 @@ public static InputObjectTypeDefinitionNode SingleInputType() => new StringValueNode("Input type for adding Single filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new SingleType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new SingleType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -129,13 +130,13 @@ public static InputObjectTypeDefinitionNode FloatInputType() => new StringValueNode("Input type for adding Float filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new FloatType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new FloatType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -146,13 +147,13 @@ public static InputObjectTypeDefinitionNode DecimalInputType() => new StringValueNode("Input type for adding Decimal filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new DecimalType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new DecimalType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -163,14 +164,14 @@ public static InputObjectTypeDefinitionNode StringInputType() => new StringValueNode("Input type for adding String filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("contains"), new StringValueNode("Contains"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("notContains"), new StringValueNode("Not Contains"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("startsWith"), new StringValueNode("Starts With"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new StringType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("contains"), new StringValueNode("Contains"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("notContains"), new StringValueNode("Not Contains"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("startsWith"), new StringValueNode("Starts With"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new StringType().ToTypeNode(), null, new List()), + new(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List()), + new(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -181,13 +182,13 @@ public static InputObjectTypeDefinitionNode DateTimeInputType() => new StringValueNode("Input type for adding DateTime filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new DateTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new DateTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Not null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -198,7 +199,7 @@ public static InputObjectTypeDefinitionNode ByteArrayInputType() => new StringValueNode("Input type for adding ByteArray filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -209,13 +210,13 @@ public static InputObjectTypeDefinitionNode LocalTimeInputType() => new StringValueNode("Input type for adding LocalTime filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gt"), new StringValueNode("Greater Than"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lt"), new StringValueNode("Less Than"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LocalTimeType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("gt"), new StringValueNode("Greater Than"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("gte"), new StringValueNode("Greater Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("lt"), new StringValueNode("Less Than"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("lte"), new StringValueNode("Less Than or Equal To"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new LocalTimeType().ToTypeNode(), null, new List()), + new(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); @@ -226,14 +227,14 @@ public static InputObjectTypeDefinitionNode UuidInputType() => new StringValueNode("Input type for adding Uuid filters"), new List(), new List { - new InputValueDefinitionNode(null, new NameNode("eq"), new StringValueNode("Equals"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("contains"), new StringValueNode("Contains"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("notContains"), new StringValueNode("Not Contains"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("startsWith"), new StringValueNode("Starts With"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("neq"), new StringValueNode("Not Equals"), new UuidType().ToTypeNode(), null, new List()), - new InputValueDefinitionNode(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List()), - new InputValueDefinitionNode(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) + new(null, new NameNode("eq"), new StringValueNode("Equals"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("contains"), new StringValueNode("Contains"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("notContains"), new StringValueNode("Not Contains"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("startsWith"), new StringValueNode("Starts With"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("endsWith"), new StringValueNode("Ends With"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("neq"), new StringValueNode("Not Equals"), new UuidType().ToTypeNode(), null, new List()), + new(null, new NameNode("caseInsensitive"), new StringValueNode("Case Insensitive"), new BooleanType().ToTypeNode(), new BooleanValueNode(false), new List()), + new(null, new NameNode("isNull"), new StringValueNode("Is null test"), new BooleanType().ToTypeNode(), null, new List()) } ); diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index add876de84..a1fd3f06e6 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -16,6 +16,7 @@ using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLStoredProcedureBuilder; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index 23687524e1..105135de0a 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -13,6 +13,7 @@ using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index a77a5f9dfa..7016c362b7 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -17,6 +17,7 @@ using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 07e2cecae3..99e3eb38f5 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 1249ee2b93..7f15678f64 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index 634dc212a9..4a43d30c3f 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedSqlTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { From 3d42007f266fab2ef1208d28c0aa2c31e2a43fd9 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 5 Dec 2023 13:22:54 +0530 Subject: [PATCH 82/85] moving back constant/correct test displaynames --- src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs | 6 +++--- .../GraphQLSupportedTypesTestsBase.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs index 6a96aa047c..c5d033de1c 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs @@ -21,18 +21,18 @@ public static class SupportedHotChocolateTypes public const string STRING_TYPE = "String"; public const string BOOLEAN_TYPE = "Boolean"; public const string BYTEARRAY_TYPE = "ByteArray"; + public const string DATETIME_TYPE = "DateTime"; public const string DATETIMEOFFSET_TYPE = "DateTimeOffset"; public const string LOCALTIME_TYPE = "LocalTime"; public const string TIME_TYPE = "Time"; } /// - /// Class representing the sql datetime types supported by DAB which all map to the same .NET type of DateTime - /// and Hotchocolate scalar type of DateTime. + /// Class representing the sql datetime types supported by DAB which in addition to the sql datetime type, + /// all map to the same .NET type of DateTime and Hotchocolate scalar type of DateTime. /// public static class SupportedDateTimeTypes { - public const string DATETIME_TYPE = "DateTime"; public const string DATE_TYPE = "date"; public const string SMALLDATETIME_TYPE = "smalldatetime"; public const string DATETIME2_TYPE = "datetime2"; diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 99e3eb38f5..99da88ee8b 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -40,10 +40,10 @@ public abstract class GraphQLSupportedTypesTestBase : SqlTestBase [DataRow(SINGLE_TYPE, 2, DisplayName = "Query by PK test selecting only single_types with typeid = 2.")] [DataRow(SINGLE_TYPE, 3, DisplayName = "Query by PK test selecting only single_types with typeid = 3.")] [DataRow(SINGLE_TYPE, 4, DisplayName = "Query by PK test selecting only single_types with typeid = 4.")] - [DataRow(FLOAT_TYPE, 1, DisplayName = "Query by PK test selecting only byte_type with typeid = 1.")] - [DataRow(FLOAT_TYPE, 2, DisplayName = "Query by PK test selecting only byte_type with typeid = 2.")] - [DataRow(FLOAT_TYPE, 3, DisplayName = "Query by PK test selecting only byte_type with typeid = 3.")] - [DataRow(FLOAT_TYPE, 4, DisplayName = "Query by PK test selecting only byte_type with typeid = 4.")] + [DataRow(FLOAT_TYPE, 1, DisplayName = "Query by PK test selecting only float_types with typeid = 1.")] + [DataRow(FLOAT_TYPE, 2, DisplayName = "Query by PK test selecting only float_types with typeid = 2.")] + [DataRow(FLOAT_TYPE, 3, DisplayName = "Query by PK test selecting only float_types with typeid = 3.")] + [DataRow(FLOAT_TYPE, 4, DisplayName = "Query by PK test selecting only float_types with typeid = 4.")] [DataRow(DECIMAL_TYPE, 1, DisplayName = "Query by PK test selecting only decimal_types with typeid = 1.")] [DataRow(DECIMAL_TYPE, 2, DisplayName = "Query by PK test selecting only decimal_types with typeid = 2.")] [DataRow(DECIMAL_TYPE, 3, DisplayName = "Query by PK test selecting only decimal_types with typeid = 3.")] From 3322c6e7d969dbcdf6590829596e78c3a56fa119 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 5 Dec 2023 13:39:19 +0530 Subject: [PATCH 83/85] Moving mssql specific code to its class --- .../MsSqlMetadataProvider.cs | 151 +++++++++++++++++- .../MetadataProviders/SqlMetadataProvider.cs | 46 +----- 2 files changed, 147 insertions(+), 50 deletions(-) diff --git a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs index e726134eff..daa1aac905 100644 --- a/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs @@ -2,15 +2,20 @@ // Licensed under the MIT License. using System.Data; +using System.Data.Common; +using System.Net; using System.Text.Json; using System.Text.Json.Nodes; using Azure.DataApiBuilder.Config.DatabasePrimitives; +using Azure.DataApiBuilder.Config.ObjectModel; using Azure.DataApiBuilder.Core.Configurations; using Azure.DataApiBuilder.Core.Models; using Azure.DataApiBuilder.Core.Resolvers; using Azure.DataApiBuilder.Core.Resolvers.Factories; +using Azure.DataApiBuilder.Service.Exceptions; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; namespace Azure.DataApiBuilder.Core.Services { @@ -81,7 +86,136 @@ public override async Task PopulateTriggerMetadataForTable(string entityName, st } /// - protected override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) + protected override void PopulateColumnDefinitionWithHasDefaultAndDbType( + SourceDefinition sourceDefinition, + DataTable allColumnsInTable) + { + foreach (DataRow columnInfo in allColumnsInTable.Rows) + { + string columnName = (string)columnInfo["COLUMN_NAME"]; + bool hasDefault = + Type.GetTypeCode(columnInfo["COLUMN_DEFAULT"].GetType()) != TypeCode.DBNull; + if (sourceDefinition.Columns.TryGetValue(columnName, out ColumnDefinition? columnDefinition)) + { + columnDefinition.HasDefault = hasDefault; + + if (hasDefault) + { + columnDefinition.DefaultValue = columnInfo["COLUMN_DEFAULT"]; + } + + columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); + if (columnDefinition.SystemType == typeof(DateTime) || columnDefinition.SystemType == typeof(DateTimeOffset)) + { + // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. + // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. + DbType dbType; + string sqlType = (string)columnInfo["DATA_TYPE"]; + if (TryResolveDbType(sqlType, out dbType)) + { + columnDefinition.DbType = dbType; + } + } + } + } + } + + /// + protected override async Task FillSchemaForStoredProcedureAsync( + Entity procedureEntity, + string entityName, + string schemaName, + string storedProcedureSourceName, + StoredProcedureDefinition storedProcedureDefinition) + { + using DbConnection conn = new SqlConnection(); + conn.ConnectionString = ConnectionString; + await QueryExecutor.SetManagedIdentityAccessTokenIfAnyAsync(conn, _dataSourceName); + await conn.OpenAsync(); + + string[] procedureRestrictions = new string[NUMBER_OF_RESTRICTIONS]; + + // To restrict the parameters for the current stored procedure, specify its name + procedureRestrictions[0] = conn.Database; + procedureRestrictions[1] = schemaName; + procedureRestrictions[2] = storedProcedureSourceName; + + DataTable procedureMetadata = await conn.GetSchemaAsync(collectionName: "Procedures", restrictionValues: procedureRestrictions); + + // Stored procedure does not exist in DB schema + if (procedureMetadata.Rows.Count == 0) + { + throw new DataApiBuilderException( + message: $"No stored procedure definition found for the given database object {storedProcedureSourceName}", + statusCode: HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); + } + + // Each row in the procedureParams DataTable corresponds to a single parameter + DataTable parameterMetadata = await conn.GetSchemaAsync(collectionName: "ProcedureParameters", restrictionValues: procedureRestrictions); + + // For each row/parameter, add an entry to StoredProcedureDefinition.Parameters dictionary + foreach (DataRow row in parameterMetadata.Rows) + { + // row["DATA_TYPE"] has value type string so a direct cast to System.Type is not supported. + // See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings + string sqlType = (string)row["DATA_TYPE"]; + Type systemType = SqlToCLRType(sqlType); + ParameterDefinition paramDefinition = new() + { + SystemType = systemType, + DbType = TypeHelper.GetDbTypeFromSystemType(systemType) + }; + + if (paramDefinition.SystemType == typeof(DateTime) || paramDefinition.SystemType == typeof(DateTimeOffset)) + { + // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. + // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. + DbType dbType; + if (TryResolveDbType(sqlType, out dbType)) + { + paramDefinition.DbType = dbType; + } + } + + // Add to parameters dictionary without the leading @ sign + storedProcedureDefinition.Parameters.TryAdd(((string)row["PARAMETER_NAME"])[1..], paramDefinition); + } + + // Loop through parameters specified in config, throw error if not found in schema + // else set runtime config defined default values. + // Note: we defer type checking of parameters specified in config until request time + Dictionary? configParameters = procedureEntity.Source.Parameters; + if (configParameters is not null) + { + foreach ((string configParamKey, object configParamValue) in configParameters) + { + if (!storedProcedureDefinition.Parameters.TryGetValue(configParamKey, out ParameterDefinition? parameterDefinition)) + { + throw new DataApiBuilderException( + message: $"Could not find parameter \"{configParamKey}\" specified in config for procedure \"{schemaName}.{storedProcedureSourceName}\"", + statusCode: HttpStatusCode.ServiceUnavailable, + subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization); + } + else + { + parameterDefinition.HasConfigDefault = true; + parameterDefinition.ConfigDefaultValue = configParamValue?.ToString(); + } + } + } + + // Generating exposed stored-procedure query/mutation name and adding to the dictionary mapping it to its entity name. + GraphQLStoredProcedureExposedNameToEntityNameMap.TryAdd(GenerateStoredProcedureGraphQLFieldName(entityName, procedureEntity), entityName); + } + + /// + /// Takes a string version of a sql date/time type and returns its corresponding DbType. + /// + /// Name of the sqlDbType.< + /// DbType of the parameter corresponding to its sqlDbTypeName. + /// Returns true when the given sqlDbTypeName datetime type is supported by DAB and resolve it to its corresponding DbType, else false. + private bool TryResolveDbType(string sqlDbTypeName, out DbType dbType) { if (Enum.TryParse(sqlDbTypeName, ignoreCase: true, out SqlDbType sqlDbType)) { @@ -91,12 +225,15 @@ protected override bool TryResolveDbType(string sqlDbTypeName, out DbType dbType // populate the DbTypes. return TypeHelper.TryGetDbTypeFromSqlDbDateTimeType(sqlDbType, out dbType); } - - // This code should never be hit because every sqlDbTypeName must have a corresponding sqlDbType. - // However, when a new data type is introduced in MsSql which maps to .NET type of DateTime, this code block - // will be hit. Returning false instead of throwing an exception in that case prevents the engine from crashing. - dbType = 0; - return false; + else + { + // This code should never be hit because every sqlDbTypeName must have a corresponding sqlDbType. + // However, when a new data type is introduced in MsSql which maps to .NET type of DateTime, this code block + // will be hit. Returning false instead of throwing an exception in that case prevents the engine from crashing. + _logger.LogWarning("Could not determine DbType for SqlDb type of {sqlDbTypeName}", sqlDbTypeName); + dbType = 0; + return false; + } } } } diff --git a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs index 6efe9eeb9f..75b81fe969 100644 --- a/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Core/Services/MetadataProviders/SqlMetadataProvider.cs @@ -50,7 +50,7 @@ public abstract class SqlMetadataProvider : protected IQueryExecutor QueryExecutor { get; } - private const int NUMBER_OF_RESTRICTIONS = 4; + protected const int NUMBER_OF_RESTRICTIONS = 4; protected string ConnectionString { get; init; } @@ -287,7 +287,7 @@ private void LogPrimaryKeys() /// /// Verify that the stored procedure exists in the database schema, then populate its database object parameters accordingly /// - private async Task FillSchemaForStoredProcedureAsync( + protected virtual async Task FillSchemaForStoredProcedureAsync( Entity procedureEntity, string entityName, string schemaName, @@ -333,22 +333,6 @@ private async Task FillSchemaForStoredProcedureAsync( DbType = TypeHelper.GetDbTypeFromSystemType(systemType) }; - if (GetDatabaseType() is DatabaseType.MSSQL && - (paramDefinition.SystemType == typeof(DateTime) || paramDefinition.SystemType == typeof(DateTimeOffset))) - { - // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. - // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - DbType dbType; - if (TryResolveDbType(sqlType, out dbType)) - { - paramDefinition.DbType = dbType; - } - else - { - _logger.LogWarning("Could not determine DbType for SqlDb type of {sqlType}", sqlType); - } - } - // Add to parameters dictionary without the leading @ sign storedProcedureDefinition.Parameters.TryAdd(((string)row["PARAMETER_NAME"])[1..], paramDefinition); } @@ -398,14 +382,6 @@ public virtual Task PopulateTriggerMetadataForTable(string entityName, string sc throw new NotImplementedException(); } - /// - /// Takes a string version of a sql date/time type and returns its corresponding DbType. - /// - /// Name of the sqlDbType.< - /// DbType of the parameter corresponding to its sqlDbTypeName. - /// Returns true when the given sqlDbTypeName datetime type is supported by DAB and resolve it to its corresponding DbType, else false. - protected virtual bool TryResolveDbType(string sqlDbTypeName, [NotNullWhen(true)] out DbType dbType) => throw new NotImplementedException(); - /// /// Generates the map used to find a given entity based /// on the path that will be used for that entity. @@ -1322,7 +1298,7 @@ protected virtual async Task GetColumnsAsync( /// /// Helper method to populate the column definition with HasDefault and DbType properties. /// - private void PopulateColumnDefinitionWithHasDefaultAndDbType( + protected virtual void PopulateColumnDefinitionWithHasDefaultAndDbType( SourceDefinition sourceDefinition, DataTable allColumnsInTable) { @@ -1341,22 +1317,6 @@ private void PopulateColumnDefinitionWithHasDefaultAndDbType( } columnDefinition.DbType = TypeHelper.GetDbTypeFromSystemType(columnDefinition.SystemType); - if (GetDatabaseType() is DatabaseType.MSSQL && - (columnDefinition.SystemType == typeof(DateTime) || columnDefinition.SystemType == typeof(DateTimeOffset))) - { - // MsSql types like date,smalldatetime,datetime,datetime2 are mapped to the same .NET type of DateTime. - // Thus to determine the actual dbtype, we use the underlying MsSql type instead of the .NET type. - DbType dbType; - string sqlType = (string)columnInfo["DATA_TYPE"]; - if (TryResolveDbType(sqlType, out dbType)) - { - columnDefinition.DbType = dbType; - } - else - { - _logger.LogWarning("Could not determine DbType for SqlDb type of {sqlType}", sqlType); - } - } } } } From 89b93ed18e01908fdfeb6661810fae64a72313a8 Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 5 Dec 2023 14:15:52 +0530 Subject: [PATCH 84/85] fixing build failures --- src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs | 1 - src/Service.GraphQLBuilder/GraphQLUtils.cs | 1 - src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs | 1 - src/Service.GraphQLBuilder/Sql/SchemaConverter.cs | 1 - src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs | 1 - .../GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs | 1 - .../GraphQLSupportedTypesTestsBase.cs | 2 +- .../GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs | 2 +- .../PostgreSqlGQLSupportedTypesTests.cs | 2 +- 9 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs index bcc405e876..7db253c240 100644 --- a/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs +++ b/src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs @@ -5,7 +5,6 @@ using HotChocolate.Types; using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes { diff --git a/src/Service.GraphQLBuilder/GraphQLUtils.cs b/src/Service.GraphQLBuilder/GraphQLUtils.cs index 5eb54f2094..cef0b6ed35 100644 --- a/src/Service.GraphQLBuilder/GraphQLUtils.cs +++ b/src/Service.GraphQLBuilder/GraphQLUtils.cs @@ -17,7 +17,6 @@ using HotChocolate.Types.NodaTime; using NodaTime.Text; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder { diff --git a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs index fd95d0f193..ddb29a16dc 100644 --- a/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs +++ b/src/Service.GraphQLBuilder/Queries/StandardQueryInputs.cs @@ -6,7 +6,6 @@ using HotChocolate.Types; using HotChocolate.Types.NodaTime; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Queries { diff --git a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs index a1fd3f06e6..add876de84 100644 --- a/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs +++ b/src/Service.GraphQLBuilder/Sql/SchemaConverter.cs @@ -16,7 +16,6 @@ using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLNaming; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLStoredProcedureBuilder; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs index 105135de0a..23687524e1 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/SchemaConverterTests.cs @@ -13,7 +13,6 @@ using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs index 7016c362b7..a77a5f9dfa 100644 --- a/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs +++ b/src/Service.Tests/GraphQLBuilder/Sql/StoredProcedureBuilderTests.cs @@ -17,7 +17,6 @@ using HotChocolate.Language; using Microsoft.VisualStudio.TestTools.UnitTesting; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Sql { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs index 99da88ee8b..dc684d3eb2 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/GraphQLSupportedTypesTestsBase.cs @@ -8,8 +8,8 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs index 7f15678f64..36f47fcb43 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/MySqlGQLSupportedTypesTests.cs @@ -5,8 +5,8 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { diff --git a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs index 4a43d30c3f..79e5684b83 100644 --- a/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLSupportedTypesTests/PostgreSqlGQLSupportedTypesTests.cs @@ -5,8 +5,8 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedDateTimeTypes; +using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLTypes.SupportedHotChocolateTypes; namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLSupportedTypesTests { From b3f0f07902f26c4b99846e2edbdbcad15c56dc9e Mon Sep 17 00:00:00 2001 From: Ayush Agarwal Date: Tue, 5 Dec 2023 15:15:41 +0530 Subject: [PATCH 85/85] ignoring datetime test for dwsql for now --- .../SqlTests/RestApiTests/Find/DwSqlFindApiTests.cs | 7 +++++++ .../SqlTests/RestApiTests/Find/FindApiTestBase.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/DwSqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/DwSqlFindApiTests.cs index 16cd14b1c9..9db00fada2 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/DwSqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/DwSqlFindApiTests.cs @@ -593,6 +593,13 @@ public override string GetQuery(string key) return _queryMap[key]; } + [TestMethod] + [Ignore] + public override Task FindByDateTimePKTest() + { + throw new NotImplementedException(); + } + // Pending Stored Procedure Support [TestMethod] [Ignore] diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs index f03982ebed..8c3111d8bc 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs @@ -36,7 +36,14 @@ await SetupAndRunRestApiTest( entityNameOrPath: _integrationEntityName, sqlQuery: GetQuery("FindByIdTest") ); + } + /// + /// Tests the REST Api for FindByDateTimePk operation without a query string. + /// + [TestMethod] + public virtual async Task FindByDateTimePKTest() + { await SetupAndRunRestApiTest( primaryKeyRoute: "categoryid/2/pieceid/1/instant/2023-08-21T15:11:04", queryString: string.Empty,