Compare commits
66 Commits
2e157656a7
...
refactor/r
| Author | SHA1 | Date | |
|---|---|---|---|
| 6681e56afc | |||
| d61f5ce885 | |||
| 6374a5c257 | |||
| 3b4954d387 | |||
| 42d586604e | |||
| 4088a52196 | |||
| 58b3c8ec95 | |||
| 68b3eb53c0 | |||
| 0d9489203f | |||
| 0a564d8aa8 | |||
| f5b2db0296 | |||
| 7a22024624 | |||
| c9cd92a55a | |||
| 93adaba322 | |||
| c16cb2a1c4 | |||
| c20162e051 | |||
| 70c2f7342d | |||
| a10f917084 | |||
| e1c3f74cd4 | |||
| e45aeea2b9 | |||
| 38f91aae84 | |||
| 9bb5c97df6 | |||
| d8c7499436 | |||
| 6d86760354 | |||
| 6b1daf77cb | |||
| d3d5ebac61 | |||
| b1924f2a4a | |||
| c27ed1e744 | |||
| 9eb54565cb | |||
| 05dfb6f424 | |||
| cf6c90ad05 | |||
| 4a9c4341c2 | |||
| ead12b6095 | |||
| 3c7fcb71c0 | |||
| 0b01b4a658 | |||
| 8d511ec81a | |||
| 685c5abca7 | |||
| b7aea848a9 | |||
| e5eb0f19e7 | |||
| 859ff5902e | |||
| 42789567f0 | |||
| 46eef255ca | |||
| aae42949b6 | |||
| bdf273c8e1 | |||
| ba8ab28b03 | |||
| 4cc8d22756 | |||
| 2a4378eb9a | |||
| cb5bbfb722 | |||
| 2736a78d4f | |||
| ddb8b2673e | |||
| a70aee6e28 | |||
| f329543793 | |||
| 645891150c | |||
| 95cb34394c | |||
| 83d6832236 | |||
| e816340755 | |||
| 64e8e2a5cc | |||
| 0edf2626a7 | |||
| 1d16276a8a | |||
| 4eae092031 | |||
| ce7fe03525 | |||
| a93780df5c | |||
| d7a2a01421 | |||
| 329e441ede | |||
| 1ad7ff3b34 | |||
| bcfbd851bd |
@@ -14,13 +14,18 @@ public class RecActionController(IMediator mediator) : ControllerBase
|
||||
/// Invokes a batch of RecActions for a given profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The identifier of the profile whose RecActions should be invoked.</param>
|
||||
/// <param name="references">Optional reference values that are passed through to all result records.</param>
|
||||
/// <param name="cancel">A token to cancel the operation.</param>
|
||||
/// <returns>An HTTP 202 Accepted response indicating the process has been started.</returns>
|
||||
[HttpPost("invoke/{profileId}")]
|
||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||
public async Task<IActionResult> Invoke([FromRoute] long profileId, CancellationToken cancel)
|
||||
public async Task<IActionResult> Invoke([FromRoute] long profileId, [FromBody] InvokeReferences references, CancellationToken cancel = default)
|
||||
{
|
||||
await mediator.Send(new InvokeBatchRecActionViewsCommand { ProfileId = profileId }, cancel);
|
||||
await mediator.Send(new InvokeBatchRecActionViewsCommand
|
||||
{
|
||||
ProfileId = profileId,
|
||||
References = references
|
||||
}, cancel);
|
||||
return Accepted();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace ReC.API.Models;
|
||||
|
||||
public class FakeRequest
|
||||
{
|
||||
public Dictionary<string, object>? Body { get; init; }
|
||||
|
||||
public Dictionary<string, object>? Header { get; init; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace ReC.API.Models;
|
||||
|
||||
public enum ResultType
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the full result object.
|
||||
/// </summary>
|
||||
Full,
|
||||
/// <summary>
|
||||
/// Return only the header part of the result.
|
||||
/// </summary>
|
||||
OnlyHeader,
|
||||
/// <summary>
|
||||
/// Return only the body part of the result.
|
||||
/// </summary>
|
||||
OnlyBody
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>420218ad-3c27-4003-9a84-36c92352f175</ProjectGuid>
|
||||
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\ReC\API\$(Version)\Rec.API.zip</DesktopBuildPackageLocation>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\ReC\API\$(Version)\Rec.API.zip</DesktopBuildPackageLocation>
|
||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||
<DeployIisAppPath>Rec.API</DeployIisAppPath>
|
||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
<Product>ReC.API</Product>
|
||||
<PackageIcon>Assets\icon.ico</PackageIcon>
|
||||
<PackageTags>digital data rest-caller rec api</PackageTags>
|
||||
<Version>2.1.0-beta</Version>
|
||||
<AssemblyVersion>2.1.0.0</AssemblyVersion>
|
||||
<FileVersion>2.1.0.0</FileVersion>
|
||||
<InformationalVersion>2.1.0-beta</InformationalVersion>
|
||||
<Version>2.2.0-beta</Version>
|
||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.2.0.0</FileVersion>
|
||||
<InformationalVersion>2.2.0-beta</InformationalVersion>
|
||||
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
|
||||
@@ -23,10 +23,11 @@ public class PostprocessingBehavior(IRecDbContext context, ISender sender) : IPi
|
||||
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = RecStatus.QuerySuccess,
|
||||
Status = RecStatus.OK,
|
||||
ActionId = request.Action.Id,
|
||||
Info = info,
|
||||
Type = ResultType.Post
|
||||
InfoDetail = info,
|
||||
Type = ResultType.Post,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
}
|
||||
}
|
||||
@@ -36,10 +37,11 @@ public class PostprocessingBehavior(IRecDbContext context, ISender sender) : IPi
|
||||
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = RecStatus.Failed,
|
||||
Status = RecStatus.Error,
|
||||
ActionId = request.Action.Id,
|
||||
Error = error,
|
||||
Type = ResultType.Post
|
||||
Type = ResultType.Post,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
|
||||
if (request.Action.ErrorAction == ErrorAction.Stop)
|
||||
|
||||
@@ -20,10 +20,11 @@ public class PreprocessingBehavior(IRecDbContext context, ISender sender) : IPip
|
||||
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = RecStatus.QuerySuccess,
|
||||
Status = RecStatus.OK,
|
||||
ActionId = request.Action.Id,
|
||||
Info = JsonSerializer.Serialize(result),
|
||||
Type = ResultType.Pre
|
||||
InfoDetail = JsonSerializer.Serialize(result),
|
||||
Type = ResultType.Pre,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
}
|
||||
}
|
||||
@@ -31,10 +32,11 @@ public class PreprocessingBehavior(IRecDbContext context, ISender sender) : IPip
|
||||
{
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = RecStatus.Failed,
|
||||
Status = RecStatus.Error,
|
||||
ActionId = request.Action.Id,
|
||||
Error = ex.ToString(),
|
||||
Type = ResultType.Pre
|
||||
Type = ResultType.Pre,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
|
||||
if (request.Action.ErrorAction == ErrorAction.Stop)
|
||||
|
||||
@@ -26,8 +26,12 @@ public record ResultViewDto
|
||||
|
||||
public string? Info { get; set; }
|
||||
|
||||
public string? InfoDetail { get; set; }
|
||||
|
||||
public string? Error { get; set; }
|
||||
|
||||
public string? BatchId { get; set; }
|
||||
|
||||
public string? AddedWho { get; init; }
|
||||
|
||||
public DateTime? AddedWhen { get; init; }
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
public record DeleteObjectProcedure : IRequest<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||
/// Target entity for the delete operation.
|
||||
/// </summary>
|
||||
public string Entity { get; set; } = null!;
|
||||
public required EntityType Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start GUID/ID (inclusive)
|
||||
|
||||
51
src/ReC.Application/Common/Procedures/EntityType.cs
Normal file
51
src/ReC.Application/Common/Procedures/EntityType.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace ReC.Application.Common.Procedures;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the target entity type for stored procedure operations (Insert, Update, Delete).
|
||||
/// </summary>
|
||||
public enum EntityType
|
||||
{
|
||||
/// <summary>
|
||||
/// A scheduled or configured action within a profile that invokes an endpoint (maps to TBREC_CFG_ACTION).
|
||||
/// </summary>
|
||||
Action,
|
||||
|
||||
/// <summary>
|
||||
/// A REST endpoint URI configuration (maps to TBREC_CFG_ENDPOINT).
|
||||
/// </summary>
|
||||
Endpoint,
|
||||
|
||||
/// <summary>
|
||||
/// Authentication credentials for an endpoint such as API key, Bearer token, or NTLM (maps to TBREC_CFG_ENDPOINT_AUTH).
|
||||
/// </summary>
|
||||
EndpointAuth,
|
||||
|
||||
/// <summary>
|
||||
/// Key-value parameters attached to an endpoint (maps to TBREC_CFG_ENDPOINT_PARAMS).
|
||||
/// </summary>
|
||||
EndpointParams,
|
||||
|
||||
/// <summary>
|
||||
/// A profile that groups one or more actions and defines execution settings (maps to TBREC_CFG_PROFILE).
|
||||
/// </summary>
|
||||
Profile,
|
||||
|
||||
/// <summary>
|
||||
/// The outcome of an action invocation including HTTP status, headers, body, and error details (maps to TBREC_OUT_RESULT).
|
||||
/// </summary>
|
||||
Result
|
||||
}
|
||||
|
||||
public static class EntityTypeExtensions
|
||||
{
|
||||
public static string ToDbString(this EntityType entityType) => entityType switch
|
||||
{
|
||||
EntityType.Action => "ACTION",
|
||||
EntityType.Endpoint => "ENDPOINT",
|
||||
EntityType.EndpointAuth => "ENDPOINT_AUTH",
|
||||
EntityType.EndpointParams => "ENDPOINT_PARAMS",
|
||||
EntityType.Profile => "PROFILE",
|
||||
EntityType.Result => "RESULT",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(entityType), $"Not expected entity type value: {entityType}")
|
||||
};
|
||||
}
|
||||
@@ -18,9 +18,9 @@ namespace ReC.Application.Common.Procedures.InsertProcedure;
|
||||
public record InsertObjectProcedure : IRequest<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||
/// Target entity for the insert operation.
|
||||
/// </summary>
|
||||
public string Entity { get; set; } = null!;
|
||||
public required EntityType Entity { get; set; }
|
||||
|
||||
//TODO: update to set in authentication middleware or similar, and remove from procedure properties
|
||||
internal string? AddedWho { get; private set; } = "ReC.API";
|
||||
@@ -76,12 +76,19 @@ public class InsertObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlE
|
||||
.Add("pPROFILE_LOG_LEVEL_ID", request.Profile?.LogLevelId, SqlDbType.TinyInt)
|
||||
.Add("pPROFILE_LANGUAGE_ID", request.Profile?.LanguageId, SqlDbType.SmallInt)
|
||||
.Add("pRESULT_ACTION_ID", request.Result?.ActionId)
|
||||
.Add("pRESULT_STATUS_ID", request.Result?.Status, SqlDbType.SmallInt)
|
||||
.Add("pRESULT_STATUS_ID", request.Result?.Status, SqlDbType.TinyInt)
|
||||
.Add("pRESULT_TYPE_ID", request.Result?.Type is not null ? (byte)request.Result.Type : null, SqlDbType.TinyInt)
|
||||
.Add("pRESULT_HEADER", request.Result?.Header)
|
||||
.Add("pRESULT_BODY", request.Result?.Body)
|
||||
.Add("pRESULT_INFO", request.Result?.Info)
|
||||
.Add("pRESULT_INFO_ID", request.Result?.Info, SqlDbType.SmallInt)
|
||||
.Add("pRESULT_INFO_DETAIL", request.Result?.InfoDetail)
|
||||
.Add("pRESULT_ERROR", request.Result?.Error)
|
||||
.Add("pRESULT_BATCH_ID", request.Result?.References?.BatchId)
|
||||
.Add("pRESULT_REFERENCE1", request.Result?.References?.Reference1)
|
||||
.Add("pRESULT_REFERENCE2", request.Result?.References?.Reference2)
|
||||
.Add("pRESULT_REFERENCE3", request.Result?.References?.Reference3)
|
||||
.Add("pRESULT_REFERENCE4", request.Result?.References?.Reference4)
|
||||
.Add("pRESULT_REFERENCE5", request.Result?.References?.Reference5)
|
||||
.Add("pENDPOINT_PARAMS_ACTIVE", request.EndpointParams?.Active)
|
||||
.Add("pENDPOINT_PARAMS_DESCRIPTION", request.EndpointParams?.Description)
|
||||
.Add("pENDPOINT_PARAMS_GROUP_ID", request.EndpointParams?.GroupId, SqlDbType.SmallInt)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
|
||||
@@ -19,6 +20,9 @@ internal sealed class StoredProcedureBuilder(string procedureName, string? retur
|
||||
_execSql.AppendLine($"{_separator}@{name} = @{name}");
|
||||
_separator = ',';
|
||||
|
||||
if (!dbType.HasValue && value is DateTime)
|
||||
dbType = SqlDbType.DateTime;
|
||||
|
||||
if (dbType.HasValue)
|
||||
_parameters.Add(new SqlParameter($"@{name}", dbType.Value) { Value = value });
|
||||
else
|
||||
@@ -27,6 +31,22 @@ internal sealed class StoredProcedureBuilder(string procedureName, string? retur
|
||||
return this;
|
||||
}
|
||||
|
||||
public StoredProcedureBuilder Add(string name, EntityType entityType)
|
||||
{
|
||||
var entityTypeStr = entityType switch
|
||||
{
|
||||
EntityType.Action => "ACTION",
|
||||
EntityType.Endpoint => "ENDPOINT",
|
||||
EntityType.EndpointAuth => "ENDPOINT_AUTH",
|
||||
EntityType.EndpointParams => "ENDPOINT_PARAMS",
|
||||
EntityType.Profile => "PROFILE",
|
||||
EntityType.Result => "RESULT",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(entityType), $"Not expected entity type value: {entityType}")
|
||||
};
|
||||
|
||||
return Add(name, entityTypeStr);
|
||||
}
|
||||
|
||||
public StoredProcedureBuilder AddOutput(string name, SqlDbType dbType)
|
||||
{
|
||||
_execSql.AppendLine($"{_separator}@{name} = @{name} OUTPUT");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.RecActions.Commands;
|
||||
|
||||
namespace ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
|
||||
public record UpdateResultDto
|
||||
{
|
||||
@@ -6,4 +8,8 @@ public record UpdateResultDto
|
||||
public short? StatusId { get; set; }
|
||||
public string? Header { get; set; }
|
||||
public string? Body { get; set; }
|
||||
public short? Info { get; set; }
|
||||
public string? InfoDetail { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public InvokeReferences? References { get; set; }
|
||||
}
|
||||
@@ -13,9 +13,9 @@ namespace ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
public record UpdateObjectProcedure : IRequest<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Target entity: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT
|
||||
/// Target entity for the update operation.
|
||||
/// </summary>
|
||||
public string Entity { get; set; } = null!;
|
||||
public required EntityType Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target GUID to update (required)
|
||||
@@ -86,9 +86,18 @@ public class UpdateObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlE
|
||||
.Add("pPROFILE_LAST_RUN", request.Profile.LastRun)
|
||||
.Add("pPROFILE_LAST_RESULT", request.Profile.LastResult)
|
||||
.Add("pRESULT_ACTION_ID", request.Result.ActionId)
|
||||
.Add("pRESULT_STATUS_ID", request.Result.StatusId, SqlDbType.SmallInt)
|
||||
.Add("pRESULT_STATUS_ID", request.Result.StatusId, SqlDbType.TinyInt)
|
||||
.Add("pRESULT_HEADER", request.Result.Header)
|
||||
.Add("pRESULT_BODY", request.Result.Body);
|
||||
.Add("pRESULT_BODY", request.Result.Body)
|
||||
.Add("pRESULT_INFO_ID", request.Result.Info, SqlDbType.SmallInt)
|
||||
.Add("pRESULT_INFO_DETAIL", request.Result.InfoDetail)
|
||||
.Add("pRESULT_ERROR", request.Result.Error)
|
||||
.Add("pRESULT_BATCH_ID", request.Result.References?.BatchId)
|
||||
.Add("pRESULT_REFERENCE1", request.Result.References?.Reference1)
|
||||
.Add("pRESULT_REFERENCE2", request.Result.References?.Reference2)
|
||||
.Add("pRESULT_REFERENCE3", request.Result.References?.Reference3)
|
||||
.Add("pRESULT_REFERENCE4", request.Result.References?.Reference4)
|
||||
.Add("pRESULT_REFERENCE5", request.Result.References?.Reference5);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class DeleteObjectProcedureValidator : AbstractValidator<DeleteObjectProcedure>
|
||||
{
|
||||
public DeleteObjectProcedureValidator()
|
||||
{
|
||||
RuleFor(x => x.Entity)
|
||||
.IsInEnum()
|
||||
.WithMessage("ENTITY must be a valid EntityType value.");
|
||||
|
||||
RuleFor(x => x.Start)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("Start GUID/ID must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.End)
|
||||
.GreaterThanOrEqualTo(x => x.Start)
|
||||
.WithMessage("End GUID/ID must be greater than or equal to Start GUID/ID.");
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,9 @@ public class InsertObjectProcedureValidator : AbstractValidator<InsertObjectProc
|
||||
{
|
||||
public InsertObjectProcedureValidator()
|
||||
{
|
||||
// ENTITY must be one of the allowed values
|
||||
RuleFor(x => x.Entity)
|
||||
.NotEmpty()
|
||||
.Must(e => e is "ACTION" or "ENDPOINT" or "ENDPOINT_AUTH" or "ENDPOINT_PARAMS" or "PROFILE" or "RESULT")
|
||||
.WithMessage("ENTITY must be one of: ACTION, ENDPOINT, ENDPOINT_AUTH, ENDPOINT_PARAMS, PROFILE, RESULT.");
|
||||
.IsInEnum()
|
||||
.WithMessage("ENTITY must be a valid EntityType value.");
|
||||
|
||||
// ACTION validation
|
||||
When(x => x.Action != null, () =>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.Results.Commands;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class InsertResultCommandValidator : AbstractValidator<InsertResultCommand>
|
||||
{
|
||||
public InsertResultCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ActionId)
|
||||
.NotNull()
|
||||
.WithMessage("ActionId is required.")
|
||||
.GreaterThan(0L)
|
||||
.When(x => x.ActionId.HasValue)
|
||||
.WithMessage("ActionId must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.References.BatchId)
|
||||
.NotEmpty()
|
||||
.WithMessage("BatchId is required.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using ReC.Application.RecActions.Commands;
|
||||
using ReC.Application.Results.Queries;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class InvokeBatchRecActionViewsCommandValidator : AbstractValidator<InvokeBatchRecActionViewsCommand>
|
||||
{
|
||||
public InvokeBatchRecActionViewsCommandValidator(ISender sender)
|
||||
{
|
||||
RuleFor(x => x.References.BatchId)
|
||||
.NotEmpty()
|
||||
.WithMessage("BatchId is required.")
|
||||
.MustAsync(async (batchId, cancel) =>
|
||||
{
|
||||
var any = await sender.Send(new AnyResultViewQuery(BatchId: batchId), cancel);
|
||||
return !any;
|
||||
})
|
||||
.WithMessage(x => $"Cannot invoke rec actions for batch '{x.References.BatchId}' because there are already results associated with it.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.Profile.Queries;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class ReadProfileViewQueryValidator : AbstractValidator<ReadProfileViewQuery>
|
||||
{
|
||||
public ReadProfileViewQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.Id)
|
||||
.GreaterThan(0)
|
||||
.When(x => x.Id.HasValue)
|
||||
.WithMessage("Id must be greater than 0.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.RecActions.Queries;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class ReadRecActionViewQueryValidator : AbstractValidator<ReadRecActionViewQuery>
|
||||
{
|
||||
public ReadRecActionViewQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.ProfileId)
|
||||
.GreaterThan(0)
|
||||
.When(x => x.ProfileId.HasValue)
|
||||
.WithMessage("ProfileId must be greater than 0.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.Results.Queries;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class ReadResultViewQueryValidator : AbstractValidator<ReadResultViewQuery>
|
||||
{
|
||||
public ReadResultViewQueryValidator()
|
||||
{
|
||||
RuleFor(x => x)
|
||||
.Must(x => x.Id.HasValue || x.ActionId.HasValue || x.ProfileId.HasValue || x.BatchId is not null)
|
||||
.WithMessage("At least one filter (Id, ActionId, ProfileId or BatchId) must be provided.");
|
||||
|
||||
RuleFor(x => x.Id)
|
||||
.GreaterThan(0)
|
||||
.When(x => x.Id.HasValue)
|
||||
.WithMessage("Id must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.ActionId)
|
||||
.GreaterThan(0)
|
||||
.When(x => x.ActionId.HasValue)
|
||||
.WithMessage("ActionId must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.ProfileId)
|
||||
.GreaterThan(0)
|
||||
.When(x => x.ProfileId.HasValue)
|
||||
.WithMessage("ProfileId must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.BatchId)
|
||||
.NotEmpty()
|
||||
.When(x => x.BatchId is not null)
|
||||
.WithMessage("BatchId must not be empty when provided.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using FluentValidation;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
|
||||
namespace ReC.Application.Common.Validations;
|
||||
|
||||
public class UpdateObjectProcedureValidator : AbstractValidator<UpdateObjectProcedure>
|
||||
{
|
||||
public UpdateObjectProcedureValidator()
|
||||
{
|
||||
RuleFor(x => x.Entity)
|
||||
.IsInEnum()
|
||||
.WithMessage("ENTITY must be a valid EntityType value.");
|
||||
|
||||
RuleFor(x => x.Id)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("Target GUID/ID must be greater than 0.");
|
||||
|
||||
RuleFor(x => x.ChangedWho)
|
||||
.MaximumLength(50)
|
||||
.When(x => x.ChangedWho != null);
|
||||
|
||||
When(x => x.Endpoint is { Description: not null }, () =>
|
||||
{
|
||||
RuleFor(x => x.Endpoint.Description)
|
||||
.MaximumLength(250);
|
||||
});
|
||||
|
||||
When(x => x.EndpointAuth is { Description: not null }, () =>
|
||||
{
|
||||
RuleFor(x => x.EndpointAuth.Description)
|
||||
.MaximumLength(250);
|
||||
});
|
||||
|
||||
When(x => x.Profile is { Name: not null }, () =>
|
||||
{
|
||||
RuleFor(x => x.Profile.Name)
|
||||
.MaximumLength(50);
|
||||
});
|
||||
|
||||
When(x => x.Profile is { Mandantor: not null }, () =>
|
||||
{
|
||||
RuleFor(x => x.Profile.Mandantor)
|
||||
.MaximumLength(50);
|
||||
});
|
||||
|
||||
When(x => x.Profile is { Description: not null }, () =>
|
||||
{
|
||||
RuleFor(x => x.Profile.Description)
|
||||
.MaximumLength(250);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointAuth.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteEndpointAuthProcedureHandler(ISender sender) : IRequestHandle
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_AUTH",
|
||||
Entity = EntityType.EndpointAuth,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointAuth.Commands;
|
||||
|
||||
@@ -24,7 +25,7 @@ public class InsertEndpointAuthProcedureHandler(ISender sender) : IRequestHandle
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_AUTH",
|
||||
Entity = EntityType.EndpointAuth,
|
||||
EndpointAuth = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointAuth.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateEndpointAuthProcedureHandler(ISender sender) : IRequestHandle
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_AUTH",
|
||||
Entity = EntityType.EndpointAuth,
|
||||
Id = request.Id,
|
||||
EndpointAuth = request.Data
|
||||
}, cancel);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointParams.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteEndpointParamsProcedureHandler(ISender sender) : IRequestHand
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_PARAMS",
|
||||
Entity = EntityType.EndpointParams,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointParams.Commands;
|
||||
|
||||
@@ -19,7 +20,7 @@ public class InsertEndpointParamsProcedureHandler(ISender sender) : IRequestHand
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_PARAMS",
|
||||
Entity = EntityType.EndpointParams,
|
||||
EndpointParams = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.EndpointParams.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateEndpointParamsProcedureHandler(ISender sender) : IRequestHand
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT_PARAMS",
|
||||
Entity = EntityType.EndpointParams,
|
||||
Id = request.Id,
|
||||
EndpointParams = request.Data
|
||||
}, cancel);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Endpoints.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteEndpointProcedureHandler(ISender sender) : IRequestHandler<De
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT",
|
||||
Entity = EntityType.Endpoint,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Endpoints.Commands;
|
||||
|
||||
@@ -16,7 +17,7 @@ public class InsertEndpointProcedureHandler(ISender sender) : IRequestHandler<In
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT",
|
||||
Entity = EntityType.Endpoint,
|
||||
Endpoint = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Endpoints.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateEndpointProcedureHandler(ISender sender) : IRequestHandler<Up
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "ENDPOINT",
|
||||
Entity = EntityType.Endpoint,
|
||||
Id = request.Id,
|
||||
Endpoint = request.Data
|
||||
}, cancel);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Profile.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteProfileProcedureHandler(ISender sender) : IRequestHandler<Del
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "PROFILE",
|
||||
Entity = EntityType.Profile,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Profile.Commands;
|
||||
|
||||
@@ -20,7 +21,7 @@ public class InsertProfileProcedureHandler(ISender sender) : IRequestHandler<Ins
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "PROFILE",
|
||||
Entity = EntityType.Profile,
|
||||
Profile = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Profile.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateProfileProcedureHandler(ISender sender) : IRequestHandler<Upd
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "PROFILE",
|
||||
Entity = EntityType.Profile,
|
||||
Id = request.Id,
|
||||
Profile = request.Data
|
||||
}, cancel);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.RecActions.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteActionProcedureHandler(ISender sender) : IRequestHandler<Dele
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "ACTION",
|
||||
Entity = EntityType.Action,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Domain.Constants;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.RecActions.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class InsertActionProcedureHandler(ISender sender) : IRequestHandler<Inse
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "ACTION",
|
||||
Entity = EntityType.Action,
|
||||
Action = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ReC.Application.RecActions.Commands;
|
||||
public record InvokeBatchRecActionViewsCommand : IRequest
|
||||
{
|
||||
public long ProfileId { get; init; }
|
||||
public required InvokeReferences References { get; init; }
|
||||
}
|
||||
|
||||
public class InvokeRecActionViewsCommandHandler(ISender sender, ILogger<InvokeRecActionViewsCommandHandler>? logger = null) : IRequestHandler<InvokeBatchRecActionViewsCommand>
|
||||
@@ -21,7 +22,11 @@ public class InvokeRecActionViewsCommandHandler(ISender sender, ILogger<InvokeRe
|
||||
{
|
||||
try
|
||||
{
|
||||
await sender.Send(new InvokeRecActionViewCommand() { Action = action }, cancel);
|
||||
await sender.Send(new InvokeRecActionViewCommand()
|
||||
{
|
||||
Action = action,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
}
|
||||
catch (RecActionException ex)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using ReC.Application.Common.Exceptions;
|
||||
using ReC.Application.Common.Options;
|
||||
using ReC.Application.Results.Commands;
|
||||
using ReC.Domain.Constants;
|
||||
using ReC.Domain.Views;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
@@ -17,6 +18,17 @@ namespace ReC.Application.RecActions.Commands;
|
||||
public record InvokeRecActionViewCommand : IRequest
|
||||
{
|
||||
public RecActionViewDto Action { get; set; } = null!;
|
||||
public required InvokeReferences References { get; set; }
|
||||
}
|
||||
|
||||
public record InvokeReferences
|
||||
{
|
||||
public required string BatchId { get; init; }
|
||||
public string? Reference1 { get; init; }
|
||||
public string? Reference2 { get; init; }
|
||||
public string? Reference3 { get; init; }
|
||||
public string? Reference4 { get; init; }
|
||||
public string? Reference5 { get; init; }
|
||||
}
|
||||
|
||||
public class InvokeRecActionViewCommandHandler(
|
||||
@@ -142,25 +154,26 @@ public class InvokeRecActionViewCommandHandler(
|
||||
var resBody = await response.Content.ReadAsStringAsync(cancel);
|
||||
var resHeaders = response.Headers.ToDictionary();
|
||||
|
||||
var statusCode = (short)response.StatusCode;
|
||||
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = response.StatusCode.ToRecStatus(),
|
||||
ActionId = action.Id,
|
||||
Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }),
|
||||
Body = resBody,
|
||||
Type = ResultType.Main
|
||||
Info = (short)response.StatusCode,
|
||||
Type = ResultType.Main,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
await sender.Send(new InsertResultCommand()
|
||||
{
|
||||
Status = RecStatus.Failed,
|
||||
Status = RecStatus.Error,
|
||||
ActionId = action.Id,
|
||||
Error = ex.ToString(),
|
||||
Type = ResultType.Main
|
||||
Type = ResultType.Main,
|
||||
References = request.References
|
||||
}, cancel);
|
||||
|
||||
if (action.ErrorAction == ErrorAction.Stop)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.RecActions.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateActionProcedureHandler(ISender sender) : IRequestHandler<Upda
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "ACTION",
|
||||
Entity = EntityType.Action,
|
||||
Id = request.Id,
|
||||
Action = request.Data
|
||||
}, cancel);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Results.Commands;
|
||||
|
||||
@@ -27,7 +28,7 @@ public class DeleteResultProcedureHandler(ISender sender) : IRequestHandler<Dele
|
||||
{
|
||||
return await sender.Send(new DeleteObjectProcedure
|
||||
{
|
||||
Entity = "RESULT",
|
||||
Entity = EntityType.Result,
|
||||
Start = request.Start,
|
||||
End = request.End,
|
||||
Force = request.Force
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using MediatR;
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.RecActions.Commands;
|
||||
using ReC.Domain.Constants;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Results.Commands;
|
||||
|
||||
@@ -10,9 +12,11 @@ public record InsertResultCommand : IInsertProcedure
|
||||
public required RecStatus Status { get; set; }
|
||||
public string? Header { get; set; }
|
||||
public string? Body { get; set; }
|
||||
public string? Info { get; set; }
|
||||
public short Info { get; set; }
|
||||
public string? InfoDetail { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public required ResultType Type { get; set; }
|
||||
public required InvokeReferences References { get; set; }
|
||||
}
|
||||
|
||||
public class InsertResultProcedureHandler(ISender sender) : IRequestHandler<InsertResultCommand, long>
|
||||
@@ -21,7 +25,7 @@ public class InsertResultProcedureHandler(ISender sender) : IRequestHandler<Inse
|
||||
{
|
||||
return await sender.Send(new InsertObjectProcedure
|
||||
{
|
||||
Entity = "RESULT",
|
||||
Entity = EntityType.Result,
|
||||
Result = request
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MediatR;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
using ReC.Application.Common.Procedures;
|
||||
|
||||
namespace ReC.Application.Results.Commands;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class UpdateResultProcedureHandler(ISender sender) : IRequestHandler<Upda
|
||||
{
|
||||
return await sender.Send(new UpdateObjectProcedure
|
||||
{
|
||||
Entity = "RESULT",
|
||||
Entity = EntityType.Result,
|
||||
Id = request.Id,
|
||||
Result = request.Data
|
||||
}, cancel);
|
||||
|
||||
35
src/ReC.Application/Results/Queries/AnyResultViewQuery.cs
Normal file
35
src/ReC.Application/Results/Queries/AnyResultViewQuery.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ReC.Domain.Views;
|
||||
|
||||
namespace ReC.Application.Results.Queries;
|
||||
|
||||
public record AnyResultViewQuery(
|
||||
long? Id = null,
|
||||
long? ActionId = null,
|
||||
long? ProfileId = null,
|
||||
string? BatchId = null
|
||||
) : IRequest<bool>;
|
||||
|
||||
public class AnyResultViewQueryHandler(IRepository<ResultView> repo) : IRequestHandler<AnyResultViewQuery, bool>
|
||||
{
|
||||
public Task<bool> Handle(AnyResultViewQuery request, CancellationToken cancel)
|
||||
{
|
||||
var q = repo.Query;
|
||||
|
||||
if(request.Id is long id)
|
||||
q = q.Where(rv => rv.Id == id);
|
||||
|
||||
if(request.ActionId is long actionId)
|
||||
q = q.Where(rv => rv.ActionId == actionId);
|
||||
|
||||
if(request.ProfileId is long profileId)
|
||||
q = q.Where(rv => rv.ProfileId == profileId);
|
||||
|
||||
if(request.BatchId is string batchId)
|
||||
q = q.Where(rv => rv.BatchId == batchId);
|
||||
|
||||
return q.AnyAsync(cancel);
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,13 @@ public record ReadResultViewQuery : IRequest<IEnumerable<ResultViewDto>>
|
||||
|
||||
public long? ProfileId { get; init; } = null;
|
||||
|
||||
public string? BatchId { get; init; } = null;
|
||||
|
||||
public bool IncludeAction { get; init; } = true;
|
||||
|
||||
public bool IncludeProfile { get; init; } = false;
|
||||
|
||||
public bool Last { get; init; } = false;
|
||||
public bool LastBatch { get; init; } = false;
|
||||
}
|
||||
|
||||
public class ReadResultViewQueryHandler(IRepository<ResultView> repo, IMapper mapper) : IRequestHandler<ReadResultViewQuery, IEnumerable<ResultViewDto>>
|
||||
@@ -39,13 +41,18 @@ public class ReadResultViewQueryHandler(IRepository<ResultView> repo, IMapper ma
|
||||
if(request.ProfileId is long profileId)
|
||||
q = q.Where(rv => rv.ProfileId == profileId);
|
||||
|
||||
if(request.IncludeAction)
|
||||
if(request.BatchId is string batchId)
|
||||
q = q.Where(rv => rv.BatchId == batchId);
|
||||
|
||||
if (request.IncludeAction)
|
||||
q = q.Include(rv => rv.Action);
|
||||
|
||||
if(request.IncludeProfile)
|
||||
q = q.Include(rv => rv.Profile);
|
||||
|
||||
var entities = request.Last ? [await q.OrderBy(rv => rv.AddedWhen).LastOrDefaultAsync(cancel)] : await q.ToListAsync(cancel);
|
||||
var entities = request.LastBatch
|
||||
? await GetLastBatchEntitiesAsync(q, cancel)
|
||||
: await q.ToListAsync(cancel);
|
||||
|
||||
if (entities.Count == 0)
|
||||
throw new NotFoundException($"No result views found for the given criteria. Criteria: {
|
||||
@@ -58,4 +65,20 @@ public class ReadResultViewQueryHandler(IRepository<ResultView> repo, IMapper ma
|
||||
|
||||
return mapper.Map<IEnumerable<ResultViewDto>>(entities);
|
||||
}
|
||||
|
||||
private static async Task<List<ResultView>> GetLastBatchEntitiesAsync(IQueryable<ResultView> q, CancellationToken cancel)
|
||||
{
|
||||
var lastBatchId = await q
|
||||
.Where(rv => rv.BatchId != null)
|
||||
.OrderByDescending(rv => rv.AddedWhen)
|
||||
.Select(rv => rv.BatchId)
|
||||
.FirstOrDefaultAsync(cancel);
|
||||
|
||||
if (lastBatchId is null)
|
||||
return [];
|
||||
|
||||
return await q
|
||||
.Where(rv => rv.BatchId == lastBatchId)
|
||||
.ToListAsync(cancel);
|
||||
}
|
||||
}
|
||||
50
src/ReC.Client/Api/InvokeReferences.cs
Normal file
50
src/ReC.Client/Api/InvokeReferences.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace ReC.Client.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional reference values that are passed through to all result records when invoking a profile.
|
||||
/// </summary>
|
||||
public class InvokeReferences
|
||||
{
|
||||
/// <summary>Batch identifier.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
BatchId { get; set; }
|
||||
|
||||
/// <summary>Reference value 1.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
Reference1 { get; set; }
|
||||
|
||||
/// <summary>Reference value 2.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
Reference2 { get; set; }
|
||||
|
||||
/// <summary>Reference value 3.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
Reference3 { get; set; }
|
||||
|
||||
/// <summary>Reference value 4.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
Reference4 { get; set; }
|
||||
|
||||
/// <summary>Reference value 5.</summary>
|
||||
public string
|
||||
#if NET
|
||||
?
|
||||
#endif
|
||||
Reference5 { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,28 @@ namespace ReC.Client.Api
|
||||
/// Invokes a ReC action for the specified profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile identifier.</param>
|
||||
/// <param name="references">Optional reference values to pass through to all result records.</param>
|
||||
/// <param name="cancellationToken">A token to cancel the operation.</param>
|
||||
/// <returns><see langword="true"/> if the request succeeds; otherwise, <see langword="false"/>.</returns>
|
||||
public async Task<bool> InvokeAsync(int profileId, CancellationToken cancellationToken = default)
|
||||
public async Task<bool> InvokeAsync(int profileId, InvokeReferences references, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var resp = await Http.PostAsync($"{ResourcePath}/invoke/{profileId}", content: null, cancellationToken);
|
||||
var content = references != null ? ReCClientHelpers.ToJsonContent(references) : null;
|
||||
var resp = await Http.PostAsync($"{ResourcePath}/invoke/{profileId}", content, cancellationToken);
|
||||
return resp.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a ReC action for the specified profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile identifier.</param>
|
||||
/// <param name="batchId">Batch identifier.</param>
|
||||
/// <param name="cancellationToken">A token to cancel the operation.</param>
|
||||
/// <returns><see langword="true"/> if the request succeeds; otherwise, <see langword="false"/>.</returns>
|
||||
public Task<bool> InvokeAsync(int profileId, string batchId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return InvokeAsync(profileId, new InvokeReferences() { BatchId = batchId }, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves Rec actions.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,431 +1,23 @@
|
||||
using ReC.Domain.Views;
|
||||
|
||||
namespace ReC.Domain.Constants;
|
||||
namespace ReC.Domain.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Represents status codes used to indicate the outcome of an operation.
|
||||
/// Represents the general outcome of an operation, independent of any specific technology or protocol.
|
||||
/// <para>
|
||||
/// Includes all standard HTTP status codes as defined in <see cref="System.Net.HttpStatusCode"/>,
|
||||
/// as well as custom non-HTTP status codes for internal operation results.
|
||||
/// Technology-specific details (e.g., HTTP status codes) are stored separately
|
||||
/// in the <c>RESULT_INFO</c> and <c>RESULT_INFO_DETAIL</c> fields.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <seealso cref="RecStatusExtensions"/>
|
||||
public enum RecStatus : short
|
||||
public enum RecStatus : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a SQL query executed successfully (value 0).
|
||||
/// Used as the result status when <see cref="RecActionView.PreprocessingQuery"/>
|
||||
/// or <see cref="RecActionView.PostprocessingQuery"/> completes without error.
|
||||
/// Indicates that the operation completed successfully (value 0).
|
||||
/// </summary>
|
||||
QuerySuccess = 0,
|
||||
OK = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an operation failed at any stage (value 999).
|
||||
/// This includes SQL query failures during preprocessing/postprocessing,
|
||||
/// HTTP request errors, or any other unhandled exception within the action pipeline.
|
||||
/// Indicates that the operation failed (value 1).
|
||||
/// When set, the <c>RESULT_ERROR</c> field should contain the error details.
|
||||
/// </summary>
|
||||
Failed = 999,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 100. System.Net.HttpStatusCode.Continue indicates that
|
||||
// the client can continue with its request.
|
||||
Continue = 100,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 101. System.Net.HttpStatusCode.SwitchingProtocols indicates
|
||||
// that the protocol version or protocol is being changed.
|
||||
SwitchingProtocols = 101,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 102. System.Net.HttpStatusCode.Processing indicates
|
||||
// that the server has accepted the complete request but hasn't completed it yet.
|
||||
Processing = 102,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 103. System.Net.HttpStatusCode.EarlyHints indicates
|
||||
// to the client that the server is likely to send a final response with the header
|
||||
// fields included in the informational response.
|
||||
EarlyHints = 103,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 200. System.Net.HttpStatusCode.OK indicates that the
|
||||
// request succeeded and that the requested information is in the response. This
|
||||
// is the most common status code to receive.
|
||||
OK = 200,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 201. System.Net.HttpStatusCode.Created indicates that
|
||||
// the request resulted in a new resource created before the response was sent.
|
||||
Created = 201,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 202. System.Net.HttpStatusCode.Accepted indicates that
|
||||
// the request has been accepted for further processing.
|
||||
Accepted = 202,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 203. System.Net.HttpStatusCode.NonAuthoritativeInformation
|
||||
// indicates that the returned meta information is from a cached copy instead of
|
||||
// the origin server and therefore may be incorrect.
|
||||
NonAuthoritativeInformation = 203,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 204. System.Net.HttpStatusCode.NoContent indicates
|
||||
// that the request has been successfully processed and that the response is intentionally
|
||||
// blank.
|
||||
NoContent = 204,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 205. System.Net.HttpStatusCode.ResetContent indicates
|
||||
// that the client should reset (not reload) the current resource.
|
||||
ResetContent = 205,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 206. System.Net.HttpStatusCode.PartialContent indicates
|
||||
// that the response is a partial response as requested by a GET request that includes
|
||||
// a byte range.
|
||||
PartialContent = 206,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 207. System.Net.HttpStatusCode.MultiStatus indicates
|
||||
// multiple status codes for a single response during a Web Distributed Authoring
|
||||
// and Versioning (WebDAV) operation. The response body contains XML that describes
|
||||
// the status codes.
|
||||
MultiStatus = 207,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 208. System.Net.HttpStatusCode.AlreadyReported indicates
|
||||
// that the members of a WebDAV binding have already been enumerated in a preceding
|
||||
// part of the multistatus response, and are not being included again.
|
||||
AlreadyReported = 208,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 226. System.Net.HttpStatusCode.IMUsed indicates that
|
||||
// the server has fulfilled a request for the resource, and the response is a representation
|
||||
// of the result of one or more instance-manipulations applied to the current instance.
|
||||
IMUsed = 226,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 300. System.Net.HttpStatusCode.Ambiguous indicates
|
||||
// that the requested information has multiple representations. The default action
|
||||
// is to treat this status as a redirect and follow the contents of the Location
|
||||
// header associated with this response. Ambiguous is a synonym for MultipleChoices.
|
||||
Ambiguous = 300,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 300. System.Net.HttpStatusCode.MultipleChoices indicates
|
||||
// that the requested information has multiple representations. The default action
|
||||
// is to treat this status as a redirect and follow the contents of the Location
|
||||
// header associated with this response. MultipleChoices is a synonym for Ambiguous.
|
||||
MultipleChoices = 300,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 301. System.Net.HttpStatusCode.Moved indicates that
|
||||
// the requested information has been moved to the URI specified in the Location
|
||||
// header. The default action when this status is received is to follow the Location
|
||||
// header associated with the response. When the original request method was POST,
|
||||
// the redirected request will use the GET method. Moved is a synonym for MovedPermanently.
|
||||
Moved = 301,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 301. System.Net.HttpStatusCode.MovedPermanently indicates
|
||||
// that the requested information has been moved to the URI specified in the Location
|
||||
// header. The default action when this status is received is to follow the Location
|
||||
// header associated with the response. MovedPermanently is a synonym for Moved.
|
||||
MovedPermanently = 301,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 302. System.Net.HttpStatusCode.Found indicates that
|
||||
// the requested information is located at the URI specified in the Location header.
|
||||
// The default action when this status is received is to follow the Location header
|
||||
// associated with the response. When the original request method was POST, the
|
||||
// redirected request will use the GET method. Found is a synonym for Redirect.
|
||||
Found = 302,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 302. System.Net.HttpStatusCode.Redirect indicates that
|
||||
// the requested information is located at the URI specified in the Location header.
|
||||
// The default action when this status is received is to follow the Location header
|
||||
// associated with the response. When the original request method was POST, the
|
||||
// redirected request will use the GET method. Redirect is a synonym for Found.
|
||||
Redirect = 302,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 303. System.Net.HttpStatusCode.RedirectMethod automatically
|
||||
// redirects the client to the URI specified in the Location header as the result
|
||||
// of a POST. The request to the resource specified by the Location header will
|
||||
// be made with a GET. RedirectMethod is a synonym for SeeOther.
|
||||
RedirectMethod = 303,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 303. System.Net.HttpStatusCode.SeeOther automatically
|
||||
// redirects the client to the URI specified in the Location header as the result
|
||||
// of a POST. The request to the resource specified by the Location header will
|
||||
// be made with a GET. SeeOther is a synonym for RedirectMethod.
|
||||
SeeOther = 303,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 304. System.Net.HttpStatusCode.NotModified indicates
|
||||
// that the client's cached copy is up to date. The contents of the resource are
|
||||
// not transferred.
|
||||
NotModified = 304,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 305. System.Net.HttpStatusCode.UseProxy indicates that
|
||||
// the request should use the proxy server at the URI specified in the Location
|
||||
// header.
|
||||
UseProxy = 305,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 306. System.Net.HttpStatusCode.Unused is a proposed
|
||||
// extension to the HTTP/1.1 specification that is not fully specified.
|
||||
Unused = 306,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 307. System.Net.HttpStatusCode.RedirectKeepVerb indicates
|
||||
// that the request information is located at the URI specified in the Location
|
||||
// header. The default action when this status is received is to follow the Location
|
||||
// header associated with the response. When the original request method was POST,
|
||||
// the redirected request will also use the POST method. RedirectKeepVerb is a synonym
|
||||
// for TemporaryRedirect.
|
||||
RedirectKeepVerb = 307,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 307. System.Net.HttpStatusCode.TemporaryRedirect indicates
|
||||
// that the request information is located at the URI specified in the Location
|
||||
// header. The default action when this status is received is to follow the Location
|
||||
// header associated with the response. When the original request method was POST,
|
||||
// the redirected request will also use the POST method. TemporaryRedirect is a
|
||||
// synonym for RedirectKeepVerb.
|
||||
TemporaryRedirect = 307,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 308. System.Net.HttpStatusCode.PermanentRedirect indicates
|
||||
// that the request information is located at the URI specified in the Location
|
||||
// header. The default action when this status is received is to follow the Location
|
||||
// header associated with the response. When the original request method was POST,
|
||||
// the redirected request will also use the POST method.
|
||||
PermanentRedirect = 308,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 400. System.Net.HttpStatusCode.BadRequest indicates
|
||||
// that the request could not be understood by the server. System.Net.HttpStatusCode.BadRequest
|
||||
// is sent when no other error is applicable, or if the exact error is unknown or
|
||||
// does not have its own error code.
|
||||
BadRequest = 400,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 401. System.Net.HttpStatusCode.Unauthorized indicates
|
||||
// that the requested resource requires authentication. The WWW-Authenticate header
|
||||
// contains the details of how to perform the authentication.
|
||||
Unauthorized = 401,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 402. System.Net.HttpStatusCode.PaymentRequired is reserved
|
||||
// for future use.
|
||||
PaymentRequired = 402,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 403. System.Net.HttpStatusCode.Forbidden indicates
|
||||
// that the server refuses to fulfill the request.
|
||||
Forbidden = 403,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 404. System.Net.HttpStatusCode.NotFound indicates that
|
||||
// the requested resource does not exist on the server.
|
||||
NotFound = 404,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 405. System.Net.HttpStatusCode.MethodNotAllowed indicates
|
||||
// that the request method (POST or GET) is not allowed on the requested resource.
|
||||
MethodNotAllowed = 405,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 406. System.Net.HttpStatusCode.NotAcceptable indicates
|
||||
// that the client has indicated with Accept headers that it will not accept any
|
||||
// of the available representations of the resource.
|
||||
NotAcceptable = 406,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 407. System.Net.HttpStatusCode.ProxyAuthenticationRequired
|
||||
// indicates that the requested proxy requires authentication. The Proxy-authenticate
|
||||
// header contains the details of how to perform the authentication.
|
||||
ProxyAuthenticationRequired = 407,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 408. System.Net.HttpStatusCode.RequestTimeout indicates
|
||||
// that the client did not send a request within the time the server was expecting
|
||||
// the request.
|
||||
RequestTimeout = 408,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 409. System.Net.HttpStatusCode.Conflict indicates that
|
||||
// the request could not be carried out because of a conflict on the server.
|
||||
Conflict = 409,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 410. System.Net.HttpStatusCode.Gone indicates that
|
||||
// the requested resource is no longer available.
|
||||
Gone = 410,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 411. System.Net.HttpStatusCode.LengthRequired indicates
|
||||
// that the required Content-length header is missing.
|
||||
LengthRequired = 411,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 412. System.Net.HttpStatusCode.PreconditionFailed indicates
|
||||
// that a condition set for this request failed, and the request cannot be carried
|
||||
// out. Conditions are set with conditional request headers like If-Match, If-None-Match,
|
||||
// or If-Unmodified-Since.
|
||||
PreconditionFailed = 412,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 413. System.Net.HttpStatusCode.RequestEntityTooLarge
|
||||
// indicates that the request is too large for the server to process.
|
||||
RequestEntityTooLarge = 413,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 414. System.Net.HttpStatusCode.RequestUriTooLong indicates
|
||||
// that the URI is too long.
|
||||
RequestUriTooLong = 414,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 415. System.Net.HttpStatusCode.UnsupportedMediaType
|
||||
// indicates that the request is an unsupported type.
|
||||
UnsupportedMediaType = 415,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 416. System.Net.HttpStatusCode.RequestedRangeNotSatisfiable
|
||||
// indicates that the range of data requested from the resource cannot be returned,
|
||||
// either because the beginning of the range is before the beginning of the resource,
|
||||
// or the end of the range is after the end of the resource.
|
||||
RequestedRangeNotSatisfiable = 416,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 417. System.Net.HttpStatusCode.ExpectationFailed indicates
|
||||
// that an expectation given in an Expect header could not be met by the server.
|
||||
ExpectationFailed = 417,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 421. System.Net.HttpStatusCode.MisdirectedRequest indicates
|
||||
// that the request was directed at a server that is not able to produce a response.
|
||||
MisdirectedRequest = 421,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 422. System.Net.HttpStatusCode.UnprocessableEntity
|
||||
// indicates that the request was well-formed but was unable to be followed due
|
||||
// to semantic errors. UnprocessableEntity is a synonym for UnprocessableContent.
|
||||
UnprocessableEntity = 422,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 422. System.Net.HttpStatusCode.UnprocessableContent
|
||||
// indicates that the request was well-formed but was unable to be followed due
|
||||
// to semantic errors. UnprocessableContent is a synonym for UnprocessableEntity.
|
||||
UnprocessableContent = 422,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 423. System.Net.HttpStatusCode.Locked indicates that
|
||||
// the source or destination resource is locked.
|
||||
Locked = 423,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 424. System.Net.HttpStatusCode.FailedDependency indicates
|
||||
// that the method couldn't be performed on the resource because the requested action
|
||||
// depended on another action and that action failed.
|
||||
FailedDependency = 424,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 426. System.Net.HttpStatusCode.UpgradeRequired indicates
|
||||
// that the client should switch to a different protocol such as TLS/1.0.
|
||||
UpgradeRequired = 426,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 428. System.Net.HttpStatusCode.PreconditionRequired
|
||||
// indicates that the server requires the request to be conditional.
|
||||
PreconditionRequired = 428,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 429. System.Net.HttpStatusCode.TooManyRequests indicates
|
||||
// that the user has sent too many requests in a given amount of time.
|
||||
TooManyRequests = 429,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 431. System.Net.HttpStatusCode.RequestHeaderFieldsTooLarge
|
||||
// indicates that the server is unwilling to process the request because its header
|
||||
// fields (either an individual header field or all the header fields collectively)
|
||||
// are too large.
|
||||
RequestHeaderFieldsTooLarge = 431,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 451. System.Net.HttpStatusCode.UnavailableForLegalReasons
|
||||
// indicates that the server is denying access to the resource as a consequence
|
||||
// of a legal demand.
|
||||
UnavailableForLegalReasons = 451,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 500. System.Net.HttpStatusCode.InternalServerError
|
||||
// indicates that a generic error has occurred on the server.
|
||||
InternalServerError = 500,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 501. System.Net.HttpStatusCode.NotImplemented indicates
|
||||
// that the server does not support the requested function.
|
||||
NotImplemented = 501,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 502. System.Net.HttpStatusCode.BadGateway indicates
|
||||
// that an intermediate proxy server received a bad response from another proxy
|
||||
// or the origin server.
|
||||
BadGateway = 502,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 503. System.Net.HttpStatusCode.ServiceUnavailable indicates
|
||||
// that the server is temporarily unavailable, usually due to high load or maintenance.
|
||||
ServiceUnavailable = 503,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 504. System.Net.HttpStatusCode.GatewayTimeout indicates
|
||||
// that an intermediate proxy server timed out while waiting for a response from
|
||||
// another proxy or the origin server.
|
||||
GatewayTimeout = 504,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 505. System.Net.HttpStatusCode.HttpVersionNotSupported
|
||||
// indicates that the requested HTTP version is not supported by the server.
|
||||
HttpVersionNotSupported = 505,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 506. System.Net.HttpStatusCode.VariantAlsoNegotiates
|
||||
// indicates that the chosen variant resource is configured to engage in transparent
|
||||
// content negotiation itself and, therefore, isn't a proper endpoint in the negotiation
|
||||
// process.
|
||||
VariantAlsoNegotiates = 506,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 507. System.Net.HttpStatusCode.InsufficientStorage
|
||||
// indicates that the server is unable to store the representation needed to complete
|
||||
// the request.
|
||||
InsufficientStorage = 507,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 508. System.Net.HttpStatusCode.LoopDetected indicates
|
||||
// that the server terminated an operation because it encountered an infinite loop
|
||||
// while processing a WebDAV request with "Depth: infinity". This status code is
|
||||
// meant for backward compatibility with clients not aware of the 208 status code
|
||||
// System.Net.HttpStatusCode.AlreadyReported appearing in multistatus response bodies.
|
||||
LoopDetected = 508,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 510. System.Net.HttpStatusCode.NotExtended indicates
|
||||
// that further extensions to the request are required for the server to fulfill
|
||||
// it.
|
||||
NotExtended = 510,
|
||||
//
|
||||
// Summary:
|
||||
// Equivalent to HTTP status 511. System.Net.HttpStatusCode.NetworkAuthenticationRequired
|
||||
// indicates that the client needs to authenticate to gain network access; it's
|
||||
// intended for use by intercepting proxies used to control access to the network.
|
||||
NetworkAuthenticationRequired = 511
|
||||
Error = 1
|
||||
}
|
||||
@@ -4,31 +4,13 @@ namespace ReC.Domain.Constants;
|
||||
|
||||
public static class RecStatusExtensions
|
||||
{
|
||||
public static HttpStatusCode? ToHttpStatusCode(this RecStatus status)
|
||||
{
|
||||
int code = (int)status;
|
||||
|
||||
if (Enum.IsDefined(typeof(HttpStatusCode), code))
|
||||
{
|
||||
return (HttpStatusCode)code;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsSuccess(this HttpStatusCode code)
|
||||
/// <summary>
|
||||
/// Converts an <see cref="HttpStatusCode"/> to a general <see cref="RecStatus"/>
|
||||
/// based on whether the HTTP status represents a success (2xx) or an error.
|
||||
/// </summary>
|
||||
public static RecStatus ToRecStatus(this HttpStatusCode code)
|
||||
{
|
||||
int value = (int)code;
|
||||
return value >= 200 && value <= 299;
|
||||
return value >= 200 && value <= 299 ? RecStatus.OK : RecStatus.Error;
|
||||
}
|
||||
|
||||
public static bool IsSuccess(this RecStatus status)
|
||||
=> status switch
|
||||
{
|
||||
RecStatus.QuerySuccess => true,
|
||||
RecStatus.Failed => false,
|
||||
_ => status.ToHttpStatusCode() is HttpStatusCode httpStatus && httpStatus.IsSuccess()
|
||||
};
|
||||
|
||||
public static RecStatus ToRecStatus(this HttpStatusCode code) => (RecStatus)(short)code;
|
||||
}
|
||||
@@ -33,6 +33,9 @@ public class RecActionView
|
||||
[Column("PROFILE_TYPE_ID")]
|
||||
public ProfileType? ProfileType { get; set; }
|
||||
|
||||
[Column("PROFILE_TYPE")]
|
||||
public string? ProfileTypeName { get; set; }
|
||||
|
||||
[Column("SEQUENCE")]
|
||||
public byte? Sequence { get; set; }
|
||||
|
||||
|
||||
@@ -42,12 +42,36 @@ public class ResultView
|
||||
[Column("RESULT_BODY")]
|
||||
public string? Body { get; set; }
|
||||
|
||||
[Column("RESULT_INFO_ID")]
|
||||
public short? InfoId { get; set; }
|
||||
|
||||
[Column("RESULT_INFO")]
|
||||
public string? Info { get; set; }
|
||||
|
||||
[Column("RESULT_INFO_DETAIL")]
|
||||
public string? InfoDetail { get; set; }
|
||||
|
||||
[Column("RESULT_ERROR")]
|
||||
public string? Error { get; set; }
|
||||
|
||||
[Column("BATCH_ID")]
|
||||
public string? BatchId { get; set; }
|
||||
|
||||
[Column("REFERENCE1")]
|
||||
public string? Reference1 { get; set; }
|
||||
|
||||
[Column("REFERENCE2")]
|
||||
public string? Reference2 { get; set; }
|
||||
|
||||
[Column("REFERENCE3")]
|
||||
public string? Reference3 { get; set; }
|
||||
|
||||
[Column("REFERENCE4")]
|
||||
public string? Reference4 { get; set; }
|
||||
|
||||
[Column("REFERENCE5")]
|
||||
public string? Reference5 { get; set; }
|
||||
|
||||
[Column("ADDED_WHO")]
|
||||
public string? AddedWho { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using ReC.Application.RecActions.Commands;
|
||||
using ReC.Application.Results.Queries;
|
||||
|
||||
namespace ReC.Tests.Application.RecActions;
|
||||
|
||||
[TestFixture]
|
||||
public class InvokeBatchDuplicateGuardTests : RecApplicationTestBase
|
||||
{
|
||||
private const long ProfileId = 3;
|
||||
|
||||
private (ISender Sender, IServiceScope Scope) CreateScopedSender()
|
||||
{
|
||||
var scope = ServiceProvider.CreateScope();
|
||||
var sender = scope.ServiceProvider.GetRequiredService<ISender>();
|
||||
return (sender, scope);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Invoke_with_existing_batchId_throws_ValidationException()
|
||||
{
|
||||
var (sender, scope) = CreateScopedSender();
|
||||
using var _ = scope;
|
||||
|
||||
// Arrange: read an existing result to get a real BatchId from the database
|
||||
var results = await sender.Send(new ReadResultViewQuery
|
||||
{
|
||||
ProfileId = ProfileId,
|
||||
IncludeAction = false,
|
||||
LastBatch = true
|
||||
});
|
||||
|
||||
var existingBatchId = results.FirstOrDefault()?.BatchId;
|
||||
Assert.That(existingBatchId, Is.Not.Null.And.Not.Empty,
|
||||
$"No results with a BatchId found for ProfileId {ProfileId}. Ensure test data exists in the database.");
|
||||
|
||||
// Act & Assert: invoking with the same BatchId should throw ValidationException
|
||||
var ex = Assert.ThrowsAsync<ValidationException>(async () =>
|
||||
await sender.Send(new InvokeBatchRecActionViewsCommand
|
||||
{
|
||||
ProfileId = ProfileId,
|
||||
References = new InvokeReferences
|
||||
{
|
||||
BatchId = existingBatchId!
|
||||
}
|
||||
}));
|
||||
|
||||
Assert.That(ex!.Errors.Any(e => e.PropertyName.Contains("BatchId")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Invoke_with_new_batchId_does_not_throw_duplicate_guard()
|
||||
{
|
||||
var (sender, scope) = CreateScopedSender();
|
||||
using var _ = scope;
|
||||
|
||||
var uniqueBatchId = $"test-{System.Guid.NewGuid():N}";
|
||||
|
||||
// This should NOT throw ValidationException for duplicate BatchId.
|
||||
// It may throw other exceptions (e.g., no actions found, endpoint errors),
|
||||
// but the duplicate guard should pass.
|
||||
try
|
||||
{
|
||||
sender.Send(new InvokeBatchRecActionViewsCommand
|
||||
{
|
||||
ProfileId = ProfileId,
|
||||
References = new InvokeReferences
|
||||
{
|
||||
BatchId = uniqueBatchId
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (ValidationException valEx) when (valEx.Errors.Any(e => e.PropertyName.Contains("BatchId")))
|
||||
{
|
||||
Assert.Fail("Duplicate guard should not trigger for a unique BatchId.");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Other exceptions (endpoint errors, etc.) are acceptable
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using ReC.Application.Common.Procedures.DeleteProcedure;
|
||||
using ReC.Application.Common.Procedures.InsertProcedure;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure;
|
||||
using ReC.Application.RecActions.Commands;
|
||||
using ReC.Application.Results.Commands;
|
||||
using ReC.Domain.Constants;
|
||||
using ReC.Tests.Application;
|
||||
@@ -25,7 +26,7 @@ public class ResultProcedureTests : RecApplicationTestBase
|
||||
[Test]
|
||||
public async Task InsertResultProcedure_runs_via_mediator()
|
||||
{
|
||||
var procedure = new InsertResultCommand { ActionId = 1, Status = HttpStatusCode.OK.ToRecStatus(), Header = "h", Body = "b", Type = Domain.Constants.ResultType.Main };
|
||||
var procedure = new InsertResultCommand { ActionId = 1, Status = HttpStatusCode.OK.ToRecStatus(), Header = "h", Body = "b", Info = 200, Type = ResultType.Main, References = new () { BatchId = DateTime.Now.ToString() } };
|
||||
|
||||
var (sender, scope) = CreateScopedSender();
|
||||
using var _ = scope;
|
||||
|
||||
Reference in New Issue
Block a user