Compare commits

...

57 Commits

Author SHA1 Message Date
165152b7cf Merge branch 'master' of http://git.dd:3000/AppStd/ReC 2025-12-12 09:18:27 +01:00
f1f9e8d791 Bump version to 1.0.3-beta in ReC.API.csproj
Updated <Version>, <AssemblyVersion>, <FileVersion>, and <InformationalVersion> fields in the project file from 1.0.2-beta to 1.0.3-beta. No other changes were made.
2025-12-12 01:54:43 +01:00
35171add0c Add ErrorAction to RecActionDto and batch error handling
Introduce ErrorAction property to RecActionDto for per-action error handling. Update InvokeRecActionsCommandHandler to check invocation results and use ErrorAction to determine whether to continue or stop on failure, enabling configurable batch processing behavior.

Add ErrorAction to RecActionDto and batch error handling

Introduce ErrorAction property to RecActionDto for per-action error handling. Update InvokeRecActionsCommandHandler to check invocation results and use ErrorAction to determine whether to continue or stop processing on failure. This enables configurable error handling in batch action execution.
2025-12-12 01:52:35 +01:00
030dcf8b58 Add ErrorAction property to RecAction and DB mapping
Added the ErrorAction property to the RecAction class for specifying error handling actions. Updated RecDbContext to map this property to the ERROR_ACTION database column.
2025-12-12 01:47:43 +01:00
71411d4027 Update InvokeRecActionCommand to return success status
Changed InvokeRecActionCommand and its handler to return a boolean indicating success or failure, allowing consumers to check if the action was successful. This improves clarity and error handling in command execution.
2025-12-12 01:43:01 +01:00
da1b05347e Add Status and Message to CreateOutResCommand
CreateOutResCommand now includes optional Status and Message properties to capture additional result details. The HTTP response status code is recorded in Status when creating an output result in InvokeRecActionCommandHandler.
2025-12-12 01:32:09 +01:00
d932fb522c Add Status and Message columns to entity mapping
Added Status and Message properties to the entity configuration in RecDbContext.cs, mapping them to the STATUS and MESSAGE database columns. This allows the entity to store and retrieve these additional fields.
2025-12-12 01:28:29 +01:00
134a808633 Add Status, Message, and Header to OutRes class
Added three new nullable properties—Status (short), Message (string), and Header (string)—to the OutRes class to support additional metadata and state information.
2025-12-12 01:25:29 +01:00
Developer 02
00efb14f2c Merge branch 'feat/client' 2025-12-11 10:49:21 +01:00
fe40cd001f Update RecActionController routes and XML docs
Changed Invoke action to use profileId as a route parameter. Updated Get method XML docs to reflect use of ReadRecActionQuery from query string instead of profileId.
2025-12-10 16:41:51 +01:00
f96b73bf38 Update RecAction route and refactor EnsureEntity logic
Changed the RecActionController Invoke route to remove the {cmd}
parameter, now accepting POST requests at "invoke" only.
Refactored DbModelOptions.EnsureEntity<T> by removing the
entities dictionary conversion logic for virtual and non-virtual
entities.
2025-12-10 16:02:41 +01:00
c8f3b29329 Refactor EnsureEntity<T> for clarity and consistency
Renamed local variable from 'cluster' to 'entities' in EnsureEntity<T> and unified logic for selecting entity options. Improved readability by consistently using entities.TryGetValue and clearer variable naming.
2025-12-10 15:00:44 +01:00
07fca00344 Replace InvalidOperationException with custom config exception
Refactored DbModelOptions and EntityBaseOptions to throw DbModelConfigurationException instead of InvalidOperationException for configuration errors. Added necessary using directives for the new exception type to improve error clarity and specificity.
2025-12-10 14:54:20 +01:00
e4aec494c8 Add DbModelConfigurationException class to exceptions
Introduced DbModelConfigurationException in the ReC.Infrastructure.Exceptions namespace. This custom exception inherits from Exception and includes both a message constructor and a parameterless constructor for flexible error handling related to DB model configuration issues.
2025-12-10 14:53:48 +01:00
009bb623b5 Add EnsureEntity<T> method to DbModelOptions
Introduce EnsureEntity<T>(bool isVirtual) to validate and ensure entity options exist for a given type in either Entities or VirtualEntities. Throws an exception if options are missing.
2025-12-10 14:24:45 +01:00
f8e7f8c974 Switch DbModelOptions to use dictionaries for entities
Changed Entities and VirtualEntities from IEnumerable to Dictionary<string, T> in DbModelOptions, enabling direct access by string keys and improving lookup efficiency.
2025-12-10 13:56:41 +01:00
959b56c4bb Add ColumnNames property to EntityBaseOptions
Added a new ColumnNames property that exposes the column names from the ColumnMappings dictionary as an IEnumerable<string> in the EntityBaseOptions record. This provides convenient access to mapped column names alongside existing property name access.
2025-12-10 13:52:49 +01:00
5a56125444 Add PropertyNames property to EntityBaseOptions
Introduce PropertyNames as a read-only property exposing the keys of ColumnMappings. Refactor EnsureProperties to use PropertyNames for improved clarity and maintainability.
2025-12-10 13:52:16 +01:00
48039b8fd5 Rename ColumnName to ColumnMappings in EntityBaseOptions
Renamed the ColumnName property to ColumnMappings in the EntityBaseOptions record for improved clarity and consistency. Updated all internal references accordingly; no changes to logic or initialization.
2025-12-10 13:46:23 +01:00
31d1d9d171 Add EnsureProperties<T> for attribute-based config validation
Added EnsureProperties<T>() to EntityBaseOptions, enabling automatic validation of required properties marked with MustConfiguredAttribute via reflection. This reduces manual configuration and improves maintainability.
2025-12-10 13:43:58 +01:00
1419455b36 Add MustConfiguredAttribute and update project file
Added MustConfiguredAttribute for property usage in ReC.Domain.Attributes. Removed explicit "Attributes\" folder reference from ReC.Domain.csproj since the folder now contains code.
2025-12-10 13:32:42 +01:00
a6111cdc66 Rename Columns to ColumnName; add Attributes folder
Renamed the Columns property to ColumnName in EntityBaseOptions.cs,
updating all references accordingly. Added an "Attributes" folder
entry to ReC.Domain.csproj for future organization.
2025-12-10 13:30:47 +01:00
3caa6b9bd3 Add params overload for EnsureProperties in EntityBaseOptions
Added a public EnsureProperties method accepting a params string[] in EntityBaseOptions. This provides a more convenient way to ensure multiple properties are configured, internally delegating to the existing IEnumerable-based method.
2025-12-10 13:28:38 +01:00
fb08a45c57 Add EnsureProperties method to EntityBaseOptions
Introduce EnsureProperties to validate required property names
against the Columns dictionary, throwing an exception if any
are missing. This helps enforce configuration completeness.
2025-12-10 13:26:42 +01:00
0588ba33d8 Add DbModelOptions record for entity configuration
Introduced DbModelOptions in ReC.Infrastructure.Options to encapsulate collections of EntityOptions and VirtualEntityOptions, providing a structured way to configure entities and virtual entities. Both properties are initialized to empty collections by default.
2025-12-10 12:42:23 +01:00
535fdbb7b4 Refactor entity options to use shared base class
Introduce EntityBaseOptions with Columns property.
Update EntityOptions and VirtualEntityOptions to inherit from EntityBaseOptions for improved code reuse and consistency.
2025-12-10 12:32:34 +01:00
4206a962ff Refactor option records and improve namespace structure
Renamed Table to TableOptions and moved it, along with EntityOptions and VirtualEntityOptions, to the ReC.Infrastructure.Options.Shared namespace. Split records into separate files for better modularity and updated EntityOptions to reference TableOptions. This enhances code organization and naming consistency.
2025-12-10 12:24:39 +01:00
ae059e4416 Add VirtualEntityOptions and EntityOptions records
Introduced two new record types: VirtualEntityOptions (currently empty) and EntityOptions, which encapsulates a Table instance. These additions lay groundwork for future entity configuration options.
2025-12-10 12:16:50 +01:00
d771dbbc9e Add Table record in ReC.Infrastructure.Options namespace
Introduced a new Table record with Name and optional Schema properties under the ReC.Infrastructure.Options namespace. This addition provides a structured way to represent database table metadata.
2025-12-10 12:10:06 +01:00
73ffaaab08 Remove RecActionView entity and its database mapping
Deleted the RecActionView class, including all properties and data annotations, removing its mapping to the VWREC_ACTION view and related relationships from the codebase.
2025-12-10 12:06:20 +01:00
b79fcf936b Refactor RecAction mapping to Fluent API in DbContext
Moved all RecAction table and column mappings from data annotations in the entity class to Fluent API configuration in RecDbContext. Also consolidated the OutRes relationship setup into the new configuration block, removing redundant mapping code. RecAction is now a plain class without EF attributes.
2025-12-10 12:04:43 +01:00
674c14dd7c Refactor Profile mapping to use Fluent API in DbContext
Moved table and column mapping for Profile from data annotations
in the entity class to Fluent API configuration in RecDbContext.
Removes dependency on DataAnnotations in Profile.cs and
centralizes entity configuration in the DbContext.
2025-12-10 12:02:20 +01:00
b326e7e1b3 Refactor EndpointAuth EF mapping to Fluent API
Moved EF Core configuration for EndpointAuth from data annotations in the entity class to Fluent API in RecDbContext. This centralizes table, key, and property mappings, improving maintainability and consistency.
2025-12-10 12:00:21 +01:00
3f8ba7d76c Move Endpoint entity config to Fluent API in DbContext
Removed data annotations from Endpoint class and defined its table, key, and column mappings using Fluent API in RecDbContext. This centralizes entity configuration and improves code maintainability.
2025-12-10 11:58:42 +01:00
13346b610a Refactor Connection entity to use Fluent API mapping
Removed data annotations from Connection.cs and moved all table and column mapping to RecDbContext's OnModelCreating using the Fluent API. This centralizes entity configuration and removes dependencies on DataAnnotations in the entity class.
2025-12-10 11:56:14 +01:00
8bc9b85049 Move RawHeader column mapping to Fluent API config
Removed data annotation from HeaderQueryResult and mapped RawHeader to REQUEST_HEADER using Fluent API in RecDbContext. Centralizes entity configuration in the DbContext.
2025-12-10 11:50:55 +01:00
141e77f315 Remove EF Core data annotations from OutRes entity
Refactored the OutRes class to eliminate all Entity Framework Core data annotations, including table, key, column, and foreign key attributes. The entity now contains only property definitions, decoupling it from direct database schema mapping and allowing for alternative configuration or usage outside of EF Core.
2025-12-10 11:48:59 +01:00
f9a4d93495 Add OutRes entity mapping to RecDbContext
Configured OutRes entity to map to TBREC_OUT_RESULT table,
including primary key and property-to-column mappings.
2025-12-10 11:47:58 +01:00
79f771b3ea Remove EF Core data annotations from EndpointParam class
Refactored EndpointParam by removing all Entity Framework Core
data annotations and related using directives. The class now
contains only plain C# properties, with database mapping
responsibilities likely moved to Fluent API or another ORM
configuration approach.
2025-12-10 11:35:45 +01:00
8738c15804 Configure EndpointParam entity in RecDbContext
Added mapping for EndpointParam to TBREC_CFG_ENDPOINT_PARAMS table, including key and property-to-column configurations in OnModelCreating.
2025-12-10 11:34:28 +01:00
70c07c9595 Enhance RecActionView entity mapping in DbContext
Added explicit table and column mappings for RecActionView in DbContext. The entity is now mapped to the "VWREC_ACTION" table in the "dbo" schema, with each property aligned to its corresponding database column for improved accuracy.
2025-12-10 11:29:27 +01:00
96fe9c99da Move BodyQueryResult column mapping to Fluent API
Replaced data annotation with Fluent API configuration for BodyQueryResult.RawBody column mapping in RecDbContext, centralizing entity configuration and removing the [Column("REQUEST_BODY")] attribute from the entity class.
2025-12-10 11:05:09 +01:00
1e62a70866 Enable cascade delete for RecAction-OutRes relationship
Added DeleteBehavior.Cascade to the RecAction-OutRes one-to-one relationship in RecDbContext. Now, deleting a RecAction will also delete its related OutRes entity automatically. Previously, the delete behavior was not specified.
2025-12-10 10:38:55 +01:00
f4aa0b5965 Redirect root URL to Swagger UI; add required usings
Added several using directives to Program.cs for new dependencies. Updated HTTP pipeline to redirect requests from "/" to "/swagger" when Swagger is enabled, improving developer experience by automatically showing the API documentation.
2025-12-10 10:12:13 +01:00
1700fe978d Bump version to 1.0.2-beta in ReC.API.csproj
Updated project, assembly, and file versions to 1.0.2-beta/1.0.2.0 for new beta release. No other changes made.
2025-12-09 13:58:21 +01:00
37c88812e1 Simplify InvokeRecActionsCommandHandler dependencies and logic
Refactored InvokeRecActionsCommandHandler to only require ISender,
removing IServiceScopeFactory, IHttpClientFactory, and ILogger.
Replaced concurrent invocation and error handling with a simple
sequential loop, streamlining batch recommendation action execution.
2025-12-09 13:56:35 +01:00
34efb662ec Update version and assembly metadata to 1.0.1-beta
Updated `<Version>`, `<AssemblyVersion>`, and `<FileVersion>`
to reflect the minor version update from 1.0.0-beta to
1.0.1-beta. `<InformationalVersion>` remains unchanged.
2025-12-08 13:08:53 +01:00
dcbff90ed8 Remove 400 Bad Request response from Delete method
The `[ProducesResponseType(StatusCodes.Status400BadRequest)]`
attribute was removed from the `Delete` method in the
`OutResController` class. This change simplifies the API
response documentation, leaving only the `204 No Content`
response explicitly defined. The method is no longer expected
to return a `400 Bad Request` status code, reflecting a change
in behavior or a decision to streamline the API's response
documentation.
2025-12-08 11:52:58 +01:00
404a1c4793 Add Delete method for fake profile in OutResController
Added a new `Delete` method to the `OutResController` class to
delete all output results for a fake/test profile. The method
is accessible via the `DELETE /fake` route and supports
cancellation via a `CancellationToken`.

Included XML documentation for the method, describing its
purpose, parameters, and return type. Added `[ProducesResponseType]`
attributes to specify possible HTTP response codes: `204 No Content`
for success and `400 Bad Request` for invalid requests.

The method uses `IMediator` to send a `DeleteOutResCommand`
with a `ProfileId` obtained from the `IConfiguration` instance.
2025-12-08 11:50:30 +01:00
1cdd28738a Add Delete endpoint to OutResController
Added a new HTTP DELETE endpoint to the OutResController to allow deletion of output results based on criteria provided in the DeleteOutResCommand. The endpoint supports cancellation via a CancellationToken and returns a 204 No Content response on success. Updated using directives to include necessary namespaces for commands and queries. Added response type annotations for better API documentation.
2025-12-08 11:47:05 +01:00
45c7259ce8 Add XML documentation for DeleteOutResCommand and handler
Enhanced the `DeleteOutResCommand` and `DeleteOutResCommandHandler`
with detailed XML documentation comments. These comments describe
the purpose, usage, and deletion logic, including criteria based
on `ActionId` or `ProfileId`. Improved clarity for maintainers
and users of the codebase.
2025-12-08 11:45:17 +01:00
6d9985051e Add DeleteOutResCommandHandler for repository deletion
Updated using directives to include necessary dependencies for
repository and domain entities. Introduced the
DeleteOutResCommandHandler class to handle DeleteOutResCommand
requests. The handler uses IRepository<OutRes> to delete records
based on ActionId or ProfileId. Implemented asynchronous
deletion logic with support for cancellation tokens.
2025-12-08 11:42:08 +01:00
b9f5a3f10c Add DeleteOutResCommandValidator for input validation
Introduce DeleteOutResCommandValidator using FluentValidation
to enforce business rules for the DeleteOutResCommand. Added
a validation rule to ensure at least one of ActionId or
ProfileId is provided, with a descriptive error message if
neither is present. Included necessary using directives for
FluentValidation and the relevant namespace.
2025-12-08 11:28:43 +01:00
243cc97aa2 Add DeleteOutResCommand for CQRS pattern
Introduced the `DeleteOutResCommand` record in the `ReC.Application.OutResults.Commands` namespace. This command implements the `IRequest` interface from `MediatR` and includes two nullable properties: `ActionId` and `ProfileId`. Added the `MediatR` library to support the CQRS pattern.
2025-12-08 11:17:11 +01:00
bb43bfa064 Refactor property in RecActionView class
Replaced the `ProfileSequence` property with `Sequence` in the
`RecActionView` class. Updated the database column mapping
from `PROFILE_SEQUENCE` to `SEQUENCE`. This change aligns
with updates to the database schema and improves naming
consistency.
2025-12-08 11:04:52 +01:00
b4966585ae Enhance logging, Swagger, and XML documentation support 2025-12-08 10:44:15 +01:00
ae548d530f Enable Swagger via configuration setting
Added a condition in `Program.cs` to enable Swagger when the `UseSwagger` configuration value is `true`, in addition to the development environment.

Introduced a new `UseSwagger` setting in `appsettings.json` with a default value of `true`, allowing Swagger to be conditionally enabled in non-development environments. This improves flexibility for Swagger usage across different environments.
2025-12-08 10:07:57 +01:00
29 changed files with 445 additions and 242 deletions

View File

@@ -1,6 +1,7 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ReC.API.Extensions; using ReC.API.Extensions;
using ReC.Application.OutResults.Commands;
using ReC.Application.OutResults.Queries; using ReC.Application.OutResults.Queries;
namespace ReC.API.Controllers; namespace ReC.API.Controllers;
@@ -55,6 +56,34 @@ public class OutResController(IMediator mediator, IConfiguration config) : Contr
_ => Ok(res), _ => Ok(res),
}; };
} }
/// <summary>
/// Deletes output results based on the provided criteria.
/// </summary>
/// <param name="command">The command containing the deletion criteria, such as ActionId or ProfileId.</param>
/// <param name="cancel">A token to cancel the operation.</param>
/// <returns>An empty response indicating success.</returns>
[HttpDelete]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> Delete([FromQuery] DeleteOutResCommand command, CancellationToken cancel)
{
await mediator.Send(command, cancel);
return NoContent();
}
/// <summary>
/// Deletes all output results for a fake/test profile.
/// </summary>
/// <param name="cancel">A token to cancel the operation.</param>
/// <returns>An empty response indicating success.</returns>
[HttpDelete("fake")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Delete(CancellationToken cancel)
{
await mediator.Send(new DeleteOutResCommand() { ProfileId = config.GetFakeProfileId() }, cancel);
return NoContent();
}
} }
/// <summary> /// <summary>

View File

@@ -18,7 +18,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
/// <param name="profileId">The ID of the profile.</param> /// <param name="profileId">The ID of the profile.</param>
/// <param name="cancel">A token to cancel the operation.</param> /// <param name="cancel">A token to cancel the operation.</param>
/// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns> /// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns>
[HttpPost("invoke/{cmd}")] [HttpPost("invoke/{profileId}")]
[ProducesResponseType(StatusCodes.Status202Accepted)] [ProducesResponseType(StatusCodes.Status202Accepted)]
public async Task<IActionResult> Invoke([FromRoute] int profileId, CancellationToken cancel) public async Task<IActionResult> Invoke([FromRoute] int profileId, CancellationToken cancel)
{ {
@@ -43,7 +43,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
/// <summary> /// <summary>
/// Gets all RecActions for a given profile. /// Gets all RecActions for a given profile.
/// </summary> /// </summary>
/// <param name="profileId">The ID of the profile.</param> /// <param name="query"></param>
/// <param name="cancel">A token to cancel the operation.</param> /// <param name="cancel">A token to cancel the operation.</param>
/// <returns>A list of RecActions for the specified profile.</returns> /// <returns>A list of RecActions for the specified profile.</returns>
[HttpGet] [HttpGet]

View File

@@ -1,9 +1,11 @@
using Microsoft.AspNetCore.Rewrite;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NLog; using NLog;
using NLog.Web; using NLog.Web;
using ReC.API.Middleware; using ReC.API.Middleware;
using ReC.Application; using ReC.Application;
using ReC.Infrastructure; using ReC.Infrastructure;
using System.Reflection;
using LogLevel = Microsoft.Extensions.Logging.LogLevel; using LogLevel = Microsoft.Extensions.Logging.LogLevel;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
@@ -48,7 +50,12 @@ try
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen(c =>
{
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
var app = builder.Build(); var app = builder.Build();
@@ -57,10 +64,13 @@ try
#pragma warning restore CS0618 #pragma warning restore CS0618
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment() || config.GetValue<bool>("UseSwagger"))
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
var rewriteOptions = new RewriteOptions().AddRedirect("^$", "swagger");
app.UseRewriter(rewriteOptions);
} }
app.UseHttpsRedirection(); app.UseHttpsRedirection();

View File

@@ -10,13 +10,15 @@
<Product>ReC.API</Product> <Product>ReC.API</Product>
<PackageIcon>Assets\icon.ico</PackageIcon> <PackageIcon>Assets\icon.ico</PackageIcon>
<PackageTags>digital data rest-caller rec api</PackageTags> <PackageTags>digital data rest-caller rec api</PackageTags>
<Version>1.0.0-beta</Version> <Version>1.0.3-beta</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion> <AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion> <FileVersion>1.0.3.0</FileVersion>
<InformationalVersion>1.0.0-beta</InformationalVersion> <InformationalVersion>1.0.3-beta</InformationalVersion>
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright> <Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />

View File

@@ -5,6 +5,7 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"UseSwagger": true,
"ConnectionStrings": { "ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL19\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;" "Default": "Server=SDD-VMP04-SQL19\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
}, },

View File

@@ -62,6 +62,8 @@ public record RecActionDto
public string? PostprocessingQuery { get; init; } public string? PostprocessingQuery { get; init; }
public string? ErrorAction { get; init; }
public UriBuilder ToEndpointUriBuilder() public UriBuilder ToEndpointUriBuilder()
{ {
var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri); var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri);

View File

@@ -8,6 +8,10 @@ public class CreateOutResCommand : IRequest
{ {
public required long ActionId { get; set; } public required long ActionId { get; set; }
public short? Status { get; set; }
public string? Message { get; set; }
public string? Header { get; set; } public string? Header { get; set; }
public string? Body { get; set; } public string? Body { get; set; }

View File

@@ -0,0 +1,46 @@
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
using ReC.Domain.Entities;
namespace ReC.Application.OutResults.Commands;
/// <summary>
/// Represents the command to delete output results based on specified criteria.
/// </summary>
/// <remarks>
/// Deletion can be performed by providing either an <see cref="ActionId"/> or a <see cref="ProfileId"/>.
/// At least one of these properties must be set for the operation to proceed.
/// </remarks>
public record DeleteOutResCommand : IRequest
{
/// <summary>
/// Gets the unique identifier for the action whose output results should be deleted.
/// </summary>
public long? ActionId { get; init; }
/// <summary>
/// Gets the unique identifier for the profile whose associated action's output results should be deleted.
/// </summary>
public long? ProfileId { get; init; }
}
/// <summary>
/// Handles the execution of the <see cref="DeleteOutResCommand"/>.
/// </summary>
public class DeleteOutResCommandHandler(IRepository<OutRes> repo) : IRequestHandler<DeleteOutResCommand>
{
/// <summary>
/// Processes the delete command by removing matching <see cref="OutRes"/> entities from the repository.
/// </summary>
/// <param name="request">The command containing the deletion criteria.</param>
/// <param name="cancel">A cancellation token that can be used to cancel the work.</param>
/// <returns>A task that represents the asynchronous delete operation.</returns>
/// <remarks>
/// The handler deletes records where <c>OutRes.ActionId</c> matches <see cref="DeleteOutResCommand.ActionId"/>
/// or where the associated <c>Action.ProfileId</c> matches <see cref="DeleteOutResCommand.ProfileId"/>.
/// </remarks>
public Task Handle(DeleteOutResCommand request, CancellationToken cancel)
{
return repo.DeleteAsync(x => x.ActionId == request.ActionId || x.Action!.ProfileId == request.ProfileId, cancel);
}
}

View File

@@ -0,0 +1,14 @@
using FluentValidation;
namespace ReC.Application.OutResults.Commands;
public class DeleteOutResCommandValidator : AbstractValidator<DeleteOutResCommand>
{
public DeleteOutResCommandValidator()
{
RuleFor(x => x)
.Must(x => x.ActionId.HasValue || x.ProfileId.HasValue)
.WithMessage("At least one of ActionId or ProfileId must be provided.")
.WithName("Identifier");
}
}

View File

@@ -1,6 +1,4 @@
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ReC.Application.RecActions.Queries; using ReC.Application.RecActions.Queries;
namespace ReC.Application.RecActions.Commands; namespace ReC.Application.RecActions.Commands;
@@ -13,40 +11,23 @@ public static class InvokeBatchRecActionsCommandExtensions
=> sender.Send(new InvokeBatchRecActionsCommand { ProfileId = profileId }, cancel); => sender.Send(new InvokeBatchRecActionsCommand { ProfileId = profileId }, cancel);
} }
public class InvokeRecActionsCommandHandler(ISender sender, IServiceScopeFactory scopeFactory, IHttpClientFactory clientFactory, ILogger<InvokeRecActionsCommandHandler>? logger = null) : IRequestHandler<InvokeBatchRecActionsCommand> public class InvokeRecActionsCommandHandler(ISender sender) : IRequestHandler<InvokeBatchRecActionsCommand>
{ {
public async Task Handle(InvokeBatchRecActionsCommand request, CancellationToken cancel) public async Task Handle(InvokeBatchRecActionsCommand request, CancellationToken cancel)
{ {
var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel); var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel);
var http = clientFactory.CreateClient(); foreach (var action in actions)
using var semaphore = new SemaphoreSlim(5);
var tasks = actions.Select(async action =>
{ {
await semaphore.WaitAsync(cancel); var ok = await sender.Send(action.ToInvokeCommand(), cancel);
try if (!ok)
{ switch (action.ErrorAction?.ToLowerInvariant())
using var scope = scopeFactory.CreateScope(); {
var sender = scope.ServiceProvider.GetRequiredService<ISender>(); case "continue":
await sender.Send(action.ToInvokeCommand(), cancel); break;
} default:
catch(Exception ex) return;
{ }
logger?.LogError( }
ex,
"Error invoking Rec action. ProfileId: {ProfileId}, Id: {Id}",
action.ProfileId,
action.Id
);
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
} }
} }

View File

@@ -8,7 +8,7 @@ using System.Text.Json;
namespace ReC.Application.RecActions.Commands; namespace ReC.Application.RecActions.Commands;
public record InvokeRecActionCommand : IRequest public record InvokeRecActionCommand : IRequest<bool>
{ {
public RecActionDto Action { get; set; } = null!; public RecActionDto Action { get; set; } = null!;
} }
@@ -22,9 +22,9 @@ public class InvokeRecActionCommandHandler(
ISender sender, ISender sender,
IHttpClientFactory clientFactory, IHttpClientFactory clientFactory,
IConfiguration? config = null IConfiguration? config = null
) : IRequestHandler<InvokeRecActionCommand> ) : IRequestHandler<InvokeRecActionCommand, bool>
{ {
public async Task Handle(InvokeRecActionCommand request, CancellationToken cancel) public async Task<bool> Handle(InvokeRecActionCommand request, CancellationToken cancel)
{ {
var action = request.Action; var action = request.Action;
using var http = clientFactory.CreateClient(); using var http = clientFactory.CreateClient();
@@ -54,12 +54,17 @@ public class InvokeRecActionCommandHandler(
var resBody = await response.Content.ReadAsStringAsync(cancel); var resBody = await response.Content.ReadAsStringAsync(cancel);
var resHeaders = response.Headers.ToDictionary(); var resHeaders = response.Headers.ToDictionary();
var statusCode = (short)response.StatusCode;
await sender.Send(new CreateOutResCommand await sender.Send(new CreateOutResCommand
{ {
Status = statusCode,
ActionId = action.Id, ActionId = action.Id,
Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }), Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }),
Body = resBody, Body = resBody,
AddedWho = config?["AddedWho"] AddedWho = config?["AddedWho"]
}, cancel); }, cancel);
return response.IsSuccessStatusCode;
} }
} }

View File

@@ -0,0 +1,6 @@
namespace ReC.Domain.Attributes;
[AttributeUsage(AttributeTargets.Property)]
public class MustConfiguredAttribute : Attribute
{
}

View File

@@ -1,9 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema; namespace ReC.Domain.Entities;
namespace ReC.Domain.Entities;
public class BodyQueryResult public class BodyQueryResult
{ {
[Column("REQUEST_BODY")]
public string? RawBody { get; init; } public string? RawBody { get; init; }
} }

View File

@@ -1,52 +1,32 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBDD_CONNECTION")]
public class Connection public class Connection
{ {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID")]
public short? Id { get; set; } public short? Id { get; set; }
[Column("BEZEICHNUNG")]
public string? Bezeichnung { get; set; } public string? Bezeichnung { get; set; }
[Column("SQL_PROVIDER")]
public string? SqlProvider { get; set; } public string? SqlProvider { get; set; }
[Column("SERVER")]
public string? Server { get; set; } public string? Server { get; set; }
[Column("DATENBANK")]
public string? Datenbank { get; set; } public string? Datenbank { get; set; }
[Column("USERNAME")]
public string? Username { get; set; } public string? Username { get; set; }
[Column("PASSWORD")]
public string? Password { get; set; } public string? Password { get; set; }
[Column("BEMERKUNG")]
public string? Bemerkung { get; set; } public string? Bemerkung { get; set; }
[Column("AKTIV")]
public bool? Aktiv { get; set; } public bool? Aktiv { get; set; }
[Column("ERSTELLTWER")]
public string? ErstelltWer { get; set; } public string? ErstelltWer { get; set; }
[Column("ERSTELLTWANN")]
public DateTime? ErstelltWann { get; set; } public DateTime? ErstelltWann { get; set; }
[Column("GEANDERTWER")]
public string? GeandertWer { get; set; } public string? GeandertWer { get; set; }
[Column("GEAENDERTWANN")]
public DateTime? GeaendertWann { get; set; } public DateTime? GeaendertWann { get; set; }
[Column("SYS_CONNECTION")]
public bool? SysConnection { get; set; } public bool? SysConnection { get; set; }
} }

View File

@@ -1,34 +1,20 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ENDPOINT")]
public class Endpoint public class Endpoint
{ {
[Key]
[Column("GUID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; } public long Id { get; set; }
[Column("ACTIVE")]
public bool? Active { get; set; } public bool? Active { get; set; }
[Column("DESCRIPTION")]
public string? Description { get; set; } public string? Description { get; set; }
[Column("URI")]
public string? Uri { get; set; } public string? Uri { get; set; }
[Column("ADDED_WHO")]
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }

View File

@@ -3,56 +3,37 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities; namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ENDPOINT_AUTH")]
public class EndpointAuth public class EndpointAuth
{ {
[Key]
[Column("GUID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long? Id { get; set; } public long? Id { get; set; }
[Column("ACTIVE")]
public bool? Active { get; set; } public bool? Active { get; set; }
[Column("DESCRIPTION")]
public string? Description { get; set; } public string? Description { get; set; }
[Column("TYPE")]
public string? Type { get; set; } public string? Type { get; set; }
[Column("API_KEY")]
public string? ApiKey { get; set; } public string? ApiKey { get; set; }
[Column("API_VALUE")]
public string? ApiValue { get; set; } public string? ApiValue { get; set; }
[Column("API_KEY_ADD_TO")]
public string? ApiKeyAddTo { get; set; } public string? ApiKeyAddTo { get; set; }
[Column("TOKEN")]
public string? Token { get; set; } public string? Token { get; set; }
[Column("USERNAME")]
public string? Username { get; set; } public string? Username { get; set; }
[Column("PASSWORD")]
public string? Password { get; set; } public string? Password { get; set; }
[Column("DOMAIN")]
public string? Domain { get; set; } public string? Domain { get; set; }
[Column("WORKSTATION")]
public string? Workstation { get; set; } public string? Workstation { get; set; }
[Column("ADDED_WHO")]
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }

View File

@@ -1,47 +1,31 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
/// <summary> /// <summary>
/// Represents the TBREC_CFG_ENDPOINT_PARAMS table. /// Represents the TBREC_CFG_ENDPOINT_PARAMS table.
/// All properties are nullable to provide flexibility on the database side, /// All properties are nullable to provide flexibility on the database side,
/// preventing breaking changes if columns are altered to be nullable in production. /// preventing breaking changes if columns are altered to be nullable in production.
/// </summary> /// </summary>
[Table("TBREC_CFG_ENDPOINT_PARAMS", Schema = "dbo")]
public class EndpointParam public class EndpointParam
{ {
[Key]
[Column("GUID")]
public long? Id { get; set; } public long? Id { get; set; }
[Column("ACTIVE")]
public bool? Active { get; set; } public bool? Active { get; set; }
[Column("DESCRIPTION")]
public string? Description { get; set; } public string? Description { get; set; }
[Column("GROUP_ID")]
public short? GroupId { get; set; } public short? GroupId { get; set; }
[Column("SEQUENCE")]
public byte? Sequence { get; set; } public byte? Sequence { get; set; }
[Column("KEY")]
public string? Key { get; set; } public string? Key { get; set; }
[Column("VALUE")]
public string? Value { get; set; } public string? Value { get; set; }
[Column("ADDED_WHO")]
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }

View File

@@ -1,9 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema; namespace ReC.Domain.Entities;
namespace ReC.Domain.Entities;
public class HeaderQueryResult public class HeaderQueryResult
{ {
[Column("REQUEST_HEADER")]
public string? RawHeader { get; init; } public string? RawHeader { get; init; }
} }

View File

@@ -1,37 +1,26 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_OUT_RESULT", Schema = "dbo")]
public class OutRes public class OutRes
{ {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID")]
public long? Id { get; set; } public long? Id { get; set; }
[Column("ACTION_ID")]
public long? ActionId { get; set; } public long? ActionId { get; set; }
[ForeignKey("ActionId")]
public RecAction? Action { get; set; } public RecAction? Action { get; set; }
[Column("RESULT_HEADER")] public short? Status { get; set; }
public string? Message { get; set; }
public string? Header { get; set; } public string? Header { get; set; }
[Column("RESULT_BODY")]
public string? Body { get; set; } public string? Body { get; set; }
[Column("ADDED_WHO")]
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }

View File

@@ -1,46 +1,28 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_PROFILE", Schema = "dbo")]
public class Profile public class Profile
{ {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("GUID")]
public long Id { get; set; } public long Id { get; set; }
[Column("ACTIVE")]
public bool? Active { get; set; } public bool? Active { get; set; }
[Column("TYPE")]
public string? Type { get; set; } public string? Type { get; set; }
[Column("MANDANTOR")]
public string? Mandantor { get; set; } public string? Mandantor { get; set; }
[Column("PROFILE_NAME")]
public string? Name { get; set; } public string? Name { get; set; }
[Column("DESCRIPTION")]
public string? Description { get; set; } public string? Description { get; set; }
[Column("LOG_LEVEL")]
public string? LogLevel { get; set; } public string? LogLevel { get; set; }
[Column("LANGUAGE")]
public string? Language { get; set; } public string? Language { get; set; }
[Column("ADDED_WHO")]
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
} }

View File

@@ -1,73 +1,49 @@
using System.ComponentModel.DataAnnotations; namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ACTION")]
public class RecAction public class RecAction
{ {
[Key]
[Column("GUID")]
public long? Id { get; set; } public long? Id { get; set; }
[Column("PROFILE_ID")]
public long? ProfileId { get; set; } public long? ProfileId { get; set; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; set; } public Profile? Profile { get; set; }
[Column("ACTIVE")]
public bool? Active { get; set; } public bool? Active { get; set; }
[Column("SEQUENCE")]
public byte? Sequence { get; set; } public byte? Sequence { get; set; }
[Column("ENDPOINT_ID")]
public long? EndpointId { get; set; } public long? EndpointId { get; set; }
[ForeignKey("EndpointId")]
public Endpoint? Endpoint { get; set; } public Endpoint? Endpoint { get; set; }
[Column("ENDPOINT_AUTH_ID")]
public long? EndpointAuthId { get; set; } public long? EndpointAuthId { get; set; }
[ForeignKey("EndpointAuthId")]
public EndpointAuth? EndpointAuth { get; set; } public EndpointAuth? EndpointAuth { get; set; }
[Column("ENDPOINT_PARAMS_ID")]
public short? EndpointParamsId { get; set; } public short? EndpointParamsId { get; set; }
[Column("SQL_CONNECTION_ID")]
public short? SqlConnectionId { get; set; } public short? SqlConnectionId { get; set; }
[ForeignKey("SqlConnectionId")]
public Connection? SqlConnection { get; set; } public Connection? SqlConnection { get; set; }
[Column("TYPE")]
public string? Type { get; set; } public string? Type { get; set; }
[Column("PREPROCESSING_QUERY")]
public string? PreprocessingQuery { get; set; } public string? PreprocessingQuery { get; set; }
[Column("HEADER_QUERY")]
public string? HeaderQuery { get; set; } public string? HeaderQuery { get; set; }
[Column("BODY_QUERY")]
public string? BodyQuery { get; set; } public string? BodyQuery { get; set; }
[Column("POSTPROCESSING_QUERY")]
public string? PostprocessingQuery { get; set; } public string? PostprocessingQuery { get; set; }
[Column("ADDED_WHO")] public string? ErrorAction { get; set; }
public string? AddedWho { get; set; } public string? AddedWho { get; set; }
[Column("ADDED_WHEN")]
public DateTime? AddedWhen { get; set; } public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO")]
public string? ChangedWho { get; set; } public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN")]
public DateTime? ChangedWhen { get; set; } public DateTime? ChangedWhen { get; set; }
public OutRes? OutRes { get; set; } public OutRes? OutRes { get; set; }

View File

@@ -11,113 +11,65 @@ namespace ReC.Domain.Entities;
/// runtime mapping errors and ensures the application can handle schema changes without /// runtime mapping errors and ensures the application can handle schema changes without
/// requiring immediate code updates. /// requiring immediate code updates.
/// </summary> /// </summary>
[Table("VWREC_ACTION", Schema = "dbo")]
public class RecActionView public class RecActionView
{ {
[Column("ACTION_ID")]
public required long Id { get; set; } public required long Id { get; set; }
[ForeignKey("Id")]
public RecAction? Root { get; set; } public RecAction? Root { get; set; }
[Column("PROFILE_ID")]
public long? ProfileId { get; set; } public long? ProfileId { get; set; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; set; } public Profile? Profile { get; set; }
[Column("PROFILE_NAME")]
[MaxLength(100)]
public string? ProfileName { get; set; } public string? ProfileName { get; set; }
[Column("PROFILE_TYPE")]
[MaxLength(20)]
public string? ProfileType { get; set; } public string? ProfileType { get; set; }
[Column("PROFILE_SEQUENCE")] public byte? Sequence { get; set; }
public byte? ProfileSequence { get; set; }
[Column("ENDPOINT_ID")]
public long? EndpointId { get; set; } public long? EndpointId { get; set; }
[Column("ENDPOINT_URI")]
[MaxLength(4000)]
public string? EndpointUri { get; set; } public string? EndpointUri { get; set; }
[Column("ENDPOINT_AUTH_ID")]
public long? EndpointAuthId { get; set; } public long? EndpointAuthId { get; set; }
[Column("ENDPOINT_AUTH_TYPE")]
[MaxLength(50)]
public string? EndpointAuthType { get; set; } public string? EndpointAuthType { get; set; }
[Column("ENDPOINT_AUTH_API_KEY")]
[MaxLength(300)]
public string? EndpointAuthApiKey { get; set; } public string? EndpointAuthApiKey { get; set; }
[Column("ENDPOINT_AUTH_API_VALUE")]
[MaxLength(300)]
public string? EndpointAuthApiValue { get; set; } public string? EndpointAuthApiValue { get; set; }
[Column("ENDPOINT_AUTH_API_KEY_ADD_TO")]
[MaxLength(20)]
public string? EndpointAuthApiKeyAddTo { get; set; } public string? EndpointAuthApiKeyAddTo { get; set; }
[Column("ENDPOINT_AUTH_TOKEN")]
[MaxLength(300)]
public string? EndpointAuthToken { get; set; } public string? EndpointAuthToken { get; set; }
[Column("ENDPOINT_AUTH_USERNAME")]
[MaxLength(200)]
public string? EndpointAuthUsername { get; set; } public string? EndpointAuthUsername { get; set; }
[Column("ENDPOINT_AUTH_PASSWORD")]
[MaxLength(200)]
public string? EndpointAuthPassword { get; set; } public string? EndpointAuthPassword { get; set; }
[Column("ENDPOINT_AUTH_DOMAIN")]
[MaxLength(100)]
public string? EndpointAuthDomain { get; set; } public string? EndpointAuthDomain { get; set; }
[Column("ENDPOINT_AUTH_WORKSTATION")]
[MaxLength(100)]
public string? EndpointAuthWorkstation { get; set; } public string? EndpointAuthWorkstation { get; set; }
[Column("ENDPOINT_PARAMS_ID")]
public short? EndpointParamsId { get; set; } public short? EndpointParamsId { get; set; }
[Column("SQL_CONNECTION_ID")]
public short? SqlConnectionId { get; set; } public short? SqlConnectionId { get; set; }
[Column("SQL_CONNECTION_SERVER")]
[MaxLength(150)]
public string? SqlConnectionServer { get; set; } public string? SqlConnectionServer { get; set; }
[Column("SQL_CONNECTION_DB")]
[MaxLength(100)]
public string? SqlConnectionDb { get; set; } public string? SqlConnectionDb { get; set; }
[Column("SQL_CONNECTION_USERNAME")]
[MaxLength(100)]
public string? SqlConnectionUsername { get; set; } public string? SqlConnectionUsername { get; set; }
[Column("SQL_CONNECTION_PASSWORD")]
[MaxLength(100)]
public string? SqlConnectionPassword { get; set; } public string? SqlConnectionPassword { get; set; }
[Column("REST_TYPE")]
[MaxLength(20)]
public string? RestType { get; set; } public string? RestType { get; set; }
[Column("PREPROCESSING_QUERY")]
public string? PreprocessingQuery { get; set; } public string? PreprocessingQuery { get; set; }
[Column("HEADER_QUERY")]
public string? HeaderQuery { get; set; } public string? HeaderQuery { get; set; }
[Column("BODY_QUERY")]
public string? BodyQuery { get; set; } public string? BodyQuery { get; set; }
[Column("POSTPROCESSING_QUERY")]
public string? PostprocessingQuery { get; set; } public string? PostprocessingQuery { get; set; }
} }

View File

@@ -0,0 +1,12 @@
namespace ReC.Infrastructure.Exceptions;
public class DbModelConfigurationException : Exception
{
public DbModelConfigurationException(string message) : base(message)
{
}
public DbModelConfigurationException()
{
}
}

View File

@@ -0,0 +1,23 @@
using ReC.Infrastructure.Exceptions;
using ReC.Infrastructure.Options.Shared;
namespace ReC.Infrastructure.Options;
public record DbModelOptions
{
public Dictionary<string, EntityOptions> Entities { get; init; } = [];
public Dictionary<string, VirtualEntityOptions> VirtualEntities { get; init; } = [];
public void EnsureEntity<T>(bool isVirtual)
{
var entities = isVirtual
? VirtualEntities.ToDictionary(kvp => kvp.Key, kvp => kvp.Value as EntityBaseOptions)
: Entities.ToDictionary(kvp => kvp.Key, kvp => kvp.Value as EntityBaseOptions);
if(entities.TryGetValue(nameof(T), out var entityOptions))
entityOptions.EnsureProperties<T>();
else
throw new DbModelConfigurationException($"Entity options for type '{typeof(T).FullName}' not found.");
}
}

View File

@@ -0,0 +1,33 @@
using ReC.Domain.Attributes;
using ReC.Infrastructure.Exceptions;
namespace ReC.Infrastructure.Options.Shared;
public record EntityBaseOptions()
{
public Dictionary<string, string> ColumnMappings { get; init; } = [];
public IEnumerable<string> PropertyNames => ColumnMappings.Select(col => col.Key);
public IEnumerable<string> ColumnNames => ColumnMappings.Select(col => col.Value);
public void EnsureProperties(IEnumerable<string> propertyNames)
{
var missingProperties = propertyNames.Except(PropertyNames).ToList();
if (missingProperties.Count != 0)
throw new DbModelConfigurationException($"The following properties are not configured: {string.Join(", ", missingProperties)}");
}
public void EnsureProperties(params string[] propertyNames)
=> EnsureProperties(propertyNames.AsEnumerable());
public void EnsureProperties<T>()
{
var propertyNames = typeof(T)
.GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(MustConfiguredAttribute)))
.Select(prop => prop.Name);
EnsureProperties(propertyNames);
}
}

View File

@@ -0,0 +1,3 @@
namespace ReC.Infrastructure.Options.Shared;
public record EntityOptions(TableOptions Table) : EntityBaseOptions;

View File

@@ -0,0 +1,3 @@
namespace ReC.Infrastructure.Options.Shared;
public record TableOptions(string Name, string? Schema = null);

View File

@@ -0,0 +1,3 @@
namespace ReC.Infrastructure.Options.Shared;
public record VirtualEntityOptions : EntityBaseOptions;

View File

@@ -30,15 +30,217 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<RecActionView>().HasNoKey(); modelBuilder.Entity<Connection>(b =>
{
b.ToTable("TBDD_CONNECTION");
modelBuilder.Entity<HeaderQueryResult>().HasNoKey(); b.HasKey(e => e.Id);
modelBuilder.Entity<BodyQueryResult>().HasNoKey(); b.Property(e => e.Id)
.HasColumnName("GUID")
.ValueGeneratedOnAdd();
b.Property(e => e.Bezeichnung).HasColumnName("BEZEICHNUNG");
b.Property(e => e.SqlProvider).HasColumnName("SQL_PROVIDER");
b.Property(e => e.Server).HasColumnName("SERVER");
b.Property(e => e.Datenbank).HasColumnName("DATENBANK");
b.Property(e => e.Username).HasColumnName("USERNAME");
b.Property(e => e.Password).HasColumnName("PASSWORD");
b.Property(e => e.Bemerkung).HasColumnName("BEMERKUNG");
b.Property(e => e.Aktiv).HasColumnName("AKTIV");
b.Property(e => e.ErstelltWer).HasColumnName("ERSTELLTWER");
b.Property(e => e.ErstelltWann).HasColumnName("ERSTELLTWANN");
b.Property(e => e.GeandertWer).HasColumnName("GEANDERTWER");
b.Property(e => e.GeaendertWann).HasColumnName("GEAENDERTWANN");
b.Property(e => e.SysConnection).HasColumnName("SYS_CONNECTION");
});
modelBuilder.Entity<Endpoint>(b =>
{
b.ToTable("TBREC_CFG_ENDPOINT");
b.HasKey(e => e.Id);
b.Property(e => e.Id)
.HasColumnName("GUID")
.ValueGeneratedOnAdd();
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.Description).HasColumnName("DESCRIPTION");
b.Property(e => e.Uri).HasColumnName("URI");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<EndpointAuth>(b =>
{
b.ToTable("TBREC_CFG_ENDPOINT_AUTH");
b.HasKey(e => e.Id);
b.Property(e => e.Id)
.HasColumnName("GUID")
.ValueGeneratedOnAdd();
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.Description).HasColumnName("DESCRIPTION");
b.Property(e => e.Type).HasColumnName("TYPE");
b.Property(e => e.ApiKey).HasColumnName("API_KEY");
b.Property(e => e.ApiValue).HasColumnName("API_VALUE");
b.Property(e => e.ApiKeyAddTo).HasColumnName("API_KEY_ADD_TO");
b.Property(e => e.Token).HasColumnName("TOKEN");
b.Property(e => e.Username).HasColumnName("USERNAME");
b.Property(e => e.Password).HasColumnName("PASSWORD");
b.Property(e => e.Domain).HasColumnName("DOMAIN");
b.Property(e => e.Workstation).HasColumnName("WORKSTATION");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<EndpointParam>(b =>
{
b.ToTable("TBREC_CFG_ENDPOINT_PARAMS", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id).HasColumnName("GUID");
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.Description).HasColumnName("DESCRIPTION");
b.Property(e => e.GroupId).HasColumnName("GROUP_ID");
b.Property(e => e.Sequence).HasColumnName("SEQUENCE");
b.Property(e => e.Key).HasColumnName("KEY");
b.Property(e => e.Value).HasColumnName("VALUE");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<OutRes>(b =>
{
b.ToTable("TBREC_OUT_RESULT", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id)
.HasColumnName("GUID")
.ValueGeneratedOnAdd();
b.Property(e => e.ActionId).HasColumnName("ACTION_ID");
b.Property(e => e.Status).HasColumnName("STATUS");
b.Property(e => e.Message).HasColumnName("MESSAGE");
b.Property(e => e.Header).HasColumnName("RESULT_HEADER");
b.Property(e => e.Body).HasColumnName("RESULT_BODY");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<Profile>(b =>
{
b.ToTable("TBREC_CFG_PROFILE", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id)
.HasColumnName("GUID")
.ValueGeneratedOnAdd();
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.Type).HasColumnName("TYPE");
b.Property(e => e.Mandantor).HasColumnName("MANDANTOR");
b.Property(e => e.Name).HasColumnName("PROFILE_NAME");
b.Property(e => e.Description).HasColumnName("DESCRIPTION");
b.Property(e => e.LogLevel).HasColumnName("LOG_LEVEL");
b.Property(e => e.Language).HasColumnName("LANGUAGE");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<RecAction>(b =>
{
b.ToTable("TBREC_CFG_ACTION");
b.HasKey(e => e.Id);
b.Property(e => e.Id).HasColumnName("GUID");
b.Property(e => e.ProfileId).HasColumnName("PROFILE_ID");
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.Sequence).HasColumnName("SEQUENCE");
b.Property(e => e.EndpointId).HasColumnName("ENDPOINT_ID");
b.Property(e => e.EndpointAuthId).HasColumnName("ENDPOINT_AUTH_ID");
b.Property(e => e.EndpointParamsId).HasColumnName("ENDPOINT_PARAMS_ID");
b.Property(e => e.SqlConnectionId).HasColumnName("SQL_CONNECTION_ID");
b.Property(e => e.Type).HasColumnName("TYPE");
b.Property(e => e.PreprocessingQuery).HasColumnName("PREPROCESSING_QUERY");
b.Property(e => e.HeaderQuery).HasColumnName("HEADER_QUERY");
b.Property(e => e.BodyQuery).HasColumnName("BODY_QUERY");
b.Property(e => e.PostprocessingQuery).HasColumnName("POSTPROCESSING_QUERY");
b.Property(e => e.ErrorAction).HasColumnName("ERROR_ACTION");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
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(act => act.OutRes)
.WithOne(res => res.Action)
.HasForeignKey<OutRes>(res => res.ActionId)
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity<RecActionView>(b =>
{
b.ToTable("VWREC_ACTION", "dbo");
b.HasNoKey();
b.Property(e => e.Id).HasColumnName("ACTION_ID");
b.Property(e => e.ProfileId).HasColumnName("PROFILE_ID");
b.Property(e => e.ProfileName).HasColumnName("PROFILE_NAME");
b.Property(e => e.ProfileType).HasColumnName("PROFILE_TYPE");
b.Property(e => e.Sequence).HasColumnName("SEQUENCE");
b.Property(e => e.EndpointId).HasColumnName("ENDPOINT_ID");
b.Property(e => e.EndpointUri).HasColumnName("ENDPOINT_URI");
b.Property(e => e.EndpointAuthId).HasColumnName("ENDPOINT_AUTH_ID");
b.Property(e => e.EndpointAuthType).HasColumnName("ENDPOINT_AUTH_TYPE");
b.Property(e => e.EndpointAuthApiKey).HasColumnName("ENDPOINT_AUTH_API_KEY");
b.Property(e => e.EndpointAuthApiValue).HasColumnName("ENDPOINT_AUTH_API_VALUE");
b.Property(e => e.EndpointAuthApiKeyAddTo).HasColumnName("ENDPOINT_AUTH_API_KEY_ADD_TO");
b.Property(e => e.EndpointAuthToken).HasColumnName("ENDPOINT_AUTH_TOKEN");
b.Property(e => e.EndpointAuthUsername).HasColumnName("ENDPOINT_AUTH_USERNAME");
b.Property(e => e.EndpointAuthPassword).HasColumnName("ENDPOINT_AUTH_PASSWORD");
b.Property(e => e.EndpointAuthDomain).HasColumnName("ENDPOINT_AUTH_DOMAIN");
b.Property(e => e.EndpointAuthWorkstation).HasColumnName("ENDPOINT_AUTH_WORKSTATION");
b.Property(e => e.EndpointParamsId).HasColumnName("ENDPOINT_PARAMS_ID");
b.Property(e => e.SqlConnectionId).HasColumnName("SQL_CONNECTION_ID");
b.Property(e => e.SqlConnectionServer).HasColumnName("SQL_CONNECTION_SERVER");
b.Property(e => e.SqlConnectionDb).HasColumnName("SQL_CONNECTION_DB");
b.Property(e => e.SqlConnectionUsername).HasColumnName("SQL_CONNECTION_USERNAME");
b.Property(e => e.SqlConnectionPassword).HasColumnName("SQL_CONNECTION_PASSWORD");
b.Property(e => e.RestType).HasColumnName("REST_TYPE");
b.Property(e => e.PreprocessingQuery).HasColumnName("PREPROCESSING_QUERY");
b.Property(e => e.HeaderQuery).HasColumnName("HEADER_QUERY");
b.Property(e => e.BodyQuery).HasColumnName("BODY_QUERY");
b.Property(e => e.PostprocessingQuery).HasColumnName("POSTPROCESSING_QUERY");
});
modelBuilder.Entity<HeaderQueryResult>(b =>
{
b.HasNoKey();
b.Property(e => e.RawHeader).HasColumnName("REQUEST_HEADER");
});
modelBuilder.Entity<BodyQueryResult>(b =>
{
b.HasNoKey();
b.Property(e => e.RawBody).HasColumnName("REQUEST_BODY");
});
modelBuilder.Entity<RecAction>()
.HasOne(act => act.OutRes)
.WithOne(res => res.Action)
.HasForeignKey<OutRes>(res => res.ActionId);
} }
} }