Compare commits

...

18 Commits

Author SHA1 Message Date
27d731a5b0 Bump version to 2.0.0-beta in ReC.API.csproj
Updated <Version>, <AssemblyVersion>, <FileVersion>, and <InformationalVersion> fields from 1.0.3-beta to 2.0.0-beta to reflect a major version change, indicating significant updates or breaking changes in the project.
2026-01-22 10:41:26 +01:00
b8f797f14d Make EndpointAuthType non-nullable with default NoAuth
Changed EndpointAuthType in RecActionViewDto from nullable to non-nullable and set its default value to EndpointAuthType.NoAuth to ensure it always has a valid value.
2026-01-22 10:37:37 +01:00
6feef53733 Update namespaces and usings for Insert procedures
Refactored InsertActionProcedure to the RecActions.Commands namespace and updated its usings to import InsertObjectProcedure. Added missing using for RecActions.Commands in InsertObjectProcedure.cs. These changes improve code organization and clarify command responsibilities.
2026-01-22 10:17:08 +01:00
878e096c57 Refactor TypeId to use RestType enum in InsertActionProcedure
Changed TypeId from byte? to RestType? in InsertActionProcedure for stronger typing. Updated InsertObjectProcedureHandler to cast RestType to byte? when creating SQL parameters. Added using directive for ReC.Domain.Constants to support the new enum type.
2026-01-22 09:55:35 +01:00
2ded140ad5 Rename SqlExceptionNumber to BadRequestSqlExceptionNumber
Clarify intent by renaming SqlExceptionNumber to BadRequestSqlExceptionNumber in both configuration and code. This makes it explicit that these SQL exception numbers are mapped to HTTP 400 Bad Request errors. All relevant usages and settings have been updated accordingly.
2026-01-22 09:33:54 +01:00
e2ca249d13 Add SQL exception 50000 to handled exception numbers
Updated appsettings.json to include 50000 in the SqlExceptionNumber array, allowing the application to recognize and handle SQL exceptions with this number in addition to existing ones. This change can be applied at runtime without requiring a restart.
2026-01-22 09:31:41 +01:00
e782eab62a Refactor handlers to use IOptionsMonitor for SQL options
Refactored Delete, Insert, and Update procedure handlers to inject IOptionsMonitor<SqlExceptionOptions> instead of SqlExceptionOptions, enabling dynamic configuration updates. Updated all references to use CurrentValue. Added necessary using directives and cleaned up redundant usings in InsertObjectProcedure.cs.
2026-01-22 09:21:39 +01:00
d8e08b237d Simplify SqlException config structure in appsettings.json
Removed the "ErrorMessages" wrapper from the SqlException section,
placing "SqlExceptionNumber" directly under "SqlException".
No changes to the exception numbers themselves. Improved
readability and clarity of the configuration.
2026-01-22 02:04:52 +01:00
Developer 02
88cb1dc16a Handle SqlException in UpdateObjectProcedureHandler
Wrap stored procedure execution in try-catch to handle SqlException.
Throw BadRequestException for configured SQL error numbers.
Update constructor to accept SqlExceptionOptions.
Add necessary using directives for new exception handling.
2026-01-22 01:54:07 +01:00
Developer 02
8d6a09213e Simplify BadRequestException error message on insert failure
Replaced the detailed error message for insert failures with only the original exception message from SqlException, removing extra guidance about referenced entities.
2026-01-22 01:53:55 +01:00
Developer 02
54f412ced2 Improve error handling in DeleteObjectProcedureHandler
Refactored DeleteObjectProcedureHandler to inject SqlExceptionOptions and wrap stored procedure execution in a try-catch block. Added logic to throw custom exceptions (DeleteObjectFailedException, BadRequestException) based on result codes and SQL exception numbers, enhancing robustness and configurability of error handling.
2026-01-22 01:53:40 +01:00
Developer 02
51b9c62188 Add SqlException config options and update dependencies
Added ConfigureSqlException methods to DependencyInjection for flexible SQL exception handling configuration. Updated RecApplicationTestBase to use new options. Bumped DigitalData.Core.Exceptions to v1.1.1.
2026-01-22 01:49:59 +01:00
Developer 02
bb5525778d Improve error handling in InsertObjectProcedureHandler
Wrap SQL execution in try-catch and inject SqlExceptionOptions.
Throw BadRequestException with a clear message when insert fails due to missing referenced entities, based on SqlException number. Other SQL exceptions are rethrown. This provides better feedback for foreign key/reference errors.
2026-01-22 01:49:13 +01:00
Developer 02
ee793632df Add SQL exception handling configuration support
Updated Program.cs to configure SQL exception handling using the new "SqlException" section in appsettings.json. Renamed "SqlExceptionTranslator" to "SqlException" and added error message mappings for specific SQL exception numbers to enable custom exception translation.
2026-01-22 01:46:46 +01:00
Developer 02
22bb585f60 Refactor SQL exception handling and config structure
Simplify SQL exception tracking by replacing error message mappings
with a list of relevant error numbers in appsettings.json. Remove
custom error message logic and related classes, introducing
SqlExceptionOptions to hold tracked error codes.
2026-01-22 01:45:50 +01:00
Developer 02
ed7237c8dd Add SqlExceptionTranslator config and options class
Introduce SqlExceptionTranslator section in appsettings.json to map common SQL error codes to user-friendly messages. Add SqlExceptionTranslatorOptions class to manage error code/message mapping and support configuration binding.
2026-01-22 01:13:02 +01:00
Developer 02
f71bcf37e9 Add SqlExceptionTranslator for custom SQL error handling
Introduced SqlExceptionTranslator and ISqlExceptionTranslator to translate SQL exceptions into user-friendly error messages for bad request scenarios. Uses configurable options to identify relevant error numbers and format messages with templates.
2026-01-22 00:34:35 +01:00
Developer 02
c7e366af60 Refactor ResultController ctor, enhance BadRequest logging
Removed IConfiguration from ResultController constructor, now only using IMediator. Added logging for BadRequestException inner exceptions in ExceptionHandlingMiddleware for improved error diagnostics.
2026-01-21 22:24:41 +01:00
14 changed files with 142 additions and 59 deletions

View File

@@ -12,7 +12,7 @@ namespace ReC.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ResultController(IMediator mediator, IConfiguration config) : ControllerBase
public class ResultController(IMediator mediator) : ControllerBase
{
/// <summary>
/// Gets output results based on the provided query parameters.

View File

@@ -63,6 +63,13 @@ public class ExceptionHandlingMiddleware
switch (exception)
{
case BadRequestException badRequestEx:
if (badRequestEx.InnerException is not null)
{
logger.LogError(
badRequestEx.InnerException,
"BadRequestException inner exception captured.");
}
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
details = new()
{

View File

@@ -37,6 +37,7 @@ try
{
options.LuckyPennySoftwareLicenseKey = builder.Configuration["LuckyPennySoftwareLicenseKey"];
options.ConfigureRecActions(config.GetSection("RecAction"));
options.ConfigureSqlException(config.GetSection("SqlException"));
});
builder.Services.AddRecInfrastructure(options =>

View File

@@ -10,10 +10,10 @@
<Product>ReC.API</Product>
<PackageIcon>Assets\icon.ico</PackageIcon>
<PackageTags>digital data rest-caller rec api</PackageTags>
<Version>1.0.3-beta</Version>
<AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.3.0</FileVersion>
<InformationalVersion>1.0.3-beta</InformationalVersion>
<Version>2.0.0-beta</Version>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<InformationalVersion>2.0.0-beta</InformationalVersion>
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>

View File

@@ -8,6 +8,11 @@
"RecAction": {
"MaxConcurrentInvocations": 5
},
// Bad request SqlException numbers numbers can be updated at runtime; no restart required.
"SqlException": {
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlexception.number
"BadRequestSqlExceptionNumbers": [ 515, 547, 2601, 2627, 50000 ]
},
"AddedWho": "ReC.API",
"FakeProfileId": 2
}

View File

@@ -20,7 +20,7 @@ public record RecActionViewDto
public long? EndpointAuthId { get; init; }
public EndpointAuthType? EndpointAuthType { get; init; }
public EndpointAuthType EndpointAuthType { get; init; } = EndpointAuthType.NoAuth;
public string? EndpointAuthTypeName { get; init; }

View File

@@ -0,0 +1,6 @@
namespace ReC.Application.Common.Options;
public class SqlExceptionOptions
{
public HashSet<int> BadRequestSqlExceptionNumbers { get; set; } = [];
}

View File

@@ -1,7 +1,10 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options;
namespace ReC.Application.Common.Procedures.DeleteProcedure;
@@ -47,7 +50,7 @@ public static class DeleteObjectProcedureExtensions
}
}
public class DeleteObjectProcedureHandler(IRepository repo) : IRequestHandler<DeleteObjectProcedure, int>
public class DeleteObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<DeleteObjectProcedure, int>
{
public async Task<int> Handle(DeleteObjectProcedure request, CancellationToken cancel)
{
@@ -59,20 +62,30 @@ public class DeleteObjectProcedureHandler(IRepository repo) : IRequestHandler<De
new SqlParameter("@pFORCE", (object?)request.Force ?? DBNull.Value)
};
var result = await repo.ExecuteQueryRawAsync(
"DECLARE @RC SMALLINT = 0; " +
"EXEC @RC = [dbo].[PRREC_DELETE_OBJECT] " +
"@pENTITY, @pSTART, @pEND, @pFORCE; " +
"SELECT @RC;",
parameters,
cancel);
// The stored procedure returns 0 on success, error codes > 0 on failure
if (result > 0)
try
{
throw new DeleteObjectFailedException(request, $"DeleteObject stored procedure failed with error code: {result}");
}
var result = await repo.ExecuteQueryRawAsync(
"DECLARE @RC SMALLINT = 0; " +
"EXEC @RC = [dbo].[PRREC_DELETE_OBJECT] " +
"@pENTITY, @pSTART, @pEND, @pFORCE; " +
"SELECT @RC;",
parameters,
cancel);
return result;
// The stored procedure returns 0 on success, error codes > 0 on failure
if (result > 0)
{
throw new DeleteObjectFailedException(request, $"DeleteObject stored procedure failed with error code: {result}");
}
return result;
}
catch (SqlException ex)
{
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
throw new BadRequestException(ex.Message, ex);
else
throw;
}
}
}

View File

@@ -1,12 +1,16 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options;
using ReC.Application.EndpointAuth.Commands;
using ReC.Application.EndpointParams.Commands;
using ReC.Application.Endpoints.Commands;
using ReC.Application.Results.Commands;
using ReC.Application.Profile.Commands;
using ReC.Application.RecActions.Commands;
using ReC.Application.Results.Commands;
namespace ReC.Application.Common.Procedures.InsertProcedure;
@@ -41,7 +45,7 @@ public static class InsertObjectProcedureExtensions
}
}
public class InsertObjectProcedureHandler(IRepository repo) : IRequestHandler<InsertObjectProcedure, long>
public class InsertObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<InsertObjectProcedure, long>
{
public async Task<long> Handle(InsertObjectProcedure request, CancellationToken cancel)
{
@@ -59,7 +63,7 @@ public class InsertObjectProcedureHandler(IRepository repo) : IRequestHandler<In
new SqlParameter("@pACTION_ENDPOINT_AUTH_ID", (object?)request.Action.EndpointAuthId ?? DBNull.Value),
new SqlParameter("@pACTION_ENDPOINT_PARAMS_ID", (object?)request.Action.EndpointParamsId ?? DBNull.Value),
new SqlParameter("@pACTION_SQL_CONNECTION_ID", (object?)request.Action.SqlConnectionId ?? DBNull.Value),
new SqlParameter("@pACTION_TYPE_ID", (object?)request.Action.TypeId ?? DBNull.Value),
new SqlParameter("@pACTION_TYPE_ID", (object?)(byte?)request.Action.TypeId ?? DBNull.Value),
new SqlParameter("@pACTION_PRE_SQL", (object?)request.Action.PreSql ?? DBNull.Value),
new SqlParameter("@pACTION_HEADER_SQL", (object?)request.Action.HeaderSql ?? DBNull.Value),
new SqlParameter("@pACTION_BODY_SQL", (object?)request.Action.BodySql ?? DBNull.Value),
@@ -110,18 +114,28 @@ public class InsertObjectProcedureHandler(IRepository repo) : IRequestHandler<In
}
};
await repo.ExecuteQueryRawAsync(
"EXEC [dbo].[PRREC_INSERT_OBJECT] " +
"@pENTITY, @pADDED_WHO, @pADDED_WHEN, " +
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, " +
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY, " +
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
"@oGUID OUTPUT",
parameters,
cancel);
try
{
await repo.ExecuteQueryRawAsync(
"EXEC [dbo].[PRREC_INSERT_OBJECT] " +
"@pENTITY, @pADDED_WHO, @pADDED_WHEN, " +
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, " +
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY, " +
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
"@oGUID OUTPUT",
parameters,
cancel);
}
catch (SqlException ex)
{
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
throw new BadRequestException(ex.Message, ex);
else
throw;
}
var guidParam = parameters.Last();

View File

@@ -1,13 +1,16 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options;
using ReC.Application.EndpointAuth.Commands;
using ReC.Application.EndpointParams.Commands;
using ReC.Application.Endpoints.Commands;
using ReC.Application.Results.Commands;
using ReC.Application.Profile.Commands;
using ReC.Application.RecActions.Commands;
using ReC.Application.Results.Commands;
namespace ReC.Application.Common.Procedures.UpdateProcedure;
@@ -47,7 +50,7 @@ public static class UpdateObjectProcedureExtensions
}
}
public class UpdateObjectProcedureHandler(IRepository repo) : IRequestHandler<UpdateObjectProcedure, int>
public class UpdateObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlExceptionOptions> sqlExOpt) : IRequestHandler<UpdateObjectProcedure, int>
{
public async Task<int> Handle(UpdateObjectProcedure request, CancellationToken cancel)
{
@@ -113,26 +116,36 @@ public class UpdateObjectProcedureHandler(IRepository repo) : IRequestHandler<Up
new SqlParameter("@pRESULT_BODY", (object?)request.Result.Body ?? DBNull.Value)
};
var result = await repo.ExecuteQueryRawAsync(
"DECLARE @RC SMALLINT = 0; " +
"EXEC @RC = [dbo].[PRREC_UPDATE_OBJECT] " +
"@pENTITY, @pGUID, @pCHANGED_WHO, @pCHANGED_WHEN, " +
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, @pPROFILE_FIRST_RUN, @pPROFILE_LAST_RUN, @pPROFILE_LAST_RESULT, " +
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY; " +
"SELECT @RC;",
parameters,
cancel);
// The stored procedure returns 0 on success, error codes > 0 on failure
if (result > 0)
try
{
throw new UpdateObjectFailedException(request, $"UpdateObject stored procedure failed with error code: {result}");
}
var result = await repo.ExecuteQueryRawAsync(
"DECLARE @RC SMALLINT = 0; " +
"EXEC @RC = [dbo].[PRREC_UPDATE_OBJECT] " +
"@pENTITY, @pGUID, @pCHANGED_WHO, @pCHANGED_WHEN, " +
"@pACTION_PROFILE_ID, @pACTION_ACTIVE, @pACTION_SEQUENCE, @pACTION_ENDPOINT_ID, @pACTION_ENDPOINT_AUTH_ID, @pACTION_ENDPOINT_PARAMS_ID, @pACTION_SQL_CONNECTION_ID, @pACTION_TYPE_ID, @pACTION_PRE_SQL, @pACTION_HEADER_SQL, @pACTION_BODY_SQL, @pACTION_POST_SQL, @pACTION_ERROR_ACTION_ID, " +
"@pENDPOINT_ACTIVE, @pENDPOINT_DESCRIPTION, @pENDPOINT_URI, " +
"@pENDPOINT_AUTH_ACTIVE, @pENDPOINT_AUTH_DESCRIPTION, @pENDPOINT_AUTH_TYPE_ID, @pENDPOINT_AUTH_API_KEY, @pENDPOINT_AUTH_API_VALUE, @pENDPOINT_AUTH_API_KEY_ADD_TO_ID, @pENDPOINT_AUTH_TOKEN, @pENDPOINT_AUTH_USERNAME, @pENDPOINT_AUTH_PASSWORD, @pENDPOINT_AUTH_DOMAIN, @pENDPOINT_AUTH_WORKSTATION, " +
"@pENDPOINT_PARAMS_ACTIVE, @pENDPOINT_PARAMS_DESCRIPTION, @pENDPOINT_PARAMS_GROUP_ID, @pENDPOINT_PARAMS_SEQUENCE, @pENDPOINT_PARAMS_KEY, @pENDPOINT_PARAMS_VALUE, " +
"@pPROFILE_ACTIVE, @pPROFILE_TYPE_ID, @pPROFILE_MANDANTOR, @pPROFILE_NAME, @pPROFILE_DESCRIPTION, @pPROFILE_LOG_LEVEL_ID, @pPROFILE_LANGUAGE_ID, @pPROFILE_FIRST_RUN, @pPROFILE_LAST_RUN, @pPROFILE_LAST_RESULT, " +
"@pRESULT_ACTION_ID, @pRESULT_STATUS_ID, @pRESULT_HEADER, @pRESULT_BODY; " +
"SELECT @RC;",
parameters,
cancel);
return result;
// The stored procedure returns 0 on success, error codes > 0 on failure
if (result > 0)
{
throw new UpdateObjectFailedException(request, $"UpdateObject stored procedure failed with error code: {result}");
}
return result;
}
catch (SqlException ex)
{
if (sqlExOpt.CurrentValue.BadRequestSqlExceptionNumbers.Contains(ex.Number))
throw new BadRequestException(ex.Message, ex);
else
throw;
}
}
}

View File

@@ -2,9 +2,11 @@
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using ReC.Application.Common.Behaviors;
using ReC.Application.Common.Constants;
using ReC.Application.Common.Options;
using ReC.Application.Common.Procedures;
using System.Reflection;
namespace ReC.Application;
@@ -116,5 +118,23 @@ public static class DependencyInjection
return this;
}
#endregion ConfigureRecActions
#region ConfigureSqlException
public ConfigurationOptions ConfigureSqlException(Action<SqlExceptionOptions> configure)
{
_configActions.Enqueue(services => services.Configure(configure));
return this;
}
public ConfigurationOptions ConfigureSqlException(IConfiguration configuration)
{
_configActions.Enqueue(services =>
{
services.Configure<SqlExceptionOptions>(configuration);
});
return this;
}
#endregion ConfigureSqlException
}
}

View File

@@ -10,7 +10,7 @@
<PackageReference Include="AutoMapper" Version="15.1.0" />
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.1" />
<PackageReference Include="FluentValidation" Version="12.1.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.0" />
<PackageReference Include="MediatR" Version="13.1.0" />

View File

@@ -1,4 +1,7 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
using ReC.Application.Common.Procedures.InsertProcedure;
using ReC.Domain.Constants;
namespace ReC.Application.RecActions.Commands;
public record InsertActionProcedure : IInsertProcedure
{
@@ -9,7 +12,7 @@ public record InsertActionProcedure : IInsertProcedure
public long? EndpointAuthId { get; set; }
public short? EndpointParamsId { get; set; }
public short? SqlConnectionId { get; set; }
public byte? TypeId { get; set; }
public RestType? TypeId { get; set; }
public string? PreSql { get; set; }
public string? HeaderSql { get; set; }
public string? BodySql { get; set; }

View File

@@ -40,6 +40,7 @@ public abstract class RecApplicationTestBase : IDisposable
{
options.LuckyPennySoftwareLicenseKey = configuration["LuckyPennySoftwareLicenseKey"];
options.ConfigureRecActions(configuration.GetSection("RecAction"));
options.ConfigureSqlException(configuration.GetSection("SqlException"));
});
services.AddRecInfrastructure(opt =>