Compare commits

...

31 Commits

Author SHA1 Message Date
35e03269e7 Add DbSets for EndpointParam and view entities
Added DbSet properties for EndpointParam, RecActionView, and ProfileView to both IRecDbContext and RecDbContext. This enables querying and interaction with these entities and views in the database. Grouped the new DbSets under a #region for better code organization.
2026-01-14 12:00:36 +01:00
8b212d541e Refactor AddedWho assignment in InsertObjectProcedure
AddedWho is now set via the AddedBy method instead of direct assignment in object initializers. The property is made internal with a private setter to prevent external modification. All ToObjectProcedure methods in insert procedure records are updated to use AddedBy for consistency and encapsulation.
2026-01-14 11:51:42 +01:00
dd4cecc15d Rename InsertObjectFailedException.Request to Procedure
Updated ExceptionHandlingMiddleware to use the new Procedure property name when logging InsertObjectFailedException. Changed error title for BadRequestException to "Bad Procedure". Removed unused System.Text.Json using directive from InsertObjectProcedure.cs.
2026-01-14 09:54:19 +01:00
0dedb506e1 Handle InsertObjectFailedException in middleware
Add specific handling for InsertObjectFailedException in ExceptionHandlingMiddleware, including detailed logging and custom error response. Refactor InsertObjectFailedException to expose the request data via a public property for improved error reporting.
2026-01-14 09:51:40 +01:00
24f146ca26 Refactor InsertObjectFailedException for more context
InsertObjectFailedException now requires an InsertObjectProcedure
instance, improving error context. Exception throwing in
InsertObjectProcedureHandler updated to pass the procedure object
instead of just a message and serialized request.
2026-01-14 09:44:31 +01:00
2692553865 Add 'addedWho' param to insert procedures for auditing
Updated IInsertProcedure and all implementations to accept an optional 'addedWho' parameter in ToObjectProcedure, defaulting to "Rec.API". This enables tracking of the entity responsible for insert operations.
2026-01-14 09:37:17 +01:00
d90c2fab96 Refactor: split insert procedures into separate files
Refactored insert procedure-related classes by moving each record type (Action, Endpoint, EndpointAuth, Profile, Result, EndpointParams) and the IInsertProcedure interface into their own files under the new InsertProcedure namespace. Updated InsertObjectProcedureValidator to use the new namespace. This improves code organization, readability, and maintainability.
2026-01-14 09:26:27 +01:00
854e36e71f Refactor insert procedures with entity-specific records
Refactored insert procedure modeling by introducing the IInsertProcedure interface and entity-specific Insert[Entity]Procedure records. Each entity now has its own record with relevant properties and a ToObjectProcedure() method, improving type safety, clarity, and extensibility for insert operations. Updated InsertObjectProcedure to use these new types.
2026-01-14 09:18:52 +01:00
1d31f2aff9 Refactor InsertObjectProcedure to use nested records
Reorganized InsertObjectProcedure by grouping related properties into nested record types (Action, Endpoint, EndpointAuth, Profile, Result, EndpointParams) for better encapsulation and maintainability. Updated handler logic to use new structure and set AddedWhen to DateTime.UtcNow. Improved error logging and added System.Text.Json usage.
2026-01-12 16:47:57 +01:00
11206cf84f Throw exception on failed InsertObject identifier return
Replace default return of 0 with InsertObjectFailedException when the stored procedure does not return a valid identifier. Exception message includes serialized request for easier debugging. Added necessary imports for exception and JSON serialization.
2026-01-12 16:27:45 +01:00
b48ebd8e88 Refactor InsertObjectFailedException constructors
Refactored InsertObjectFailedException to use explicit constructors: parameterless, message-only, and message with inner exception. Removed constructors with optional parameters for clearer and more standard .NET exception handling.
2026-01-12 16:21:16 +01:00
12d17e0808 Add InsertObjectFailedException custom exception class
Introduced InsertObjectFailedException in the ReC.Application.Common.Exceptions namespace. This exception provides constructors for custom messages and inner exceptions, and is intended to signal failures during object insertion operations.
2026-01-12 16:16:52 +01:00
1dee3180d5 Refactor InsertObjectProcedure into entity-based regions
Organized properties into regions by target entity for clarity.
No functional changes; update improves code readability
and maintainability.
2026-01-12 16:07:52 +01:00
bd4046a6c1 Add InsertObjectProcedureValidator with entity-specific rules
Introduced InsertObjectProcedureValidator using FluentValidation to enforce required fields and string length constraints for InsertObjectProcedure. Validation rules are applied conditionally based on the Entity type, ensuring correct data for ACTION, ENDPOINT, PROFILE, RESULT, and ENDPOINT_PARAMS. Optional string fields also receive length checks.
2026-01-12 16:03:10 +01:00
af6f94c1ed Improve handling of stored procedure return value type
Check if return value is already a long before parsing its
string representation. This enhances robustness and efficiency
when the value is of the correct type.
2026-01-12 15:48:03 +01:00
7bfb56b664 Add InsertObjectProcedure MediatR command and handler
Introduces InsertObjectProcedure and its handler to support generic, parameterized insertion of various object types (ACTION, ENDPOINT, etc.) via a single stored procedure. The handler maps request properties to SQL parameters, executes the procedure, and returns the output GUID. This enables flexible and unified object creation through MediatR.
2026-01-12 15:19:54 +01:00
1a6eced316 Rename InsertObject to InsertObjectProcedure
Renamed the InsertObject MediatR request record to InsertObjectProcedure and moved it from InsertObject.cs to InsertObjectProcedure.cs. The structure and functionality remain unchanged; it still defines an Entity property with a default value of "ACTION".
2026-01-12 14:35:16 +01:00
c82749bcbf Add TODO comment for appsettings.json config in DbContext
A TODO comment was added above OnModelCreating in RecDbContext to note that configuration should be updated to use appsettings.json in the future. No functional changes were made.
2026-01-12 14:25:15 +01:00
e8fa149532 Move InsertObjectResult mapping to Fluent API
Removed [Column] attribute from InsertObjectResult and configured column mapping for NewObjectId in RecDbContext using the Fluent API. This centralizes entity mapping logic in the DbContext.
2026-01-12 14:20:41 +01:00
aaa7beb92a Add RecResults DbSet for InsertObjectResult entities
Added RecResults DbSet to IRecDbContext and RecDbContext to support managing InsertObjectResult entities. Updated IRecDbContext to include the new DbSet and ensured SaveChangesAsync is defined. Enables querying and persisting InsertObjectResult via EF Core.
2026-01-12 14:19:19 +01:00
5cce52ec27 Add ProfileViews and RecResultViews to IRecDbContext
Updated IRecDbContext to include DbSet properties for ProfileView and ResultView entities, enabling management of these collections within the context.
2026-01-12 14:17:34 +01:00
3f36f048b2 Add InsertObjectResult class for object insert results
Introduced InsertObjectResult in ReC.Domain.QueryOutput with a required NewObjectId property mapped to the "oGUID" column. This class will be used to represent the result of object insert operations. Added necessary using directive for data annotations.
2026-01-12 14:17:04 +01:00
92e8d9e778 Move query result classes to QueryOutput namespace
BodyQueryResult and HeaderQueryResult were relocated from ReC.Domain.Entities to ReC.Domain.QueryOutput. Updated all references in IRecDbContext and RecDbContext to use the new namespace.
2026-01-12 14:11:05 +01:00
2d04670fef Set default Entity to "ACTION" in InsertObject
Added XML summary to Entity property describing valid values.
Changed default value from null to "ACTION" for clarity.
2026-01-12 14:08:03 +01:00
c0085b4c18 Add InsertObject record for MediatR-based insert requests
Introduced InsertObject record in ReC.Application.Common.Procedures namespace. This record implements MediatR's IRequest<long> and includes a non-nullable Entity property to support object insertion operations.
2026-01-12 13:54:04 +01:00
59ea5e3e67 Update action invocation logic and remove EndpointAuth include
Changed invocation filter to use Results.Any() instead of Root.OutRes.
Removed eager loading of EndpointAuth from the query.
2026-01-12 13:26:54 +01:00
a9f2c4c2f7 Add Action and Profile relationships to Result entity
Added navigation properties and foreign key relationships from Result to Action (one-to-many) and Profile (many-to-one) entities using ActionId and ProfileId. This enhances entity associations in the data model.
2026-01-12 13:03:56 +01:00
001f4bf2c5 Configure one-to-many relationship for Profile-Actions
Added entity mapping to establish a one-to-many relationship between profiles and actions, with actions referencing their parent profile via the ProfileId foreign key.
2026-01-12 12:54:00 +01:00
bfe6c12ee0 Annotate ProfileView and add Actions property
ProfileView is now mapped to the VWREC_PROFILE table using the
[Table] attribute. Added an Actions property to support related
RecActionView collections.
2026-01-12 12:50:05 +01:00
e1260e49f0 Add one-to-many Action-Results relationship mapping
Added entity configuration to define a one-to-many relationship between Actions and Results. Each Action can have multiple Results, and each Result references its parent Action via the ActionId foreign key.
2026-01-12 12:47:21 +01:00
8b86eca838 Add obsolete navigation props to RecActionView class
Added Results, Root, Endpoint, EndpointAuth, and SqlConnection navigation properties to RecActionView, all marked as [Obsolete] to guide usage toward related procedures or views. Foreign key attributes applied where relevant.
2026-01-12 12:39:53 +01:00
19 changed files with 444 additions and 6 deletions

View File

@@ -66,7 +66,7 @@ public class ExceptionHandlingMiddleware
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
details = new()
{
Title = "Bad Request",
Title = "Bad Procedure",
Detail = badRequestEx.Message
};
break;
@@ -106,6 +106,23 @@ public class ExceptionHandlingMiddleware
};
break;
case InsertObjectFailedException insertFailedEx:
logger.LogError(
insertFailedEx,
"Insert operation failed during request processing. {procedure}",
JsonSerializer.Serialize(
insertFailedEx.Procedure,
options: new() { WriteIndented = true }
));
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
details = new()
{
Title = "Insert Operation Failed",
Detail = insertFailedEx.Message
};
break;
default:
logger.LogError(exception, "Unhandled exception occurred.");
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
@@ -120,4 +137,4 @@ public class ExceptionHandlingMiddleware
if (details is not null)
await context.Response.WriteAsJsonAsync(details);
}
}
}

View File

@@ -0,0 +1,23 @@
using ReC.Application.Common.Procedures.InsertProcedure;
namespace ReC.Application.Common.Exceptions;
public class InsertObjectFailedException : Exception
{
public InsertObjectProcedure Procedure { get; }
public InsertObjectFailedException(InsertObjectProcedure procedure) : base()
{
Procedure = procedure;
}
public InsertObjectFailedException(InsertObjectProcedure procedure, string? message) : base(message)
{
Procedure = procedure;
}
public InsertObjectFailedException(InsertObjectProcedure procedure, string? message, Exception? innerException) : base(message, innerException)
{
Procedure = procedure;
}
}

View File

@@ -1,15 +1,21 @@
using Microsoft.EntityFrameworkCore;
using ReC.Domain.Entities;
using ReC.Domain.QueryOutput;
using ReC.Domain.Views;
namespace ReC.Application.Common.Interfaces;
public interface IRecDbContext
{
#region DbSets
public DbSet<EndpointParam> EndpointParams { get; set; }
public DbSet<RecActionView> RecActionViews { get; set; }
public DbSet<ProfileView> ProfileViews { get; set; }
public DbSet<ResultView> RecResultViews { get; set; }
public DbSet<OutRes> OutRes { get; set; }
public DbSet<HeaderQueryResult> HeaderQueryResults { get; set; }
@@ -26,5 +32,8 @@ public interface IRecDbContext
public DbSet<RecAction> RecActions { get; set; }
public DbSet<InsertObjectResult> RecResults { get; set; }
#endregion DbSets
public Task<int> SaveChangesAsync(CancellationToken cancel = default);
}

View File

@@ -0,0 +1,6 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public interface IInsertProcedure
{
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API");
}

View File

@@ -0,0 +1,27 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertActionProcedure : IInsertProcedure
{
public long? ProfileId { get; set; }
public bool? Active { get; set; }
public byte? Sequence { get; set; }
public long? EndpointId { get; set; }
public long? EndpointAuthId { get; set; }
public short? EndpointParamsId { get; set; }
public short? SqlConnectionId { get; set; }
public byte? TypeId { get; set; }
public string? PreSql { get; set; }
public string? HeaderSql { get; set; }
public string? BodySql { get; set; }
public string? PostSql { get; set; }
public byte? ErrorActionId { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "ACTION",
Action = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,25 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertEndpointAuthProcedure : IInsertProcedure
{
public bool? Active { get; set; }
public string? Description { get; set; }
public byte? TypeId { get; set; }
public string? ApiKey { get; set; }
public string? ApiValue { get; set; }
public bool? ApiKeyAddToId { get; set; }
public string? Token { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public string? Domain { get; set; }
public string? Workstation { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "ENDPOINT_AUTH",
EndpointAuth = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,20 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertEndpointParamsProcedure : IInsertProcedure
{
public bool? Active { get; set; }
public string? Description { get; set; }
public short? GroupId { get; set; }
public byte? Sequence { get; set; }
public string? Key { get; set; }
public string? Value { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "ENDPOINT_PARAMS",
EndpointParams = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,17 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertEndpointProcedure : IInsertProcedure
{
public bool? Active { get; set; }
public string? Description { get; set; }
public string? Uri { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "ENDPOINT",
Endpoint = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,123 @@
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
using Microsoft.Data.SqlClient;
using ReC.Application.Common.Exceptions;
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertObjectProcedure : IRequest<long>
{
/// <summary>
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
/// </summary>
public string Entity { get; set; } = null!;
internal string? AddedWho { get; private set; }
public InsertObjectProcedure AddedBy(string addedWho)
{
AddedWho = addedWho;
return this;
}
public InsertActionProcedure Action { get; set; } = new();
public InsertEndpointProcedure Endpoint { get; set; } = new();
public InsertEndpointAuthProcedure EndpointAuth { get; set; } = new();
public InsertProfileProcedure Profile { get; set; } = new();
public InsertResultProcedure Result { get; set; } = new();
public InsertEndpointParamsProcedure EndpointParams { get; set; } = new();
}
public class InsertObjectProcedureHandler(IRepository repo) : IRequestHandler<InsertObjectProcedure, long>
{
public async Task<long> Handle(InsertObjectProcedure request, CancellationToken cancel)
{
var parameters = new[]
{
new SqlParameter("@pENTITY", request.Entity ?? (object)DBNull.Value),
new SqlParameter("@pADDED_WHO", (object?)request.AddedWho ?? DBNull.Value),
new SqlParameter("@pADDED_WHEN", (object?)DateTime.UtcNow ?? DBNull.Value),
new SqlParameter("@pACTION_PROFILE_ID", (object?)request.Action.ProfileId ?? DBNull.Value),
new SqlParameter("@pACTION_ACTIVE", (object?)request.Action.Active ?? DBNull.Value),
new SqlParameter("@pACTION_SEQUENCE", (object?)request.Action.Sequence ?? DBNull.Value),
new SqlParameter("@pACTION_ENDPOINT_ID", (object?)request.Action.EndpointId ?? DBNull.Value),
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_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),
new SqlParameter("@pACTION_POST_SQL", (object?)request.Action.PostSql ?? DBNull.Value),
new SqlParameter("@pACTION_ERROR_ACTION_ID", (object?)request.Action.ErrorActionId ?? DBNull.Value),
new SqlParameter("@pENDPOINT_ACTIVE", (object?)request.Endpoint.Active ?? DBNull.Value),
new SqlParameter("@pENDPOINT_DESCRIPTION", (object?)request.Endpoint.Description ?? DBNull.Value),
new SqlParameter("@pENDPOINT_URI", (object?)request.Endpoint.Uri ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_ACTIVE", (object?)request.EndpointAuth.Active ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_DESCRIPTION", (object?)request.EndpointAuth.Description ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_TYPE_ID", (object?)request.EndpointAuth.TypeId ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_API_KEY", (object?)request.EndpointAuth.ApiKey ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_API_VALUE", (object?)request.EndpointAuth.ApiValue ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_API_KEY_ADD_TO_ID", (object?)request.EndpointAuth.ApiKeyAddToId ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_TOKEN", (object?)request.EndpointAuth.Token ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_USERNAME", (object?)request.EndpointAuth.Username ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_PASSWORD", (object?)request.EndpointAuth.Password ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_DOMAIN", (object?)request.EndpointAuth.Domain ?? DBNull.Value),
new SqlParameter("@pENDPOINT_AUTH_WORKSTATION", (object?)request.EndpointAuth.Workstation ?? DBNull.Value),
new SqlParameter("@pPROFILE_ACTIVE", (object?)request.Profile.Active ?? DBNull.Value),
new SqlParameter("@pPROFILE_TYPE_ID", (object?)request.Profile.TypeId ?? DBNull.Value),
new SqlParameter("@pPROFILE_MANDANTOR", (object?)request.Profile.Mandantor ?? DBNull.Value),
new SqlParameter("@pPROFILE_NAME", (object?)request.Profile.Name ?? DBNull.Value),
new SqlParameter("@pPROFILE_DESCRIPTION", (object?)request.Profile.Description ?? DBNull.Value),
new SqlParameter("@pPROFILE_LOG_LEVEL_ID", (object?)request.Profile.LogLevelId ?? DBNull.Value),
new SqlParameter("@pPROFILE_LANGUAGE_ID", (object?)request.Profile.LanguageId ?? DBNull.Value),
new SqlParameter("@pRESULT_ACTION_ID", (object?)request.Result.ActionId ?? DBNull.Value),
new SqlParameter("@pRESULT_STATUS_ID", (object?)request.Result.StatusId ?? DBNull.Value),
new SqlParameter("@pRESULT_HEADER", (object?)request.Result.Header ?? DBNull.Value),
new SqlParameter("@pRESULT_BODY", (object?)request.Result.Body ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_ACTIVE", (object?)request.EndpointParams.Active ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_DESCRIPTION", (object?)request.EndpointParams.Description ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_GROUP_ID", (object?)request.EndpointParams.GroupId ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_SEQUENCE", (object?)request.EndpointParams.Sequence ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_KEY", (object?)request.EndpointParams.Key ?? DBNull.Value),
new SqlParameter("@pENDPOINT_PARAMS_VALUE", (object?)request.EndpointParams.Value ?? DBNull.Value),
new SqlParameter
{
ParameterName = "@oGUID",
SqlDbType = System.Data.SqlDbType.BigInt,
Direction = System.Data.ParameterDirection.Output
}
};
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);
var guidParam = parameters.Last();
if (guidParam.Value != DBNull.Value)
if (guidParam.Value is long longValue)
return longValue;
else if (long.TryParse(guidParam.Value.ToString(), out var guid))
return guid;
throw new InsertObjectFailedException(request, "InsertObject stored procedure did not return a valid identifier.");
}
}

View File

@@ -0,0 +1,21 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertProfileProcedure : IInsertProcedure
{
public bool? Active { get; set; }
public byte? TypeId { get; set; }
public string? Mandantor { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public byte? LogLevelId { get; set; }
public short? LanguageId { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "PROFILE",
Profile = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,18 @@
namespace ReC.Application.Common.Procedures.InsertProcedure;
public record InsertResultProcedure : IInsertProcedure
{
public long? ActionId { get; set; }
public short? StatusId { get; set; }
public string? Header { get; set; }
public string? Body { get; set; }
public InsertObjectProcedure ToObjectProcedure(string addedWho = "Rec.API")
{
return new InsertObjectProcedure
{
Entity = "RESULT",
Result = this
}.AddedBy(addedWho);
}
}

View File

@@ -0,0 +1,87 @@
using FluentValidation;
using ReC.Application.Common.Procedures.InsertProcedure;
namespace ReC.Application.Common.Validations;
public class InsertObjectProcedureValidator : AbstractValidator<InsertObjectProcedure>
{
public InsertObjectProcedureValidator()
{
// ENTITY must be one of the allowed values
RuleFor(x => x.Entity)
.NotEmpty()
.Must(e => e is "ACTION" or "ENDPOINT" or "ENDPOINT_AUTH" or "ENDPOINT_PARAMS" or "PROFILE" or "RESULT")
.WithMessage("ENTITY must be one of: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT.");
// ACTION validation
When(x => x.Entity == "ACTION", () =>
{
RuleFor(x => x.ActionProfileId)
.NotNull()
.WithMessage("ACTION requires ActionProfileId (maps to @pACTION_PROFILE_ID).");
RuleFor(x => x.ActionEndpointId)
.NotNull()
.WithMessage("ACTION requires ActionEndpointId (maps to @pACTION_ENDPOINT_ID).");
});
// ENDPOINT validation
When(x => x.Entity == "ENDPOINT", () =>
{
RuleFor(x => x.EndpointUri)
.NotEmpty()
.WithMessage("ENDPOINT requires EndpointUri (maps to @pENDPOINT_URI).")
.MaximumLength(2000);
});
// PROFILE validation
When(x => x.Entity == "PROFILE", () =>
{
RuleFor(x => x.ProfileName)
.NotEmpty()
.WithMessage("PROFILE requires ProfileName (maps to @pPROFILE_NAME).")
.MaximumLength(50);
RuleFor(x => x.ProfileMandantor)
.MaximumLength(50)
.When(x => x.ProfileMandantor != null);
RuleFor(x => x.ProfileDescription)
.MaximumLength(250)
.When(x => x.ProfileDescription != null);
});
// RESULT validation
When(x => x.Entity == "RESULT", () =>
{
RuleFor(x => x.ResultActionId)
.NotNull()
.WithMessage("RESULT requires ResultActionId (maps to @pRESULT_ACTION_ID).");
RuleFor(x => x.ResultStatusId)
.NotNull()
.WithMessage("RESULT requires ResultStatusId (maps to @pRESULT_STATUS_ID).");
});
// ENDPOINT_PARAMS validation
When(x => x.Entity == "ENDPOINT_PARAMS", () =>
{
RuleFor(x => x.EndpointParamsGroupId)
.NotNull()
.WithMessage("ENDPOINT_PARAMS requires EndpointParamsGroupId (maps to @pENDPOINT_PARAMS_GROUP_ID).");
});
// Simple length guards for some string fields (optional but cheap)
RuleFor(x => x.AddedWho)
.MaximumLength(50)
.When(x => x.AddedWho != null);
RuleFor(x => x.EndpointDescription)
.MaximumLength(250)
.When(x => x.EndpointDescription != null);
RuleFor(x => x.EndpointAuthDescription)
.MaximumLength(250)
.When(x => x.EndpointAuthDescription != null);
}
}

View File

@@ -25,9 +25,11 @@ public class ReadRecActionViewQueryHandler(IRepository<RecActionView> repo, IMap
query = repo.Where(act => act.ProfileId == profileId);
if (request.Invoked is bool invoked)
query = invoked ? query.Where(act => act.Root!.OutRes != null) : query.Where(act => act.Root!.OutRes == null);
query = invoked
? query.Where(act => act.Results!.Any())
: query.Where(act => !act.Results!.Any());
var actions = await query.Include(act => act.EndpointAuth).ToListAsync(cancel);
var actions = await query.ToListAsync(cancel);
if (actions.Count == 0)
throw new NotFoundException($"No actions found for the profile {request.ProfileId}.");

View File

@@ -1,4 +1,4 @@
namespace ReC.Domain.Entities;
namespace ReC.Domain.QueryOutput;
public class BodyQueryResult
{

View File

@@ -1,4 +1,4 @@
namespace ReC.Domain.Entities;
namespace ReC.Domain.QueryOutput;
public class HeaderQueryResult
{

View File

@@ -0,0 +1,8 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.QueryOutput;
public class InsertObjectResult
{
public required long NewObjectId { get; set; }
}

View File

@@ -6,6 +6,8 @@ namespace ReC.Domain.Views;
[Table("VWREC_PROFILE", Schema = "dbo")]
public record ProfileView
{
public virtual IEnumerable<RecActionView>? Actions { get; init; }
public long Id { get; init; }
public bool Active { get; init; }

View File

@@ -15,9 +15,12 @@ namespace ReC.Domain.Views;
[Table("VWREC_ACTION", Schema = "dbo")]
public class RecActionView
{
public virtual IEnumerable<ResultView>? Results { get; set; }
public required long Id { get; set; }
[ForeignKey("Id")]
[Obsolete("Use the related procedure or view.")]
public RecAction? Root { get; set; }
public long? ProfileId { get; set; }
@@ -34,6 +37,7 @@ public class RecActionView
public long? EndpointId { get; set; }
[ForeignKey("EndpointId")]
[Obsolete("Use the related procedure or view.")]
public Endpoint? Endpoint { get; set; }
public string? EndpointUri { get; set; }
@@ -41,6 +45,7 @@ public class RecActionView
public long? EndpointAuthId { get; set; }
[ForeignKey("EndpointAuthId")]
[Obsolete("Use the related procedure or view.")]
public EndpointAuth? EndpointAuth { get; set; }
public EndpointAuthType? EndpointAuthType { get; set; }
@@ -70,6 +75,7 @@ public class RecActionView
public short? SqlConnectionId { get; set; }
[ForeignKey("SqlConnectionId")]
[Obsolete("Use the related procedure or view.")]
public Connection? SqlConnection { get; set; }
public string? SqlConnectionServer { get; set; }

View File

@@ -1,12 +1,14 @@
using Microsoft.EntityFrameworkCore;
using ReC.Application.Common.Interfaces;
using ReC.Domain.Entities;
using ReC.Domain.QueryOutput;
using ReC.Domain.Views;
namespace ReC.Infrastructure;
public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(options), IRecDbContext
{
#region DB Sets
public DbSet<EndpointParam> EndpointParams { get; set; }
public DbSet<RecActionView> RecActionViews { get; set; }
@@ -31,6 +33,10 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
public DbSet<RecAction> RecActions { get; set; }
public DbSet<InsertObjectResult> RecResults { get; set; }
#endregion DB Sets
// TODO: Update to configure via appsettings.json
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
@@ -191,6 +197,10 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.FirstRun).HasColumnName("FIRST_RUN");
b.Property(e => e.LastRun).HasColumnName("LAST_RUN");
b.Property(e => e.LastResult).HasColumnName("LAST_RESULT");
b.HasMany(e => e.Actions)
.WithOne(a => a.Profile)
.HasForeignKey(a => a.ProfileId);
});
modelBuilder.Entity<RecAction>(b =>
@@ -262,6 +272,10 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.PostprocessingQuery).HasColumnName("POSTPROCESSING_QUERY");
b.Property(e => e.ErrorAction).HasColumnName("ERROR_ACTION_ID");
b.Property(e => e.ErrorActionName).HasColumnName("ERROR_ACTION");
b.HasMany(e => e.Results)
.WithOne(r => r.Action)
.HasForeignKey(r => r.ActionId);
});
modelBuilder.Entity<ResultView>(b =>
@@ -281,6 +295,14 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
b.HasOne(r => r.Action)
.WithMany(a => a.Results)
.HasForeignKey(r => r.ActionId);
b.HasOne(r => r.Profile)
.WithMany()
.HasForeignKey(r => r.ProfileId);
});
modelBuilder.Entity<HeaderQueryResult>(b =>
@@ -295,5 +317,10 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.RawBody).HasColumnName("REQUEST_BODY");
});
modelBuilder.Entity<InsertObjectResult>(b =>
{
b.HasNoKey();
b.Property(e => e.NewObjectId).HasColumnName("oGUID");
});
}
}