chore: alle Projekte in das Verzeichnis src verschieben
This commit is contained in:
14
src/WorkFlow.API/Attributes/APIKeyAuthAttribute.cs
Normal file
14
src/WorkFlow.API/Attributes/APIKeyAuthAttribute.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WorkFlow.API.Filters;
|
||||
|
||||
namespace WorkFlow.API.Attributes
|
||||
{
|
||||
//TODO: move APIKeyAuthAttribute to Core.API
|
||||
public class APIKeyAuthAttribute : ServiceFilterAttribute
|
||||
{
|
||||
public APIKeyAuthAttribute()
|
||||
: base(typeof(APIKeyAuthFilter))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/WorkFlow.API/Controllers/ConfigController.cs
Normal file
20
src/WorkFlow.API/Controllers/ConfigController.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
96
src/WorkFlow.API/Controllers/ControllerExtensions.cs
Normal file
96
src/WorkFlow.API/Controllers/ControllerExtensions.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/WorkFlow.API/Controllers/PlaceholderAuthController.cs
Normal file
24
src/WorkFlow.API/Controllers/PlaceholderAuthController.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WorkFlow.API.Models;
|
||||
using WorkFlow.API.Attributes;
|
||||
|
||||
namespace WorkFlow.API.Controllers;
|
||||
|
||||
//TODO: implement up-to-date AuthController in UserManager
|
||||
[APIKeyAuth]
|
||||
[Route("api/Auth")]
|
||||
[ApiController]
|
||||
[Tags("Auth")]
|
||||
public class PlaceholderAuthController : ControllerBase
|
||||
{
|
||||
[HttpPost]
|
||||
public IActionResult CreateTokenViaBody([FromBody] Login login)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[HttpGet("check")]
|
||||
[Authorize]
|
||||
public IActionResult Check() => Ok();
|
||||
}
|
||||
20
src/WorkFlow.API/Controllers/ProfileController.cs
Normal file
20
src/WorkFlow.API/Controllers/ProfileController.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
126
src/WorkFlow.API/Controllers/ProfileControlsTFController.cs
Normal file
126
src/WorkFlow.API/Controllers/ProfileControlsTFController.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
127
src/WorkFlow.API/Controllers/ProfileObjStateController.cs
Normal file
127
src/WorkFlow.API/Controllers/ProfileObjStateController.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/WorkFlow.API/Controllers/StateController.cs
Normal file
20
src/WorkFlow.API/Controllers/StateController.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
54
src/WorkFlow.API/Controllers/UserController.cs
Normal file
54
src/WorkFlow.API/Controllers/UserController.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/WorkFlow.API/Extensions/DIExtensions.cs
Normal file
22
src/WorkFlow.API/Extensions/DIExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WorkFlow.API.Filters;
|
||||
using WorkFlow.API.Models;
|
||||
|
||||
namespace WorkFlow.API.Extensions
|
||||
{
|
||||
public static class DIExtensions
|
||||
{
|
||||
public static IServiceCollection AddAPIKeyAuth(this IServiceCollection services, Func<string?, bool> isValidKey, string headerName = "X-API-Key")
|
||||
=> services.AddSingleton<APIKeyAuthFilter>(provider => new(isValidKey: isValidKey, headerName: headerName));
|
||||
|
||||
public static IServiceCollection AddAPIKeyAuth(this IServiceCollection services, APIKeyAuthOptions options, bool configureOptions = true)
|
||||
{
|
||||
if(configureOptions)
|
||||
services.TryAddSingleton(Options.Create(options));
|
||||
|
||||
return services.AddAPIKeyAuth(isValidKey: key => options.Key is null || options.Key == key, headerName: options.HeaderName);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/WorkFlow.API/Filters/APIKeyAuthFilter.cs
Normal file
22
src/WorkFlow.API/Filters/APIKeyAuthFilter.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WorkFlow.API.Filters;
|
||||
|
||||
public class APIKeyAuthFilter : IAuthorizationFilter
|
||||
{
|
||||
private readonly Func<string?, bool> isValidKey;
|
||||
private readonly string headerName;
|
||||
|
||||
public APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key")
|
||||
{
|
||||
this.isValidKey = isValidKey;
|
||||
this.headerName = headerName;
|
||||
}
|
||||
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
if (!isValidKey(context.HttpContext.Request.Headers[headerName]))
|
||||
context.Result = new UnauthorizedResult();
|
||||
}
|
||||
}
|
||||
42
src/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs
Normal file
42
src/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using WorkFlow.API.Models;
|
||||
|
||||
namespace WorkFlow.API.Filters;
|
||||
|
||||
public class APIKeyAuthHeaderOpFilter : IOperationFilter
|
||||
{
|
||||
private readonly APIKeyAuthOptions apiKeyAuthOptions;
|
||||
private readonly IWebHostEnvironment environment;
|
||||
|
||||
public APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> options, IWebHostEnvironment environment)
|
||||
{
|
||||
this.environment = environment;
|
||||
apiKeyAuthOptions = options.Value;
|
||||
}
|
||||
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
var param = new OpenApiParameter
|
||||
{
|
||||
Name = apiKeyAuthOptions.HeaderName,
|
||||
In = ParameterLocation.Header,
|
||||
Required = true,
|
||||
AllowEmptyValue = false,
|
||||
Schema = new OpenApiSchema
|
||||
{
|
||||
Type = "string"
|
||||
}
|
||||
};
|
||||
|
||||
if(environment.IsDevelopment())
|
||||
param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key);
|
||||
|
||||
if (apiKeyAuthOptions.SwaggerDescription is not null)
|
||||
param.Description = apiKeyAuthOptions.SwaggerDescription;
|
||||
|
||||
operation.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
10
src/WorkFlow.API/Jenkinsfile
vendored
Normal file
10
src/WorkFlow.API/Jenkinsfile
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'dotnet build'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/WorkFlow.API/LazyServiceProvider.cs
Normal file
18
src/WorkFlow.API/LazyServiceProvider.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace WorkFlow.API;
|
||||
|
||||
public class LazyServiceProvider : IServiceProvider
|
||||
{
|
||||
private Lazy<IServiceProvider>? _serviceProvider;
|
||||
|
||||
public Func<IServiceProvider> Factory
|
||||
{
|
||||
set => _serviceProvider = new(value);
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType)
|
||||
{
|
||||
if (_serviceProvider is null)
|
||||
throw new InvalidOperationException("GetService cannot be called before _serviceProvider is set.");
|
||||
return _serviceProvider.Value.GetService(serviceType);
|
||||
}
|
||||
}
|
||||
11
src/WorkFlow.API/Models/APIKeyAuthOptions.cs
Normal file
11
src/WorkFlow.API/Models/APIKeyAuthOptions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace WorkFlow.API.Models
|
||||
{
|
||||
public class APIKeyAuthOptions
|
||||
{
|
||||
public string? Key { get; init; } = null;
|
||||
|
||||
public string HeaderName { get; init; } = "X-API-Key";
|
||||
|
||||
public string? SwaggerDescription { get; init; } = null;
|
||||
}
|
||||
}
|
||||
12
src/WorkFlow.API/Models/AuthTokenKeys.cs
Normal file
12
src/WorkFlow.API/Models/AuthTokenKeys.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace WorkFlow.API.Models;
|
||||
|
||||
public class AuthTokenKeys
|
||||
{
|
||||
public string Cookie { get; init; } = "AuthToken";
|
||||
|
||||
public string QueryString { get; init; } = "AuthToken";
|
||||
|
||||
public string Issuer { get; init; } = "auth.digitaldata.works";
|
||||
|
||||
public string Audience { get; init; } = "work-flow.digitaldata.works";
|
||||
}
|
||||
4
src/WorkFlow.API/Models/Login.cs
Normal file
4
src/WorkFlow.API/Models/Login.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.API.Models
|
||||
{
|
||||
public record Login(int? UserId, string? Username, string Password);
|
||||
}
|
||||
19
src/WorkFlow.API/Models/ModelExtensions.cs
Normal file
19
src/WorkFlow.API/Models/ModelExtensions.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using DigitalData.UserManager.Application.DTOs.User;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace WorkFlow.API.Models
|
||||
{
|
||||
public static class ModelExtensions
|
||||
{
|
||||
public static List<Claim> ToClaimList(this UserReadDto user) => new()
|
||||
{
|
||||
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new (ClaimTypes.Name, user.Username),
|
||||
new (ClaimTypes.Surname, user.Name ?? ""),
|
||||
new (ClaimTypes.GivenName, user.Prename ?? ""),
|
||||
new (ClaimTypes.Email, user.Email ?? "")
|
||||
};
|
||||
|
||||
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value);
|
||||
}
|
||||
}
|
||||
161
src/WorkFlow.API/Program.cs
Normal file
161
src/WorkFlow.API/Program.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using WorkFlow.Application;
|
||||
using DigitalData.UserManager.Application;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WorkFlow.Infrastructure;
|
||||
using DigitalData.Core.API;
|
||||
using DigitalData.Core.Application;
|
||||
using DigitalData.UserManager.Application.DTOs.User;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using WorkFlow.API.Models;
|
||||
using NLog;
|
||||
using NLog.Web;
|
||||
using WorkFlow.API.Extensions;
|
||||
using WorkFlow.API.Filters;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using DigitalData.Auth.Client;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using WorkFlow.API;
|
||||
using Microsoft.Extensions.Options;
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Info("Logging initialized.");
|
||||
|
||||
try
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var config = builder.Configuration;
|
||||
|
||||
// Add NLogger
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Host.UseNLog();
|
||||
|
||||
// Add services to the container
|
||||
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.AddWorkFlow().AddUserManager<WFDBContext>();
|
||||
builder.Services.AddCookieBasedLocalizer();
|
||||
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
|
||||
builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor()
|
||||
{
|
||||
Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object)
|
||||
});
|
||||
|
||||
bool disableAPIKeyAuth = config.GetValue<bool>("DisableAPIKeyAuth") && builder.IsDevOrDiP();
|
||||
if (disableAPIKeyAuth)
|
||||
builder.Services.AddAPIKeyAuth(new APIKeyAuthOptions());
|
||||
else
|
||||
if (config.GetSection("APIKeyAuth").Get<APIKeyAuthOptions>() is APIKeyAuthOptions options)
|
||||
builder.Services.AddAPIKeyAuth(options);
|
||||
else
|
||||
throw new("The API Key Authorization configuration is not available in the app settings, even though the app is not in development or DiP mode and API Key Authorization is not disabled.");
|
||||
|
||||
var lazyProvider = new LazyServiceProvider();
|
||||
|
||||
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get<AuthTokenKeys>() ?? new();
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(opt =>
|
||||
{
|
||||
opt.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
|
||||
{
|
||||
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
|
||||
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
|
||||
return new List<SecurityKey>() { publicKey.SecurityKey };
|
||||
},
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = authTokenKeys.Issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = authTokenKeys.Audience,
|
||||
};
|
||||
|
||||
opt.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
// if there is no token read related cookie or query string
|
||||
if (context.Token is null) // if there is no token
|
||||
{
|
||||
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
|
||||
context.Token = cookieToken;
|
||||
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
|
||||
context.Token = queryStrToken;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(setupAct =>
|
||||
{
|
||||
setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "Bearer"
|
||||
});
|
||||
|
||||
setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
Array.Empty<string>()
|
||||
}
|
||||
});
|
||||
|
||||
if (!disableAPIKeyAuth)
|
||||
setupAct.OperationFilter<APIKeyAuthHeaderOpFilter>();
|
||||
|
||||
if (config.GetSection("OpenApiInfo").Get<OpenApiInfo>() is OpenApiInfo openApiInfo)
|
||||
setupAct.SwaggerDoc(openApiInfo?.Version ?? "v1", openApiInfo);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
lazyProvider.Factory = () => app.Services;
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.IsDevOrDiP() && app.Configuration.GetValue<bool>("EnableSwagger"))
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCookieBasedLocalizer("de-DE");
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "Stopped program because of exception.");
|
||||
throw;
|
||||
}
|
||||
41
src/WorkFlow.API/Properties/launchSettings.json
Normal file
41
src/WorkFlow.API/Properties/launchSettings.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:56180",
|
||||
"sslPort": 44397
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5130",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7120;http://localhost:5130",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": false,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/WorkFlow.API/WFKey.cs
Normal file
8
src/WorkFlow.API/WFKey.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace WorkFlow.API
|
||||
{
|
||||
public static class WFKey
|
||||
{
|
||||
public static readonly string WrongPassword = nameof(WrongPassword);
|
||||
public static readonly string UserNotFoundOrWrongPassword = nameof(UserNotFoundOrWrongPassword);
|
||||
}
|
||||
}
|
||||
35
src/WorkFlow.API/WorkFlow.API.csproj
Normal file
35
src/WorkFlow.API/WorkFlow.API.csproj
Normal file
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PackageId>WorkFlow.API</PackageId>
|
||||
<Version>1.1.0</Version>
|
||||
<Company>Digital Data GmbH</Company>
|
||||
<Product>WorkFlow.API</Product>
|
||||
<Title>WorkFlow.API</Title>
|
||||
<AssemblyVersion>1.1.0</AssemblyVersion>
|
||||
<FileVersion>1.1.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfile - Copy.pubxml" />
|
||||
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet7.pubxml" />
|
||||
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet8.pubxml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" />
|
||||
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
|
||||
<PackageReference Include="NLog" Version="5.3.4" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkFlow.Application\WorkFlow.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
8
src/WorkFlow.API/appsettings.Development.json
Normal file
8
src/WorkFlow.API/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
91
src/WorkFlow.API/appsettings.json
Normal file
91
src/WorkFlow.API/appsettings.json
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
10
src/WorkFlow.Application/Contracts/IConfigService.cs
Normal file
10
src/WorkFlow.Application/Contracts/IConfigService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
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>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
10
src/WorkFlow.Application/Contracts/IProfileService.cs
Normal file
10
src/WorkFlow.Application/Contracts/IProfileService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
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>
|
||||
{
|
||||
}
|
||||
}
|
||||
10
src/WorkFlow.Application/Contracts/IStateService.cs
Normal file
10
src/WorkFlow.Application/Contracts/IStateService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
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>
|
||||
{
|
||||
}
|
||||
}
|
||||
25
src/WorkFlow.Application/DIExtensions.cs
Normal file
25
src/WorkFlow.Application/DIExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
13
src/WorkFlow.Application/DTO/BaseCreateDto.cs
Normal file
13
src/WorkFlow.Application/DTO/BaseCreateDto.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkFlow.Application.DTO
|
||||
{
|
||||
public record BaseCreateDto
|
||||
{
|
||||
[JsonIgnore]
|
||||
public required string AddedWho { get; set; } = "UNKNOWN";
|
||||
|
||||
[JsonIgnore]
|
||||
public required DateTime AddedWhen = DateTime.Now;
|
||||
}
|
||||
}
|
||||
16
src/WorkFlow.Application/DTO/BaseUpdateDto.cs
Normal file
16
src/WorkFlow.Application/DTO/BaseUpdateDto.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using DigitalData.Core.Abstractions;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WorkFlow.Application.DTO
|
||||
{
|
||||
public record BaseUpdateDto : IUnique<int>
|
||||
{
|
||||
public required int Id { get; init; }
|
||||
|
||||
[JsonIgnore]
|
||||
public required string ChangedWho { get; set; } = "UNKNOWN";
|
||||
|
||||
[JsonIgnore]
|
||||
public required DateTime ChangedWhen = DateTime.Now;
|
||||
}
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/Config/ConfigCreateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/Config/ConfigCreateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.Config
|
||||
{
|
||||
public record ConfigCreateDto(string Title, string String);
|
||||
}
|
||||
10
src/WorkFlow.Application/DTO/Config/ConfigDto.cs
Normal file
10
src/WorkFlow.Application/DTO/Config/ConfigDto.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace WorkFlow.Application.DTO.Config
|
||||
{
|
||||
public record ConfigDto(int Id,
|
||||
string Title,
|
||||
string String,
|
||||
string AddedWho,
|
||||
DateTime AddedWhen,
|
||||
string? ChangedWho = null,
|
||||
DateTime? ChangedWhen = null);
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/Config/ConfigUpdateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/Config/ConfigUpdateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.Config
|
||||
{
|
||||
public record ConfigUpdateDto(string Title, string String) : BaseUpdateDto;
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/Profile/ProfileCreateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/Profile/ProfileCreateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.Profile
|
||||
{
|
||||
public record ProfileCreateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseCreateDto;
|
||||
}
|
||||
12
src/WorkFlow.Application/DTO/Profile/ProfileDto.cs
Normal file
12
src/WorkFlow.Application/DTO/Profile/ProfileDto.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
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);
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/Profile/ProfileUpdateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/Profile/ProfileUpdateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.Profile
|
||||
{
|
||||
public record ProfileUpdateDto(string IntlName, int ExtId1, bool Active, byte TypeId) : BaseUpdateDto;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace WorkFlow.Application.DTO.ProfileControlsTF
|
||||
{
|
||||
public record ProfileControlsTFCreateDto(
|
||||
int ProfileId,
|
||||
int UserId,
|
||||
long ObjId,
|
||||
string ObjType,
|
||||
string AttrName,
|
||||
string CtrlType,
|
||||
string CtrlCaption,
|
||||
bool Mandatory,
|
||||
bool ReadOnly,
|
||||
string? ChoiceList = null) : BaseCreateDto;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using DigitalData.UserManager.Application.DTOs.User;
|
||||
using WorkFlow.Application.DTO.Profile;
|
||||
|
||||
namespace WorkFlow.Application.DTO.ProfileControlsTF
|
||||
{
|
||||
public record ProfileControlsTFDto(int Id,
|
||||
int ProfileId,
|
||||
int UserId,
|
||||
long ObjId,
|
||||
string ObjType,
|
||||
string AttrName,
|
||||
string CtrlType,
|
||||
string CtrlCaption,
|
||||
bool Mandatory,
|
||||
bool ReadOnly,
|
||||
string AddedWho,
|
||||
DateTime AddedWhen,
|
||||
string? ChoiceList = null,
|
||||
ProfileDto? Profile = null,
|
||||
UserReadDto? User = null);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace WorkFlow.Application.DTO.ProfileObjState
|
||||
{
|
||||
public record ProfileObjStateCreateDto(
|
||||
int ProfileId,
|
||||
int UserId,
|
||||
long ObjId,
|
||||
int StateId,
|
||||
string? State2 = null,
|
||||
string? State3 = null,
|
||||
string? State4 = null) : BaseCreateDto;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using DigitalData.UserManager.Application.DTOs.User;
|
||||
using WorkFlow.Application.DTO.Profile;
|
||||
using WorkFlow.Application.DTO.State;
|
||||
|
||||
namespace WorkFlow.Application.DTO.ProfileObjState
|
||||
{
|
||||
public record ProfileObjStateDto(int Id,
|
||||
int ProfileId,
|
||||
int UserId,
|
||||
long ObjId,
|
||||
int StateId,
|
||||
string AddedWho,
|
||||
DateTime AddedWhen,
|
||||
string? State2 = null,
|
||||
string? State3 = null,
|
||||
string? State4 = null,
|
||||
ProfileDto? Profile = null,
|
||||
UserReadDto? User = null,
|
||||
StateDto? State = null);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/State/StateCreateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/State/StateCreateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.State
|
||||
{
|
||||
public record StateCreateDto(string IntlState) : BaseCreateDto;
|
||||
}
|
||||
4
src/WorkFlow.Application/DTO/State/StateDto.cs
Normal file
4
src/WorkFlow.Application/DTO/State/StateDto.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace WorkFlow.Application.DTO.State
|
||||
{
|
||||
public record StateDto(int Id, string IntlState, string AddedWho, DateTime AddedWhen);
|
||||
}
|
||||
9
src/WorkFlow.Application/DTO/State/StateUpdateDto.cs
Normal file
9
src/WorkFlow.Application/DTO/State/StateUpdateDto.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
}
|
||||
37
src/WorkFlow.Application/MappingProfile.cs
Normal file
37
src/WorkFlow.Application/MappingProfile.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/WorkFlow.Application/Services/ConfigService.cs
Normal file
17
src/WorkFlow.Application/Services/ConfigService.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/WorkFlow.Application/Services/ProfileObjStateService.cs
Normal file
34
src/WorkFlow.Application/Services/ProfileObjStateService.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
16
src/WorkFlow.Application/Services/ProfileService.cs
Normal file
16
src/WorkFlow.Application/Services/ProfileService.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
17
src/WorkFlow.Application/Services/StateService.cs
Normal file
17
src/WorkFlow.Application/Services/StateService.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
19
src/WorkFlow.Application/WorkFlow.Application.csproj
Normal file
19
src/WorkFlow.Application/WorkFlow.Application.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DigitalData.Core.Application" Version="3.2.0" />
|
||||
<PackageReference Include="UserManager.Application" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkFlow.Domain\WorkFlow.Domain.csproj" />
|
||||
<ProjectReference Include="..\WorkFlow.Infrastructure\WorkFlow.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
36
src/WorkFlow.Domain/Entities/Config.cs
Normal file
36
src/WorkFlow.Domain/Entities/Config.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using DigitalData.Core.Abstractions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace WorkFlow.Domain.Entities
|
||||
{
|
||||
[Table("TBMWF_CONFIG", Schema = "dbo")]
|
||||
public class Config : IUnique<int>
|
||||
{
|
||||
[Key]
|
||||
[Column("GUID")]
|
||||
public int Id { get; init; }
|
||||
|
||||
[Required]
|
||||
[Column("CONF_TITLE", TypeName = "varchar(100)")]
|
||||
public required string Title { get; init; }
|
||||
|
||||
[Required]
|
||||
[Column("CONF_STRING", TypeName = "varchar(900)")]
|
||||
public required string String { 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; }
|
||||
}
|
||||
}
|
||||
44
src/WorkFlow.Domain/Entities/Profile.cs
Normal file
44
src/WorkFlow.Domain/Entities/Profile.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
68
src/WorkFlow.Domain/Entities/ProfileControlsTF.cs
Normal file
68
src/WorkFlow.Domain/Entities/ProfileControlsTF.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
57
src/WorkFlow.Domain/Entities/ProfileObjState.cs
Normal file
57
src/WorkFlow.Domain/Entities/ProfileObjState.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
26
src/WorkFlow.Domain/Entities/State.cs
Normal file
26
src/WorkFlow.Domain/Entities/State.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using DigitalData.Core.Abstractions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace WorkFlow.Domain.Entities
|
||||
{
|
||||
[Table("TBMWF_WF_STATE", Schema = "dbo")]
|
||||
public class State : IUnique<int>
|
||||
{
|
||||
[Key]
|
||||
[Column("GUID")]
|
||||
public int Id { get; init; }
|
||||
|
||||
[Required]
|
||||
[Column("INTL_STATE", TypeName = "varchar(100)")]
|
||||
public required string IntlState { 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; }
|
||||
}
|
||||
}
|
||||
13
src/WorkFlow.Domain/WorkFlow.Domain.csproj
Normal file
13
src/WorkFlow.Domain/WorkFlow.Domain.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="UserManager.Domain" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
using DigitalData.Core.Abstractions.Infrastructure;
|
||||
using WorkFlow.Domain.Entities;
|
||||
|
||||
namespace WorkFlow.Infrastructure.Contracts
|
||||
{
|
||||
public interface IConfigRepository : ICRUDRepository<Config, int>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using DigitalData.Core.Abstractions.Infrastructure;
|
||||
using WorkFlow.Domain.Entities;
|
||||
|
||||
namespace WorkFlow.Infrastructure.Contracts
|
||||
{
|
||||
public interface IProfileRepository : ICRUDRepository<Profile, int>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using DigitalData.Core.Abstractions.Infrastructure;
|
||||
using WorkFlow.Domain.Entities;
|
||||
|
||||
namespace WorkFlow.Infrastructure.Contracts
|
||||
{
|
||||
public interface IStateRepository : ICRUDRepository<State, int>
|
||||
{
|
||||
}
|
||||
}
|
||||
21
src/WorkFlow.Infrastructure/DIExtensions.cs
Normal file
21
src/WorkFlow.Infrastructure/DIExtensions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/WorkFlow.Infrastructure/Repositories/ConfigRepository.cs
Normal file
15
src/WorkFlow.Infrastructure/Repositories/ConfigRepository.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
13
src/WorkFlow.Infrastructure/Repositories/StateRepository.cs
Normal file
13
src/WorkFlow.Infrastructure/Repositories/StateRepository.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
46
src/WorkFlow.Infrastructure/WFDBContext.cs
Normal file
46
src/WorkFlow.Infrastructure/WFDBContext.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using DigitalData.UserManager.Domain.Entities;
|
||||
using DigitalData.UserManager.Infrastructure;
|
||||
using DigitalData.UserManager.Infrastructure.Contracts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WorkFlow.Domain.Entities;
|
||||
|
||||
namespace WorkFlow.Infrastructure;
|
||||
|
||||
public class WFDBContext : DbContext, IUserManagerDbContext
|
||||
{
|
||||
public DbSet<Config> Configs { get; set; }
|
||||
|
||||
public DbSet<ProfileControlsTF> ProfileControlsTFs { get; set; }
|
||||
|
||||
public DbSet<Profile> Profiles { get; set; }
|
||||
|
||||
public DbSet<ProfileObjState> ProfileObjStates { get; set; }
|
||||
|
||||
public DbSet<State> States { get; set; }
|
||||
|
||||
public DbSet<GroupOfUser> GroupOfUsers { get; set; }
|
||||
|
||||
public DbSet<Group> Groups { get; set; }
|
||||
|
||||
public DbSet<ModuleOfUser> ModuleOfUsers { get; set; }
|
||||
|
||||
public DbSet<Module> Modules { get; set; }
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
public DbSet<UserRep> UserReps { get; set; }
|
||||
|
||||
public DbSet<ClientUser> ClientUsers { get; set; }
|
||||
|
||||
public WFDBContext(DbContextOptions options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
//configure model builder for user manager tables
|
||||
modelBuilder.ConfigureUserManager();
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
18
src/WorkFlow.Infrastructure/WorkFlow.Infrastructure.csproj
Normal file
18
src/WorkFlow.Infrastructure/WorkFlow.Infrastructure.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.0" />
|
||||
<PackageReference Include="UserManager.Infrastructure" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkFlow.Domain\WorkFlow.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user