Compare commits

...

45 Commits

Author SHA1 Message Date
39fcee2b50 Add ResultViewController for output result retrieval
Introduce ResultViewController with endpoints to fetch output results by query, for fake/test profiles, and by action ID. Supports returning full, header, or body parts of results via a new ViewResultType enum. Utilizes MediatR and configuration injection for flexible and testable result access. Includes XML docs for improved API clarity.
2025-12-16 12:51:17 +01:00
d0597e28e8 Mark OutResController as obsolete with deprecation notice
Added [Obsolete] attribute to OutResController, indicating it is deprecated and will be removed in future versions. Developers are advised to use ResultViewController instead.
2025-12-15 17:13:05 +01:00
d8d77652ac Implement ReadResultViewQuery handler with filtering
Added ReadResultViewQueryHandler to support querying ResultView entities with optional filters (Id, ActionId, ProfileId). Integrated repository and AutoMapper, included error handling for no results, and improved using directives.
2025-12-15 17:11:23 +01:00
62612897bd Add ReadResultViewQuery and clean up DTO namespace
Introduced ReadResultViewQuery for querying result views with optional filters (Id, ActionId, ProfileId). Cleaned up ResultViewDto.cs by removing an unused using directive and reformatting the namespace declaration. Added necessary usings to support the new query.
2025-12-15 17:05:49 +01:00
99c50fb348 Make ResultViewDto immutable; add Root property
Refactored ResultViewDto to use init-only properties for immutability. Added a new Root property of type OutResDto?. Included a using directive for ReC.Domain.Entities to support the new property.
2025-12-15 17:02:11 +01:00
8aaf11f39d Add Root navigation property to ResultView with FK
Added OutRes? Root navigation property to ResultView, annotated with [ForeignKey("Id")], to establish an optional relationship to the OutRes entity. Also added the necessary using directive for DataAnnotations.Schema.
2025-12-15 17:01:35 +01:00
152189fefd Refactor: Rename RecResultViewDto to ResultViewDto
Replaced all usage of RecResultViewDto with ResultViewDto for improved naming consistency. Updated AutoMapper profile to map ResultView to ResultViewDto. Removed RecResultViewDto.cs and added ResultViewDto.cs with the same properties. No functional changes.
2025-12-15 16:57:05 +01:00
5a4b8427be Rename RecResultView entity to ResultView throughout codebase
Replaced RecResultView with ResultView, updating all references in DbContext, entity mapping, and AutoMapper profiles. Added the new ResultView class and removed the old RecResultView class. No changes to properties or structure.
2025-12-15 16:56:37 +01:00
e4a644a636 Add mappings for RecResultView and ProfileView DTOs
Added AutoMapper profile mappings for RecResultView to RecResultViewDto and ProfileView to ProfileViewDto to enable automatic entity-to-DTO conversions.
2025-12-15 16:55:45 +01:00
25c6c41b26 Add RecResultViewDto for result view data representation
Introduced RecResultViewDto in the ReC.Application.Common.Dto namespace. This DTO encapsulates properties for result entity view data, including related action and profile details, status, content, and audit information.
2025-12-15 16:53:25 +01:00
c672a10c97 Add navigation properties to RecResultView class
Added Action and Profile navigation properties to RecResultView, enabling direct access to related RecActionView and ProfileView data for improved object relationships and easier data retrieval in views or APIs.
2025-12-15 16:45:46 +01:00
1e21218f31 Make RecResultView properties nullable for flexibility
Changed ActionId, ProfileId, StatusCode, AddedWho, and AddedWhen
to nullable types in RecResultView to better support optional or
missing data scenarios. This improves compatibility with cases
where these fields may not always have values.
2025-12-15 16:43:39 +01:00
68e7ee54f9 Rename StatusId/Status to StatusCode/StatusName for clarity
Renamed the StatusId property to StatusCode and Status to StatusName in RecResultView. Updated RecDbContext mappings accordingly to maintain consistency and improve code clarity.
2025-12-15 16:42:01 +01:00
2a749267b3 Rename ResultHeader/ResultBody to Header/Body in RecResultView
Renamed the ResultHeader and ResultBody properties in the RecResultView class to Header and Body. Updated the RecDbContext entity configuration to map these new property names to the existing RESULT_HEADER and RESULT_BODY database columns. No changes were made to the database schema.
2025-12-15 16:35:15 +01:00
edc1de2034 Add RecResultView entity and DbSets to RecDbContext
Added DbSet properties for RecResultView and OutRes to RecDbContext. Configured entity mapping for RecResultView to the VWREC_RESULT view, including key and property-to-column mappings. This enables querying RecResultView and OutRes through the context.
2025-12-15 16:33:57 +01:00
ae79a60605 Add RecResultView entity for recommendation results
Introduced the RecResultView class in the ReC.Domain.Entities namespace. This class encapsulates properties related to recommendation results, including identifiers, status, result details, and audit metadata such as who added or changed the result and when.
2025-12-15 16:33:41 +01:00
6b8286a386 Move logging config to appsettings.Logging.json
Extracted "Logging" and "NLog" sections from appsettings.json into a new appsettings.Logging.json file. No changes were made to the logging settings themselves; this improves configuration separation and maintainability.
2025-12-15 16:15:01 +01:00
1fabc29e4f Auto-load appsettings.*.json files at startup
Dynamically adds all appsettings.*.json files in the root directory to the configuration, excluding appsettings.Development.json and appsettings.migration.json. This streamlines configuration management by automatically including relevant environment or custom config files.
2025-12-15 15:51:31 +01:00
56fb34d987 Update Status property mapping to STATUS_ID column
Changed the database column mapping for the Status property from "STATUS" to "STATUS_ID" to ensure correct association with the updated schema.
2025-12-15 15:31:30 +01:00
47ddde239e Update ProfileType mapping to PROFILE_TYPE_ID column
Changed the entity configuration to map the ProfileType property to the "PROFILE_TYPE_ID" column instead of "PROFILE_TYPE" for consistency with database schema.
2025-12-15 15:23:32 +01:00
5a4d2d8553 Update profile type fields to use ProfileType enum
Replaced byte/byte? with the strongly-typed ProfileType/ProfileType? enum for profile type fields in ProfileView and RecActionView. Added the necessary using directive for ReC.Domain.Constants. This improves type safety and code readability.
2025-12-15 15:07:47 +01:00
4e209e29fc Refactor ReadRecActionViewQuery for optional ProfileId
Refactored ReadRecActionViewQuery to remove base class inheritance and make ProfileId optional. Simplified query construction and filtering logic in the handler to conditionally filter by ProfileId only when provided. Removed unused constructors and methods for a cleaner API.
2025-12-15 15:07:26 +01:00
78aaea67e6 Refactor InvokeBatchRecActionViewsCommand structure
Decouple InvokeBatchRecActionViewsCommand from ReadRecActionQueryBase, making it a plain IRequest with an explicit ProfileId property. Update the extension and handler to use the new structure, improving clarity and separation of concerns.
2025-12-15 15:06:46 +01:00
98261f4e21 Refactor ProfileType to enum in RecActionViewDto
Changed ProfileType from string? to ProfileType? enum for improved type safety. Updated URI scheme assignment to use ToUriBuilderScheme(), centralizing scheme mapping logic.
2025-12-15 15:05:56 +01:00
aab8174500 Add ProfileType enum and extension for URI scheme
Introduced the ProfileType enum with Http and Https values in the ReC.Domain.Constants namespace. Added ProfileTypeExtensions with a ToUriBuilderScheme method to convert enum values to their lowercase string representations for URI building.
2025-12-15 15:05:10 +01:00
fb6d6af12b Change ProfileType to byte? in RecActionView
Updated the ProfileType property in RecActionView from string? to byte? to improve type safety and performance by using a numeric value instead of a string.
2025-12-15 14:37:57 +01:00
f82f4d2c65 Refactor RestType enum: remove Invalid, use byte
RestType now inherits from byte for efficiency. The Invalid value has been removed from the enum, and the IsValid extension method was updated to reflect this change by only checking for None.
2025-12-15 14:35:26 +01:00
90ee3f6a5d Explicitly set ErrorAction enum underlying type to byte
Changed ErrorAction enum declaration to use byte as its underlying type for improved memory efficiency and clarity.
2025-12-15 14:28:13 +01:00
a24ec1ab3e Set ApiKeyLocation enum underlying type to byte
Changed ApiKeyLocation enum to use byte as its underlying type instead of the default int, reducing memory usage and improving clarity for serialization scenarios.
2025-12-15 14:27:45 +01:00
b8c30d520e Set EndpointAuthType enum underlying type to byte
Explicitly define EndpointAuthType as a byte-based enum for improved memory efficiency and better interoperability with systems requiring a specific underlying type.
2025-12-15 14:22:13 +01:00
f69f323542 Make ProfileView string properties nullable
Changed several ProfileView properties (Type, Mandantor, ProfileName, LogLevel, Language, AddedWho) from non-nullable strings with default values to nullable strings. This allows these fields to be null instead of defaulting to an empty string.
2025-12-15 14:03:15 +01:00
3621820060 Refactor RecActionView Profile property; add ProfileViewDto
Changed RecActionView.Profile to use ProfileView type instead of Profile entity. Added new ProfileViewDto class to represent profile data as a DTO, including related properties and audit fields.
2025-12-15 14:01:11 +01:00
0e0f27c124 Rename ProfileGuid to Id in ProfileView and DbContext
Renamed the ProfileGuid property to Id in the ProfileView record.
Updated all DbContext mappings and primary key definitions to use
Id, while maintaining the mapping to the PROFILE_GUID column in
the database view.
2025-12-15 13:51:16 +01:00
5404530785 Add ProfileView entity mapped to VWREC_PROFILE view
Introduced the ProfileView entity as a record to represent data from the VWREC_PROFILE database view. Registered ProfileView in RecDbContext and configured its property mappings and primary key in OnModelCreating. This enables querying profile data via EF Core.
2025-12-15 13:51:00 +01:00
289b6109e4 Refactor DeleteRecActionsCommand to use RecAction entity
Switched from using RecActionDto to RecAction entity in the
DeleteRecActionsCommandHandler and updated repository types
and using statements accordingly. This aligns the handler
with domain-driven design and improves consistency.
2025-12-15 13:24:11 +01:00
679c065aaa Refactor CreateRecAction to use domain entities
Switched from using RecActionDto to RecAction domain entity in the CreateRecActionCommandHandler and updated relevant imports. This aligns the command handler with domain-driven design principles.
2025-12-15 13:23:56 +01:00
a02cac8778 Refactor handler to use AutoMapper and entity repository
Updated ObtainEndpointCommandHandler to depend on IRepository<Endpoint> and IMapper. Now maps Endpoint entities to EndpointDto using AutoMapper before returning, ensuring proper separation of concerns and DTO exposure.
2025-12-15 13:08:21 +01:00
42fd176fad Refactor RestType null check with pattern matching
Refactored the null check for action.RestType to use pattern matching, assigning it to a local variable restType if not null. Now throws DataIntegrityException if RestType is null, and uses restType for creating the HttpRequestMessage. This improves code clarity and ensures RestType is non-null in subsequent logic.
2025-12-15 12:53:43 +01:00
e529027587 Refactor HTTP method handling to use RestType enum
Replaced string-based HTTP method mapping with a strongly-typed RestType enum for improved type safety. Updated the ToHttpMethod extension to accept RestType, validate its value, and convert it to the appropriate HttpMethod. This reduces errors from invalid or misspelled method names.
2025-12-15 12:48:10 +01:00
cc2adab5e5 Rename ToHttpMethod to ToHttpMethodName in RestTypeExtensions
Renamed the extension method ToHttpMethod to ToHttpMethodName in the RestTypeExtensions class for improved clarity. The method's functionality remains unchanged; it still returns the uppercase string representation of the RestType enum value.
2025-12-15 12:47:59 +01:00
a0233fd876 Update RecActionViewDto: new props, type changes, renames
Renamed ProfileSequence to Sequence. Added display name properties for enums/types (e.g., EndpointAuthTypeName, RestTypeName, ErrorActionName). Added EndpointAuthApiKey. Changed RestType to enum and ErrorAction to nullable, with corresponding name properties.
2025-12-15 12:37:33 +01:00
0afe9870c0 Update RecActionView model and mapping for enums and names
Refactored RecActionView to use enum types for EndpointAuthType, ApiKeyLocation, and RestType, and added properties for their display names. Introduced ErrorAction and its display name. Updated RecDbContext mapping to treat RecActionView as a view with a key, map new enum ID and name fields, and align property mappings with the database view structure. This enhances clarity and supports both ID and human-readable values in the model.
2025-12-15 12:01:39 +01:00
784b4b1f05 Refactor to use DTOs instead of domain entities
Replaced references to ReC.Domain.Entities with ReC.Application.Common.Dto across multiple files to use DTOs in the application layer. Updated MappingProfile.cs namespace to ReC.Application.RecActionViews and adjusted using statements accordingly. These changes improve separation of concerns and ensure commands and mappings operate on DTOs rather than domain entities.
2025-12-15 11:58:43 +01:00
1634b4b7b1 Add RestType enum and extension methods for HTTP verbs
Introduced RestType enum in ReC.Domain.Constants to represent HTTP methods, including support for None and Invalid values. Added RestTypeExtensions with ToHttpMethod and IsValid methods to facilitate HTTP method handling and validation.
2025-12-15 11:40:44 +01:00
576b2d59d9 Update IRecDbContext to use domain model DbSet types
Changed DbSet properties in IRecDbContext from DTO types to their corresponding domain model classes for Connections, Endpoints, EndpointAuths, Profiles, and RecActions. This aligns the interface with the domain model and removes reliance on DTOs.
2025-12-15 11:30:48 +01:00
29 changed files with 494 additions and 129 deletions

View File

@@ -6,6 +6,7 @@ using ReC.Application.OutResults.Queries;
namespace ReC.API.Controllers;
[Obsolete("This controller is deprecated and will be removed in future versions. Use the new ResultViewController instead.")]
[Route("api/[controller]")]
[ApiController]
public class OutResController(IMediator mediator, IConfiguration config) : ControllerBase

View File

@@ -0,0 +1,40 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using ReC.API.Extensions;
using ReC.Application.ResultViews.Queries;
namespace ReC.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ResultViewController(IMediator mediator, IConfiguration config) : ControllerBase
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Get([FromQuery] ReadResultViewQuery query, CancellationToken cancel) => Ok(await mediator.Send(query, cancel));
[HttpGet("fake")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Get(CancellationToken cancel) => Ok(await mediator.Send(new ReadResultViewQuery()
{
ProfileId = config.GetFakeProfileId()
}, cancel));
[HttpGet("fake/{actionId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Get([FromRoute] long actionId, CancellationToken cancel, ResultType resultType = ResultType.Full)
{
var res = (await mediator.Send(new ReadResultViewQuery()
{
ProfileId = config.GetFakeProfileId(),
ActionId = actionId
}, cancel)).First();
return resultType switch
{
ResultType.Body => res.Body is null ? Ok(new object { }) : Ok(res.Body.JsonToDynamic()),
ResultType.Header => res.Header is null ? Ok(new object { }) : Ok(res.Header.JsonToDynamic()),
_ => Ok(res),
};
}
}

View File

@@ -25,6 +25,13 @@ try
var config = builder.Configuration;
Directory
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
.Where(file => Path.GetFileName(file) != $"appsettings.migration.json")
.ToList()
.ForEach(file => config.AddJsonFile(file, true, true));
// Add services to the container.
builder.Services.AddRecServices(options =>
{

View File

@@ -0,0 +1,59 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\Rec.API",
"logFileNamePrefix": "${shortdate}.Rec.API"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"warningLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
"rules": [
{
"logger": "*",
"level": "Info",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Warn",
"writeTo": "warningLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
}
}

View File

@@ -1,10 +1,4 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"UseSwagger": true,
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL19\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
@@ -15,56 +9,5 @@
"MaxConcurrentInvocations": 5
},
"AddedWho": "ReC.API",
"FakeProfileId": 2,
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\Rec.API",
"logFileNamePrefix": "${shortdate}.Rec.API"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"warningLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
"rules": [
{
"logger": "*",
"level": "Info",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Warn",
"writeTo": "warningLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
}
"FakeProfileId": 2
}

View File

@@ -16,5 +16,8 @@ public class DtoMappingProfile : AutoMapper.Profile
CreateMap<EndpointParam, EndpointParamDto>();
CreateMap<Profile, ProfileDto>();
CreateMap<RecAction, RecActionDto>();
CreateMap<ResultView, ResultViewDto>();
CreateMap<ProfileView, ProfileViewDto>();
}
}

View File

@@ -0,0 +1,42 @@
namespace ReC.Application.Common.Dto;
public class ProfileViewDto
{
public long Id { get; init; }
public IEnumerable<RecActionViewDto>? RecActions { get; set; }
public bool Active { get; init; }
public byte TypeId { get; init; }
public string? Type { get; init; }
public string? Mandantor { get; init; }
public string? ProfileName { get; init; }
public string? Description { get; init; }
public byte LogLevelId { get; init; }
public string? LogLevel { get; init; }
public short LanguageId { get; init; }
public string? Language { get; init; }
public string? AddedWho { get; init; }
public DateTime AddedWhen { get; init; }
public string? ChangedWho { get; init; }
public DateTime? ChangedWhen { get; init; }
public DateTime? FirstRun { get; init; }
public DateTime? LastRun { get; init; }
public string? LastResult { get; init; }
}

View File

@@ -10,9 +10,9 @@ public record RecActionViewDto
public string? ProfileName { get; init; }
public string? ProfileType { get; init; }
public ProfileType? ProfileType { get; init; }
public byte? ProfileSequence { get; init; }
public byte? Sequence { get; init; }
public long? EndpointId { get; init; }
@@ -22,12 +22,16 @@ public record RecActionViewDto
public EndpointAuthType? EndpointAuthType { get; init; }
public string? EndpointAuthTypeName { get; init; }
public string? EndpointAuthApiKey { get; init; }
public string? EndpointAuthApiValue { get; init; }
public ApiKeyLocation? EndpointAuthApiKeyAddTo { get; init; }
public string? EndpointAuthApiKeyAddToName { get; init; }
public string? EndpointAuthToken { get; init; }
public string? EndpointAuthUsername { get; init; }
@@ -50,7 +54,9 @@ public record RecActionViewDto
public string? SqlConnectionPassword { get; init; }
public string? RestType { get; init; }
public RestType? RestType { get; init; }
public string? RestTypeName { get; init; }
public string? PreprocessingQuery { get; init; }
@@ -64,7 +70,9 @@ public record RecActionViewDto
public string? PostprocessingQuery { get; init; }
public ErrorAction ErrorAction { get; init; }
public ErrorAction? ErrorAction { get; init; }
public string? ErrorActionName { get; init; }
public UriBuilder ToEndpointUriBuilder()
{
@@ -72,8 +80,8 @@ public record RecActionViewDto
builder.Port = -1;
if (ProfileType is not null)
builder.Scheme = ProfileType;
if (ProfileType is ProfileType type)
builder.Scheme = type.ToUriBuilderScheme();
return builder;
}

View File

@@ -0,0 +1,34 @@
namespace ReC.Application.Common.Dto;
public record ResultViewDto
{
public long Id { get; init; }
public OutResDto? Root { get; init; }
public long? ActionId { get; init; }
public RecActionViewDto? Action { get; init; }
public long? ProfileId { get; init; }
public ProfileViewDto? Profile { get; init; }
public string? ProfileName { get; init; }
public short? StatusCode { get; init; }
public string? StatusName { get; init; }
public string? Header { get; init; }
public string? Body { get; init; }
public string? AddedWho { get; init; }
public DateTime? AddedWhen { get; init; }
public string? ChangedWho { get; init; }
public DateTime? ChangedWhen { get; init; }
}

View File

@@ -1,25 +1,28 @@
using System.Diagnostics.CodeAnalysis;
using ReC.Domain.Constants;
using System.Diagnostics.CodeAnalysis;
namespace ReC.Application.Common;
public static class HttpExtensions
{
private static readonly Dictionary<string, HttpMethod> _methods = new(StringComparer.OrdinalIgnoreCase)
private static readonly Dictionary<RestType, HttpMethod> _methods = new()
{
["GET"] = HttpMethod.Get,
["POST"] = HttpMethod.Post,
["PUT"] = HttpMethod.Put,
["DELETE"] = HttpMethod.Delete,
["PATCH"] = HttpMethod.Patch,
["HEAD"] = HttpMethod.Head,
["OPTIONS"] = HttpMethod.Options,
["TRACE"] = HttpMethod.Trace,
["CONNECT"] = HttpMethod.Connect
[RestType.Get] = HttpMethod.Get,
[RestType.Post] = HttpMethod.Post,
[RestType.Put] = HttpMethod.Put,
[RestType.Delete] = HttpMethod.Delete,
[RestType.Patch] = HttpMethod.Patch,
[RestType.Head] = HttpMethod.Head,
[RestType.Options] = HttpMethod.Options,
[RestType.Trace] = HttpMethod.Trace,
[RestType.Connect] = HttpMethod.Connect
};
public static HttpMethod ToHttpMethod(this string method) => _methods.TryGetValue(method, out var httpMethod)
? httpMethod
: new HttpMethod(method);
public static HttpMethod ToHttpMethod(this RestType method) => !method.IsValid()
? throw new ArgumentOutOfRangeException(nameof(method), $"The RestType value '{method}' is not valid.")
: _methods.TryGetValue(method, out var httpMethod)
? httpMethod
: new HttpMethod(method.ToHttpMethodName());
public static HttpRequestMessage ToHttpRequestMessage(this HttpMethod method, [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri)
=> new(method, requestUri);

View File

@@ -15,15 +15,15 @@ public interface IRecDbContext
public DbSet<BodyQueryResult> BodyQueryResults { get; set; }
public DbSet<ConnectionDto> Connections { get; set; }
public DbSet<Connection> Connections { get; set; }
public DbSet<EndpointDto> Endpoints { get; set; }
public DbSet<Endpoint> Endpoints { get; set; }
public DbSet<EndpointAuthDto> EndpointAuths { get; set; }
public DbSet<EndpointAuth> EndpointAuths { get; set; }
public DbSet<ProfileDto> Profiles { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<RecActionDto> RecActions { get; set; }
public DbSet<RecAction> RecActions { get; set; }
public Task<int> SaveChangesAsync(CancellationToken cancel = default);
}

View File

@@ -1,6 +1,8 @@
using DigitalData.Core.Abstraction.Application.Repository;
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
using Microsoft.EntityFrameworkCore;
using ReC.Application.Common.Dto;
using ReC.Domain.Entities;
namespace ReC.Application.Endpoints.Commands;
@@ -10,16 +12,16 @@ public class ObtainEndpointCommand : IRequest<EndpointDto>
public string Uri { get; init; } = null!;
}
public class ObtainEndpointCommandHandler(IRepository<EndpointDto> repo) : IRequestHandler<ObtainEndpointCommand, EndpointDto>
public class ObtainEndpointCommandHandler(IRepository<Endpoint> repo, IMapper mapper) : IRequestHandler<ObtainEndpointCommand, EndpointDto>
{
public async Task<EndpointDto> Handle(ObtainEndpointCommand request, CancellationToken cancel)
{
var endpoint = await repo.Where(e => e.Uri == request.Uri).FirstOrDefaultAsync(cancel);
if (endpoint is not null)
return endpoint;
return mapper.Map<EndpointDto>(endpoint);
endpoint = await repo.CreateAsync(request, cancel);
return endpoint;
return mapper.Map<EndpointDto>(endpoint);
}
}

View File

@@ -1,5 +1,5 @@
using ReC.Application.Endpoints.Commands;
using ReC.Domain.Entities;
using ReC.Application.Common.Dto;
namespace ReC.Application.Endpoints;

View File

@@ -4,7 +4,10 @@ using ReC.Domain.Constants;
namespace ReC.Application.RecActionViews.Commands;
public record InvokeBatchRecActionViewsCommand : ReadRecActionQueryBase, IRequest;
public record InvokeBatchRecActionViewsCommand : IRequest
{
public long ProfileId { get; init; }
}
public static class InvokeBatchRecActionViewsCommandExtensions
{
@@ -16,7 +19,7 @@ public class InvokeRecActionViewsCommandHandler(ISender sender) : IRequestHandle
{
public async Task Handle(InvokeBatchRecActionViewsCommand request, CancellationToken cancel)
{
var actions = await sender.Send(request.ToReadQuery(q => q.Invoked = false), cancel);
var actions = await sender.Send(new ReadRecActionViewQuery() { ProfileId = request.ProfileId, Invoked = false }, cancel);
foreach (var action in actions)
{

View File

@@ -35,14 +35,14 @@ public class InvokeRecActionViewCommandHandler(
using var http = clientFactory.CreateClient(Http.ClientName);
if (action.RestType is null)
if (action.RestType is not RestType restType)
throw new DataIntegrityException(
$"Rec action could not be invoked because the RestType value is null. " +
$"ProfileId: {action.ProfileId}, " +
$"Id: {action.Id}"
);
using var httpReq = action.RestType
using var httpReq = restType
.ToHttpMethod()
.ToHttpRequestMessage(action.EndpointUri);

View File

@@ -1,7 +1,7 @@
using ReC.Application.RecActions.Commands;
using ReC.Domain.Entities;
using ReC.Application.Common.Dto;
using ReC.Application.RecActions.Commands;
namespace ReC.Application.RecActions;
namespace ReC.Application.RecActionViews;
// TODO: update to inject AddedWho from the current host/user contex
public class MappingProfile : AutoMapper.Profile

View File

@@ -8,32 +8,21 @@ using ReC.Application.Common.Dto;
namespace ReC.Application.RecActionViews.Queries;
public record ReadRecActionQueryBase
public record ReadRecActionViewQuery : IRequest<IEnumerable<RecActionViewDto>>
{
public long ProfileId { get; init; }
public ReadRecActionViewQuery ToReadQuery(Action<ReadRecActionViewQuery> modify)
{
ReadRecActionViewQuery query = new(this);
modify(query);
return query;
}
}
public record ReadRecActionViewQuery : ReadRecActionQueryBase, IRequest<IEnumerable<RecActionViewDto>>
{
public ReadRecActionViewQuery(ReadRecActionQueryBase root) : base(root) { }
public long? ProfileId { get; init; } = null;
public bool? Invoked { get; set; } = null;
public ReadRecActionViewQuery() { }
}
public class ReadRecActionViewQueryHandler(IRepository<RecActionView> repo, IMapper mapper) : IRequestHandler<ReadRecActionViewQuery, IEnumerable<RecActionViewDto>>
{
public async Task<IEnumerable<RecActionViewDto>> Handle(ReadRecActionViewQuery request, CancellationToken cancel)
{
var query = repo.Where(act => act.ProfileId == request.ProfileId);
var query = repo.Query;
if (request.ProfileId is long profileId)
query = repo.Where(act => act.ProfileId == profileId);
if (request.Invoked is bool invoked)
query = invoked ? query.Where(act => act.Root!.OutRes != null) : query.Where(act => act.Root!.OutRes == null);

View File

@@ -1,8 +1,8 @@
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using ReC.Application.Endpoints.Commands;
using ReC.Domain.Entities;
using ReC.Application.Endpoints.Commands;
namespace ReC.Application.RecActions.Commands;
@@ -27,7 +27,7 @@ public record CreateRecActionCommand : IRequest
public long? EndpointAuthId { get; set; }
}
public class CreateRecActionCommandHandler(ISender sender, IRepository<RecActionDto> repo) : IRequestHandler<CreateRecActionCommand>
public class CreateRecActionCommandHandler(ISender sender, IRepository<RecAction> 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<RecActionDto> repo) : IRequestHandler<DeleteRecActionsCommand>
public class DeleteRecActionsCommandHandler(IRepository<RecAction> repo) : IRequestHandler<DeleteRecActionsCommand>
{
public async Task Handle(DeleteRecActionsCommand request, CancellationToken cancel)
{

View File

@@ -0,0 +1,49 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Exceptions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using ReC.Application.Common.Dto;
using ReC.Domain.Entities;
using System.Text.Json;
namespace ReC.Application.ResultViews.Queries;
public record ReadResultViewQuery : IRequest<IEnumerable<ResultViewDto>>
{
public long? Id { get; init; } = null;
public long? ActionId { get; init; } = null;
public long? ProfileId { get; init; } = null;
}
public class ReadResultViewQueryHandler(IRepository<ResultView> repo, IMapper mapper) : IRequestHandler<ReadResultViewQuery, IEnumerable<ResultViewDto>>
{
public async Task<IEnumerable<ResultViewDto>> Handle(ReadResultViewQuery 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);
var entities = await q.ToListAsync(cancel);
if (entities.Count == 0)
throw new NotFoundException($"No result views found for the given criteria. Criteria: {
JsonSerializer.Serialize(request, options: new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
})}"
);
return mapper.Map<IEnumerable<ResultViewDto>>(entities);
}
}

View File

@@ -1,6 +1,6 @@
namespace ReC.Domain.Constants;
public enum ApiKeyLocation
public enum ApiKeyLocation : byte
{
Header = 0,
Query = 1

View File

@@ -1,6 +1,6 @@
namespace ReC.Domain.Constants;
public enum EndpointAuthType
public enum EndpointAuthType : byte
{
NoAuth = 0,
ApiKey = 1,

View File

@@ -1,6 +1,6 @@
namespace ReC.Domain.Constants;
public enum ErrorAction
public enum ErrorAction : byte
{
Stop = 0,
Continue = 1,

View File

@@ -0,0 +1,12 @@
namespace ReC.Domain.Constants;
public enum ProfileType : byte
{
Http = 1,
Https = 2
}
public static class ProfileTypeExtensions
{
public static string ToUriBuilderScheme(this ProfileType profileType) => profileType.ToString().ToLower();
}

View File

@@ -0,0 +1,28 @@
namespace ReC.Domain.Constants;
public enum RestType : byte
{
None = 0,
Get = 1,
Post = 2,
Put = 3,
Patch = 4,
Delete = 5,
Head = 6,
Options = 7,
Connect = 8,
Trace = 9,
}
public static class RestTypeExtensions
{
public static string ToHttpMethodName(this RestType restType)
{
return restType.ToString().ToUpper();
}
public static bool IsValid(this RestType restType)
{
return restType != RestType.None;
}
}

View File

@@ -0,0 +1,42 @@
using ReC.Domain.Constants;
namespace ReC.Domain.Entities;
public record ProfileView
{
public long Id { get; init; }
public bool Active { get; init; }
public ProfileType TypeId { get; init; }
public string? Type { get; init; }
public string? Mandantor { get; init; }
public string? ProfileName { get; init; }
public string? Description { get; init; }
public byte LogLevelId { get; init; }
public string? LogLevel { get; init; }
public short LanguageId { get; init; }
public string? Language { get; init; }
public string? AddedWho { get; init; }
public DateTime AddedWhen { get; init; }
public string? ChangedWho { get; init; }
public DateTime? ChangedWhen { get; init; }
public DateTime? FirstRun { get; init; }
public DateTime? LastRun { get; init; }
public string? LastResult { get; init; }
}

View File

@@ -22,11 +22,11 @@ public class RecActionView
public long? ProfileId { get; set; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; set; }
public ProfileView? Profile { get; set; }
public string? ProfileName { get; set; }
public string? ProfileType { get; set; }
public ProfileType? ProfileType { get; set; }
public byte? Sequence { get; set; }
@@ -44,12 +44,16 @@ public class RecActionView
public EndpointAuthType? EndpointAuthType { get; set; }
public string? EndpointAuthTypeName { get; set; }
public string? EndpointAuthApiKey { get; set; }
public string? EndpointAuthApiValue { get; set; }
public ApiKeyLocation? EndpointAuthApiKeyAddTo { get; set; }
public string? EndpointAuthApiKeyAddToName { get; set; }
public string? EndpointAuthToken { get; set; }
public string? EndpointAuthUsername { get; set; }
@@ -75,7 +79,9 @@ public class RecActionView
public string? SqlConnectionPassword { get; set; }
public string? RestType { get; set; }
public RestType? RestType { get; set; }
public string? RestTypeName { get; set; }
public string? PreprocessingQuery { get; set; }
@@ -84,4 +90,8 @@ public class RecActionView
public string? BodyQuery { get; set; }
public string? PostprocessingQuery { get; set; }
public ErrorAction? ErrorAction { get; set; }
public string? ErrorActionName { get; set; }
}

View File

@@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace ReC.Domain.Entities;
public class ResultView
{
public long Id { get; set; }
[ForeignKey("Id")]
public OutRes? Root { get; set; }
public long? ActionId { get; set; }
public RecActionView? Action { get; set; }
public long? ProfileId { get; set; }
public ProfileView? Profile { get; set; }
public string? ProfileName { get; set; }
public short? StatusCode { get; set; }
public string? StatusName { get; set; }
public string? Header { get; set; }
public string? Body { get; set; }
public string? AddedWho { get; set; }
public DateTime? AddedWhen { get; set; }
public string? ChangedWho { get; set; }
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -10,6 +10,10 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
public DbSet<RecActionView> RecActionViews { get; set; }
public DbSet<ProfileView> ProfileViews { get; set; }
public DbSet<ResultView> RecResultViews { get; set; }
public DbSet<OutRes> OutRes { get; set; }
public DbSet<HeaderQueryResult> HeaderQueryResults { get; set; }
@@ -131,7 +135,7 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
.ValueGeneratedOnAdd();
b.Property(e => e.ActionId).HasColumnName("ACTION_ID");
b.Property(e => e.Status).HasColumnName("STATUS");
b.Property(e => e.Status).HasColumnName("STATUS_ID");
b.Property(e => e.Header).HasColumnName("RESULT_HEADER");
b.Property(e => e.Body).HasColumnName("RESULT_BODY");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
@@ -163,6 +167,31 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<ProfileView>(b =>
{
b.ToView("VWREC_PROFILE", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id).HasColumnName("PROFILE_GUID");
b.Property(e => e.Active).HasColumnName("ACTIVE");
b.Property(e => e.TypeId).HasColumnName("TYPE_ID");
b.Property(e => e.Type).HasColumnName("TYPE");
b.Property(e => e.Mandantor).HasColumnName("MANDANTOR");
b.Property(e => e.ProfileName).HasColumnName("PROFILE_NAME");
b.Property(e => e.Description).HasColumnName("DESCRIPTION");
b.Property(e => e.LogLevelId).HasColumnName("LOG_LEVEL_ID");
b.Property(e => e.LogLevel).HasColumnName("LOG_LEVEL");
b.Property(e => e.LanguageId).HasColumnName("LANGUAGE_ID");
b.Property(e => e.Language).HasColumnName("LANGUAGE");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
b.Property(e => e.FirstRun).HasColumnName("FIRST_RUN");
b.Property(e => e.LastRun).HasColumnName("LAST_RUN");
b.Property(e => e.LastResult).HasColumnName("LAST_RESULT");
});
modelBuilder.Entity<RecAction>(b =>
{
b.ToTable("TBREC_CFG_ACTION");
@@ -196,21 +225,23 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
modelBuilder.Entity<RecActionView>(b =>
{
b.ToTable("VWREC_ACTION", "dbo");
b.HasNoKey();
b.ToView("VWREC_ACTION", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id).HasColumnName("ACTION_ID");
b.Property(e => e.Id).HasColumnName("ACTION_GUID");
b.Property(e => e.ProfileId).HasColumnName("PROFILE_ID");
b.Property(e => e.ProfileName).HasColumnName("PROFILE_NAME");
b.Property(e => e.ProfileType).HasColumnName("PROFILE_TYPE");
b.Property(e => e.ProfileType).HasColumnName("PROFILE_TYPE_ID");
b.Property(e => e.Sequence).HasColumnName("SEQUENCE");
b.Property(e => e.EndpointId).HasColumnName("ENDPOINT_ID");
b.Property(e => e.EndpointUri).HasColumnName("ENDPOINT_URI");
b.Property(e => e.EndpointAuthId).HasColumnName("ENDPOINT_AUTH_ID");
b.Property(e => e.EndpointAuthType).HasColumnName("ENDPOINT_AUTH_TYPE");
b.Property(e => e.EndpointAuthType).HasColumnName("ENDPOINT_AUTH_TYPE_ID");
b.Property(e => e.EndpointAuthTypeName).HasColumnName("ENDPOINT_AUTH_TYPE");
b.Property(e => e.EndpointAuthApiKey).HasColumnName("ENDPOINT_AUTH_API_KEY");
b.Property(e => e.EndpointAuthApiValue).HasColumnName("ENDPOINT_AUTH_API_VALUE");
b.Property(e => e.EndpointAuthApiKeyAddTo).HasColumnName("ENDPOINT_AUTH_API_KEY_ADD_TO");
b.Property(e => e.EndpointAuthApiKeyAddTo).HasColumnName("ENDPOINT_AUTH_API_KEY_ADD_TO_ID");
b.Property(e => e.EndpointAuthApiKeyAddToName).HasColumnName("ENDPOINT_AUTH_API_KEY_ADD_TO");
b.Property(e => e.EndpointAuthToken).HasColumnName("ENDPOINT_AUTH_TOKEN");
b.Property(e => e.EndpointAuthUsername).HasColumnName("ENDPOINT_AUTH_USERNAME");
b.Property(e => e.EndpointAuthPassword).HasColumnName("ENDPOINT_AUTH_PASSWORD");
@@ -222,11 +253,33 @@ public class RecDbContext(DbContextOptions<RecDbContext> options) : DbContext(op
b.Property(e => e.SqlConnectionDb).HasColumnName("SQL_CONNECTION_DB");
b.Property(e => e.SqlConnectionUsername).HasColumnName("SQL_CONNECTION_USERNAME");
b.Property(e => e.SqlConnectionPassword).HasColumnName("SQL_CONNECTION_PASSWORD");
b.Property(e => e.RestType).HasColumnName("REST_TYPE");
b.Property(e => e.RestType).HasColumnName("REST_TYPE_ID");
b.Property(e => e.RestTypeName).HasColumnName("REST_TYPE");
b.Property(e => e.PreprocessingQuery).HasColumnName("PREPROCESSING_QUERY");
b.Property(e => e.HeaderQuery).HasColumnName("HEADER_QUERY");
b.Property(e => e.BodyQuery).HasColumnName("BODY_QUERY");
b.Property(e => e.PostprocessingQuery).HasColumnName("POSTPROCESSING_QUERY");
b.Property(e => e.ErrorAction).HasColumnName("ERROR_ACTION_ID");
b.Property(e => e.ErrorActionName).HasColumnName("ERROR_ACTION");
});
modelBuilder.Entity<ResultView>(b =>
{
b.ToView("VWREC_RESULT", "dbo");
b.HasKey(e => e.Id);
b.Property(e => e.Id).HasColumnName("RESULT_GUID");
b.Property(e => e.ActionId).HasColumnName("ACTION_ID");
b.Property(e => e.ProfileId).HasColumnName("PROFILE_ID");
b.Property(e => e.ProfileName).HasColumnName("PROFILE_NAME");
b.Property(e => e.StatusCode).HasColumnName("STATUS_ID");
b.Property(e => e.StatusName).HasColumnName("STATUS");
b.Property(e => e.Header).HasColumnName("RESULT_HEADER");
b.Property(e => e.Body).HasColumnName("RESULT_BODY");
b.Property(e => e.AddedWho).HasColumnName("ADDED_WHO");
b.Property(e => e.AddedWhen).HasColumnName("ADDED_WHEN");
b.Property(e => e.ChangedWho).HasColumnName("CHANGED_WHO");
b.Property(e => e.ChangedWhen).HasColumnName("CHANGED_WHEN");
});
modelBuilder.Entity<HeaderQueryResult>(b =>