diff --git a/src/Service.Tests/SqlTests/RestApiTests/Delete/DeleteApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Delete/DeleteApiTestBase.cs index f03b519689..87cffa252c 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Delete/DeleteApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Delete/DeleteApiTestBase.cs @@ -159,6 +159,27 @@ await SetupAndRunRestApiTest( ); } + /// + /// Tests that a cast failure of primary key value type results in HTTP 400 Bad Request. + /// e.g. Attempt to cast a string '{}' to the 'id' column type of int will fail. + /// + [TestMethod] + public async Task DeleteWithUncastablePKValue() + { + await SetupAndRunRestApiTest( + primaryKeyRoute: "id/{}", + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: string.Empty, + operationType: Operation.Delete, + requestBody: string.Empty, + exceptionExpected: true, + expectedErrorMessage: "Parameter \"{}\" cannot be resolved as column \"id\" with type \"Int32\".", + expectedStatusCode: HttpStatusCode.BadRequest, + expectedSubStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest.ToString() + ); + } + /// /// DeleteWithSqlInjectionTest attempts to inject a SQL statement /// through the primary key route of a delete operation. diff --git a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs index 51efef72d7..d4cbbe3cbe 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Find/FindApiTestBase.cs @@ -1451,6 +1451,24 @@ await SetupAndRunRestApiTest( ); } + /// + /// Tests that a cast failure of primary key value type results in HTTP 400 Bad Request. + /// e.g. Attempt to cast a string '{}' to the 'id' column type of int will fail. + /// + [TestMethod] + public async Task FindWithUncastablePKValue() + { + await SetupAndRunRestApiTest( + primaryKeyRoute: "id/{}", + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: null, + exceptionExpected: true, + expectedErrorMessage: "Parameter \"{}\" cannot be resolved as column \"id\" with type \"Int32\".", + expectedStatusCode: HttpStatusCode.BadRequest + ); + } + /// /// Tests the REST Api for FindById operation with attempts at /// Sql Injection in the primary key route. diff --git a/src/Service.Tests/SqlTests/RestApiTests/Insert/InsertApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Insert/InsertApiTestBase.cs index 74ff7830b0..fea23c18d9 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Insert/InsertApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Insert/InsertApiTestBase.cs @@ -362,6 +362,32 @@ await SetupAndRunRestApiTest( ); } + /// + /// Tests that a cast failure of primary key value type results in HTTP 400 Bad Request. + /// e.g. Attempt to cast a string '{}' to the 'publisher_id' column type of int will fail. + /// + [TestMethod] + public async Task InsertWithUncastablePKValue() + { + string requestBody = @" + { + ""title"": ""BookTitle"", + ""publisher_id"": ""StringFailsToCastToInt"" + }"; + + await SetupAndRunRestApiTest( + primaryKeyRoute: string.Empty, + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: null, + operationType: Operation.Insert, + requestBody: requestBody, + exceptionExpected: true, + expectedErrorMessage: "Parameter \"StringFailsToCastToInt\" cannot be resolved as column \"publisher_id\" with type \"Int32\".", + expectedStatusCode: HttpStatusCode.BadRequest + ); + } + /// /// Tests the InsertOne functionality with a missing field in the request body: /// A non-nullable field in the Json Body is missing. diff --git a/src/Service.Tests/SqlTests/RestApiTests/Patch/PatchApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Patch/PatchApiTestBase.cs index fa36113aa6..a9ccc82c51 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Patch/PatchApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Patch/PatchApiTestBase.cs @@ -473,6 +473,31 @@ await SetupAndRunRestApiTest( ); } + /// + /// Tests that a cast failure of primary key value type results in HTTP 400 Bad Request. + /// e.g. Attempt to cast a string '{}' to the 'publisher_id' column type of int will fail. + /// + [TestMethod] + public async Task PatchWithUncastablePKValue() + { + string requestBody = @" + { + ""publisher_id"": ""StringFailsToCastToInt"" + }"; + + await SetupAndRunRestApiTest( + primaryKeyRoute: "id/1", + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: null, + operationType: Operation.UpsertIncremental, + requestBody: requestBody, + exceptionExpected: true, + expectedErrorMessage: "Parameter \"StringFailsToCastToInt\" cannot be resolved as column \"publisher_id\" with type \"Int32\".", + expectedStatusCode: HttpStatusCode.BadRequest + ); + } + /// /// Tests the Patch functionality with a REST PATCH request /// without a primary key route. We expect a failure and so diff --git a/src/Service.Tests/SqlTests/RestApiTests/Put/PutApiTestBase.cs b/src/Service.Tests/SqlTests/RestApiTests/Put/PutApiTestBase.cs index 1f556d2d0a..bc5988da8e 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/Put/PutApiTestBase.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/Put/PutApiTestBase.cs @@ -643,6 +643,32 @@ await SetupAndRunRestApiTest( ); } + /// + /// Tests that a cast failure of primary key value type results in HTTP 400 Bad Request. + /// e.g. Attempt to cast a string '{}' to the 'publisher_id' column type of int will fail. + /// + [TestMethod] + public async Task PutWithUncastablePKValue() + { + string requestBody = @" + { + ""title"": ""BookTitle"", + ""publisher_id"": ""StringFailsToCastToInt"" + }"; + + await SetupAndRunRestApiTest( + primaryKeyRoute: "id/1", + queryString: string.Empty, + entityNameOrPath: _integrationEntityName, + sqlQuery: null, + operationType: Operation.Upsert, + requestBody: requestBody, + exceptionExpected: true, + expectedErrorMessage: "Parameter \"StringFailsToCastToInt\" cannot be resolved as column \"publisher_id\" with type \"Int32\".", + expectedStatusCode: HttpStatusCode.BadRequest + ); + } + /// /// Tests the Put functionality with a REST PUT request /// with the request body having null value for non-nullable column diff --git a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs index 18bad19b13..8a573d53de 100644 --- a/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs +++ b/src/Service/Resolvers/Sql Query Structures/BaseSqlQueryStructure.cs @@ -125,7 +125,11 @@ public Type GetColumnSystemType(string columnName) } else { - throw new ArgumentException($"{columnName} is not a valid column of {DatabaseObject.Name}"); + throw new DataApiBuilderException( + message: $"{columnName} is not a valid column of {DatabaseObject.Name}", + statusCode: HttpStatusCode.BadRequest, + subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest + ); } } @@ -205,17 +209,14 @@ protected object GetParamAsColumnSystemType(string param, string columnName) { return ParseParamAsSystemType(param, 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) - { - throw new ArgumentException($"Parameter \"{param}\" cannot be resolved as column \"{columnName}\" " + - $"with type \"{systemType.Name}\"."); - } - - throw; + 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); } }