From 1c27a649f3e8a2bc0ee73853fdc2ad4a659026ff Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 16 Feb 2023 13:28:22 +0530 Subject: [PATCH 01/28] new method in metadataprovider for dev,prod mode info --- .../Services/MetadataProviders/CosmosSqlMetadataProvider.cs | 5 +++++ .../Services/MetadataProviders/ISqlMetadataProvider.cs | 6 ++++++ .../Services/MetadataProviders/SqlMetadataProvider.cs | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs index fb6ee3a6eb..597fa7d275 100644 --- a/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs @@ -235,5 +235,10 @@ public string GetDefaultSchemaName() { return string.Empty; } + + public bool IsDevelopmentMode() + { + return _runtimeConfig.HostGlobalSettings.Mode is HostModeType.Development; + } } } diff --git a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs index b628101dd4..66bc80531f 100644 --- a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs @@ -159,5 +159,11 @@ public DatabaseObject GetDatabaseObjectForGraphQLType(string graphqlType) /// Retrieves the default schema name for this metadata provider. /// public string GetDefaultSchemaName(); + + /// + /// + /// + /// + public bool IsDevelopmentMode(); } } diff --git a/src/Service/Services/MetadataProviders/SqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/SqlMetadataProvider.cs index dc0d0f636c..7763c116a7 100644 --- a/src/Service/Services/MetadataProviders/SqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/SqlMetadataProvider.cs @@ -1495,6 +1495,11 @@ public bool VerifyForeignKeyExistsInDB( /// public void SetPartitionKeyPath(string database, string container, string partitionKeyPath) => throw new NotImplementedException(); + + public bool IsDevelopmentMode() + { + return _runtimeConfigProvider.IsDeveloperMode(); + } } } From 6b3b60fcc1863bfcb1ddd4537b8ae4daaafaa1ba Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 16 Feb 2023 13:29:21 +0530 Subject: [PATCH 02/28] refactoring system type conversion to reduce code duplciation --- .../BaseSqlQueryStructure.cs | 54 +++++++++++++++++++ .../SqlDeleteQueryStructure.cs | 3 +- .../SqlExecuteQueryStructure.cs | 40 +++++--------- .../SqlInsertQueryStructure.cs | 3 +- .../Sql Query Structures/SqlQueryStructure.cs | 4 +- .../SqlUpdateQueryStructure.cs | 2 +- .../SqlUpsertQueryStructure.cs | 2 +- 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index edbe5eae8a..fca9b1b281 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -488,5 +488,59 @@ public void ProcessOdataClause(FilterClause odataClause) { return filterClause.Expression.Accept(visitor); } + + /// + /// Gets the value of the parameter cast as the system type + /// of the stored procedure parameter this parameter is associated with + /// + protected object GetParamAsSystemType(string param, string fieldName, Type systemType) + { + try + { + return ParseParamAsSystemType(param, systemType); + } + catch (Exception e) + { + if (e is FormatException || + e is ArgumentNullException || + e is OverflowException) + { + if(MetadataProvider.IsDevelopmentMode()) + { + if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) + { + throw new DataApiBuilderException( + message: $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + + $@"with type ""{systemType.Name}"".", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, + innerException:e + ); + } + else + { + throw new DataApiBuilderException( + message: $"Parameter \"{param}\" cannot be resolved as column \"{fieldName}\" " + + $"with type \"{systemType.Name}\".", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, + innerException: e); + } + + } + else + { + throw new DataApiBuilderException( + message: $"", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, + innerException:e); + } + } + + throw; + } + } + } } diff --git a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs index eea519cfb2..176fdfb1a5 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Net; using Azure.DataApiBuilder.Auth; @@ -45,7 +46,7 @@ public SqlDeleteStructure( Predicates.Add(new Predicate( new PredicateOperand(new Column(DatabaseObject.SchemaName, DatabaseObject.Name, backingColumn!)), PredicateOperation.Equal, - new PredicateOperand($"@{MakeParamWithValue(GetParamAsColumnSystemType(param.Value.ToString()!, backingColumn!))}") + new PredicateOperand($"@{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, backingColumn!, GetColumnSystemType(backingColumn!)))}") )); } } diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index e95b998070..9a9d18f76d 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -46,9 +46,18 @@ public SqlExecuteStructure( // Parameterize, then add referencing parameter to ProcedureParameters dictionary try { - string parameterizedName = MakeParamWithValue(requestParamValue is null ? null : - GetParamAsProcedureParameterType(requestParamValue.ToString()!, paramKey)); - ProcedureParameters.Add(paramKey, $"@{parameterizedName}"); + string? parametrizedName = null; + if(requestParamValue is not null) + { + Type systemType = GetUnderlyingStoredProcedureDefinition().Parameters[paramKey].SystemType!; + parametrizedName = MakeParamWithValue(GetParamAsSystemType(requestParamValue.ToString()!, paramKey, systemType)); + } + else + { + parametrizedName = MakeParamWithValue(null); + } + + ProcedureParameters.Add(paramKey, $"@{parametrizedName}"); } catch (ArgumentException ex) { @@ -80,30 +89,5 @@ public SqlExecuteStructure( } } } - - /// - /// Gets the value of the parameter cast as the system type - /// of the stored procedure parameter this parameter is associated with - /// - private object GetParamAsProcedureParameterType(string param, string procParamName) - { - Type systemType = GetUnderlyingStoredProcedureDefinition().Parameters[procParamName].SystemType!; - try - { - return ParseParamAsSystemType(param, systemType); - } - catch (Exception e) - { - if (e is FormatException || - e is ArgumentNullException || - e is OverflowException) - { - throw new ArgumentException($@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{procParamName}"" " + - $@"with type ""{systemType.Name}"".", innerException: e); - } - - throw; - } - } } } diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index 84bf618da9..bae8347b96 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config; @@ -84,7 +85,7 @@ private void PopulateColumnsAndParams(string columnName, object? value) if (value != null) { paramName = MakeParamWithValue( - GetParamAsColumnSystemType(value.ToString()!, columnName)); + GetParamAsSystemType(value.ToString()!, columnName, GetColumnSystemType(columnName))); } else { diff --git a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs index d02034077f..da9089d25a 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -506,7 +506,7 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues { column.TableAlias = SourceAlias; column.ParamName = column.Value is not null ? - "@" + MakeParamWithValue(GetParamAsColumnSystemType(column.Value!.ToString()!, column.ColumnName)) : + "@" + MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : "@" + MakeParamWithValue(null); } } @@ -538,7 +538,7 @@ private void PopulateParamsAndPredicates(string field, string backingColumn, obj if (value != null) { parameterName = MakeParamWithValue( - GetParamAsColumnSystemType(value.ToString()!, backingColumn)); + GetParamAsSystemType(value.ToString()!, backingColumn, GetColumnSystemType(backingColumn))); Predicates.Add(new Predicate( new PredicateOperand(new Column(DatabaseObject.SchemaName, DatabaseObject.Name, backingColumn, SourceAlias)), op, diff --git a/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs index 70ad005aed..391222aeaa 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs @@ -167,7 +167,7 @@ private Predicate CreatePredicateForParam(KeyValuePair param) new PredicateOperand( new Column(tableSchema: DatabaseObject.SchemaName, tableName: DatabaseObject.Name, param.Key)), PredicateOperation.Equal, - new PredicateOperand($"@{MakeParamWithValue(GetParamAsColumnSystemType(param.Value.ToString()!, param.Key))}")); + new PredicateOperand($"@{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, param.Key, GetColumnSystemType(param.Key)))}")); } return predicate; diff --git a/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs index 7e1febe66f..07eefdc022 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs @@ -115,7 +115,7 @@ private void PopulateColumns( string paramIdentifier; if (param.Value != null) { - paramIdentifier = MakeParamWithValue(GetParamAsColumnSystemType(param.Value.ToString()!, backingColumn!)); + paramIdentifier = MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, backingColumn!, GetColumnSystemType(backingColumn!))); } else { From 8eb5d59c8b3e879ad6accc39d027fbea6f9fe2b9 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 16 Feb 2023 13:30:30 +0530 Subject: [PATCH 03/28] fix formatting --- .../Sql Query Structures/BaseSqlQueryStructure.cs | 8 ++++---- .../Sql Query Structures/SqlDeleteQueryStructure.cs | 1 - .../Sql Query Structures/SqlExecuteQueryStructure.cs | 2 +- .../Sql Query Structures/SqlInsertQueryStructure.cs | 1 - 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index fca9b1b281..6f8531dd3a 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -505,7 +505,7 @@ protected object GetParamAsSystemType(string param, string fieldName, Type syste e is ArgumentNullException || e is OverflowException) { - if(MetadataProvider.IsDevelopmentMode()) + if (MetadataProvider.IsDevelopmentMode()) { if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) { @@ -514,7 +514,7 @@ e is ArgumentNullException || $@"with type ""{systemType.Name}"".", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException:e + innerException: e ); } else @@ -526,7 +526,7 @@ e is ArgumentNullException || subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, innerException: e); } - + } else { @@ -534,7 +534,7 @@ e is ArgumentNullException || message: $"", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException:e); + innerException: e); } } diff --git a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs index 176fdfb1a5..f914047685 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Net; using Azure.DataApiBuilder.Auth; diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index 9a9d18f76d..83cd801791 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -47,7 +47,7 @@ public SqlExecuteStructure( try { string? parametrizedName = null; - if(requestParamValue is not null) + if (requestParamValue is not null) { Type systemType = GetUnderlyingStoredProcedureDefinition().Parameters[paramKey].SystemType!; parametrizedName = MakeParamWithValue(GetParamAsSystemType(requestParamValue.ToString()!, paramKey, systemType)); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index bae8347b96..d3ce5d1f10 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config; From da4f2487f2d588d1fb68050bb46bb2802d2c126d Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Mon, 20 Feb 2023 11:34:10 +0530 Subject: [PATCH 04/28] refactoring try-catch blocks --- .../BaseSqlQueryStructure.cs | 21 ++++++++++++---- .../SqlExecuteQueryStructure.cs | 13 ---------- .../SqlInsertQueryStructure.cs | 25 ++++++------------- .../Sql Query Structures/SqlQueryStructure.cs | 23 +++++------------ 4 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 4b4a402060..03565b0522 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -104,11 +104,22 @@ public Type GetColumnSystemType(string columnName) } else { - throw new DataApiBuilderException( + if(MetadataProvider.IsDevelopmentMode()) + { + throw new DataApiBuilderException( message: $"{columnName} is not a valid column of {DatabaseObject.Name}", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest ); + } + else + { + throw new DataApiBuilderException( + message: $"{columnName} is not a valid field of {DatabaseObject.Name}", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest + ); + } } } @@ -493,7 +504,7 @@ public void ProcessOdataClause(FilterClause odataClause) /// Gets the value of the parameter cast as the system type /// of the stored procedure parameter this parameter is associated with /// - protected object GetParamAsSystemType(string param, string fieldName, Type systemType) + protected object GetParamAsSystemType(string param, string paramName, Type systemType) { try { @@ -510,7 +521,7 @@ e is ArgumentNullException || if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) { throw new DataApiBuilderException( - message: $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + + message: $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{paramName}"" " + $@"with type ""{systemType.Name}"".", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, @@ -520,7 +531,7 @@ e is ArgumentNullException || else { throw new DataApiBuilderException( - message: $"Parameter \"{param}\" cannot be resolved as column \"{fieldName}\" " + + message: $"Parameter \"{param}\" cannot be resolved as column \"{paramName}\" " + $"with type \"{systemType.Name}\".", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, @@ -531,7 +542,7 @@ e is ArgumentNullException || else { throw new DataApiBuilderException( - message: $"", + message: $"Invalid value supplied for field: {paramName}", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, innerException: e); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index d4b33bf656..fe15f4a401 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -44,8 +44,6 @@ public SqlExecuteStructure( if (requestParams.TryGetValue(paramKey, out object? requestParamValue)) { // Parameterize, then add referencing parameter to ProcedureParameters dictionary - try - { string? parametrizedName = null; if (requestParamValue is not null) { @@ -58,17 +56,6 @@ public SqlExecuteStructure( } ProcedureParameters.Add(paramKey, $"@{parametrizedName}"); - } - catch (ArgumentException ex) - { - // In the case GetParamAsProcedureParameterType fails to parse as SystemType from database metadata - // Keep message being returned to the client more generalized to not expose schema info - throw new DataApiBuilderException( - message: $"Invalid value supplied for field: {paramKey}", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: ex); - } } else { diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index 13322b3e93..47be2adec6 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -79,28 +79,17 @@ private void PopulateColumnsAndParams(string columnName, object? value) InsertColumns.Add(columnName); string paramName; - try + if (value != null) { - if (value != null) - { - paramName = MakeParamWithValue( - GetParamAsSystemType(value.ToString()!, columnName, GetColumnSystemType(columnName))); - } - else - { - paramName = MakeParamWithValue(null); - } - - Values.Add($"{paramName}"); + paramName = MakeParamWithValue( + GetParamAsSystemType(value.ToString()!, columnName, GetColumnSystemType(columnName))); } - catch (ArgumentException ex) + else { - throw new DataApiBuilderException( - message: ex.Message, - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: ex); + paramName = MakeParamWithValue(null); } + + Values.Add($"{paramName}"); } /// diff --git a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs index 25ad76ec73..9bca999235 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -500,25 +500,14 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues return; } - try + foreach (PaginationColumn column in afterJsonValues) { - foreach (PaginationColumn column in afterJsonValues) - { - column.TableAlias = SourceAlias; - column.ParamName = column.Value is not null ? - "@" + MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : - "@" + MakeParamWithValue(null); - } + column.TableAlias = SourceAlias; + column.ParamName = column.Value is not null ? + "@" + MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : + "@" + MakeParamWithValue(null); } - catch (ArgumentException ex) - { - throw new DataApiBuilderException( - message: ex.Message, - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: ex); - } - + PaginationMetadata.PaginationPredicate = new KeysetPaginationPredicate(afterJsonValues.ToList()); } From 84e300cf1a8cd24bc9412511add3a79ca80813f8 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 4 Mar 2023 18:43:28 +0530 Subject: [PATCH 05/28] re-wording the exception error message --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 03565b0522..213e91969c 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -542,7 +542,7 @@ e is ArgumentNullException || else { throw new DataApiBuilderException( - message: $"Invalid value supplied for field: {paramName}", + message: $"Invalid value provided for field: {paramName}", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, innerException: e); From 513b78ce6fa9ecdda92d8359afd273374a28213c Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 4 Mar 2023 20:02:33 +0530 Subject: [PATCH 06/28] removing unnecessary inclusion of @ --- .../Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs | 2 +- .../Sql Query Structures/SqlExecuteQueryStructure.cs | 2 +- .../Resolvers/Sql Query Structures/SqlQueryStructure.cs | 4 ++-- .../Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs index f914047685..e171dad750 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlDeleteQueryStructure.cs @@ -45,7 +45,7 @@ public SqlDeleteStructure( Predicates.Add(new Predicate( new PredicateOperand(new Column(DatabaseObject.SchemaName, DatabaseObject.Name, backingColumn!)), PredicateOperation.Equal, - new PredicateOperand($"@{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, backingColumn!, GetColumnSystemType(backingColumn!)))}") + new PredicateOperand($"{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, backingColumn!, GetColumnSystemType(backingColumn!)))}") )); } } diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index fe15f4a401..95438f4b97 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -55,7 +55,7 @@ public SqlExecuteStructure( parametrizedName = MakeParamWithValue(null); } - ProcedureParameters.Add(paramKey, $"@{parametrizedName}"); + ProcedureParameters.Add(paramKey, $"{parametrizedName}"); } else { diff --git a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs index e34398ca4a..18d8e38ce5 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -504,8 +504,8 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues { column.TableAlias = SourceAlias; column.ParamName = column.Value is not null ? - "@" + MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : - "@" + MakeParamWithValue(null); + MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : + MakeParamWithValue(null); } PaginationMetadata.PaginationPredicate = new KeysetPaginationPredicate(afterJsonValues.ToList()); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs index 9569f2c7cc..f8b231f226 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlUpdateQueryStructure.cs @@ -167,7 +167,7 @@ private Predicate CreatePredicateForParam(KeyValuePair param) new PredicateOperand( new Column(tableSchema: DatabaseObject.SchemaName, tableName: DatabaseObject.Name, param.Key)), PredicateOperation.Equal, - new PredicateOperand($"@{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, param.Key, GetColumnSystemType(param.Key)))}")); + new PredicateOperand($"{MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, param.Key, GetColumnSystemType(param.Key)))}")); } return predicate; From b12a44c30e79a7809807dbed5e89cec4c311214d Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 21 Mar 2023 09:51:52 +0530 Subject: [PATCH 07/28] displays entity name, adds method descriptions, remomves unused code block --- .../BaseSqlQueryStructure.cs | 27 +------------------ .../MetadataProviders/ISqlMetadataProvider.cs | 3 ++- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 897ac5e718..bbeb8b65c9 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -133,7 +133,7 @@ public Type GetColumnSystemType(string columnName) else { throw new DataApiBuilderException( - message: $"{columnName} is not a valid field of {DatabaseObject.Name}", + message: $"{columnName} is not a valid field of {EntityName}", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest ); @@ -341,30 +341,6 @@ protected List GenerateOutputColumns() return outputColumns; } - /// - /// Gets the value of the parameter cast as the system type - /// of the column this parameter is associated with - /// - /// columnName is not a valid column of table or param - /// does not have a valid value type - protected object GetParamAsColumnSystemType(string param, string columnName) - { - Type systemType = GetColumnSystemType(columnName); - try - { - return ParseParamAsSystemType(param, systemType); - } - catch (Exception e) when (e is FormatException || e is ArgumentNullException || e is OverflowException) - { - throw new DataApiBuilderException( - message: $"Parameter \"{param}\" cannot be resolved as column \"{columnName}\" " + - $"with type \"{systemType.Name}\".", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: e); - } - } - /// /// Tries to parse the string parameter to the given system type /// Useful for inferring parameter types for columns or procedure parameters @@ -520,7 +496,6 @@ public void ProcessOdataClause(FilterClause odataClause) /// /// Gets the value of the parameter cast as the system type - /// of the stored procedure parameter this parameter is associated with /// protected object GetParamAsSystemType(string param, string paramName, Type systemType) { diff --git a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs index 66bc80531f..ff6a7afcad 100644 --- a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs @@ -161,7 +161,8 @@ public DatabaseObject GetDatabaseObjectForGraphQLType(string graphqlType) public string GetDefaultSchemaName(); /// - /// + /// Returns true when the engine is running in Development mode. When running in Production + /// mode, it returns false. /// /// public bool IsDevelopmentMode(); From 7a62c83b9b6c713df456631f81c66719082c071f Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 23 Mar 2023 10:50:56 +0530 Subject: [PATCH 08/28] adds a REST GET request test with invalid params for postgresql --- .../Find/PostgreSqlFindApiTests.cs | 17 +++++++++++++++++ src/Service.Tests/TestHelper.cs | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs index 4194b15dd0..62176aaede 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -821,6 +822,22 @@ public override Task FindStoredProcedureWithNonexistentParameter() throw new NotImplementedException(); } + [TestMethod] + public async Task TestBadRequest() + { + TestHelper.ChangeHostTypeInConfigFile("dab-config.PostgreSql.json", Config.HostModeType.Production, DatabaseEngine); + await SetupAndRunRestApiTest( + primaryKeyRoute: "id/one", + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: null, + exceptionExpected: true, + expectedErrorMessage: "Invalid value provided for field: id", + expectedStatusCode: HttpStatusCode.BadRequest + ); + TestHelper.ChangeHostTypeInConfigFile("dab-config.PostgreSql.json", Config.HostModeType.Development, DatabaseEngine); + } + public override string GetDefaultSchema() { return DEFAULT_SCHEMA; diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index e668c841a6..d225a7dbd5 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -237,5 +237,23 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config, string entit }, ""entities"": {}" + "}"; + + public static void ChangeHostTypeInConfigFile(string fileName, HostModeType hostModeType, string databaseType) + { + RuntimeConfigProvider configProvider = TestHelper.GetRuntimeConfigProvider(databaseType); + RuntimeConfig config = configProvider.GetRuntimeConfiguration(); + HostGlobalSettings customHostGlobalSettings = config.HostGlobalSettings with { Mode = hostModeType }; + JsonElement serializedCustomHostGlobalSettings = + JsonSerializer.SerializeToElement(customHostGlobalSettings, RuntimeConfig.SerializerOptions); + Dictionary customRuntimeSettings = new(config.RuntimeSettings); + customRuntimeSettings.Remove(GlobalSettingsType.Host); + customRuntimeSettings.Add(GlobalSettingsType.Host, serializedCustomHostGlobalSettings); + RuntimeConfig configWithCustomHostMode = + config with { RuntimeSettings = customRuntimeSettings }; + File.WriteAllText( + fileName, + JsonSerializer.Serialize(configWithCustomHostMode, RuntimeConfig.SerializerOptions)); + + } } } From cd1d40b9004930ee0061a9ca36541c0b5a5eb669 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Fri, 24 Mar 2023 23:21:12 +0530 Subject: [PATCH 09/28] adds test cases for sql dbs, removes incorrect test case --- .../Configuration/ConfigurationTests.cs | 166 ++++++++++++++++++ .../Find/PostgreSqlFindApiTests.cs | 17 -- src/Service.Tests/TestHelper.cs | 32 +++- 3 files changed, 196 insertions(+), 19 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 6c08fccf24..a1f2af07db 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -946,6 +946,172 @@ public async Task TestPathRewriteMiddlewareForGraphQL( } } + /// + /// Validates the error message that is shown for requests with incorrect parameter type + /// when the engine is running in Production mode. The error messages in Production mode is + /// very generic to not reveal information about the underlying database objects backing the entity. + /// This test runs against a PostgreSql database. + /// + /// Type of REST request + /// Endpoint for the REST request + /// Right error message that should be shown to the end user + [DataTestMethod] + [TestCategory(TestCategory.POSTGRESQL)] + [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for PostrgeSql")] + [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for PostrgeSql")] + [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for PostrgeSql")] + [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for PostrgeSql")] + [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for PostrgeSql")] + [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for PostrgeSql")] + + public async Task TestErrorMessageForRestApiForPostgreSql( + string requestType, + string requestPath, + string expectedErrorMessage) + { + const string CUSTOM_CONFIG = "custom-config.json"; + TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.POSTGRESQL); + string[] args = new[] + { + $"--ConfigFileName={CUSTOM_CONFIG}" + }; + + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpRequestMessage request; + if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + { + request = new(httpMethod, requestPath); + } + else + { + request = new(httpMethod, requestPath) + { + Content = JsonContent.Create(TestHelper.REQUESTBODY) + }; + } + + HttpResponseMessage response = await client.SendAsync(request); + string body = await response.Content.ReadAsStringAsync(); + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + Assert.IsTrue(body.Contains(expectedErrorMessage)); + } + } + + /// + /// Validates the error message that is shown for requests with incorrect parameter type + /// when the engine is running in Production mode. The error messages in Production mode is + /// very generic to not reveal information about the underlying database objects backing the entity. + /// This test runs against a MsSql database. + /// + /// Type of REST request + /// Endpoint for the REST request + /// Right error message that should be shown to the end user + [DataTestMethod] + [TestCategory(TestCategory.MSSQL)] + [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for MsSql")] + [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for MsSql")] + [DataRow("GET", "/api/GetBook?id=one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on stored-procedure in production mode for MsSql")] + [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for MsSql")] + [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for MsSql")] + [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for MsSql")] + [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for MsSql")] + + public async Task TestErrorMessageForRestApiForMsSql( + string requestType, + string requestPath, + string expectedErrorMessage) + { + const string CUSTOM_CONFIG = "custom-config.json"; + TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.MSSQL); + string[] args = new[] + { + $"--ConfigFileName={CUSTOM_CONFIG}" + }; + + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpRequestMessage request; + if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + { + request = new(httpMethod, requestPath); + } + else + { + request = new(httpMethod, requestPath) + { + Content = JsonContent.Create(TestHelper.REQUESTBODY) + }; + } + + HttpResponseMessage response = await client.SendAsync(request); + string body = await response.Content.ReadAsStringAsync(); + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + Assert.IsTrue(body.Contains(expectedErrorMessage)); + } + } + + /// + /// Validates the error message that is shown for requests with incorrect parameter type + /// when the engine is running in Production mode. The error messages in Production mode is + /// very generic to not reveal information about the underlying database objects backing the entity. + /// This test runs against a MySql database. + /// + /// Type of REST request + /// Endpoint for the REST request + /// Right error message that should be shown to the end user + [DataTestMethod] + [TestCategory(TestCategory.MYSQL)] + [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for MySql")] + [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for MySql")] + [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for MySql")] + [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for MySql")] + [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for MySql")] + [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for MySql")] + + public async Task TestErrorMessageForRestApiForMySql( + string requestType, + string requestPath, + string expectedErrorMessage) + { + const string CUSTOM_CONFIG = "custom-config.json"; + TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.MYSQL); + string[] args = new[] + { + $"--ConfigFileName={CUSTOM_CONFIG}" + }; + + using (TestServer server = new(Program.CreateWebHostBuilder(args))) + using (HttpClient client = server.CreateClient()) + { + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpRequestMessage request; + if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + { + request = new(httpMethod, requestPath); + } + else + { + request = new(httpMethod, requestPath) + { + Content = JsonContent.Create(TestHelper.REQUESTBODY) + }; + } + + HttpResponseMessage response = await client.SendAsync(request); + string body = await response.Content.ReadAsStringAsync(); + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + Assert.IsTrue(body.Contains(expectedErrorMessage)); + } + } + /// /// Tests that the when Rest or GraphQL is disabled Globally, /// any requests made will get a 404 response. diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs index cf65be3c0c..c3906d3bd1 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/PostgreSqlFindApiTests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Net; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -884,22 +883,6 @@ public override Task FindStoredProcedureWithNonexistentParameter() throw new NotImplementedException(); } - [TestMethod] - public async Task TestBadRequest() - { - TestHelper.ChangeHostTypeInConfigFile("dab-config.PostgreSql.json", Config.HostModeType.Production, DatabaseEngine); - await SetupAndRunRestApiTest( - primaryKeyRoute: "id/one", - queryString: string.Empty, - entityNameOrPath: _integrationEntityName, - sqlQuery: null, - exceptionExpected: true, - expectedErrorMessage: "Invalid value provided for field: id", - expectedStatusCode: HttpStatusCode.BadRequest - ); - TestHelper.ChangeHostTypeInConfigFile("dab-config.PostgreSql.json", Config.HostModeType.Development, DatabaseEngine); - } - public override string GetDefaultSchema() { return DEFAULT_SCHEMA; diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index d225a7dbd5..beb12f419a 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -3,10 +3,13 @@ using System.Collections.Generic; using System.IO; +using System.Net; +using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Service.Configurations; +using Azure.DataApiBuilder.Service.Exceptions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -16,6 +19,14 @@ namespace Azure.DataApiBuilder.Service.Tests { public class TestHelper { + + public const string REQUESTBODY = @" + { + ""title"": ""New book"", + ""publisher_id"": ""one"" + } + "; + /// /// Given the testing environment, retrieve the config path. /// @@ -238,7 +249,7 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config, string entit ""entities"": {}" + "}"; - public static void ChangeHostTypeInConfigFile(string fileName, HostModeType hostModeType, string databaseType) + public static void ChangeHostTypeInConfigFile(HostModeType hostModeType, string databaseType) { RuntimeConfigProvider configProvider = TestHelper.GetRuntimeConfigProvider(databaseType); RuntimeConfig config = configProvider.GetRuntimeConfiguration(); @@ -251,9 +262,26 @@ public static void ChangeHostTypeInConfigFile(string fileName, HostModeType host RuntimeConfig configWithCustomHostMode = config with { RuntimeSettings = customRuntimeSettings }; File.WriteAllText( - fileName, + "custom-config.json", JsonSerializer.Serialize(configWithCustomHostMode, RuntimeConfig.SerializerOptions)); } + + public static HttpMethod GetHttpMethod(string httpMethod) + { + switch (httpMethod) + { + case "GET": return HttpMethod.Get; + case "POST": return HttpMethod.Post; + case "PUT": return HttpMethod.Put; + case "PATCH": return HttpMethod.Patch; + case "DELETE": return HttpMethod.Delete; + default: + throw new DataApiBuilderException( + message: "HTTP Request Type not supported.", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.NotSupported); + } + } } } From fc6cfe0d20b8a35942b8cdfaad65c94f89574c29 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 25 Mar 2023 00:39:15 +0530 Subject: [PATCH 10/28] adds test and method descriptions to tests --- .../Configuration/ConfigurationTests.cs | 65 +++++++++++-------- src/Service.Tests/TestHelper.cs | 48 ++++++++++---- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index a1f2af07db..9b9bd548ab 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -947,7 +947,7 @@ public async Task TestPathRewriteMiddlewareForGraphQL( } /// - /// Validates the error message that is shown for requests with incorrect parameter type + /// Validates the error message that is returned for REST requests with incorrect parameter type /// when the engine is running in Production mode. The error messages in Production mode is /// very generic to not reveal information about the underlying database objects backing the entity. /// This test runs against a PostgreSql database. @@ -957,20 +957,23 @@ public async Task TestPathRewriteMiddlewareForGraphQL( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.POSTGRESQL)] - [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for PostrgeSql")] - [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for PostrgeSql")] - [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for PostrgeSql")] - [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for PostrgeSql")] - [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for PostrgeSql")] - [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for PostrgeSql")] + [DataRow("GET", "/api/Book/id/one", null ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("GET", "/api/books_view_all/id/one", null,"Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] + [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES ,"Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validate the error message for a PUT request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for PostrgeSql")] + [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] + [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] public async Task TestErrorMessageForRestApiForPostgreSql( string requestType, string requestPath, + string requestBody, string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.POSTGRESQL); + TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.POSTGRESQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" @@ -990,7 +993,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( { request = new(httpMethod, requestPath) { - Content = JsonContent.Create(TestHelper.REQUESTBODY) + Content = JsonContent.Create(requestBody) }; } @@ -1002,7 +1005,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( } /// - /// Validates the error message that is shown for requests with incorrect parameter type + /// Validates the error message that is returned for REST requests with incorrect parameter type /// when the engine is running in Production mode. The error messages in Production mode is /// very generic to not reveal information about the underlying database objects backing the entity. /// This test runs against a MsSql database. @@ -1012,21 +1015,24 @@ public async Task TestErrorMessageForRestApiForPostgreSql( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.MSSQL)] - [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for MsSql")] - [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for MsSql")] - [DataRow("GET", "/api/GetBook?id=one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on stored-procedure in production mode for MsSql")] - [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for MsSql")] - [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for MsSql")] - [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for MsSql")] - [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for MsSql")] + [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MsSql database")] + [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode for MsSql")] + [DataRow("GET", "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode for MsSql")] + [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] + [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] + [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] public async Task TestErrorMessageForRestApiForMsSql( string requestType, string requestPath, + string requestBody, string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.MSSQL); + TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MSSQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" @@ -1046,7 +1052,7 @@ public async Task TestErrorMessageForRestApiForMsSql( { request = new(httpMethod, requestPath) { - Content = JsonContent.Create(TestHelper.REQUESTBODY) + Content = JsonContent.Create(requestBody) }; } @@ -1058,7 +1064,7 @@ public async Task TestErrorMessageForRestApiForMsSql( } /// - /// Validates the error message that is shown for requests with incorrect parameter type + /// Validates the error message that is returned for REST requests with incorrect parameter type /// when the engine is running in Production mode. The error messages in Production mode is /// very generic to not reveal information about the underlying database objects backing the entity. /// This test runs against a MySql database. @@ -1068,20 +1074,23 @@ public async Task TestErrorMessageForRestApiForMsSql( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.MYSQL)] - [DataRow("GET", "/api/Book/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on tables in production mode for MySql")] - [DataRow("GET", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad GET request on views in production mode for MySql")] - [DataRow("POST","/api/Book", "Invalid value provided for field: publisher_id", DisplayName = "Validates generic error message for a bad POST request on tables in production mode for MySql")] - [DataRow("PUT", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PUT request on tables in production mode for MySql")] - [DataRow("PATCH", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad PATCH request on tables in production mode for MySql")] - [DataRow("DELETE", "/api/books_view_all/id/one", "Invalid value provided for field: id", DisplayName = "Validates generic error message for a bad DELETE request on tables in production mode for MySql")] + [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for MySql")] + [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES ,"Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] public async Task TestErrorMessageForRestApiForMySql( string requestType, string requestPath, + string requestBody, string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(HostModeType.Production, TestCategory.MYSQL); + TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MYSQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" @@ -1101,7 +1110,7 @@ public async Task TestErrorMessageForRestApiForMySql( { request = new(httpMethod, requestPath) { - Content = JsonContent.Create(TestHelper.REQUESTBODY) + Content = JsonContent.Create(requestBody) }; } diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index beb12f419a..9ebb170e70 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -19,14 +19,6 @@ namespace Azure.DataApiBuilder.Service.Tests { public class TestHelper { - - public const string REQUESTBODY = @" - { - ""title"": ""New book"", - ""publisher_id"": ""one"" - } - "; - /// /// Given the testing environment, retrieve the config path. /// @@ -249,7 +241,34 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config, string entit ""entities"": {}" + "}"; - public static void ChangeHostTypeInConfigFile(HostModeType hostModeType, string databaseType) + /// + /// A valid REST API request body with correct parameter types for all the fields. + /// + public const string REQUEST_BODY_WITH_CORRECT_PARAM_TYPES = @" + { + ""title"": ""New book"", + ""publisher_id"": 1234 + } + "; + + /// + /// An invalid REST API request body with incorrect parameter type for publisher_id field. + /// + public const string REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES = @" + { + ""title"": ""New book"", + ""publisher_id"": ""one"" + } + "; + + /// + /// Utility method that reads the config file for a given database type and constructs a + /// new config file with changes just in the host mode section. + /// + /// Name of the new config file to be constructed + /// HostMode for the engine + /// Database type + public static void ChangeHostTypeInConfigFile(string configFileName, HostModeType hostModeType, string databaseType) { RuntimeConfigProvider configProvider = TestHelper.GetRuntimeConfigProvider(databaseType); RuntimeConfig config = configProvider.GetRuntimeConfiguration(); @@ -262,11 +281,18 @@ public static void ChangeHostTypeInConfigFile(HostModeType hostModeType, string RuntimeConfig configWithCustomHostMode = config with { RuntimeSettings = customRuntimeSettings }; File.WriteAllText( - "custom-config.json", + configFileName, JsonSerializer.Serialize(configWithCustomHostMode, RuntimeConfig.SerializerOptions)); } + /// + /// Utility method that fetches the HTTP method as HttpMethod type + /// from the given string type. + /// + /// + /// + /// public static HttpMethod GetHttpMethod(string httpMethod) { switch (httpMethod) @@ -278,7 +304,7 @@ public static HttpMethod GetHttpMethod(string httpMethod) case "DELETE": return HttpMethod.Delete; default: throw new DataApiBuilderException( - message: "HTTP Request Type not supported.", + message: "HTTP Verb Type not supported.", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.NotSupported); } From f1b22907505d477cde434180ce12dd089d561025 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 25 Mar 2023 00:42:58 +0530 Subject: [PATCH 11/28] Fix formatting --- .../Configuration/ConfigurationTests.cs | 44 +++++++++---------- src/Service.Tests/TestHelper.cs | 15 ++++--- .../BaseSqlQueryStructure.cs | 2 +- .../SqlExecuteQueryStructure.cs | 22 +++++----- .../SqlInsertQueryStructure.cs | 3 -- .../Sql Query Structures/SqlQueryStructure.cs | 2 +- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 9b9bd548ab..5ad290944d 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -957,14 +957,14 @@ public async Task TestPathRewriteMiddlewareForGraphQL( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.POSTGRESQL)] - [DataRow("GET", "/api/Book/id/one", null ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow("GET", "/api/books_view_all/id/one", null,"Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] - [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES ,"Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] + [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validate the error message for a PUT request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for PostrgeSql")] - [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] - [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] + [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] public async Task TestErrorMessageForRestApiForPostgreSql( string requestType, @@ -982,9 +982,9 @@ public async Task TestErrorMessageForRestApiForPostgreSql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); HttpRequestMessage request; - if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) { request = new(httpMethod, requestPath); @@ -996,11 +996,11 @@ public async Task TestErrorMessageForRestApiForPostgreSql( Content = JsonContent.Create(requestBody) }; } - + HttpResponseMessage response = await client.SendAsync(request); string body = await response.Content.ReadAsStringAsync(); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(body.Contains(expectedErrorMessage)); + Assert.IsTrue(body.Contains(expectedErrorMessage)); } } @@ -1018,7 +1018,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MsSql database")] [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode for MsSql")] [DataRow("GET", "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode for MsSql")] - [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] @@ -1041,9 +1041,9 @@ public async Task TestErrorMessageForRestApiForMsSql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); HttpRequestMessage request; - if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) { request = new(httpMethod, requestPath); @@ -1055,11 +1055,11 @@ public async Task TestErrorMessageForRestApiForMsSql( Content = JsonContent.Create(requestBody) }; } - + HttpResponseMessage response = await client.SendAsync(request); string body = await response.Content.ReadAsStringAsync(); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(body.Contains(expectedErrorMessage)); + Assert.IsTrue(body.Contains(expectedErrorMessage)); } } @@ -1076,12 +1076,12 @@ public async Task TestErrorMessageForRestApiForMsSql( [TestCategory(TestCategory.MYSQL)] [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MySql")] [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for MySql")] - [DataRow("POST","/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES ,"Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES ,"Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] public async Task TestErrorMessageForRestApiForMySql( string requestType, @@ -1099,9 +1099,9 @@ public async Task TestErrorMessageForRestApiForMySql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); HttpRequestMessage request; - if("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || + if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) { request = new(httpMethod, requestPath); @@ -1113,11 +1113,11 @@ public async Task TestErrorMessageForRestApiForMySql( Content = JsonContent.Create(requestBody) }; } - + HttpResponseMessage response = await client.SendAsync(request); string body = await response.Content.ReadAsStringAsync(); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(body.Contains(expectedErrorMessage)); + Assert.IsTrue(body.Contains(expectedErrorMessage)); } } diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index 9ebb170e70..3cb4f7ea81 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -297,11 +297,16 @@ public static HttpMethod GetHttpMethod(string httpMethod) { switch (httpMethod) { - case "GET": return HttpMethod.Get; - case "POST": return HttpMethod.Post; - case "PUT": return HttpMethod.Put; - case "PATCH": return HttpMethod.Patch; - case "DELETE": return HttpMethod.Delete; + case "GET": + return HttpMethod.Get; + case "POST": + return HttpMethod.Post; + case "PUT": + return HttpMethod.Put; + case "PATCH": + return HttpMethod.Patch; + case "DELETE": + return HttpMethod.Delete; default: throw new DataApiBuilderException( message: "HTTP Verb Type not supported.", diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index bbeb8b65c9..9d9374d889 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -122,7 +122,7 @@ public Type GetColumnSystemType(string columnName) } else { - if(MetadataProvider.IsDevelopmentMode()) + if (MetadataProvider.IsDevelopmentMode()) { throw new DataApiBuilderException( message: $"{columnName} is not a valid column of {DatabaseObject.Name}", diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index 95438f4b97..b8d7d260b3 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -44,18 +44,18 @@ public SqlExecuteStructure( if (requestParams.TryGetValue(paramKey, out object? requestParamValue)) { // Parameterize, then add referencing parameter to ProcedureParameters dictionary - string? parametrizedName = null; - if (requestParamValue is not null) - { - Type systemType = GetUnderlyingStoredProcedureDefinition().Parameters[paramKey].SystemType!; - parametrizedName = MakeParamWithValue(GetParamAsSystemType(requestParamValue.ToString()!, paramKey, systemType)); - } - else - { - parametrizedName = MakeParamWithValue(null); - } + string? parametrizedName = null; + if (requestParamValue is not null) + { + Type systemType = GetUnderlyingStoredProcedureDefinition().Parameters[paramKey].SystemType!; + parametrizedName = MakeParamWithValue(GetParamAsSystemType(requestParamValue.ToString()!, paramKey, systemType)); + } + else + { + parametrizedName = MakeParamWithValue(null); + } - ProcedureParameters.Add(paramKey, $"{parametrizedName}"); + ProcedureParameters.Add(paramKey, $"{parametrizedName}"); } else { diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index c5813ab5b5..b0ff6d40c1 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Collections.Generic; -using System.Net; using Azure.DataApiBuilder.Auth; using Azure.DataApiBuilder.Config; -using Azure.DataApiBuilder.Service.Exceptions; using Azure.DataApiBuilder.Service.GraphQLBuilder.Mutations; using Azure.DataApiBuilder.Service.Models; using Azure.DataApiBuilder.Service.Services; diff --git a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs index 1288e7c4fc..4fba9cdbcd 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -489,7 +489,7 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : MakeParamWithValue(null); } - + PaginationMetadata.PaginationPredicate = new KeysetPaginationPredicate(afterJsonValues.ToList()); } From d6d8dd5a56b8bb246739a00a04fe1fad9efe72ca Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 25 Mar 2023 01:39:59 +0530 Subject: [PATCH 12/28] fixing tests --- src/Service.Tests/Configuration/ConfigurationTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 5ad290944d..b1466886de 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -1022,7 +1022,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] - [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] public async Task TestErrorMessageForRestApiForMsSql( @@ -1080,7 +1080,7 @@ public async Task TestErrorMessageForRestApiForMsSql( [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] public async Task TestErrorMessageForRestApiForMySql( From c3a04e303acb83384c7b6d7de945cbfa2366789e Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sun, 26 Mar 2023 20:40:21 +0530 Subject: [PATCH 13/28] Renaming test helper method --- src/Service.Tests/Configuration/ConfigurationTests.cs | 6 +++--- src/Service.Tests/TestHelper.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index b1466886de..04096d8e60 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -973,7 +973,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.POSTGRESQL); + TestHelper.ConstructNewConfigWithSpecifiedHostMode(CUSTOM_CONFIG, HostModeType.Production, TestCategory.POSTGRESQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" @@ -1032,7 +1032,7 @@ public async Task TestErrorMessageForRestApiForMsSql( string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MSSQL); + TestHelper.ConstructNewConfigWithSpecifiedHostMode(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MSSQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" @@ -1090,7 +1090,7 @@ public async Task TestErrorMessageForRestApiForMySql( string expectedErrorMessage) { const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ChangeHostTypeInConfigFile(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MYSQL); + TestHelper.ConstructNewConfigWithSpecifiedHostMode(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MYSQL); string[] args = new[] { $"--ConfigFileName={CUSTOM_CONFIG}" diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index 3cb4f7ea81..24b18a59b7 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -268,7 +268,7 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config, string entit /// Name of the new config file to be constructed /// HostMode for the engine /// Database type - public static void ChangeHostTypeInConfigFile(string configFileName, HostModeType hostModeType, string databaseType) + public static void ConstructNewConfigWithSpecifiedHostMode(string configFileName, HostModeType hostModeType, string databaseType) { RuntimeConfigProvider configProvider = TestHelper.GetRuntimeConfigProvider(databaseType); RuntimeConfig config = configProvider.GetRuntimeConfiguration(); From 36c9d081330b2649f023360a751fcaf46f8ef414 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 28 Mar 2023 00:36:40 +0530 Subject: [PATCH 14/28] using RestMethod enum in tests --- .../Configuration/ConfigurationTests.cs | 71 +++++++++---------- src/Service.Tests/SqlTests/SqlTestHelper.cs | 2 +- src/Service.Tests/TestHelper.cs | 29 -------- 3 files changed, 35 insertions(+), 67 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 04096d8e60..6a0ab0a17b 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -957,17 +957,17 @@ public async Task TestPathRewriteMiddlewareForGraphQL( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.POSTGRESQL)] - [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] - [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validate the error message for a PUT request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for PostrgeSql")] - [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] - [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] + [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validate the error message for a PUT request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] + [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] public async Task TestErrorMessageForRestApiForPostgreSql( - string requestType, + RestMethod requestType, string requestPath, string requestBody, string expectedErrorMessage) @@ -982,10 +982,9 @@ public async Task TestErrorMessageForRestApiForPostgreSql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = SqlTestHelper.ConvertRestMethodToHttpMethod(requestType); HttpRequestMessage request; - if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || - "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + if (requestType is RestMethod.Get || requestType is RestMethod.Delete) { request = new(httpMethod, requestPath); } @@ -1015,18 +1014,18 @@ public async Task TestErrorMessageForRestApiForPostgreSql( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.MSSQL)] - [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MsSql database")] - [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode for MsSql")] - [DataRow("GET", "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode for MsSql")] - [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] - [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] - [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] + [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MsSql database")] + [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode for MsSql")] + [DataRow(RestMethod.Get, "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode for MsSql")] + [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] + [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] + [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] + [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] public async Task TestErrorMessageForRestApiForMsSql( - string requestType, + RestMethod requestType, string requestPath, string requestBody, string expectedErrorMessage) @@ -1041,10 +1040,9 @@ public async Task TestErrorMessageForRestApiForMsSql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = SqlTestHelper.ConvertRestMethodToHttpMethod(requestType); HttpRequestMessage request; - if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || - "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + if (requestType is RestMethod.Get || requestType is RestMethod.Delete) { request = new(httpMethod, requestPath); } @@ -1074,17 +1072,17 @@ public async Task TestErrorMessageForRestApiForMsSql( /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.MYSQL)] - [DataRow("GET", "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow("GET", "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for MySql")] - [DataRow("POST", "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("PUT", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow("PUT", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("PATCH", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow("PATCH", "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow("DELETE", "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for MySql")] + [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] + [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] + [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] public async Task TestErrorMessageForRestApiForMySql( - string requestType, + RestMethod requestType, string requestPath, string requestBody, string expectedErrorMessage) @@ -1099,10 +1097,9 @@ public async Task TestErrorMessageForRestApiForMySql( using (TestServer server = new(Program.CreateWebHostBuilder(args))) using (HttpClient client = server.CreateClient()) { - HttpMethod httpMethod = TestHelper.GetHttpMethod(requestType); + HttpMethod httpMethod = SqlTestHelper.ConvertRestMethodToHttpMethod(requestType); HttpRequestMessage request; - if ("GET".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase) || - "DELETE".Equals(requestType, comparisonType: StringComparison.OrdinalIgnoreCase)) + if (requestType is RestMethod.Get || requestType is RestMethod.Delete) { request = new(httpMethod, requestPath); } diff --git a/src/Service.Tests/SqlTests/SqlTestHelper.cs b/src/Service.Tests/SqlTests/SqlTestHelper.cs index f842802242..aab2c3b9a7 100644 --- a/src/Service.Tests/SqlTests/SqlTestHelper.cs +++ b/src/Service.Tests/SqlTests/SqlTestHelper.cs @@ -223,7 +223,7 @@ public static HttpMethod GetHttpMethodFromOperation(Config.Operation operationTy /// /// /// HttpMethod corresponding the RestMethod provided as input. - private static HttpMethod ConvertRestMethodToHttpMethod(RestMethod? restMethod) + public static HttpMethod ConvertRestMethodToHttpMethod(RestMethod? restMethod) { switch (restMethod) { diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index 24b18a59b7..60385a213c 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -285,34 +285,5 @@ public static void ConstructNewConfigWithSpecifiedHostMode(string configFileName JsonSerializer.Serialize(configWithCustomHostMode, RuntimeConfig.SerializerOptions)); } - - /// - /// Utility method that fetches the HTTP method as HttpMethod type - /// from the given string type. - /// - /// - /// - /// - public static HttpMethod GetHttpMethod(string httpMethod) - { - switch (httpMethod) - { - case "GET": - return HttpMethod.Get; - case "POST": - return HttpMethod.Post; - case "PUT": - return HttpMethod.Put; - case "PATCH": - return HttpMethod.Patch; - case "DELETE": - return HttpMethod.Delete; - default: - throw new DataApiBuilderException( - message: "HTTP Verb Type not supported.", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.NotSupported); - } - } } } From 9327e66964f7f6076c37b0842d29a5c86a217e52 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 28 Mar 2023 00:44:25 +0530 Subject: [PATCH 15/28] removing code duplication when throwing exception --- .../BaseSqlQueryStructure.cs | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 9d9374d889..bb46fba741 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -122,22 +122,21 @@ public Type GetColumnSystemType(string columnName) } else { + string errorMessage = string.Empty; if (MetadataProvider.IsDevelopmentMode()) { - throw new DataApiBuilderException( - message: $"{columnName} is not a valid column of {DatabaseObject.Name}", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest - ); + errorMessage = $"{columnName} is not a valid column of {DatabaseObject.Name}"; } else { - throw new DataApiBuilderException( - message: $"{columnName} is not a valid field of {EntityName}", + errorMessage = $"{columnName} is not a valid field of {EntityName}"; + } + + throw new DataApiBuilderException( + message: errorMessage, statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest ); - } } } @@ -509,37 +508,30 @@ protected object GetParamAsSystemType(string param, string paramName, Type syste e is ArgumentNullException || e is OverflowException) { + string errorMessage = string.Empty; if (MetadataProvider.IsDevelopmentMode()) { if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) { - throw new DataApiBuilderException( - message: $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{paramName}"" " + - $@"with type ""{systemType.Name}"".", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: e - ); + errorMessage = $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{paramName}"" " + + $@"with type ""{systemType.Name}""."; } else { - throw new DataApiBuilderException( - message: $"Parameter \"{param}\" cannot be resolved as column \"{paramName}\" " + - $"with type \"{systemType.Name}\".", - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: e); + errorMessage = $"Parameter \"{param}\" cannot be resolved as column \"{paramName}\" " + + $"with type \"{systemType.Name}\"."; } - } else { - throw new DataApiBuilderException( - message: $"Invalid value provided for field: {paramName}", + errorMessage = $"Invalid value provided for field: {paramName}"; + } + + throw new DataApiBuilderException( + message: errorMessage, statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, innerException: e); - } } throw; From d1426f49386906acf5987a3fcbbd5eff6806634a Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 28 Mar 2023 00:46:55 +0530 Subject: [PATCH 16/28] replacing != with is not check --- .../Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs | 2 +- .../Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index b0ff6d40c1..b248808262 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -87,7 +87,7 @@ private void PopulateColumnsAndParams(string columnName, object? value) InsertColumns.Add(columnName); string paramName; - if (value != null) + if (value is not null) { paramName = MakeParamWithValue( GetParamAsSystemType(value.ToString()!, columnName, GetColumnSystemType(columnName))); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs index e95e5b88d0..500190cea6 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlUpsertQueryStructure.cs @@ -121,7 +121,7 @@ private void PopulateColumns( MetadataProvider.TryGetBackingColumn(EntityName, param.Key, out string? backingColumn); // Create Parameter and map it to column for downstream logic to utilize. string paramIdentifier; - if (param.Value != null) + if (param.Value is not null) { paramIdentifier = MakeParamWithValue(GetParamAsSystemType(param.Value.ToString()!, backingColumn!, GetColumnSystemType(backingColumn!))); } From 7d68f0b8fe790141afa85824113fdd3d2251c0c8 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 28 Mar 2023 01:01:52 +0530 Subject: [PATCH 17/28] fix formatting --- src/Service.Tests/TestHelper.cs | 3 --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index 60385a213c..775e84ed09 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -3,13 +3,10 @@ using System.Collections.Generic; using System.IO; -using System.Net; -using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; using Azure.DataApiBuilder.Config; using Azure.DataApiBuilder.Service.Configurations; -using Azure.DataApiBuilder.Service.Exceptions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index bb46fba741..0570945b9b 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -122,14 +122,14 @@ public Type GetColumnSystemType(string columnName) } else { - string errorMessage = string.Empty; + string errorMessage; if (MetadataProvider.IsDevelopmentMode()) { errorMessage = $"{columnName} is not a valid column of {DatabaseObject.Name}"; } else { - errorMessage = $"{columnName} is not a valid field of {EntityName}"; + errorMessage = $"{columnName} is not a valid field of {EntityName}"; } throw new DataApiBuilderException( @@ -508,7 +508,7 @@ protected object GetParamAsSystemType(string param, string paramName, Type syste e is ArgumentNullException || e is OverflowException) { - string errorMessage = string.Empty; + string errorMessage; if (MetadataProvider.IsDevelopmentMode()) { if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) From 4f947e5639216855cf0a4a99233c7bafb86c5b3f Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Tue, 28 Mar 2023 01:30:00 +0530 Subject: [PATCH 18/28] updating test method name, fixing formatting --- src/Service.Tests/Configuration/ConfigurationTests.cs | 6 +++--- .../Sql Query Structures/BaseSqlQueryStructure.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 6a0ab0a17b..0d70147b68 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -966,7 +966,7 @@ public async Task TestPathRewriteMiddlewareForGraphQL( [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] - public async Task TestErrorMessageForRestApiForPostgreSql( + public async Task TestGenericErrorMessageForRestApiInProductionModeForPostgreSql( RestMethod requestType, string requestPath, string requestBody, @@ -1024,7 +1024,7 @@ public async Task TestErrorMessageForRestApiForPostgreSql( [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] - public async Task TestErrorMessageForRestApiForMsSql( + public async Task TestGenericErrorMessageForRestApiInProductionModeForMsSql( RestMethod requestType, string requestPath, string requestBody, @@ -1081,7 +1081,7 @@ public async Task TestErrorMessageForRestApiForMsSql( [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] - public async Task TestErrorMessageForRestApiForMySql( + public async Task TestGenericErrorMessageForRestApiInProductionModeForMySql( RestMethod requestType, string requestPath, string requestBody, diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 0570945b9b..847b20a095 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -528,10 +528,10 @@ e is ArgumentNullException || } throw new DataApiBuilderException( - message: errorMessage, - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: e); + message: errorMessage, + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, + innerException: e); } throw; From 83f860647d31a7936453ab1c78f0ca803aa2c50e Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Wed, 29 Mar 2023 17:35:11 +0530 Subject: [PATCH 19/28] removing duplicate tests --- .../Configuration/ConfigurationTests.cs | 139 ++---------------- 1 file changed, 13 insertions(+), 126 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 0d70147b68..63ebf2724e 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -950,81 +950,25 @@ public async Task TestPathRewriteMiddlewareForGraphQL( /// Validates the error message that is returned for REST requests with incorrect parameter type /// when the engine is running in Production mode. The error messages in Production mode is /// very generic to not reveal information about the underlying database objects backing the entity. - /// This test runs against a PostgreSql database. - /// - /// Type of REST request - /// Endpoint for the REST request - /// Right error message that should be shown to the end user - [DataTestMethod] - [TestCategory(TestCategory.POSTGRESQL)] - [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for PostrgeSql")] - [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validate the error message for a PUT request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key on a table in production mode for PostrgeSql")] - [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key on a table in production mode for PostrgeSql")] - - public async Task TestGenericErrorMessageForRestApiInProductionModeForPostgreSql( - RestMethod requestType, - string requestPath, - string requestBody, - string expectedErrorMessage) - { - const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ConstructNewConfigWithSpecifiedHostMode(CUSTOM_CONFIG, HostModeType.Production, TestCategory.POSTGRESQL); - string[] args = new[] - { - $"--ConfigFileName={CUSTOM_CONFIG}" - }; - - using (TestServer server = new(Program.CreateWebHostBuilder(args))) - using (HttpClient client = server.CreateClient()) - { - HttpMethod httpMethod = SqlTestHelper.ConvertRestMethodToHttpMethod(requestType); - HttpRequestMessage request; - if (requestType is RestMethod.Get || requestType is RestMethod.Delete) - { - request = new(httpMethod, requestPath); - } - else - { - request = new(httpMethod, requestPath) - { - Content = JsonContent.Create(requestBody) - }; - } - - HttpResponseMessage response = await client.SendAsync(request); - string body = await response.Content.ReadAsStringAsync(); - Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(body.Contains(expectedErrorMessage)); - } - } - - /// - /// Validates the error message that is returned for REST requests with incorrect parameter type - /// when the engine is running in Production mode. The error messages in Production mode is - /// very generic to not reveal information about the underlying database objects backing the entity. - /// This test runs against a MsSql database. + /// This test runs against a MsSql database. However, generic error messages will be returned in Production + /// mode when run against PostgreSql and MySql databases. /// /// Type of REST request /// Endpoint for the REST request /// Right error message that should be shown to the end user [DataTestMethod] [TestCategory(TestCategory.MSSQL)] - [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MsSql database")] - [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode for MsSql")] - [DataRow(RestMethod.Get, "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode for MsSql")] - [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MsSql")] - [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MsSql")] - [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MsSql")] - [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MsSql")] - - public async Task TestGenericErrorMessageForRestApiInProductionModeForMsSql( + [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode")] + [DataRow(RestMethod.Get, "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode")] + [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode")] + + public async Task TestGenericErrorMessageForRestApiInProductionMode( RestMethod requestType, string requestPath, string requestBody, @@ -1061,63 +1005,6 @@ public async Task TestGenericErrorMessageForRestApiInProductionModeForMsSql( } } - /// - /// Validates the error message that is returned for REST requests with incorrect parameter type - /// when the engine is running in Production mode. The error messages in Production mode is - /// very generic to not reveal information about the underlying database objects backing the entity. - /// This test runs against a MySql database. - /// - /// Type of REST request - /// Endpoint for the REST request - /// Right error message that should be shown to the end user - [DataTestMethod] - [TestCategory(TestCategory.MYSQL)] - [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter on a view in production mode for MySql")] - [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PUT request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode for MySql")] - [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode for MySql")] - [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode for MySql")] - - public async Task TestGenericErrorMessageForRestApiInProductionModeForMySql( - RestMethod requestType, - string requestPath, - string requestBody, - string expectedErrorMessage) - { - const string CUSTOM_CONFIG = "custom-config.json"; - TestHelper.ConstructNewConfigWithSpecifiedHostMode(CUSTOM_CONFIG, HostModeType.Production, TestCategory.MYSQL); - string[] args = new[] - { - $"--ConfigFileName={CUSTOM_CONFIG}" - }; - - using (TestServer server = new(Program.CreateWebHostBuilder(args))) - using (HttpClient client = server.CreateClient()) - { - HttpMethod httpMethod = SqlTestHelper.ConvertRestMethodToHttpMethod(requestType); - HttpRequestMessage request; - if (requestType is RestMethod.Get || requestType is RestMethod.Delete) - { - request = new(httpMethod, requestPath); - } - else - { - request = new(httpMethod, requestPath) - { - Content = JsonContent.Create(requestBody) - }; - } - - HttpResponseMessage response = await client.SendAsync(request); - string body = await response.Content.ReadAsStringAsync(); - Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(body.Contains(expectedErrorMessage)); - } - } - /// /// Tests that the when Rest or GraphQL is disabled Globally, /// any requests made will get a 404 response. From 81944a09ba2f8972fc25e4088ba90dc109faa3df Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Wed, 29 Mar 2023 17:46:27 +0530 Subject: [PATCH 20/28] moving constants to the same test file --- .../Configuration/ConfigurationTests.cs | 34 +++++++++++++++---- src/Service.Tests/TestHelper.cs | 20 ----------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index 63ebf2724e..a956bf4bdd 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -51,6 +51,26 @@ public class ConfigurationTests private const int RETRY_COUNT = 5; private const int RETRY_WAIT_SECONDS = 1; + /// + /// A valid REST API request body with correct parameter types for all the fields. + /// + public const string REQUEST_BODY_WITH_CORRECT_PARAM_TYPES = @" + { + ""title"": ""New book"", + ""publisher_id"": 1234 + } + "; + + /// + /// An invalid REST API request body with incorrect parameter type for publisher_id field. + /// + public const string REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES = @" + { + ""title"": ""New book"", + ""publisher_id"": ""one"" + } + "; + public TestContext TestContext { get; set; } [TestInitialize] @@ -960,13 +980,13 @@ public async Task TestPathRewriteMiddlewareForGraphQL( [TestCategory(TestCategory.MSSQL)] [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode")] [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode")] - [DataRow(RestMethod.Get, "/api/GetBook?id=one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode")] - [DataRow(RestMethod.Post, "/api/Book", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode")] - [DataRow(RestMethod.Put, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode")] - [DataRow(RestMethod.Put, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode")] - [DataRow(RestMethod.Patch, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode")] - [DataRow(RestMethod.Patch, "/api/Book/id/1", TestHelper.REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode")] - [DataRow(RestMethod.Delete, "/api/Book/id/one", TestHelper.REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Get, "/api/GetBook?id=one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode")] + [DataRow(RestMethod.Post, "/api/Book", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Put, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Put, "/api/Book/id/1", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Patch, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode")] + [DataRow(RestMethod.Patch, "/api/Book/id/1", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode")] + [DataRow(RestMethod.Delete, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode")] public async Task TestGenericErrorMessageForRestApiInProductionMode( RestMethod requestType, diff --git a/src/Service.Tests/TestHelper.cs b/src/Service.Tests/TestHelper.cs index 775e84ed09..c29f4ea81c 100644 --- a/src/Service.Tests/TestHelper.cs +++ b/src/Service.Tests/TestHelper.cs @@ -238,26 +238,6 @@ public static void AddMissingEntitiesToConfig(RuntimeConfig config, string entit ""entities"": {}" + "}"; - /// - /// A valid REST API request body with correct parameter types for all the fields. - /// - public const string REQUEST_BODY_WITH_CORRECT_PARAM_TYPES = @" - { - ""title"": ""New book"", - ""publisher_id"": 1234 - } - "; - - /// - /// An invalid REST API request body with incorrect parameter type for publisher_id field. - /// - public const string REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES = @" - { - ""title"": ""New book"", - ""publisher_id"": ""one"" - } - "; - /// /// Utility method that reads the config file for a given database type and constructs a /// new config file with changes just in the host mode section. From df9e214dc625d7421d95992c91eacf8578162d8f Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Wed, 29 Mar 2023 18:05:15 +0530 Subject: [PATCH 21/28] adding parameter name in function calls --- .../Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs | 2 +- .../Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs | 2 +- src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs index b8d7d260b3..f854d0fdb9 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlExecuteQueryStructure.cs @@ -52,7 +52,7 @@ public SqlExecuteStructure( } else { - parametrizedName = MakeParamWithValue(null); + parametrizedName = MakeParamWithValue(value: null); } ProcedureParameters.Add(paramKey, $"{parametrizedName}"); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs index b248808262..c187845ebf 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlInsertQueryStructure.cs @@ -94,7 +94,7 @@ private void PopulateColumnsAndParams(string columnName, object? value) } else { - paramName = MakeParamWithValue(null); + paramName = MakeParamWithValue(value: null); } Values.Add($"{paramName}"); diff --git a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs index 4fba9cdbcd..8a21f38f7e 100644 --- a/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/SqlQueryStructure.cs @@ -487,7 +487,7 @@ public void AddPaginationPredicate(IEnumerable afterJsonValues column.TableAlias = SourceAlias; column.ParamName = column.Value is not null ? MakeParamWithValue(GetParamAsSystemType(column.Value!.ToString()!, column.ColumnName, GetColumnSystemType(column.ColumnName))) : - MakeParamWithValue(null); + MakeParamWithValue(value: null); } PaginationMetadata.PaginationPredicate = new KeysetPaginationPredicate(afterJsonValues.ToList()); From 15ff00a972dd9400d89d27f35ab5dad641947b8a Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Wed, 29 Mar 2023 18:19:30 +0530 Subject: [PATCH 22/28] updating GetParamAsSystemType method description to include exceptions --- .../Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 847b20a095..f327f62cfa 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -496,6 +496,12 @@ public void ProcessOdataClause(FilterClause odataClause) /// /// Gets the value of the parameter cast as the system type /// + /// Parameter value as a string + /// Parameter name + /// System type to which the parameter value is parsed to + /// The parameter value parsed to the specified system type + /// Throws a DataApiBuilderException when the conversion of parameter value to the specified system type fails. The error message returned will be different in development + /// and production modes. In production mode, the error message returned will be generic so as to not reveal information about the database object backing the entity protected object GetParamAsSystemType(string param, string paramName, Type systemType) { try From f5870d5cd06c33a6720552f32e65e20556525ec6 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Wed, 29 Mar 2023 18:35:29 +0530 Subject: [PATCH 23/28] using runtimeconfigprovider's implementation in cosmossqlmetadataprovider to determine dev mode --- .../Services/MetadataProviders/CosmosSqlMetadataProvider.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs index 597fa7d275..13b4be19aa 100644 --- a/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/CosmosSqlMetadataProvider.cs @@ -23,6 +23,7 @@ public class CosmosSqlMetadataProvider : ISqlMetadataProvider private readonly RuntimeConfig _runtimeConfig; private Dictionary _partitionKeyPaths = new(); private Dictionary _graphQLSingularTypeToEntityNameMap = new(); + private readonly RuntimeConfigProvider _runtimeConfigProvider; /// public Dictionary GraphQLStoredProcedureExposedNameToEntityNameMap { get; set; } = new(); @@ -35,6 +36,7 @@ public class CosmosSqlMetadataProvider : ISqlMetadataProvider public CosmosSqlMetadataProvider(RuntimeConfigProvider runtimeConfigProvider, IFileSystem fileSystem) { _fileSystem = fileSystem; + _runtimeConfigProvider = runtimeConfigProvider; _runtimeConfig = runtimeConfigProvider.GetRuntimeConfiguration(); _entities = _runtimeConfig.Entities; @@ -238,7 +240,7 @@ public string GetDefaultSchemaName() public bool IsDevelopmentMode() { - return _runtimeConfig.HostGlobalSettings.Mode is HostModeType.Development; + return _runtimeConfigProvider.IsDeveloperMode(); } } } From 566fb022ea9d2cb8755a7dafb30ecb9219b6aba4 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 30 Mar 2023 14:48:48 +0530 Subject: [PATCH 24/28] returning exposed column names when mappings are defined --- .../Configuration/ConfigurationTests.cs | 1 + .../BaseSqlQueryStructure.cs | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index a956bf4bdd..a09cceeb1c 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -981,6 +981,7 @@ public async Task TestPathRewriteMiddlewareForGraphQL( [DataRow(RestMethod.Get, "/api/Book/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a table in production mode")] [DataRow(RestMethod.Get, "/api/books_view_all/id/one", null, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type on a view in production mode")] [DataRow(RestMethod.Get, "/api/GetBook?id=one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a GET request on a stored-procedure with incorrect parameter type in production mode")] + [DataRow(RestMethod.Get, "/api/GQLmappings/column1/one", null, "Invalid value provided for field: column1", DisplayName = "Validates the error message for a GET request with incorrect primary key parameter type with alias defined for primary key column on a table in production mode")] [DataRow(RestMethod.Post, "/api/Book", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a POST request with incorrect parameter type in the request body on a table in production mode")] [DataRow(RestMethod.Put, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PUT request with incorrect primary key parameter type on a table in production mode")] [DataRow(RestMethod.Put, "/api/Book/id/1", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a bad PUT request with incorrect parameter type in the request body on a table in production mode")] diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index f327f62cfa..44e7655bdd 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -129,7 +129,13 @@ public Type GetColumnSystemType(string columnName) } else { - errorMessage = $"{columnName} is not a valid field of {EntityName}"; + string fieldNameToBeDisplayedInErrorMsg = columnName; + if (MetadataProvider.TryGetExposedColumnName(EntityName, columnName, out string? exposedColumnName)) + { + fieldNameToBeDisplayedInErrorMsg = exposedColumnName!; + } + + errorMessage = $"{fieldNameToBeDisplayedInErrorMsg} is not a valid field of {EntityName}"; } throw new DataApiBuilderException( @@ -530,7 +536,17 @@ e is ArgumentNullException || } else { - errorMessage = $"Invalid value provided for field: {paramName}"; + string fieldNameToBeDisplayedInErrorMessage = paramName; + + if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.Table || MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.View) + { + if (MetadataProvider.TryGetExposedColumnName(EntityName, paramName, out string? exposedName)) + { + fieldNameToBeDisplayedInErrorMessage = exposedName!; + } + } + + errorMessage = $"Invalid value provided for field: {fieldNameToBeDisplayedInErrorMessage}"; } throw new DataApiBuilderException( From 867aac739e5e473826b8d1638000ac8787293ed6 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Thu, 30 Mar 2023 15:29:59 +0530 Subject: [PATCH 25/28] reverting change in unused code path --- .../BaseSqlQueryStructure.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 44e7655bdd..fcd8a6c6cb 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -122,24 +122,8 @@ public Type GetColumnSystemType(string columnName) } else { - string errorMessage; - if (MetadataProvider.IsDevelopmentMode()) - { - errorMessage = $"{columnName} is not a valid column of {DatabaseObject.Name}"; - } - else - { - string fieldNameToBeDisplayedInErrorMsg = columnName; - if (MetadataProvider.TryGetExposedColumnName(EntityName, columnName, out string? exposedColumnName)) - { - fieldNameToBeDisplayedInErrorMsg = exposedColumnName!; - } - - errorMessage = $"{fieldNameToBeDisplayedInErrorMsg} is not a valid field of {EntityName}"; - } - throw new DataApiBuilderException( - message: errorMessage, + message: $"{columnName} is not a valid column of {DatabaseObject.Name}", statusCode: HttpStatusCode.BadRequest, subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest ); From 29429175f48eb10d94b84138dc448de3cdb33c7a Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Fri, 31 Mar 2023 09:50:22 +0530 Subject: [PATCH 26/28] using when with exceptions, renaming method param name, updating method descriptions --- .../BaseSqlQueryStructure.cs | 63 +++++++++---------- .../MetadataProviders/ISqlMetadataProvider.cs | 1 - 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index fcd8a6c6cb..99c8b61eab 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -486,61 +486,56 @@ public void ProcessOdataClause(FilterClause odataClause) /// /// Gets the value of the parameter cast as the system type /// - /// Parameter value as a string - /// Parameter name + /// Parameter value as a string + /// Field name whose value is being converted to the specified system type. This is used only for constructing the error messages incase of conversion failures /// System type to which the parameter value is parsed to /// The parameter value parsed to the specified system type /// Throws a DataApiBuilderException when the conversion of parameter value to the specified system type fails. The error message returned will be different in development /// and production modes. In production mode, the error message returned will be generic so as to not reveal information about the database object backing the entity - protected object GetParamAsSystemType(string param, string paramName, Type systemType) + protected object GetParamAsSystemType(string paramValue, string fieldName, Type systemType) { try { - return ParseParamAsSystemType(param, systemType); + return ParseParamAsSystemType(paramValue, systemType); } - catch (Exception e) + catch (Exception e) when (e is FormatException || e is ArgumentNullException || e is OverflowException) { - if (e is FormatException || - e is ArgumentNullException || - e is OverflowException) + + string errorMessage; + if (MetadataProvider.IsDevelopmentMode()) { - string errorMessage; - if (MetadataProvider.IsDevelopmentMode()) + if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) { - if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) - { - errorMessage = $@"Parameter ""{param}"" cannot be resolved as stored procedure parameter ""{paramName}"" " + - $@"with type ""{systemType.Name}""."; - } - else - { - errorMessage = $"Parameter \"{param}\" cannot be resolved as column \"{paramName}\" " + - $"with type \"{systemType.Name}\"."; - } + errorMessage = $@"Parameter ""{paramValue}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + + $@"with type ""{systemType.Name}""."; } else { - string fieldNameToBeDisplayedInErrorMessage = paramName; + errorMessage = $"Parameter \"{paramValue}\" cannot be resolved as column \"{fieldName}\" " + + $"with type \"{systemType.Name}\"."; + } + } + else + { + string fieldNameToBeDisplayedInErrorMessage = fieldName; - if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.Table || MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.View) + if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.Table || MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.View) + { + if (MetadataProvider.TryGetExposedColumnName(EntityName, fieldName, out string? exposedName)) { - if (MetadataProvider.TryGetExposedColumnName(EntityName, paramName, out string? exposedName)) - { - fieldNameToBeDisplayedInErrorMessage = exposedName!; - } + fieldNameToBeDisplayedInErrorMessage = exposedName!; } - - errorMessage = $"Invalid value provided for field: {fieldNameToBeDisplayedInErrorMessage}"; } - throw new DataApiBuilderException( - message: errorMessage, - statusCode: HttpStatusCode.BadRequest, - subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, - innerException: e); + errorMessage = $"Invalid value provided for field: {fieldNameToBeDisplayedInErrorMessage}"; } - throw; + throw new DataApiBuilderException( + message: errorMessage, + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest, + innerException: e); + } } diff --git a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs index ff6a7afcad..9b09c0af50 100644 --- a/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs +++ b/src/Service/Services/MetadataProviders/ISqlMetadataProvider.cs @@ -164,7 +164,6 @@ public DatabaseObject GetDatabaseObjectForGraphQLType(string graphqlType) /// Returns true when the engine is running in Development mode. When running in Production /// mode, it returns false. /// - /// public bool IsDevelopmentMode(); } } From 7c0a1078900c551481b154cb05c592960dd7313c Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Fri, 31 Mar 2023 11:41:35 +0530 Subject: [PATCH 27/28] renaming paramValue to fieldValue --- .../Sql Query Structures/BaseSqlQueryStructure.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 99c8b61eab..b8af16f715 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -486,17 +486,17 @@ public void ProcessOdataClause(FilterClause odataClause) /// /// Gets the value of the parameter cast as the system type /// - /// Parameter value as a string + /// Field value as a string /// Field name whose value is being converted to the specified system type. This is used only for constructing the error messages incase of conversion failures /// System type to which the parameter value is parsed to /// The parameter value parsed to the specified system type /// Throws a DataApiBuilderException when the conversion of parameter value to the specified system type fails. The error message returned will be different in development /// and production modes. In production mode, the error message returned will be generic so as to not reveal information about the database object backing the entity - protected object GetParamAsSystemType(string paramValue, string fieldName, Type systemType) + protected object GetParamAsSystemType(string fieldValue, string fieldName, Type systemType) { try { - return ParseParamAsSystemType(paramValue, systemType); + return ParseParamAsSystemType(fieldValue, systemType); } catch (Exception e) when (e is FormatException || e is ArgumentNullException || e is OverflowException) { @@ -506,12 +506,12 @@ protected object GetParamAsSystemType(string paramValue, string fieldName, Type { if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) { - errorMessage = $@"Parameter ""{paramValue}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + + errorMessage = $@"Parameter ""{fieldValue}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + $@"with type ""{systemType.Name}""."; } else { - errorMessage = $"Parameter \"{paramValue}\" cannot be resolved as column \"{fieldName}\" " + + errorMessage = $"Parameter \"{fieldValue}\" cannot be resolved as column \"{fieldName}\" " + $"with type \"{systemType.Name}\"."; } } From d959726cc14d7a4b772683c76ce3c24a6b563298 Mon Sep 17 00:00:00 2001 From: Shyam Sundar J Date: Sat, 1 Apr 2023 08:58:42 +0530 Subject: [PATCH 28/28] storing sourcetype in a variable, removing extra white line --- src/Service.Tests/Configuration/ConfigurationTests.cs | 1 - .../Sql Query Structures/BaseSqlQueryStructure.cs | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs index e217935f5f..5cd190d1b0 100644 --- a/src/Service.Tests/Configuration/ConfigurationTests.cs +++ b/src/Service.Tests/Configuration/ConfigurationTests.cs @@ -988,7 +988,6 @@ public async Task TestPathRewriteMiddlewareForGraphQL( [DataRow(RestMethod.Patch, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a PATCH request with incorrect primary key parameter type on a table in production mode")] [DataRow(RestMethod.Patch, "/api/Book/id/1", REQUEST_BODY_WITH_INCORRECT_PARAM_TYPES, "Invalid value provided for field: publisher_id", DisplayName = "Validates the error message for a PATCH request with incorrect parameter type in the request body on a table in production mode")] [DataRow(RestMethod.Delete, "/api/Book/id/one", REQUEST_BODY_WITH_CORRECT_PARAM_TYPES, "Invalid value provided for field: id", DisplayName = "Validates the error message for a DELETE request with incorrect primary key parameter type on a table in production mode")] - public async Task TestGenericErrorMessageForRestApiInProductionMode( RestMethod requestType, string requestPath, diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index b8af16f715..f98d30b5c8 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -490,7 +490,7 @@ public void ProcessOdataClause(FilterClause odataClause) /// Field name whose value is being converted to the specified system type. This is used only for constructing the error messages incase of conversion failures /// System type to which the parameter value is parsed to /// The parameter value parsed to the specified system type - /// Throws a DataApiBuilderException when the conversion of parameter value to the specified system type fails. The error message returned will be different in development + /// Raised when the conversion of parameter value to the specified system type fails. The error message returned will be different in development /// and production modes. In production mode, the error message returned will be generic so as to not reveal information about the database object backing the entity protected object GetParamAsSystemType(string fieldValue, string fieldName, Type systemType) { @@ -502,9 +502,10 @@ protected object GetParamAsSystemType(string fieldValue, string fieldName, Type { string errorMessage; + SourceType sourceTypeOfDbObject = MetadataProvider.EntityToDatabaseObject[EntityName].SourceType; if (MetadataProvider.IsDevelopmentMode()) { - if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.StoredProcedure) + if (sourceTypeOfDbObject is SourceType.StoredProcedure) { errorMessage = $@"Parameter ""{fieldValue}"" cannot be resolved as stored procedure parameter ""{fieldName}"" " + $@"with type ""{systemType.Name}""."; @@ -518,8 +519,7 @@ protected object GetParamAsSystemType(string fieldValue, string fieldName, Type else { string fieldNameToBeDisplayedInErrorMessage = fieldName; - - if (MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.Table || MetadataProvider.EntityToDatabaseObject[EntityName].SourceType is SourceType.View) + if (sourceTypeOfDbObject is SourceType.Table || sourceTypeOfDbObject is SourceType.View) { if (MetadataProvider.TryGetExposedColumnName(EntityName, fieldName, out string? exposedName)) {