Compare commits

46 Commits

Author SHA1 Message Date
63df235943 feat(ExceptionHandlingMiddleware): Add to handle exceptions by middleware 2025-07-30 17:16:10 +02:00
78f2788388 feat(API): add global exception handler 2025-07-30 15:55:19 +02:00
13acf6de08 refactor: remove legacy services and controllers 2025-07-30 15:39:30 +02:00
5466b35b95 feat(Repositories.DependencyInjection): add config, controlTf, state and button repositories with auto-mapper configurations. 2025-07-30 15:00:05 +02:00
d0e306b7e4 chore: update core libs 2025-07-30 14:31:30 +02:00
4fcc0a08b8 refactor(ObjectDto): Verschieben in das Verzeichnis Dto.
- Profil-Dienst entfernen
2025-07-30 13:32:56 +02:00
904536bd09 refactor(Profil): dto in den Ordner DTO verschieben.
- Ordner DTO in Dto umbenennen
2025-07-30 13:21:18 +02:00
6c0f39e3ed refactor(domain): Aktualisieren Sie die Entität „ProfileControlsTF“, damit sie dem neuen Schema entspricht
- Die Implementierung von IUnique<int> und zugehörige Navigationseigenschaften wurden entfernt
- Der Primärschlüsseltyp wurde von int zu long geändert
- Die Spaltentypen und -namen wurden aktualisiert, um sie an das Datenbankschema anzupassen
- Erforderliche Eigenschaften wurden in nullbare Typen mit Validierung auf Anwendungsebene konvertiert
- XML-Kommentare wurden hinzugefügt, um Designentscheidungen hinsichtlich der Nullbarkeit zu verdeutlichen
2025-07-30 11:01:29 +02:00
8ceaa9cb21 feat(ReadObject): created to handle objects.
- Add ObjectDto and mapping profile
2025-07-29 22:19:48 +02:00
27e4b4b2ef feat(ReadProfile): add logic to read buttons 2025-07-29 22:09:11 +02:00
82eb03b420 refactor(ReadProfile): update to read buttons 2025-07-29 21:49:21 +02:00
559127a931 feat(ReadButton): add dto and handler 2025-07-29 21:41:54 +02:00
Developer 02
87857862e4 feat(Button): create repositry 2025-07-29 19:55:20 +02:00
8655f78c8c feat(Button): add entity for TBMWF_PROF_BUTTONS table 2025-07-29 18:23:21 +02:00
168a4b0791 feat: add ReadProfileAsync extension method for IMediator 2025-07-25 17:50:38 +02:00
dd4cd1b39e feat(ReadProfile): Unterstützung für das bedingte Laden von Profilobjekten hinzugefügt
- Flag „IncludeObject” in ReadProfile-Anfrage eingeführt
- IProfileObjRepository in ReadProfileHandler eingefügt
- Handler aktualisiert, um Profilobjekte zu laden, wenn IncludeObject wahr ist
2025-07-25 17:42:58 +02:00
8eb8801c41 feat(DI): add method to inject IProfileObjRepository 2025-07-25 17:07:40 +02:00
eb7ed81cac feat(IProfileObjRepository): Schnittstelle von ProfileObjRepository erstellen und in ProfileObjRepository implementieren 2025-07-25 17:04:58 +02:00
b7c6477ec2 feat(repo): Hinzufügen von ProfileObjRepository zum Abrufen von Profilobjekten über eine SQL-Funktion 2025-07-25 17:01:11 +02:00
b7f9efa9b6 feat: füge Spaltenattribute zur Klasse ProfileObject hinzu 2025-07-25 16:48:29 +02:00
bf5566cefc Laden Sie umgebungsspezifische App-Einstellungsdateien mit Ausnahme von „Development“
- Listet die Dateien „appsettings.*.json“ im Stammverzeichnis des Inhalts auf
- Schließt „appsettings.Development.json“ vom Laden aus
- Fügt die verbleibenden JSON-Dateien zur Konfiguration hinzu
2025-07-25 16:26:05 +02:00
f8e51e02a0 refactor: disable api key 2025-07-25 15:30:37 +02:00
ad1fd3163e Refactoring (Controller): Aktualisierung zur Verwendung der aktuellen Version von TryGetUserId 2025-07-25 10:44:12 +02:00
bed5fae01c Refactoring: Vereinfachung der TryGetUserId-Methode und Umstellung des Erweiterungsziels auf ClaimsPrincipal
- TryGetUserId wurde so geändert, dass es direkt die Methode expression-bodied mit int.TryParse verwendet.
- Die Erweiterungsmethoden wurden aktualisiert, sodass sie nun statt auf ControllerBase auf ClaimsPrincipal abzielen, um eine bessere Abstraktion und Wiederverwendbarkeit zu erreichen.
- Andere Methoden wurden aus Gründen der Konsistenz unverändert gelassen.
2025-07-24 18:09:59 +02:00
a378862791 refactor(ProfileController): Aktualisierung, um JWT zum Abrufen der Benutzer-ID anstelle der Abfragezeichenfolge zu verwenden 2025-07-24 17:53:31 +02:00
dd2dbee037 update Auth.Client 2025-07-24 17:35:13 +02:00
b24f518bba fix(Profil): Typ von TypeId byte festgelegt 2025-07-24 16:46:16 +02:00
dd5babfdbe inject MediatRLicense 2025-07-24 15:41:29 +02:00
dc7da91872 feat(di): register MediatR with service collection 2025-07-24 14:09:20 +02:00
fe358623da feat(ProfileController): Hinzufügen von ProfileController mit GET-Endpunkt unter Verwendung von MediatR
- Implementiert die Aktion `GetAsync` zum Abrufen von Profildaten über MediatR
- Fügt die Attribute `[Authorize]` und `[APIKeyAuth]` für den gesicherten Zugriff hinzu
- Protokolliert Ausnahmen und gibt entsprechende HTTP-Statuscodes zurück
2025-07-24 13:41:44 +02:00
c08c5aacf3 feat: Lesevorgang für Benutzerprofil mittels MediatR und Repository implementiert
- ReadProfile-Request eingeführt, um Benutzerprofil anhand der UserId abzurufen
- ReadProfileHandler hinzugefügt, der das Profil aus dem IProfileRepository liest
- Asynchrone Verarbeitung mit Unterstützung für CancellationToken integriert
2025-07-24 13:22:12 +02:00
14f5c73d43 feat(profile): implement ReadProfile query with MediatR
Added ReadProfile query and its handler using MediatR pattern to retrieve user profile by user ID via IProfileRepository.
2025-07-24 11:40:20 +02:00
b25d4eb028 feat: add documentation comments 2025-07-24 11:28:29 +02:00
8c08beba4e feat(repository): Implementieren Sie ProfileRepository mit ReadAsync unter Verwendung von FNMWF_GET_PROFILES. 2025-07-24 11:24:20 +02:00
30bb3ffa11 chore: update dependency injection methods of repositories 2025-07-24 11:00:48 +02:00
a9faf74803 chore: update references of Contracts.Repositories 2025-07-24 10:56:02 +02:00
22e4b4f54f refactor(Contracts.Repositories): Verschieben Sie es in die Anwendungsschicht, um die Anforderungen einer sauberen Architektur zu erfüllen. 2025-07-24 10:35:30 +02:00
a954a24888 feat(Controller): Nicht erforderliche Post-, Put- und Delete-Methoden ignorieren 2025-07-23 16:20:46 +02:00
a78c117a47 feat: extend default Profile with sample ProfileObjects
- Added two sample `ProfileObject` instances to the static `Default` Profile
- Includes object metadata like ObjStateId, ObjectId, headlines, and sublines
- Enhances the default response of `GET /api/profile` for testing/demo purposes
2025-07-21 10:24:43 +02:00
07e16f8aca feat(domain): ProfileObject-Entität zur Repräsentation von Objekt-Metadaten hinzugefügt
ProfileObject-Klasse zu WorkFlow.Domain.Entities hinzugefügt mit folgenden Eigenschaften:
- ObjStateId
- ObjectId
- Headline1, Headline2
- Subline1, Subline2
- CmdCheckIn
2025-07-21 10:18:57 +02:00
0b70016ab6 refactor(controller): ProfileController vereinfacht und Standardprofil-Antwort hinzugefügt
- Basisklasse CRUDControllerBaseWithErrorHandling entfernt
- Statisches Standard-Profilobjekt für Test-/Demo-Zwecke eingeführt
- Generisches CRUD-Verhalten durch einfachen GET-Endpunkt ersetzt
- Fehlerbehandlung mit Logging und HTTP-500-Antwort verbessert
2025-07-21 10:14:11 +02:00
537891b8c5 refactor(ProfileService): CRUDService-Implementierung entfernen 2025-07-18 16:10:39 +02:00
f8be2d9f26 refactor(repository): simplify Profile and ProfileObjState repositories
- Removed inheritance from ICRUDRepository in IProfileRepository and related implementation
- Cleaned up ProfileRepository to no longer extend CRUDRepository
- Removed `profileActive` filter from IProfileObjStateRepository and implementation
- Adjusted Read and ReadAsync methods accordingly
2025-07-18 16:08:08 +02:00
547d723f47 refactor(Profile): simplify Profile entity and remove unused metadata
- Removed dependency on IUnique<int> interface
- Removed validation and database annotations like [Required], [Key]
- Renamed/updated column mappings and replaced required fields with nullable types
- Removed metadata fields such as AddedWho, AddedWhen, ChangedWho, ChangedWhen, etc.
- Cleaned up namespace and using directives
2025-07-18 15:44:49 +02:00
1fcdcf6c0a chore: alle Projekte in das Verzeichnis src verschieben 2025-07-18 14:48:28 +02:00
a5bffdf1ce chore(solution): move projects to src-solution folder 2025-07-18 14:44:31 +02:00
104 changed files with 1169 additions and 1366 deletions

View File

@@ -1,20 +0,0 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>
{
public ConfigController(ILogger<ConfigController> logger, IConfigService service) : base(logger, service)
{
}
}

View File

@@ -1,96 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers
{
[APIKeyAuth]
public static class ControllerExtensions
{
public static bool TryGetUserId(this ControllerBase controller, out int? id)
{
var value = controller.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (value is null)
{
id = default;
return false;
}
if(int.TryParse(value, out int id_int))
{
id = id_int;
return true;
}
else
{
id = null;
return false;
}
}
public static bool TryGetUsername(this ControllerBase controller, out string username)
{
var value = controller.User.FindFirstValue(ClaimTypes.Name);
if (value is null)
{
username = string.Empty;
return false;
}
else
{
username = value;
return true;
}
}
public static bool TryGetName(this ControllerBase controller, out string name)
{
var value = controller.User.FindFirstValue(ClaimTypes.Surname);
if (value is null)
{
name = string.Empty;
return false;
}
else
{
name = value;
return true;
}
}
public static bool TryGetPrename(this ControllerBase controller, out string prename)
{
var value = controller.User.FindFirstValue(ClaimTypes.GivenName);
if (value is null)
{
prename = string.Empty;
return false;
}
else
{
prename = value;
return true;
}
}
public static bool TryGetEmail(this ControllerBase controller, out string email)
{
var value = controller.User.FindFirstValue(ClaimTypes.Email);
if (value is null)
{
email = string.Empty;
return false;
}
else
{
email = value;
return true;
}
}
}
}

View File

@@ -1,20 +0,0 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>
{
public ProfileController(ILogger<ProfileController> logger, IProfileService service) : base(logger, service)
{
}
}

View File

@@ -1,126 +0,0 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileControlsTFController : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>
{
private readonly ILogger<ProfileControlsTFController> logger;
public ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[NonAction]
public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto);
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = false,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileControlsTFCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -1,127 +0,0 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileObjStateController : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>
{
private readonly ILogger<ProfileObjStateController> logger;
public ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[NonAction]
public override Task<IActionResult> Update(ProfileObjStateUpdateDto updateDto) => base.Update(updateDto);
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser, withState,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileObjStateCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}
}

View File

@@ -1,20 +0,0 @@
using DigitalData.Core.API;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>
{
public StateController(ILogger<StateController> logger, IStateService service) : base(logger, service)
{
}
}

View File

@@ -1,54 +0,0 @@
using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> logger;
private readonly IUserService userService;
public UserController(ILogger<UserController> logger, IUserService userService)
{
this.logger = logger;
this.userService = userService;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if(id is int id_int)
return await userService.ReadByIdAsync(id_int).ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
else
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net7\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net8\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -1,91 +0,0 @@
{
"DiPMode": true,
"EnableSwagger": true,
"DisableAPIKeyAuth": false,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"NLog": {
"throwConfigExceptions": true,
"variables": {
"logDirectory": "E:\\LogFiles\\Digital Data\\workFlow.API",
"logFileNamePrefix": "${shortdate}-workFlow.API"
},
"targets": {
"infoLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
"maxArchiveDays": 30
},
"errorLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
"maxArchiveDays": 30
},
"criticalLogs": {
"type": "File",
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
"maxArchiveDays": 30
}
},
// Trace, Debug, Info, Warn, Error and *Fatal*
"rules": [
{
"logger": "*",
"minLevel": "Info",
"maxLevel": "Warn",
"writeTo": "infoLogs"
},
{
"logger": "*",
"level": "Error",
"writeTo": "errorLogs"
},
{
"logger": "*",
"level": "Fatal",
"writeTo": "criticalLogs"
}
]
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"DirectorySearchOptions": {
"ServerName": "DD-VMP01-DC01",
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works",
"UserCacheExpirationDays": 1,
"CustomSearchFilters": {
"User": "(&(objectClass=user)(sAMAccountName=*))",
"Group": "(&(objectClass=group) (samAccountName=*))"
}
},
"APIKeyAuth": {
"Key": "ULbcOUiAXAoCXPviyCGtObZUGnrCHNgDmtNbQNpq5MOhB0EFQn18dObdQ93INNy8xIcnOPMJfEHqOotllELVrJ2R5AjqOfQszT2j00w215GanD3UiJGwFhwmdoNFsmNj",
"HeaderName": "X-API-Key",
"SwaggerDescription": "Required header for API key authentication. Enter a valid API key."
},
"OpenApiInfo": {
"Title": "WorkFlow API",
"Contact": {
"Email": "info-flow@digitaldata.works",
"Name": "Digital Data GmbH",
"Url": "https://digitaldata.works/"
}
},
"AuthClientParams": {
"Url": "https://localhost:7192/auth-hub",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "work-flow.digitaldata.works",
"Content": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QCd7dH/xOUITFZbitMa/xnh8a0LyL6ZBvSRAwkI9ceplTRSHJXoM1oB+xtjWE1kOuHVLe941Tm03szS4+/rHIm0Ejva/KKlv7sPFAHE/pWuoPS303vOHgI4HAFcuwywA8CghUWzaaK5LU/Hl8srWwxBHv5hKIUjJFJygeAIENvFOZ1gFbB3MPEC99PiPOwAmfl4tMQUmSsFyspl/RWVi7bTv26ZE+m3KPcWppmvmYjXlSitxRaySxnfFvpca/qWfd/uUUg2KWKtpAwWVkqr0qD9v3TyKSgHoGDsrFpwSx8qufUJSinmZ1u/0iKl6TXeHubYS4C4SUSVjOWXymI2ZQIDAQAB-----END PUBLIC KEY-----"
}
],
"RetryDelay": "00:00:05"
}
}

View File

@@ -1,10 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IConfigService : ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
{
}
}

View File

@@ -1,15 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileControlsTFService : ICRUDService<ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>
{
Task<DataResult<IEnumerable<ProfileControlsTFDto>>> ReadAsync(
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -1,15 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileObjStateService : ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
{
Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -1,10 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IProfileService : ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
{
}
}

View File

@@ -1,10 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts
{
public interface IStateService : ICRUDService<StateCreateDto, StateDto, State, int>
{
}
}

View File

@@ -1,25 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.Services;
using WorkFlow.Infrastructure;
namespace WorkFlow.Application
{
public static class DIExtensions
{
public static IServiceCollection AddWorkFlowServices(this IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingProfile).Assembly);
services.TryAddScoped<IConfigService, ConfigService>();
services.TryAddScoped<IProfileControlsTFService, ProfileControlsTFService>();
services.TryAddScoped<IProfileObjStateService, ProfileObjStateService>();
services.TryAddScoped<IProfileService, ProfileService>();
services.TryAddScoped<IStateService, StateService>();
return services;
}
public static IServiceCollection AddWorkFlow(this IServiceCollection services) => services.AddWorkFlowRepositories().AddWorkFlowServices();
}
}

View File

@@ -1,4 +0,0 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileCreateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseCreateDto;
}

View File

@@ -1,12 +0,0 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileDto(int Id,
string IntlName,
int ExtId1,
bool Active,
byte TypeId,
string AddedWho,
DateTime AddedWhen,
string? ChangedWho = null,
DateTime? ChangedWhen = null);
}

View File

@@ -1,4 +0,0 @@
namespace WorkFlow.Application.DTO.Profile
{
public record ProfileUpdateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseUpdateDto;
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.ProfileControlsTF
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileControlsTFUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.ProfileObjState
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileObjStateUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions;
namespace WorkFlow.Application.DTO.State
{
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record StateUpdateDto(int Id) : IUnique<int>;
}

View File

@@ -1,37 +0,0 @@
using AutoMapper;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application
{
public class MappingProfile : AutoMapper.Profile
{
public MappingProfile()
{
// Mapping entity to DTO
CreateMap<Config, ConfigDto>();
CreateMap<Domain.Entities.Profile, ProfileDto>();
CreateMap<ProfileControlsTF, ProfileControlsTFDto>();
CreateMap<ProfileObjState, ProfileObjStateDto>();
CreateMap<State, StateDto>();
// Mapping create-DTO to entity
CreateMap<ConfigCreateDto, Config>();
CreateMap<ProfileCreateDto, Domain.Entities.Profile>();
CreateMap<ProfileControlsTFCreateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateCreateDto, ProfileObjState>();
CreateMap<StateCreateDto, State>();
// Mapping update-DTO to entity
CreateMap<ConfigUpdateDto, Config>();
CreateMap<ProfileUpdateDto, Domain.Entities.Profile>();
CreateMap<ProfileControlsTFUpdateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateUpdateDto, ProfileObjState>();
CreateMap<StateUpdateDto, State>();
}
}
}

View File

@@ -1,17 +0,0 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ConfigService : CRUDService<IConfigRepository, ConfigCreateDto, ConfigDto, Config, int>,
IConfigService, ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
{
public ConfigService(IConfigRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -1,35 +0,0 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
{
public class ProfileControlsTFService : CRUDService<IProfileControlsTFRepository, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>,
IProfileControlsTFService, ICRUDService<ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>
{
public ProfileControlsTFService(IProfileControlsTFRepository repository, IMapper mapper) : base(repository, mapper)
{
}
public async Task<DataResult<IEnumerable<ProfileControlsTFDto>>> ReadAsync(
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pctf_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
var pctf_dto_list = _mapper.Map<IEnumerable<ProfileControlsTFDto>>(pctf_list);
return Result.Success(pctf_dto_list);
}
}
}

View File

@@ -1,34 +0,0 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using DigitalData.Core.DTO;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ProfileObjStateService : CRUDService<IProfileObjStateRepository, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>,
IProfileObjStateService, ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
{
public ProfileObjStateService(IProfileObjStateRepository repository, IMapper mapper) : base(repository, mapper)
{
}
public async Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pos_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
var post_dto_list = _mapper.Map<IEnumerable<ProfileObjStateDto>>(pos_list);
return Result.Success(post_dto_list);
}
}

View File

@@ -1,16 +0,0 @@
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class ProfileService : CRUDService<IProfileRepository, ProfileCreateDto, ProfileDto, Profile, int>,
IProfileService, ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
{
public ProfileService(IProfileRepository repository, AutoMapper.IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -1,17 +0,0 @@
using AutoMapper;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Core.Application;
using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services;
public class StateService : CRUDService<IStateRepository, StateCreateDto, StateDto, State, int>,
IStateService, ICRUDService<StateCreateDto, StateDto, State, int>
{
public StateService(IStateRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -1,44 +0,0 @@
using DigitalData.Core.Abstractions;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROFILE", Schema = "dbo")]
public class Profile : IUnique<int>
{
[Key]
[Column("GUID")]
public int Id { get; init; }
[Required]
[Column("INTL_NAME", TypeName = "varchar(200)")]
public required string IntlName { get; init; }
[Required]
[Column("EXT_ID1")]
public required int ExtId1 { get; init; }
[Required]
[Column("ACTIVE")]
public required bool Active { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[Column("CHANGED_WHO", TypeName = "varchar(30)")]
public string? ChangedWho { get; init; }
[Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; init; }
[Required]
[Column("TYPE_ID")]
public required byte TypeId { get; init; }
}
}

View File

@@ -1,68 +0,0 @@
using DigitalData.Core.Abstractions;
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROF_CONTROLS_TF", Schema = "dbo")]
public class ProfileControlsTF : IUnique<int>
{
[Key]
[Column("GUID")]
public int Id { get; init; }
[Required]
[Column("MWF_PROFILE_ID")]
public required int ProfileId { get; init; }
[Required]
[Column("USR_ID")]
public required int UserId { get; init; }
[Required]
[Column("OBJ_ID")]
public required long ObjId { get; init; }
[Required]
[Column("OBJ_TYPE", TypeName = "varchar(10)")]
public required string ObjType { get; init; }
[Required]
[Column("ATTR_NAME", TypeName = "varchar(100)")]
public required string AttrName { get; init; }
[Required]
[Column("CTRL_TYPE", TypeName = "varchar(10)")]
public required string CtrlType { get; init; }
[Required]
[Column("CTRL_CAPTION", TypeName = "varchar(100)")]
public required string CtrlCaption { get; init; }
[Required]
[Column("MANDATORY")]
public required bool Mandatory { get; init; }
[Column("CHOICE_LIST", TypeName = "nvarchar(max)")]
public string? ChoiceList { get; init; }
[Required]
[Column("READ_ONLY")]
public required bool ReadOnly { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(100)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = default;
[ForeignKey("UserId")]
public User? User { get; set; } = default;
}
}

View File

@@ -1,57 +0,0 @@
using DigitalData.Core.Abstractions;
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities
{
[Table("TBMWF_PROFILE_OBJ_STATE", Schema = "dbo")]
public class ProfileObjState : IUnique<int>
{
[Key]
[Column("GUID")]
public int Id { get; init; }
[Required]
[Column("MWF_PROFILE_ID")]
public required int ProfileId { get; init; }
[Required]
[Column("USR_ID")]
public required int UserId { get; init; }
[Required]
[Column("OBJ_ID")]
public required long ObjId { get; init; }
[Required]
[Column("STATE_ID")]
public required int StateId { get; init; }
[Column("STATE2", TypeName = "nvarchar(3000)")]
public string? State2 { get; init; }
[Column("STATE3", TypeName = "nvarchar(3000)")]
public string? State3 { get; init; }
[Column("STATE4", TypeName = "nvarchar(3000)")]
public string? State4 { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = null;
[ForeignKey("UserId")]
public User? User { get; init; } = null;
[ForeignKey("StateId")]
public State? State { get; init; } = null;
}
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IConfigRepository : ICRUDRepository<Config, int>
{
}
}

View File

@@ -1,14 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileControlsTFRepository : ICRUDRepository<ProfileControlsTF, int>
{
Task<IEnumerable<ProfileControlsTF>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -1,14 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileObjStateRepository : ICRUDRepository<ProfileObjState, int>
{
Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null);
}
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IProfileRepository : ICRUDRepository<Profile, int>
{
}
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure.Contracts
{
public interface IStateRepository : ICRUDRepository<State, int>
{
}
}

View File

@@ -1,21 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Infrastructure.Contracts;
using WorkFlow.Infrastructure.Repositories;
namespace WorkFlow.Infrastructure
{
public static class DIExtensions
{
public static IServiceCollection AddWorkFlowRepositories(this IServiceCollection services)
{
services.TryAddScoped<IConfigRepository, ConfigRepository>();
services.TryAddScoped<IProfileControlsTFRepository, ProfileControlsTFRepository>();
services.TryAddScoped<IProfileObjStateRepository, ProfileObjStateRepository>();
services.TryAddScoped<IProfileRepository, ProfileRepository>();
services.TryAddScoped<IStateRepository, StateRepository>();
return services;
}
}
}

View File

@@ -1,15 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
//TODO: Make the db context type generic so that it can be used by other projects with different db contexts.
public class ConfigRepository : CRUDRepository<Config, int, WFDBContext>, IConfigRepository, ICRUDRepository<Config, int>
{
public ConfigRepository(WFDBContext dbContext) : base(dbContext, dbContext.Configs)
{
}
}
}

View File

@@ -1,57 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
public class ProfileControlsTFRepository : CRUDRepository<ProfileControlsTF, int, WFDBContext>, IProfileControlsTFRepository, ICRUDRepository<ProfileControlsTF, int>
{
public ProfileControlsTFRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileControlsTFs)
{
}
protected override IQueryable<ProfileControlsTF> ReadOnly() => base.ReadOnly().Include(pctf => pctf.Profile).Include(pctf => pctf.User);
protected IQueryable<ProfileControlsTF> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, int? profileId = null, int? userId = null, string? username = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileControlsTF>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}
}

View File

@@ -1,62 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileObjStateRepository : CRUDRepository<ProfileObjState, int, WFDBContext>, IProfileObjStateRepository, ICRUDRepository<ProfileObjState, int>
{
public ProfileObjStateRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileObjStates)
{
}
protected override IQueryable<ProfileObjState> ReadOnly() => base.ReadOnly().Include(pos => pos.Profile).Include(pos => pos.State);
protected IQueryable<ProfileObjState> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, bool withState = true, int? profileId = null, int? userId = null, string? username = null, int? stateId = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (withState)
query = query.Include(pctf => pctf.State);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (stateId is null)
query = query.Where(pctf => pctf.State!.Id == stateId);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}

View File

@@ -1,13 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileRepository : CRUDRepository<Profile, int, WFDBContext>, IProfileRepository, ICRUDRepository<Profile, int>
{
public ProfileRepository(WFDBContext dbContext) : base(dbContext, dbContext.Profiles)
{
}
}

View File

@@ -1,13 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories;
public class StateRepository : CRUDRepository<State, int, WFDBContext>, IStateRepository, ICRUDRepository<State, int>
{
public StateRepository(WFDBContext dbContext) : base(dbContext, dbContext.States)
{
}
}

View File

@@ -3,13 +3,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.9.34622.214 VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlow.Domain", "WorkFlow.Domain\WorkFlow.Domain.csproj", "{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
scripts\GetProfile.sql = scripts\GetProfile.sql
README.md = README.md
EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlow.Infrastructure", "WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj", "{62526D0D-3365-4113-854A-3656191D7C63}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Application", "WorkFlow.Application\WorkFlow.Application.csproj", "{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.API", "src\WorkFlow.API\WorkFlow.API.csproj", "{2B724243-4C79-F3A4-EE25-B9A53C81464C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.API", "WorkFlow.API\WorkFlow.API.csproj", "{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Application", "src\WorkFlow.Application\WorkFlow.Application.csproj", "{F1B4AC83-5137-C20B-641C-1699B46007A0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Domain", "src\WorkFlow.Domain\WorkFlow.Domain.csproj", "{92A11048-6B9C-374E-87A0-BD6D8F864B77}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlow.Infrastructure", "src\WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj", "{94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,26 +25,32 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B724243-4C79-F3A4-EE25-B9A53C81464C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B724243-4C79-F3A4-EE25-B9A53C81464C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B724243-4C79-F3A4-EE25-B9A53C81464C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71E9264E-A2F0-4E5A-B010-8E4618C0C6AC}.Release|Any CPU.Build.0 = Release|Any CPU {2B724243-4C79-F3A4-EE25-B9A53C81464C}.Release|Any CPU.Build.0 = Release|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1B4AC83-5137-C20B-641C-1699B46007A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1B4AC83-5137-C20B-641C-1699B46007A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1B4AC83-5137-C20B-641C-1699B46007A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62526D0D-3365-4113-854A-3656191D7C63}.Release|Any CPU.Build.0 = Release|Any CPU {F1B4AC83-5137-C20B-641C-1699B46007A0}.Release|Any CPU.Build.0 = Release|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {92A11048-6B9C-374E-87A0-BD6D8F864B77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Debug|Any CPU.Build.0 = Debug|Any CPU {92A11048-6B9C-374E-87A0-BD6D8F864B77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Release|Any CPU.ActiveCfg = Release|Any CPU {92A11048-6B9C-374E-87A0-BD6D8F864B77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5700B5DD-D17E-4E17-ADE5-48C95A0CC364}.Release|Any CPU.Build.0 = Release|Any CPU {92A11048-6B9C-374E-87A0-BD6D8F864B77}.Release|Any CPU.Build.0 = Release|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Debug|Any CPU.Build.0 = Debug|Any CPU {94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Release|Any CPU.ActiveCfg = Release|Any CPU {94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FB33592-EF0D-47C3-9CDE-03B2EF12BE00}.Release|Any CPU.Build.0 = Release|Any CPU {94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2B724243-4C79-F3A4-EE25-B9A53C81464C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{F1B4AC83-5137-C20B-641C-1699B46007A0} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{92A11048-6B9C-374E-87A0-BD6D8F864B77} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{94F2D67D-649E-63D2-A3BF-0BEC98C2D7E6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1ECB3995-5040-40BC-BC70-906E64BB4E01} SolutionGuid = {1ECB3995-5040-40BC-BC70-906E64BB4E01}
EndGlobalSection EndGlobalSection

4
scripts/GetProfile.sql Normal file
View File

@@ -0,0 +1,4 @@
--PROFILES
select * from FNMWF_GET_PROFILES (1) --USER_ID
--PROFILE_OBJECTS
SELECT * FROM [FNMWF_GET_PROFILE_OBJECTS] (1,1) --USERID, PROFILE_ID

View File

@@ -0,0 +1,74 @@
using System.Security.Claims;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
public static class ControllerExtensions
{
public static bool TryGetUserId(this ClaimsPrincipal user, out int id) => int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier), out id);
public static bool TryGetUsername(this ClaimsPrincipal user, out string username)
{
var value = user.FindFirstValue(ClaimTypes.Name);
if (value is null)
{
username = string.Empty;
return false;
}
else
{
username = value;
return true;
}
}
public static bool TryGetName(this ClaimsPrincipal user, out string name)
{
var value = user.FindFirstValue(ClaimTypes.Surname);
if (value is null)
{
name = string.Empty;
return false;
}
else
{
name = value;
return true;
}
}
public static bool TryGetPrename(this ClaimsPrincipal user, out string prename)
{
var value = user.FindFirstValue(ClaimTypes.GivenName);
if (value is null)
{
prename = string.Empty;
return false;
}
else
{
prename = value;
return true;
}
}
public static bool TryGetEmail(this ClaimsPrincipal user, out string email)
{
var value = user.FindFirstValue(ClaimTypes.Email);
if (value is null)
{
email = string.Empty;
return false;
}
else
{
email = value;
return true;
}
}
}

View File

@@ -13,7 +13,7 @@ namespace WorkFlow.API.Controllers;
public class PlaceholderAuthController : ControllerBase public class PlaceholderAuthController : ControllerBase
{ {
[HttpPost] [HttpPost]
public IActionResult CreateTokenViaBody([FromBody] Login login) public IActionResult CreateToken([FromBody] Login login)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -0,0 +1,37 @@
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
using WorkFlow.Application.Profiles;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : ControllerBase
{
private readonly ILogger<ProfileController> _logger;
private readonly IMediator _mediator;
public ProfileController(ILogger<ProfileController> logger, IMediator mediator)
{
_logger = logger;
_mediator = mediator;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
if (!User.TryGetUserId(out var userId))
{
_logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return Unauthorized("Failed to retrieve user identity.");
}
var profile = await _mediator.ReadProfileAsync(userId);
return profile is null ? NotFound() : Ok(profile);
}
}

View File

@@ -0,0 +1,42 @@
using DigitalData.Core.Abstraction.Application.DTO;
using DigitalData.UserManager.Application.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
[Obsolete("Use MediatR")]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> logger;
private readonly IUserService userService;
public UserController(ILogger<UserController> logger, IUserService userService)
{
this.logger = logger;
this.userService = userService;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
if (!User.TryGetUserId(out var id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return Unauthorized("Failed to retrieve user identity.");
}
return await userService.ReadByIdAsync(id).ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
}
}

View File

@@ -0,0 +1,84 @@
using DigitalData.Core.Exceptions;
using System.Net;
using System.Text.Json;
namespace WorkFlow.API.Middleware;
//TODO: Fix and use DigitalData.Core.Exceptions.Middleware
/// <summary>
/// Middleware for handling exceptions globally in the application.
/// Captures exceptions thrown during the request pipeline execution,
/// logs them, and returns an appropriate HTTP response with a JSON error message.
/// </summary>
[Obsolete("Use DigitalData.Core.Exceptions.Middleware")]
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
/// </summary>
/// <param name="next">The next middleware in the request pipeline.</param>
/// <param name="logger">The logger instance for logging exceptions.</param>
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
/// <summary>
/// Invokes the middleware to handle the HTTP request.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context); // Continue down the pipeline
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, _logger);
}
}
/// <summary>
/// Handles exceptions by logging them and writing an appropriate JSON response.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <param name="exception">The exception that occurred.</param>
/// <param name="logger">The logger instance for logging the exception.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
context.Response.ContentType = "application/json";
string message;
switch (exception)
{
case BadRequestException badRequestEx:
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
message = badRequestEx.Message;
break;
case NotFoundException notFoundEx:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
message = notFoundEx.Message;
break;
default:
logger.LogError(exception, "Unhandled exception occurred.");
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
message = "An unexpected error occurred.";
break;
}
await context.Response.WriteAsync(JsonSerializer.Serialize(new
{
message
}));
}
}

View File

@@ -1,22 +1,22 @@
using WorkFlow.Application; using DigitalData.Auth.Client;
using DigitalData.UserManager.Application; using DigitalData.Core.Abstractions.Security.Extensions;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Infrastructure;
using DigitalData.Core.API;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using WorkFlow.API.Models; using Microsoft.OpenApi.Models;
using NLog; using NLog;
using NLog.Web; using NLog.Web;
using WorkFlow.API;
using WorkFlow.API.Extensions; using WorkFlow.API.Extensions;
using WorkFlow.API.Filters; using WorkFlow.API.Filters;
using Microsoft.OpenApi.Models; using WorkFlow.API.Middleware;
using DigitalData.Auth.Client; using WorkFlow.API.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer; using WorkFlow.Application;
using WorkFlow.API; using WorkFlow.Infrastructure;
using Microsoft.Extensions.Options;
using DigitalData.Core.Abstractions.Security;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized."); logger.Info("Logging initialized.");
@@ -26,22 +26,38 @@ try
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration; var config = builder.Configuration;
Directory
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
.ToList()
.ForEach(file => config.AddJsonFile(file, true, true));
// Add NLogger // Add NLogger
builder.Logging.ClearProviders(); builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
builder.Host.UseNLog(); if (!builder.Environment.IsDevelopment())
{
builder.Logging.ClearProviders();
builder.Host.UseNLog();
}
// Add services to the container // Add services to the container
var cnn_str = config.GetConnectionString("Default") ?? throw new("Default connection string not found."); var cnn_str = config.GetConnectionString("Default") ?? throw new("Default connection string not found.");
builder.Services.AddDbContext<WFDBContext>(options => options.UseSqlServer(cnn_str).EnableDetailedErrors()); builder.Services.AddDbContext<WFDBContext>(options => options.UseSqlServer(cnn_str).EnableDetailedErrors());
builder.Services.AddWorkFlow().AddUserManager<WFDBContext>(); var mediatRLicense = config["MediatRLicense"]
builder.Services.AddCookieBasedLocalizer(); ?? throw new InvalidOperationException(
"The 'MediatRLicense' configuration value is missing or empty." +
"Please ensure it is properly set in the configuration source.");
builder.Services.AddWorkFlowServices(opt => opt.MediatRLicense = mediatRLicense).AddWorkFlowRepositories();
builder.Services.AddUserManager<WFDBContext>();
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions")); builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor() builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor()
{ {
Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object) Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object)
}); });
bool disableAPIKeyAuth = config.GetValue<bool>("DisableAPIKeyAuth") && builder.IsDevOrDiP(); bool disableAPIKeyAuth = config.GetValue<bool>("DisableAPIKeyAuth");
if (disableAPIKeyAuth) if (disableAPIKeyAuth)
builder.Services.AddAPIKeyAuth(new APIKeyAuthOptions()); builder.Services.AddAPIKeyAuth(new APIKeyAuthOptions());
else else
@@ -135,8 +151,10 @@ try
lazyProvider.Factory = () => app.Services; lazyProvider.Factory = () => app.Services;
app.UseMiddleware<ExceptionHandlingMiddleware>();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.IsDevOrDiP() && app.Configuration.GetValue<bool>("EnableSwagger")) if (app.Configuration.GetValue<bool>("EnableSwagger"))
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
@@ -148,8 +166,6 @@ try
app.UseAuthorization(); app.UseAuthorization();
app.UseCookieBasedLocalizer("de-DE");
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();

View File

@@ -22,7 +22,7 @@
"https": { "https": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7120;http://localhost:5130", "applicationUrl": "https://localhost:7120;http://localhost:5130",
"environmentVariables": { "environmentVariables": {

View File

@@ -20,16 +20,19 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" /> <PackageReference Include="DigitalData.Auth.Client" Version="1.3.7" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.0.0" />
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
<PackageReference Include="NLog" Version="5.3.4" /> <PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="UserManager" Version="1.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\WorkFlow.Application\WorkFlow.Application.csproj" /> <ProjectReference Include="..\WorkFlow.Application\WorkFlow.Application.csproj" />
<ProjectReference Include="..\WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,18 @@
{
"DisableAPIKeyAuth": true,
"APIKeyAuth": {
"Key": "ULbcOUiAXAoCXPviyCGtObZUGnrCHNgDmtNbQNpq5MOhB0EFQn18dObdQ93INNy8xIcnOPMJfEHqOotllELVrJ2R5AjqOfQszT2j00w215GanD3UiJGwFhwmdoNFsmNj",
"HeaderName": "X-API-Key",
"SwaggerDescription": "Required header for API key authentication. Enter a valid API key."
},
"AuthClientParams": {
"Url": "http://172.24.12.39:9090/auth-hub",
"RetryDelay": "00:00:05",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "work-flow.digitaldata.works"
}
]
}
}

View File

@@ -0,0 +1,11 @@
{
"DirectorySearchOptions": {
"ServerName": "DD-VMP01-DC01",
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works",
"UserCacheExpirationDays": 1,
"CustomSearchFilters": {
"User": "(&(objectClass=user)(sAMAccountName=*))",
"Group": "(&(objectClass=group) (samAccountName=*))"
}
}
}

View File

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

View File

@@ -0,0 +1,17 @@
{
"DiPMode": true,
"EnableSwagger": true,
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"OpenApiInfo": {
"Title": "WorkFlow API",
"Contact": {
"Email": "info-flow@digitaldata.works",
"Name": "Digital Data GmbH",
"Url": "https://digitaldata.works/"
}
},
"MediatRLicense": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx1Y2t5UGVubnlTb2Z0d2FyZUxpY2Vuc2VLZXkvYmJiMTNhY2I1OTkwNGQ4OWI0Y2IxYzg1ZjA4OGNjZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2x1Y2t5cGVubnlzb2Z0d2FyZS5jb20iLCJhdWQiOiJMdWNreVBlbm55U29mdHdhcmUiLCJleHAiOiIxNzg0ODUxMjAwIiwiaWF0IjoiMTc1MzM2MjQ5MSIsImFjY291bnRfaWQiOiIwMTk4M2M1OWU0YjM3MjhlYmZkMzEwM2MyYTQ4NmU4NSIsImN1c3RvbWVyX2lkIjoiY3RtXzAxazB5NmV3MmQ4YTk4Mzg3aDJnbTRuOWswIiwic3ViX2lkIjoiLSIsImVkaXRpb24iOiIwIiwidHlwZSI6IjIifQ.ZqsFG7kv_-xGfxS6ACk3i0iuNiVUXX2AvPI8iAcZ6-z2170lGv__aO32tWpQccD9LCv5931lBNLWSblKS0MT3gOt-5he2TEftwiSQGFwoIBgtOHWsNRMinUrg2trceSp3IhyS3UaMwnxZDrCvx4-0O-kpOzVpizeHUAZNr5U7oSCWO34bpKdae6grtM5e3f93Z1vs7BW_iPgItd-aLvPwApbaG9VhmBTKlQ7b4Jh64y7UXJ9mKP7Qb_Oa97oEg0oY5DPHOWTZWeE1EzORgVr2qkK2DELSHuZ_EIUhODojkClPNAKtvEl_qEjpq0HZCIvGwfCCRlKlSkQqIeZdFkiXg"
}

View File

@@ -0,0 +1,28 @@
using MediatR;
namespace WorkFlow.Application.Buttons;
public record ButtonDto
{
public int Id { get; init; }
public required int ProfileId { get; init; }
public byte? DialogNo { get; init; }
public string? BtnType { get; init; }
public string? Icon { get; init; }
public string? ForeColor { get; init; }
public string? BackColor { get; init; }
public string? Command { get; init; }
public string? DialogCommand { get; init; }
public string? ConfirmationText { get; init; }
}
public record ReadButtonRequest : IRequest<ButtonDto>;

View File

@@ -0,0 +1,23 @@
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts.Repositories;
/// <summary>
/// Repository for retrieving <see cref="ProfileObject"/> entities from the database.
/// </summary>
public interface IProfileObjRepository
{
/// <summary>
/// Retrieves the list of <see cref="ProfileObject"/> associated with a given user ID and profile ID by calling a database function.
/// </summary>
/// <param name="userId">The unique identifier of the user whose profile is to be retrieved.</param>
/// <param name="profileId">The unique identifier of the profile whose object is to be retrieved.</param>
/// <param name="cancel">Propagates notification that operations should be canceled.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the <see cref="ProfileObject"/> object if found; otherwise, <c>null</c>.
/// </returns>
/// <remarks>
/// Logs an error if no profile is found, or if multiple profiles are returned, indicating potential data issues.
/// </remarks>
public Task<IEnumerable<ProfileObject>> ReadAsync(int userId, int profileId, CancellationToken cancel = default);
}

View File

@@ -0,0 +1,22 @@
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Contracts.Repositories;
/// <summary>
/// Repository implementation for retrieving <see cref="Profile"/> entities from the database.
/// </summary>
public interface IProfileRepository
{
/// <summary>
/// Retrieves the <see cref="Profile"/> associated with a given user ID by calling a database function.
/// </summary>
/// <param name="userId">The unique identifier of the user whose profile is to be retrieved.</param>
/// <param name="cancel">Propagates notification that operations should be canceled.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the <see cref="Profile"/> object if found; otherwise, <c>null</c>.
/// </returns>
/// <remarks>
/// Logs an error if no profile is found, or if multiple profiles are returned, indicating potential data issues.
/// </remarks>
Task<Profile?> ReadAsync(int userId, CancellationToken cancel = default);
}

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace WorkFlow.Application.DTO namespace WorkFlow.Application.Dto
{ {
public record BaseCreateDto public record BaseCreateDto
{ {

View File

@@ -1,9 +1,8 @@
using DigitalData.Core.Abstractions; using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace WorkFlow.Application.DTO namespace WorkFlow.Application.Dto
{ {
public record BaseUpdateDto : IUnique<int> public record BaseUpdateDto
{ {
public required int Id { get; init; } public required int Id { get; init; }

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.Config namespace WorkFlow.Application.Dto.Config
{ {
public record ConfigCreateDto(string Title, string String); public record ConfigCreateDto(string Title, string String);
} }

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.Config namespace WorkFlow.Application.Dto.Config
{ {
public record ConfigDto(int Id, public record ConfigDto(int Id,
string Title, string Title,

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.Config namespace WorkFlow.Application.Dto.Config
{ {
public record ConfigUpdateDto(string Title, string String) : BaseUpdateDto; public record ConfigUpdateDto(string Title, string String) : BaseUpdateDto;
} }

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.ProfileControlsTF namespace WorkFlow.Application.Dto.ProfileControlsTF
{ {
public record ProfileControlsTFCreateDto( public record ProfileControlsTFCreateDto(
int ProfileId, int ProfileId,

View File

@@ -1,7 +1,6 @@
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
using WorkFlow.Application.DTO.Profile;
namespace WorkFlow.Application.DTO.ProfileControlsTF namespace WorkFlow.Application.Dto.ProfileControlsTF
{ {
public record ProfileControlsTFDto(int Id, public record ProfileControlsTFDto(int Id,
int ProfileId, int ProfileId,
@@ -16,6 +15,5 @@ namespace WorkFlow.Application.DTO.ProfileControlsTF
string AddedWho, string AddedWho,
DateTime AddedWhen, DateTime AddedWhen,
string? ChoiceList = null, string? ChoiceList = null,
ProfileDto? Profile = null,
UserReadDto? User = null); UserReadDto? User = null);
} }

View File

@@ -0,0 +1,6 @@
namespace WorkFlow.Application.Dto.ProfileControlsTF;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileControlsTFUpdateDto(long Id);

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.ProfileObjState namespace WorkFlow.Application.Dto.ProfileObjState
{ {
public record ProfileObjStateCreateDto( public record ProfileObjStateCreateDto(
int ProfileId, int ProfileId,

View File

@@ -1,8 +1,7 @@
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
using WorkFlow.Application.DTO.Profile; using WorkFlow.Application.Dto.State;
using WorkFlow.Application.DTO.State;
namespace WorkFlow.Application.DTO.ProfileObjState namespace WorkFlow.Application.Dto.ProfileObjState
{ {
public record ProfileObjStateDto(int Id, public record ProfileObjStateDto(int Id,
int ProfileId, int ProfileId,
@@ -14,7 +13,6 @@ namespace WorkFlow.Application.DTO.ProfileObjState
string? State2 = null, string? State2 = null,
string? State3 = null, string? State3 = null,
string? State4 = null, string? State4 = null,
ProfileDto? Profile = null,
UserReadDto? User = null, UserReadDto? User = null,
StateDto? State = null); StateDto? State = null);
} }

View File

@@ -0,0 +1,6 @@
namespace WorkFlow.Application.Dto.ProfileObjState;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record ProfileObjStateUpdateDto(long Id);

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.State namespace WorkFlow.Application.Dto.State
{ {
public record StateCreateDto(string IntlState) : BaseCreateDto; public record StateCreateDto(string IntlState) : BaseCreateDto;
} }

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.Application.DTO.State namespace WorkFlow.Application.Dto.State
{ {
public record StateDto(int Id, string IntlState, string AddedWho, DateTime AddedWhen); public record StateDto(int Id, string IntlState, string AddedWho, DateTime AddedWhen);
} }

View File

@@ -0,0 +1,6 @@
namespace WorkFlow.Application.Dto.State;
/// <summary>
/// This Data Transfer Object (DTO) serves as a placeholder and does not support updates.
/// </summary>
public record StateUpdateDto(int Id);

View File

@@ -0,0 +1,26 @@
using Microsoft.Extensions.DependencyInjection;
namespace WorkFlow.Application;
public static class DependencyInjection
{
public static IServiceCollection AddWorkFlowServices(this IServiceCollection services, Action<WorkFlowServiceOptions>? options = null)
{
WorkFlowServiceOptions diOptions = new();
options?.Invoke(diOptions);
services.AddAutoMapper(typeof(MappingProfile).Assembly);
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly);
cfg.LicenseKey = diOptions.MediatRLicense;
});
return services;
}
public class WorkFlowServiceOptions
{
public string MediatRLicense { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,14 @@
namespace WorkFlow.Application.Dto;
public class ObjectDto
{
public long? ObjStateId { get; set; }
public long? Id { get; set; }
public IEnumerable<string> Headlines { get; set; } = Array.Empty<string>();
public IEnumerable<string> Sublines { get; set; } = Array.Empty<string>();
public string? CmdCheckIn { get; set; }
}

View File

@@ -0,0 +1,24 @@
using WorkFlow.Application.Buttons;
namespace WorkFlow.Application.Dto;
public class ProfileDto
{
public int? Id { get; init; }
public byte? TypeId { get; init; }
public string? Caption { get; init; }
public string? Subtitle { get; init; }
public int? CountObj { get; init; }
public string? ForeColor { get; init; }
public string? BackColor { get; init; }
public IEnumerable<ObjectDto> Objects { get; init; } = Array.Empty<ObjectDto>();
public IEnumerable<ButtonDto>? Buttons { get; set; } = Array.Empty<ButtonDto>();
}

View File

@@ -0,0 +1,41 @@
using WorkFlow.Application.Buttons;
using WorkFlow.Application.Dto;
using WorkFlow.Application.Dto.Config;
using WorkFlow.Application.Dto.ProfileControlsTF;
using WorkFlow.Application.Dto.ProfileObjState;
using WorkFlow.Application.Dto.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application;
public class MappingProfile : AutoMapper.Profile
{
public MappingProfile()
{
// Mapping entity to DTO
CreateMap<Config, ConfigDto>();
CreateMap<Profile, ProfileDto>();
CreateMap<ProfileControlsTF, ProfileControlsTFDto>();
CreateMap<ProfileObjState, ProfileObjStateDto>();
CreateMap<State, StateDto>();
CreateMap<Button, ButtonDto>();
CreateMap<ProfileObject, ObjectDto>()
.ForMember(dest => dest.Headlines, opt => opt.MapFrom(src =>
new[] { src.Headline1, src.Headline2 }))
.ForMember(dest => dest.Sublines, opt => opt.MapFrom(src =>
new[] { src.Subline1, src.Subline2 }));
// Mapping create-DTO to entity
CreateMap<ConfigCreateDto, Config>();
CreateMap<ProfileControlsTFCreateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateCreateDto, ProfileObjState>();
CreateMap<StateCreateDto, State>();
// Mapping update-DTO to entity
CreateMap<ConfigUpdateDto, Config>();
CreateMap<ProfileControlsTFUpdateDto, ProfileControlsTF>();
CreateMap<ProfileObjStateUpdateDto, ProfileObjState>();
CreateMap<StateUpdateDto, State>();
}
}

View File

@@ -0,0 +1,73 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using MediatR;
using WorkFlow.Application.Buttons;
using WorkFlow.Application.Contracts.Repositories;
using WorkFlow.Application.Dto;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Application.Profiles;
/// <summary>
/// Represents a request to read a user profile by their user ID.
/// </summary>
/// <param name="UserId">The ID of the user whose profile is being requested.</param>
public record ReadProfileQuery(int UserId, bool IncludeObject = true) : IRequest<ProfileDto?>;
/// <summary>
/// Handles the <see cref="ReadProfileQuery"/> request by retrieving the user profile
/// from the data store using the <see cref="IProfileRepository"/>.
/// </summary>
public class ReadProfileHandler : IRequestHandler<ReadProfileQuery, ProfileDto?>
{
private readonly IProfileRepository _profileRepository;
private readonly IProfileObjRepository _objRepository;
private readonly IRepository<Button> _bttnRepository;
private readonly IMapper _mapper;
/// <summary>
/// Initializes a new instance of the <see cref="ReadProfileHandler"/> class.
/// </summary>
/// <param name="profileRepository">The profile repository used to access profile data.</param>
/// <param name="objRepository">The profile object repository used to access object data.</param>
public ReadProfileHandler(IProfileRepository profileRepository, IProfileObjRepository objRepository, IRepository<Button> buttonRepository, IMapper mapper)
{
_profileRepository = profileRepository;
_objRepository = objRepository;
_bttnRepository = buttonRepository;
_mapper = mapper;
}
/// <summary>
/// Handles the <see cref="ReadProfileQuery"/> request by retrieving the profile
/// corresponding to the specified user ID.
/// </summary>
/// <param name="request">The request containing the user ID.</param>
/// <param name="cancel">A cancellation token for the operation.</param>
/// <returns>The user profile if found; otherwise, <c>null</c>.</returns>
public async Task<ProfileDto?> Handle(ReadProfileQuery request, CancellationToken cancel = default)
{
var profile = await _profileRepository.ReadAsync(request.UserId, cancel);
if (request.IncludeObject && profile?.Id is int profileId)
profile.Objects = await _objRepository.ReadAsync(request.UserId, profileId, cancel);
var profileDto = _mapper.Map<ProfileDto>(profile);
if (profile?.Id is int pId)
{
var bttns = await _bttnRepository.Read(b => b.ProfileId == pId).ToListAsync(cancel);
profileDto.Buttons = _mapper.Map<IEnumerable<ButtonDto>>(bttns);
}
return profileDto;
}
}
public static class ReadProfileQueryExtensions
{
public static Task<ProfileDto?> ReadProfileAsync(this IMediator mediator, int userId, bool includeObject = true)
=> mediator.Send(new ReadProfileQuery(UserId: userId, IncludeObject: includeObject));
}

View File

@@ -7,13 +7,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Application" Version="3.2.0" /> <PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.0.0" />
<PackageReference Include="UserManager.Application" Version="3.1.2" /> <PackageReference Include="DigitalData.Core.Application" Version="3.3.4" />
<PackageReference Include="MediatR" Version="13.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.20" />
<PackageReference Include="UserManager" Version="1.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\WorkFlow.Domain\WorkFlow.Domain.csproj" /> <ProjectReference Include="..\WorkFlow.Domain\WorkFlow.Domain.csproj" />
<ProjectReference Include="..\WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,115 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
[Table("TBMWF_PROF_BUTTONS")]
public class Button
{
[Key]
[Column("GUID", TypeName = "int")]
public int Id { get; set; }
[Required]
[Column("MWF_PROFILE_ID", TypeName = "int")]
public required int ProfileId { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("DIALOG_NO", TypeName = "tinyint")]
public byte? DialogNo { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("BTN_TYPE", TypeName = "nvarchar(20)")]
public string? BtnType { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("TEXT", TypeName = "nvarchar(500)")]
public string? Text { get; set; }
[Column("ICON", TypeName = "nvarchar(100)")]
public string? Icon { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("FORE_COLOR", TypeName = "nvarchar(100)")]
public string? ForeColor { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("BACK_COLOR", TypeName = "nvarchar(100)")]
public string? BackColor { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("COMMAND", TypeName = "nvarchar(max)")]
public string? Command { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("DIALOG_COMMAND", TypeName = "nvarchar(max)")]
public string? DialogCommand { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("CONFIRMATION_TEXT", TypeName = "nvarchar(250)")]
public string? ConfirmationText { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("ADDED_WHO", TypeName = "nvarchar(100)")]
public string? AddedWho { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("ADDED_WHEN", TypeName = "datetime")]
public DateTime? AddedWhen { get; set; }
[Column("CHANGED_WHO", TypeName = "nvarchar(100)")]
public string? ChangedWho { get; set; }
[Column("CHANGED_WHEN", TypeName = "datetime")]
public DateTime? ChangedWhen { get; set; }
}

View File

@@ -1,11 +1,10 @@
using DigitalData.Core.Abstractions; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities namespace WorkFlow.Domain.Entities
{ {
[Table("TBMWF_CONFIG", Schema = "dbo")] [Table("TBMWF_CONFIG", Schema = "dbo")]
public class Config : IUnique<int> public class Config
{ {
[Key] [Key]
[Column("GUID")] [Column("GUID")]

View File

@@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
public class Profile
{
[Column("PROFILE_ID")]
public int? Id { get; set; }
[Column("TYPE_ID")]
public byte? TypeId { get; set; }
[Column("CAPTION")]
public string? Caption { get; set; }
[Column("SUBTITLE")]
public string? Subtitle { get; set; }
[Column("COUNTOBJ")]
public int? CountObj { get; set; }
[Column("FORE_COLOR")]
public string? ForeColor { get; set; }
[Column("BACK_COLOR")]
public string? BackColor { get; set; }
[NotMapped]
public IEnumerable<ProfileObject>? Objects { get; set; }
}

View File

@@ -0,0 +1,102 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
[Table("TBMWF_PROF_CONTROLS_TF", Schema = "dbo")]
public class ProfileControlsTF
{
[Key]
[Column("GUID", TypeName = "bigint")]
public long Id { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("OBJ_STATE_ID", TypeName = "bigint")]
public long? StateId { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("DIALOG_NO", TypeName = "tinyint")]
public byte? DialogNo { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("ATTR_NAME", TypeName = "nvarchar(100)")]
public string? AttrName { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("CTRL_TYPE", TypeName = "nvarchar(10)")]
public string? CtrlType { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("CAPTION", TypeName = "nvarchar(100)")]
public string? Caption { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("TEXT", TypeName = "nvarchar(500)")]
public string? Text { get; set; }
[Column("ICON", TypeName = "nvarchar(100)")]
public string? Icon { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("MANDATORY", TypeName = "bit")]
public bool? Mandatory { get; set; }
[Column("CHOICE_LIST", TypeName = "nvarchar(max)")]
public string? ChoiceList { get; set; }
/// <summary>
/// Although this field is marked as <c>nullable</c> in the database schema to allow
/// for greater flexibility during database-level operations or migrations, it is
/// treated as <c>required</c> and <c>not null</c> within the application logic.
/// Validation should be enforced at the application level to ensure a value is provided.
/// </summary>
[Column("READ_ONLY", TypeName = "bit")]
public bool? ReadOnly { get; set; }
[Column("SEQU", TypeName = "tinyint")]
public byte? Sequ { get; set; } = 0;
[Column("ADDED_WHO", TypeName = "nvarchar(100)")]
public required string AddedWho { get; set; }
[Column("ADDED_WHEN", TypeName = "datetime")]
public DateTime AddedWhen { get; set; }
[ForeignKey("StateId")]
public virtual ProfileObjState? State { get; set; }
}

View File

@@ -0,0 +1,55 @@
using DigitalData.UserManager.Domain.Entities;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
[Table("TBMWF_PROFILE_OBJ_STATE", Schema = "dbo")]
public class ProfileObjState
{
[Key]
[Column("GUID")]
public long Id { get; init; }
[Required]
[Column("MWF_PROFILE_ID")]
public required int ProfileId { get; init; }
[Required]
[Column("USR_ID")]
public required int UserId { get; init; }
[Required]
[Column("OBJ_ID")]
public required long ObjId { get; init; }
[Required]
[Column("STATE_ID")]
public required int StateId { get; init; }
[Column("STATE2", TypeName = "nvarchar(3000)")]
public string? State2 { get; init; }
[Column("STATE3", TypeName = "nvarchar(3000)")]
public string? State3 { get; init; }
[Column("STATE4", TypeName = "nvarchar(3000)")]
public string? State4 { get; init; }
[Required]
[Column("ADDED_WHO", TypeName = "varchar(30)")]
public required string AddedWho { get; init; }
[Required]
[Column("ADDED_WHEN", TypeName = "datetime")]
public required DateTime AddedWhen { get; init; }
[ForeignKey("ProfileId")]
public Profile? Profile { get; init; } = null;
[ForeignKey("UserId")]
public User? User { get; init; } = null;
[ForeignKey("StateId")]
public State? State { get; init; } = null;
}

View File

@@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities;
public class ProfileObject
{
[Column("ObjStateID")]
public long? ObjStateId { get; set; }
[Column("ObjectID")]
public long? Id { get; set; }
[Column("Headline1")]
public string? Headline1 { get; set; }
[Column("Headline2")]
public string? Headline2 { get; set; }
[Column("Subline1")]
public string? Subline1 { get; set; }
[Column("Subline2")]
public string? Subline2 { get; set; }
[Column("CMD_CheckIn")]
public string? CmdCheckIn { get; set; }
}

View File

@@ -1,11 +1,10 @@
using DigitalData.Core.Abstractions; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace WorkFlow.Domain.Entities namespace WorkFlow.Domain.Entities
{ {
[Table("TBMWF_WF_STATE", Schema = "dbo")] [Table("TBMWF_WF_STATE", Schema = "dbo")]
public class State : IUnique<int> public class State
{ {
[Key] [Key]
[Column("GUID")] [Column("GUID")]

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="UserManager.Domain" Version="3.0.1" /> <PackageReference Include="UserManager" Version="1.1.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,29 @@
using DigitalData.Core.Infrastructure;
using DigitalData.Core.Infrastructure.AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using WorkFlow.Application.Contracts.Repositories;
using WorkFlow.Application.Dto.Config;
using WorkFlow.Application.Dto.ProfileControlsTF;
using WorkFlow.Application.Dto.State;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Repositories;
namespace WorkFlow.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddWorkFlowRepositories(this IServiceCollection services)
{
services.TryAddScoped<IProfileRepository, ProfileRepository>();
services.TryAddScoped<IProfileObjRepository, ProfileObjRepository>();
services.AddDbRepository<WFDBContext, Config>(c => c.Configs).UseAutoMapper(typeof(ConfigUpdateDto));
services.AddDbRepository<WFDBContext, ProfileControlsTF>(c => c.ProfileControlsTFs).UseAutoMapper(typeof(ProfileControlsTFUpdateDto));
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper(typeof(StateUpdateDto));
services.AddDbRepository<WFDBContext, Button>(c => c.Buttons).UseAutoMapper();
services.AddDbRepository<WFDBContext, State>(c => c.States).UseAutoMapper(typeof(StateUpdateDto));
return services;
}
}

Some files were not shown because too many files have changed in this diff Show More