Compare commits

...

17 Commits

Author SHA1 Message Date
3da16ba640 Introduce DTOs and refactor app layer to use them
Added DTO classes for Connection, Endpoint, EndpointAuth, EndpointParam, Profile, and RecAction. Updated AutoMapper profiles, DbContext, and command handlers to use DTOs instead of domain entities. This decouples the application layer from the domain model, improving maintainability and flexibility. Cleaned up some using directives and file headers.
2025-12-12 15:16:38 +01:00
71a0220c3f Refactor: replace RecActionDto with RecActionViewDto
Standardize usage of RecActionViewDto across the codebase:
- Update pipeline behaviors, mapping profiles, and commands to use RecActionViewDto.
- Remove RecActionDto and introduce RecActionViewDto with equivalent properties and methods.
- Adjust query handlers and related interfaces to work with RecActionViewDto.
This clarifies DTO usage and aligns the model with domain intent.
2025-12-12 15:04:43 +01:00
f53603083a Refactor batch invocation to use RecActionViews
Replaced all batch RecAction logic with RecActionViews equivalents. Updated controller methods to call InvokeBatchRecActionView instead of InvokeBatchRecAction. Removed InvokeBatchRecActionsCommand and its handler, and added InvokeBatchRecActionViewsCommand with similar logic for RecActionViews. This shifts batch processing from RecActions to RecActionViews across the codebase.
2025-12-12 15:00:26 +01:00
92e7d44d3b Rename InvokeRecActionCommand to InvokeRecActionViewCommand
Refactored the RecAction invocation logic by renaming and moving InvokeRecActionCommand.cs to InvokeRecActionViewCommand.cs. Updated all class, record, and extension method names accordingly. The command handling logic remains unchanged, but all references and namespaces now reflect the new naming. Removed the old InvokeRecActionCommand.cs file.
2025-12-12 14:58:33 +01:00
f8211e9e9d Refactor RecAction query to use ReadRecActionViewQuery
Replaced ReadRecActionQuery and its handler with ReadRecActionViewQuery and ReadRecActionViewQueryHandler. Updated controller methods to use the new query type. This change clarifies and consolidates RecAction view query logic, improving code organization and domain alignment.
2025-12-12 14:52:11 +01:00
1782844543 Refactor RecAction commands to new namespace
Moved CreateRecActionCommand and DeleteRecActionsCommand (and their handlers) from ReCActionViews.Commands to ReCActions.Commands. Updated all references in controller and mapping profile. Removed old files to improve code organization. No changes to command logic.
2025-12-12 14:47:48 +01:00
2aa7cabcbd Refactor RecActions to RecActionViews namespaces
Renamed command and query files, namespaces, and usings from RecActions to RecActionViews for improved clarity and organization. Updated controller and mapping profile references accordingly. No changes to business logic or handler implementations.
2025-12-12 14:43:32 +01:00
28f35101f9 Refactor auth type to enum in RecActionDto and handler
Changed EndpointAuthType from string to enum in RecActionDto. Updated InvokeRecActionCommandHandler to use enum values in switch statements for improved type safety and maintainability.
2025-12-12 14:00:09 +01:00
f8c5502905 Refactor auth type properties to use strong enum type
Changed Type in EndpointAuth and EndpointAuthType in RecActionView from string? to EndpointAuthType? for improved type safety and clarity. This enforces the use of a specific enum for authentication types instead of plain strings.
2025-12-12 13:58:39 +01:00
961b87de3d Add EndpointAuthType enum for endpoint authentication types
Introduced the EndpointAuthType enum in the ReC.Domain.Constants namespace to represent various endpoint authentication methods, including NoAuth, ApiKey, BearerToken, JwtBearer, BasicAuth, DigestAuth, OAuth1, OAuth2, AwsSignature, and NtlmAuth. Each type is assigned a unique integer value for clear identification.
2025-12-12 13:57:52 +01:00
6b036f4f91 Add EF Core data annotations for table and FK mapping
Added [Table] and [ForeignKey] attributes to entity classes to explicitly map them to database tables/views and define relationships. Updated using directives as needed. Improves entity mapping clarity and robustness against schema changes.
2025-12-12 13:55:22 +01:00
46b7ae29cd Update IRecDbContext DbSets and add new entity sets
Changed DbSet properties to get/set in IRecDbContext, renamed Actions to RecActionViews for consistency, and added DbSets for Connections, Endpoints, EndpointAuths, Profiles, and RecActions. Updated RecDbContext implementation accordingly.
2025-12-12 13:42:49 +01:00
84e403f411 Refactor API key auth handling with switch and error check
Refactored the "API Key" authentication logic to use a switch
statement on the API key location, improving code clarity.
Added a default case to throw a DataIntegrityException for
unsupported API key locations, enhancing error handling.
2025-12-12 13:21:09 +01:00
3b77345aee Refactor API key location to use enum instead of string
Changed EndpointAuthApiKeyAddTo from string? to ApiKeyLocation? enum in RecActionDto for type safety. Updated related logic in InvokeRecActionCommandHandler to use the enum, and added the necessary using directive for ReC.Domain.Constants.
2025-12-12 13:17:54 +01:00
6d04a4afd1 Refactor API key location to use enum for type safety
Updated EndpointAuth and RecActionView to use the ApiKeyLocation enum for API key location properties instead of nullable strings. Added necessary using directives for ReC.Domain.Constants to support this change, improving type safety and code clarity.
2025-12-12 13:16:32 +01:00
1f250d55b0 Add ApiKeyLocation enum for API key placement options
Introduced the ApiKeyLocation enum in the ReC.Domain.Constants namespace to specify API key locations, supporting both Header and Query options.
2025-12-12 13:10:09 +01:00
c422de445d Refactor ErrorAction to use enum for type safety
Changed RecActionDto.ErrorAction from string to ErrorAction enum, updating all related logic to use enum values instead of strings. Added necessary using directives. This improves type safety, reduces risk of typos, and enhances maintainability.
2025-12-12 13:04:23 +01:00
32 changed files with 445 additions and 154 deletions

View File

@@ -3,7 +3,8 @@ using Microsoft.AspNetCore.Mvc;
using ReC.API.Extensions;
using ReC.API.Models;
using ReC.Application.RecActions.Commands;
using ReC.Application.RecActions.Queries;
using ReC.Application.RecActionViews.Commands;
using ReC.Application.RecActionViews.Queries;
using System.Text.Json;
namespace ReC.API.Controllers;
@@ -22,7 +23,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
[ProducesResponseType(StatusCodes.Status202Accepted)]
public async Task<IActionResult> Invoke([FromRoute] int profileId, CancellationToken cancel)
{
await mediator.InvokeBatchRecAction(profileId, cancel);
await mediator.InvokeBatchRecActionView(profileId, cancel);
return Accepted();
}
@@ -35,7 +36,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
[ProducesResponseType(StatusCodes.Status202Accepted)]
public async Task<IActionResult> Invoke(CancellationToken cancel)
{
await mediator.InvokeBatchRecAction(config.GetFakeProfileId(), cancel);
await mediator.InvokeBatchRecActionView(config.GetFakeProfileId(), cancel);
return Accepted();
}
@@ -48,7 +49,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
/// <returns>A list of RecActions for the specified profile.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Get([FromQuery] ReadRecActionQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
public async Task<IActionResult> Get([FromQuery] ReadRecActionViewQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
/// <summary>
/// Gets all RecActions for a fake/test profile.
@@ -58,7 +59,7 @@ public class RecActionController(IMediator mediator, IConfiguration config) : Co
/// <returns>A list of RecActions for the fake profile.</returns>
[HttpGet("fake")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Get(CancellationToken cancel, [FromQuery] bool invoked = false) => Ok(await mediator.Send(new ReadRecActionQuery()
public async Task<IActionResult> Get(CancellationToken cancel, [FromQuery] bool invoked = false) => Ok(await mediator.Send(new ReadRecActionViewQuery()
{
ProfileId = config.GetFakeProfileId(),
Invoked = invoked

View File

@@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
namespace ReC.Application.Common.Behaviors;
public class BodyQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext) : IPipelineBehavior<TRequest, TResponse>
where TRequest : RecActionDto
where TRequest : RecActionViewDto
where TResponse : notnull
{
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)

View File

@@ -8,7 +8,7 @@ using System.Text.Json;
namespace ReC.Application.Common.Behaviors;
public class HeaderQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext, ILogger<HeaderQueryBehavior<TRequest, TResponse>>? logger = null) : IPipelineBehavior<TRequest, TResponse>
where TRequest : RecActionDto
where TRequest : RecActionViewDto
where TResponse : notnull
{
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)

View File

@@ -0,0 +1,32 @@
namespace ReC.Application.Common.Dto;
public record ConnectionDto
{
public short? Id { get; set; }
public string? Bezeichnung { get; set; }
public string? SqlProvider { get; set; }
public string? Server { get; set; }
public string? Datenbank { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public string? Bemerkung { get; set; }
public bool? Aktiv { get; set; }
public string? ErstelltWer { get; set; }
public DateTime? ErstelltWann { get; set; }
public string? GeandertWer { get; set; }
public DateTime? GeaendertWann { get; set; }
public bool? SysConnection { get; set; }
}

View File

@@ -6,7 +6,15 @@ public class DtoMappingProfile : AutoMapper.Profile
{
public DtoMappingProfile()
{
CreateMap<RecActionView, RecActionDto>();
CreateMap<RecActionView, RecActionViewDto>();
CreateMap<OutRes, OutResDto>();
CreateMap<Connection, ConnectionDto>();
CreateMap<EndpointAuth, EndpointAuthDto>();
CreateMap<Endpoint, EndpointDto>();
CreateMap<EndpointParam, EndpointParamDto>();
CreateMap<Profile, ProfileDto>();
CreateMap<RecAction, RecActionDto>();
}
}

View File

@@ -0,0 +1,40 @@
using ReC.Domain.Constants;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Application.Common.Dto;
[Table("TBREC_CFG_ENDPOINT_AUTH")]
public record EndpointAuthDto
{
public long? Id { get; set; }
public bool? Active { get; set; }
public string? Description { get; set; }
public EndpointAuthType? Type { get; set; }
public string? ApiKey { get; set; }
public string? ApiValue { get; set; }
public ApiKeyLocation? ApiKeyAddTo { 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 string? AddedWho { get; set; }
public DateTime? AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Application.Common.Dto;
public record EndpointDto
{
public long Id { get; set; }
public bool? Active { get; set; }
public string? Description { get; set; }
public string? Uri { get; set; }
public string? AddedWho { get; set; }
public DateTime? AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Application.Common.Dto;
/// <summary>
/// Represents the TBREC_CFG_ENDPOINT_PARAMS table.
/// All properties are nullable to provide flexibility on the database side,
/// preventing breaking changes if columns are altered to be nullable in production.
/// </summary>
public record EndpointParamDto
{
public long? Id { get; set; }
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 string? AddedWho { get; set; }
public DateTime? AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Application.Common.Dto;
[Table("TBREC_CFG_PROFILE", Schema = "dbo")]
public record ProfileDto
{
public long Id { get; set; }
public bool? Active { get; set; }
public string? Type { get; set; }
public string? Mandantor { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? LogLevel { get; set; }
public string? Language { get; set; }
public string? AddedWho { get; set; }
public DateTime? AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -1,78 +1,54 @@
namespace ReC.Application.Common.Dto;
using ReC.Domain.Constants;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Application.Common.Dto;
[Table("TBREC_CFG_ACTION")]
public record RecActionDto
{
public required long Id { get; init; }
public long? Id { get; set; }
public long? ProfileId { get; init; }
public long? ProfileId { get; set; }
public string? ProfileName { get; init; }
public ProfileDto? Profile { get; set; }
public string? ProfileType { get; init; }
public bool? Active { get; set; }
public byte? ProfileSequence { get; init; }
public byte? Sequence { get; set; }
public long? EndpointId { get; init; }
public long? EndpointId { get; set; }
public string? EndpointUri { get; init; }
public EndpointDto? Endpoint { get; set; }
public long? EndpointAuthId { get; init; }
public long? EndpointAuthId { get; set; }
public string? EndpointAuthType { get; init; }
public EndpointAuthDto? EndpointAuth { get; set; }
public string? EndpointAuthApiKey { get; init; }
public short? EndpointParamsId { get; set; }
public string? EndpointAuthApiValue { get; init; }
public short? SqlConnectionId { get; set; }
public string? EndpointAuthApiKeyAddTo { get; init; }
public ConnectionDto? SqlConnection { get; set; }
public string? EndpointAuthToken { get; init; }
public string? Type { get; set; }
public string? EndpointAuthUsername { get; init; }
public string? PreprocessingQuery { get; set; }
public string? EndpointAuthPassword { get; init; }
public string? HeaderQuery { get; set; }
public string? EndpointAuthDomain { get; init; }
public string? BodyQuery { get; set; }
public string? EndpointAuthWorkstation { get; init; }
public string? PostprocessingQuery { get; set; }
public short? EndpointParamsId { get; init; }
public ErrorAction? ErrorAction { get; set; }
public short? SqlConnectionId { get; init; }
public string? AddedWho { get; set; }
public string? SqlConnectionServer { get; init; }
public DateTime? AddedWhen { get; set; }
public string? SqlConnectionDb { get; init; }
public string? ChangedWho { get; set; }
public string? SqlConnectionUsername { get; init; }
public DateTime? ChangedWhen { get; set; }
public string? SqlConnectionPassword { get; init; }
public string? RestType { get; init; }
public string? PreprocessingQuery { get; init; }
public string? HeaderQuery { get; init; }
public Dictionary<string, string>? Headers { get; set; }
public string? BodyQuery { get; init; }
public string? Body { get; set; }
public string? PostprocessingQuery { get; init; }
public string? ErrorAction { get; init; }
public UriBuilder ToEndpointUriBuilder()
{
var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri);
builder.Port = -1;
if (ProfileType is not null)
builder.Scheme = ProfileType;
return builder;
}
public OutResDto? OutRes { get; set; }
}

View File

@@ -0,0 +1,80 @@
using ReC.Domain.Constants;
namespace ReC.Application.Common.Dto;
public record RecActionViewDto
{
public required long Id { get; init; }
public long? ProfileId { get; init; }
public string? ProfileName { get; init; }
public string? ProfileType { get; init; }
public byte? ProfileSequence { get; init; }
public long? EndpointId { get; init; }
public string? EndpointUri { get; init; }
public long? EndpointAuthId { get; init; }
public EndpointAuthType? EndpointAuthType { get; init; }
public string? EndpointAuthApiKey { get; init; }
public string? EndpointAuthApiValue { get; init; }
public ApiKeyLocation? EndpointAuthApiKeyAddTo { get; init; }
public string? EndpointAuthToken { get; init; }
public string? EndpointAuthUsername { get; init; }
public string? EndpointAuthPassword { get; init; }
public string? EndpointAuthDomain { get; init; }
public string? EndpointAuthWorkstation { get; init; }
public short? EndpointParamsId { get; init; }
public short? SqlConnectionId { get; init; }
public string? SqlConnectionServer { get; init; }
public string? SqlConnectionDb { get; init; }
public string? SqlConnectionUsername { get; init; }
public string? SqlConnectionPassword { get; init; }
public string? RestType { get; init; }
public string? PreprocessingQuery { get; init; }
public string? HeaderQuery { get; init; }
public Dictionary<string, string>? Headers { get; set; }
public string? BodyQuery { get; init; }
public string? Body { get; set; }
public string? PostprocessingQuery { get; init; }
public ErrorAction ErrorAction { get; init; }
public UriBuilder ToEndpointUriBuilder()
{
var builder = EndpointUri is null ? new UriBuilder() : new UriBuilder(EndpointUri);
builder.Port = -1;
if (ProfileType is not null)
builder.Scheme = ProfileType;
return builder;
}
}

View File

@@ -5,15 +5,25 @@ namespace ReC.Application.Common.Interfaces;
public interface IRecDbContext
{
public DbSet<EndpointParam> EndpointParams { get; }
public DbSet<EndpointParam> EndpointParams { get; set; }
public DbSet<RecActionView> Actions { get; }
public DbSet<RecActionView> RecActionViews { get; set; }
public DbSet<OutRes> OutRes { get; }
public DbSet<OutRes> OutRes { get; set; }
public DbSet<HeaderQueryResult> HeaderQueryResults { get; }
public DbSet<HeaderQueryResult> HeaderQueryResults { get; set; }
public DbSet<BodyQueryResult> BodyQueryResults { get; }
public DbSet<BodyQueryResult> BodyQueryResults { get; set; }
public DbSet<ConnectionDto> Connections { get; set; }
public DbSet<EndpointDto> Endpoints { get; set; }
public DbSet<EndpointAuthDto> EndpointAuths { get; set; }
public DbSet<ProfileDto> Profiles { get; set; }
public DbSet<RecActionDto> RecActions { get; set; }
public Task<int> SaveChangesAsync(CancellationToken cancel = default);
}

View File

@@ -5,14 +5,14 @@ using ReC.Domain.Entities;
namespace ReC.Application.Endpoints.Commands;
public class ObtainEndpointCommand : IRequest<Endpoint>
public class ObtainEndpointCommand : IRequest<EndpointDto>
{
public string Uri { get; init; } = null!;
}
public class ObtainEndpointCommandHandler(IRepository<Endpoint> repo) : IRequestHandler<ObtainEndpointCommand, Endpoint>
public class ObtainEndpointCommandHandler(IRepository<EndpointDto> repo) : IRequestHandler<ObtainEndpointCommand, EndpointDto>
{
public async Task<Endpoint> Handle(ObtainEndpointCommand request, CancellationToken cancel)
public async Task<EndpointDto> Handle(ObtainEndpointCommand request, CancellationToken cancel)
{
var endpoint = await repo.Where(e => e.Uri == request.Uri).FirstOrDefaultAsync(cancel);

View File

@@ -8,7 +8,7 @@ public class MappingProfile : AutoMapper.Profile
{
public MappingProfile()
{
CreateMap<ObtainEndpointCommand, Endpoint>()
CreateMap<ObtainEndpointCommand, EndpointDto>()
.ForMember(e => e.Active, exp => exp.MapFrom(cmd => true))
.ForMember(e => e.AddedWhen, exp => exp.MapFrom(cmd => DateTime.UtcNow))
.ForMember(e => e.AddedWho, exp => exp.MapFrom(cmd => "ReC.API"));

View File

@@ -0,0 +1,34 @@
using MediatR;
using ReC.Application.RecActionViews.Queries;
using ReC.Domain.Constants;
namespace ReC.Application.RecActionViews.Commands;
public record InvokeBatchRecActionViewsCommand : ReadRecActionQueryBase, IRequest;
public static class InvokeBatchRecActionViewsCommandExtensions
{
public static Task InvokeBatchRecActionView(this ISender sender, long profileId, CancellationToken cancel = default)
=> sender.Send(new InvokeBatchRecActionViewsCommand { ProfileId = profileId }, cancel);
}
public class InvokeRecActionViewsCommandHandler(ISender sender) : IRequestHandler<InvokeBatchRecActionViewsCommand>
{
public async Task Handle(InvokeBatchRecActionViewsCommand request, CancellationToken cancel)
{
var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel);
foreach (var action in actions)
{
var ok = await sender.Send(action.ToInvokeCommand(), cancel);
if (!ok)
switch (action.ErrorAction)
{
case ErrorAction.Continue:
break;
default:
return;
}
}
}
}

View File

@@ -5,30 +5,31 @@ using ReC.Application.Common.Constants;
using ReC.Application.Common.Dto;
using ReC.Application.Common.Exceptions;
using ReC.Application.OutResults.Commands;
using ReC.Domain.Constants;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace ReC.Application.RecActions.Commands;
namespace ReC.Application.RecActionViews.Commands;
public record InvokeRecActionCommand : IRequest<bool>
public record InvokeRecActionViewCommand : IRequest<bool>
{
public RecActionDto Action { get; set; } = null!;
public RecActionViewDto Action { get; set; } = null!;
}
public static class InvokeRecActionCommandExtensions
public static class InvokeRecActionViewCommandExtensions
{
public static InvokeRecActionCommand ToInvokeCommand(this RecActionDto dto) => new() { Action = dto };
public static InvokeRecActionViewCommand ToInvokeCommand(this RecActionViewDto dto) => new() { Action = dto };
}
public class InvokeRecActionCommandHandler(
public class InvokeRecActionViewCommandHandler(
ISender sender,
IHttpClientFactory clientFactory,
IConfiguration? config = null
) : IRequestHandler<InvokeRecActionCommand, bool>
) : IRequestHandler<InvokeRecActionViewCommand, bool>
{
public async Task<bool> Handle(InvokeRecActionCommand request, CancellationToken cancel)
public async Task<bool> Handle(InvokeRecActionViewCommand request, CancellationToken cancel)
{
var action = request.Action;
@@ -57,35 +58,42 @@ public class InvokeRecActionCommandHandler(
switch (action.EndpointAuthType)
{
case "No Auth":
case EndpointAuthType.NoAuth:
break;
case "API Key":
case EndpointAuthType.ApiKey:
if (action.EndpointAuthApiKey is string apiKey && action.EndpointAuthApiValue is string apiValue)
{
if (action.EndpointAuthApiKeyAddTo == "Header")
switch (action.EndpointAuthApiKeyAddTo)
{
httpReq.Headers.Add(apiKey, apiValue);
}
else // Defaults to Query String
{
var uriBuilder = new UriBuilder(httpReq.RequestUri!);
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
query[apiKey] = apiValue;
uriBuilder.Query = query.ToString();
httpReq.RequestUri = uriBuilder.Uri;
case ApiKeyLocation.Header:
httpReq.Headers.Add(apiKey, apiValue);
break;
case ApiKeyLocation.Query:
var uriBuilder = new UriBuilder(httpReq.RequestUri!);
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
query[apiKey] = apiValue;
uriBuilder.Query = query.ToString();
httpReq.RequestUri = uriBuilder.Uri;
break;
default:
throw new DataIntegrityException(
$"The API key location '{action.EndpointAuthApiKeyAddTo}' is not supported. " +
$"ProfileId: {action.ProfileId}, " +
$"Id: {action.Id}"
);
}
}
break;
case "Bearer Token":
case "JWT Bearer":
case "OAuth 2.0": // OAuth 2.0 uses Bearer tokens for authenticated requests
case EndpointAuthType.BearerToken:
case EndpointAuthType.JwtBearer:
case EndpointAuthType.OAuth2:
if (action.EndpointAuthToken is string authToken)
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
break;
case "Basic Auth":
case EndpointAuthType.BasicAuth:
if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword)
{
var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}"));
@@ -93,7 +101,7 @@ public class InvokeRecActionCommandHandler(
}
break;
case "NTLM Auth":
case EndpointAuthType.NtlmAuth:
if (!string.IsNullOrWhiteSpace(action.EndpointAuthUsername))
{
var credentials = new NetworkCredential(
@@ -105,9 +113,9 @@ public class InvokeRecActionCommandHandler(
}
break;
case "Digest Auth":
case "OAuth 1.0":
case "AWS Signature":
case EndpointAuthType.DigestAuth:
case EndpointAuthType.OAuth1:
case EndpointAuthType.AwsSignature:
// These authentication methods require more complex implementations,
// often involving multi-step handshakes or specialized libraries.
// They are left as placeholders for future implementation.

View File

@@ -8,7 +8,7 @@ public class MappingProfile : AutoMapper.Profile
{
public MappingProfile()
{
CreateMap<CreateRecActionCommand, RecAction>()
CreateMap<CreateRecActionCommand, RecActionDto>()
.ForMember(e => e.Active, exp => exp.MapFrom(cmd => true))
.ForMember(e => e.AddedWhen, exp => exp.MapFrom(cmd => DateTime.UtcNow))
.ForMember(e => e.AddedWho, exp => exp.MapFrom(cmd => "ReC.API"));

View File

@@ -6,32 +6,32 @@ using Microsoft.EntityFrameworkCore;
using DigitalData.Core.Exceptions;
using ReC.Application.Common.Dto;
namespace ReC.Application.RecActions.Queries;
namespace ReC.Application.RecActionViews.Queries;
public record ReadRecActionQueryBase
{
public long ProfileId { get; init; }
public ReadRecActionQuery ToReadQuery(Action<ReadRecActionQuery> modify)
public ReadRecActionViewQuery ToReadQuery(Action<ReadRecActionViewQuery> modify)
{
ReadRecActionQuery query = new(this);
ReadRecActionViewQuery query = new(this);
modify(query);
return query;
}
}
public record ReadRecActionQuery : ReadRecActionQueryBase, IRequest<IEnumerable<RecActionDto>>
public record ReadRecActionViewQuery : ReadRecActionQueryBase, IRequest<IEnumerable<RecActionViewDto>>
{
public ReadRecActionQuery(ReadRecActionQueryBase root) : base(root) { }
public ReadRecActionViewQuery(ReadRecActionQueryBase root) : base(root) { }
public bool? Invoked { get; set; } = null;
public ReadRecActionQuery() { }
public ReadRecActionViewQuery() { }
}
public class ReadRecActionQueryHandler(IRepository<RecActionView> repo, IMapper mapper) : IRequestHandler<ReadRecActionQuery, IEnumerable<RecActionDto>>
public class ReadRecActionViewQueryHandler(IRepository<RecActionView> repo, IMapper mapper) : IRequestHandler<ReadRecActionViewQuery, IEnumerable<RecActionViewDto>>
{
public async Task<IEnumerable<RecActionDto>> Handle(ReadRecActionQuery request, CancellationToken cancel)
public async Task<IEnumerable<RecActionViewDto>> Handle(ReadRecActionViewQuery request, CancellationToken cancel)
{
var query = repo.Where(act => act.ProfileId == request.ProfileId);
@@ -43,6 +43,6 @@ public class ReadRecActionQueryHandler(IRepository<RecActionView> repo, IMapper
if (actions.Count == 0)
throw new NotFoundException($"No actions found for the profile {request.ProfileId}.");
return mapper.Map<IEnumerable<RecActionDto>>(actions);
return mapper.Map<IEnumerable<RecActionViewDto>>(actions);
}
}

View File

@@ -27,7 +27,7 @@ public record CreateRecActionCommand : IRequest
public long? EndpointAuthId { get; set; }
}
public class CreateRecActionCommandHandler(ISender sender, IRepository<RecAction> repo) : IRequestHandler<CreateRecActionCommand>
public class CreateRecActionCommandHandler(ISender sender, IRepository<RecActionDto> repo) : IRequestHandler<CreateRecActionCommand>
{
public async Task Handle(CreateRecActionCommand request, CancellationToken cancel)
{

View File

@@ -11,7 +11,7 @@ public class DeleteRecActionsCommand : IRequest
public required long ProfileId { get; init; }
}
public class DeleteRecActionsCommandHandler(IRepository<RecAction> repo) : IRequestHandler<DeleteRecActionsCommand>
public class DeleteRecActionsCommandHandler(IRepository<RecActionDto> repo) : IRequestHandler<DeleteRecActionsCommand>
{
public async Task Handle(DeleteRecActionsCommand request, CancellationToken cancel)
{

View File

@@ -1,33 +0,0 @@
using MediatR;
using ReC.Application.RecActions.Queries;
namespace ReC.Application.RecActions.Commands;
public record InvokeBatchRecActionsCommand : ReadRecActionQueryBase, IRequest;
public static class InvokeBatchRecActionsCommandExtensions
{
public static Task InvokeBatchRecAction(this ISender sender, long profileId, CancellationToken cancel = default)
=> sender.Send(new InvokeBatchRecActionsCommand { ProfileId = profileId }, cancel);
}
public class InvokeRecActionsCommandHandler(ISender sender) : IRequestHandler<InvokeBatchRecActionsCommand>
{
public async Task Handle(InvokeBatchRecActionsCommand request, CancellationToken cancel)
{
var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel);
foreach (var action in actions)
{
var ok = await sender.Send(action.ToInvokeCommand(), cancel);
if (!ok)
switch (action.ErrorAction?.ToLowerInvariant())
{
case "continue":
break;
default:
return;
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace ReC.Domain.Constants;
public enum ApiKeyLocation
{
Header = 0,
Query = 1
}

View File

@@ -0,0 +1,15 @@
namespace ReC.Domain.Constants;
public enum EndpointAuthType
{
NoAuth = 0,
ApiKey = 1,
BearerToken = 2,
JwtBearer = 3,
BasicAuth = 4,
DigestAuth = 5,
OAuth1 = 6,
OAuth2 = 7,
AwsSignature = 8,
NtlmAuth = 9
}

View File

@@ -1,5 +1,8 @@
namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBDD_CONNECTION")]
public class Connection
{
public short? Id { get; set; }

View File

@@ -1,5 +1,8 @@
namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ENDPOINT")]
public class Endpoint
{
public long Id { get; set; }

View File

@@ -1,8 +1,9 @@
using System.ComponentModel.DataAnnotations;
using ReC.Domain.Constants;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ENDPOINT_AUTH")]
public class EndpointAuth
{
public long? Id { get; set; }
@@ -11,13 +12,13 @@ public class EndpointAuth
public string? Description { get; set; }
public string? Type { get; set; }
public EndpointAuthType? Type { get; set; }
public string? ApiKey { get; set; }
public string? ApiValue { get; set; }
public string? ApiKeyAddTo { get; set; }
public ApiKeyLocation? ApiKeyAddTo { get; set; }
public string? Token { get; set; }

View File

@@ -1,10 +1,13 @@
namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
/// <summary>
/// Represents the TBREC_CFG_ENDPOINT_PARAMS table.
/// All properties are nullable to provide flexibility on the database side,
/// preventing breaking changes if columns are altered to be nullable in production.
/// </summary>
[Table("TBREC_CFG_ENDPOINT_PARAMS", Schema = "dbo")]
public class EndpointParam
{
public long? Id { get; set; }

View File

@@ -1,5 +1,8 @@
namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_OUT_RESULT", Schema = "dbo")]
public class OutRes
{
public long? Id { get; set; }

View File

@@ -1,5 +1,8 @@
namespace ReC.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_PROFILE", Schema = "dbo")]
public class Profile
{
public long Id { get; set; }

View File

@@ -1,7 +1,9 @@
using ReC.Domain.Constants;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
[Table("TBREC_CFG_ACTION")]
public class RecAction
{
public long? Id { get; set; }

View File

@@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using ReC.Domain.Constants;
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
@@ -11,14 +11,17 @@ namespace ReC.Domain.Entities;
/// runtime mapping errors and ensures the application can handle schema changes without
/// requiring immediate code updates.
/// </summary>
[Table("VWREC_ACTION", Schema = "dbo")]
public class RecActionView
{
public required long Id { get; set; }
[ForeignKey("Id")]
public RecAction? Root { get; set; }
public long? ProfileId { get; set; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; set; }
public string? ProfileName { get; set; }
@@ -29,6 +32,9 @@ public class RecActionView
public long? EndpointId { get; set; }
[ForeignKey("EndpointId")]
public Endpoint? Endpoint { get; set; }
public string? EndpointUri { get; set; }
public long? EndpointAuthId { get; set; }
@@ -36,13 +42,13 @@ public class RecActionView
[ForeignKey("EndpointAuthId")]
public EndpointAuth? EndpointAuth { get; set; }
public string? EndpointAuthType { get; set; }
public EndpointAuthType? EndpointAuthType { get; set; }
public string? EndpointAuthApiKey { get; set; }
public string? EndpointAuthApiValue { get; set; }
public string? EndpointAuthApiKeyAddTo { get; set; }
public ApiKeyLocation? EndpointAuthApiKeyAddTo { get; set; }
public string? EndpointAuthToken { get; set; }
@@ -58,6 +64,9 @@ public class RecActionView
public short? SqlConnectionId { get; set; }
[ForeignKey("SqlConnectionId")]
public Connection? SqlConnection { get; set; }
public string? SqlConnectionServer { get; set; }
public string? SqlConnectionDb { get; set; }

View File

@@ -8,7 +8,7 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
{
public DbSet<EndpointParam> EndpointParams { get; set; }
public DbSet<RecActionView> Actions { get; set; }
public DbSet<RecActionView> RecActionViews { get; set; }
public DbSet<OutRes> OutRes { get; set; }