Added automatic audit feature #87
@@ -1,8 +1,10 @@
|
|||||||
using Microsoft.FeatureManagement;
|
using Microsoft.FeatureManagement;
|
||||||
using PostFundManagement.Application.Extensions;
|
using PostFundManagement.Application.Extensions;
|
||||||
using PostFundManagement.Application.Filters;
|
using PostFundManagement.Application.Filters;
|
||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Health;
|
using PostFundManagement.Domain.Health;
|
||||||
using PostFundManagement.Domain.Mediator;
|
using PostFundManagement.Domain.Mediator;
|
||||||
|
using PostFundManagement.Infrastructure.Database.Services;
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
using static PostFundManagement.Domain.Extensions.Constants;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
@@ -27,6 +29,7 @@ builder.Services.AddApiVersioning(options =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddScoped<ActiveUserFilter>();
|
builder.Services.AddScoped<ActiveUserFilter>();
|
||||||
|
builder.Services.AddTransient<ITenantContextService, TenantContextService>();
|
||||||
|
|
||||||
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
|
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
|
||||||
builder.Services.AddApiServices(builder.Configuration);
|
builder.Services.AddApiServices(builder.Configuration);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using PostFundManagement.Application.Services.Communications;
|
using PostFundManagement.Application.Services.Communications;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
public interface ICommunicationsService
|
public interface ICommunicationsService
|
||||||
{
|
{
|
||||||
|
[Audit]
|
||||||
ValueTask<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
using PostFundManagement.Application.Services.Evidence;
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
public interface IEvidenceService
|
public interface IEvidenceService
|
||||||
{
|
{
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken);
|
ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken);
|
||||||
@@ -26,6 +31,7 @@ public interface IEvidenceService
|
|||||||
|
|
||||||
ValueTask<Result<Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<string>> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<string>> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using PostFundManagement.Application.Services.Execution;
|
using PostFundManagement.Application.Services.Execution;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
@@ -10,17 +11,22 @@ public interface IExecutionService
|
|||||||
|
|
||||||
ValueTask<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using PostFundManagement.Application.Services.Governance;
|
using PostFundManagement.Application.Services.Governance;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
@@ -18,12 +19,16 @@ public interface IGovernanceService
|
|||||||
|
|
||||||
ValueTask<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default);
|
ValueTask<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default);
|
ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using PostFundManagement.Application.Services.Grants;
|
using PostFundManagement.Application.Services.Grants;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
@@ -18,17 +19,23 @@ public interface IGrantsService
|
|||||||
|
|
||||||
ValueTask<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default);
|
ValueTask<Result> UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
using PostFundManagement.Domain.Attributes;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
public interface IIdentityService
|
public interface IIdentityService
|
||||||
{
|
{
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using PostFundManagement.Application.Services.Projects;
|
using PostFundManagement.Application.Services.Projects;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
public interface IProjectService
|
public interface IProjectService
|
||||||
{
|
{
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
@@ -14,8 +16,10 @@ public interface IProjectService
|
|||||||
|
|
||||||
ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
@@ -26,16 +30,20 @@ public interface IProjectService
|
|||||||
|
|
||||||
ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default);
|
ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default);
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
using PostFundManagement.Application.Services.Users;
|
using PostFundManagement.Application.Services.Users;
|
||||||
using PostFundManagement.Domain;
|
using PostFundManagement.Domain;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
using PostFundManagement.Domain.Models;
|
using PostFundManagement.Domain.Models;
|
||||||
|
|
||||||
namespace PostFundManagement.Application.Abstractions.Services;
|
namespace PostFundManagement.Application.Abstractions.Services;
|
||||||
|
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default);
|
ValueTask<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default);
|
ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
[Audit]
|
||||||
ValueTask<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default);
|
ValueTask<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
ValueTask<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken);
|
ValueTask<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using PostFundManagement.Application.Abstractions;
|
using PostFundManagement.Application.Abstractions;
|
||||||
using PostFundManagement.Application.Abstractions.Services;
|
using PostFundManagement.Application.Abstractions.Services;
|
||||||
|
using PostFundManagement.Application.Interceptors;
|
||||||
using PostFundManagement.Application.Services.Communications;
|
using PostFundManagement.Application.Services.Communications;
|
||||||
using PostFundManagement.Application.Services.Evidence;
|
using PostFundManagement.Application.Services.Evidence;
|
||||||
using PostFundManagement.Application.Services.Execution;
|
using PostFundManagement.Application.Services.Execution;
|
||||||
@@ -15,32 +16,60 @@ namespace PostFundManagement.Application.Extensions;
|
|||||||
|
|
||||||
public static class Services
|
public static class Services
|
||||||
{
|
{
|
||||||
|
public static IServiceCollection AddAuditedCachedScoped<TInterface, TImplementation, TCacheDecorator>(this IServiceCollection services)
|
||||||
|
where TInterface : class
|
||||||
|
where TImplementation : class, TInterface
|
||||||
|
where TCacheDecorator : class, TInterface
|
||||||
|
{
|
||||||
|
services.AddScoped<TImplementation>();
|
||||||
|
services.AddScoped<TCacheDecorator>();
|
||||||
|
|
||||||
|
services.AddScoped(sp =>
|
||||||
|
{
|
||||||
|
var proxyGenerator = sp.GetRequiredService<ProxyGenerator>();
|
||||||
|
var interceptor = sp.GetRequiredService<AuditInterceptor>();
|
||||||
|
|
||||||
|
var actual = sp.GetRequiredService<TImplementation>();
|
||||||
|
|
||||||
|
var cached = ActivatorUtilities.CreateInstance<TCacheDecorator>(sp, actual);
|
||||||
|
|
||||||
|
return proxyGenerator.CreateInterfaceProxyWithTarget<TInterface>(cached, interceptor.ToInterceptor());
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAuditedScoped<TInterface, TImplementation>(this IServiceCollection services)
|
||||||
|
where TInterface : class
|
||||||
|
where TImplementation : class, TInterface
|
||||||
|
{
|
||||||
|
services.AddScoped<TImplementation>();
|
||||||
|
services.AddScoped(sp =>
|
||||||
|
{
|
||||||
|
var proxyGenerator = sp.GetRequiredService<ProxyGenerator>();
|
||||||
|
var actual = sp.GetRequiredService<TImplementation>();
|
||||||
|
var interceptor = sp.GetRequiredService<AuditInterceptor>();
|
||||||
|
|
||||||
|
return proxyGenerator.CreateInterfaceProxyWithTarget<TInterface>(actual, interceptor.ToInterceptor());
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddRegisterServicesWithCaching(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddRegisterServicesWithCaching(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddScoped<ICommunicationsService, CommunicationsService>();
|
services.AddScoped<AuditInterceptor>();
|
||||||
services.Decorate<ICommunicationsService, CommunicationsCacheService>();
|
services.AddSingleton<ProxyGenerator>();
|
||||||
|
|
||||||
services.AddScoped<IEvidenceService, EvidenceService>();
|
services.AddAuditedCachedScoped<ICommunicationsService, CommunicationsService, CommunicationsCacheService>();
|
||||||
services.Decorate<IEvidenceService, EvidenceCacheService>();
|
services.AddAuditedCachedScoped<IEvidenceService, EvidenceService, EvidenceCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IUserService, UserService, UserCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IIdentityService, IdentityService, IdentityCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IExecutionService, ExecutionService, ExecutionCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IGovernanceService, GovernanceService, GovernanceCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IGrantsService, GrantsService, GrantsCacheService>();
|
||||||
|
services.AddAuditedCachedScoped<IProjectService, ProjectService, ProjectCacheService>();
|
||||||
|
|
||||||
services.AddScoped<IUserService, UserService>();
|
|
||||||
services.Decorate<IUserService, UserCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IIdentityService, IdentityService>();
|
|
||||||
services.Decorate<IIdentityService, IdentityCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IExecutionService, ExecutionService>();
|
|
||||||
services.Decorate<IExecutionService, ExecutionCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IGovernanceService, GovernanceService>();
|
|
||||||
services.Decorate<IGovernanceService, GovernanceCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IGrantsService, GrantsService>();
|
|
||||||
services.Decorate<IGrantsService, GrantsCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IProjectService, ProjectService>();
|
|
||||||
services.Decorate<IProjectService, ProjectCacheService>();
|
|
||||||
|
|
||||||
services.AddScoped<IToolService, ToolService>();
|
services.AddScoped<IToolService, ToolService>();
|
||||||
services.Decorate<IToolService, ToolCacheService>();
|
services.Decorate<IToolService, ToolCacheService>();
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public sealed class ActiveUserFilter(IIdentityService identityService) : IEndpoi
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(path) && !path!.EndsWith("integration/sync", StringComparison.InvariantCultureIgnoreCase))
|
if (!string.IsNullOrWhiteSpace(path) && !path!.EndsWith("integration/sync", StringComparison.InvariantCultureIgnoreCase))
|
||||||
if ((await identityService.UserIsAuthorisedAsync(userId, context.HttpContext.RequestAborted)).IsFailed)
|
if ((await identityService.UserIsAuthorisedAsync(userId, context.HttpContext.RequestAborted)).IsFailed)
|
||||||
return (object?)Results.Json(new { message = "Account approval is pending or inactive." }, statusCode: StatusCodes.Status403Forbidden);
|
return Results.Json(new { message = "Account approval is pending or inactive." }, statusCode: StatusCodes.Status403Forbidden);
|
||||||
|
|
||||||
context.HttpContext.Items[UserIdItemKey] = userId;
|
context.HttpContext.Items[UserIdItemKey] = userId;
|
||||||
context.HttpContext.Items[EmailtemKey] = email;
|
context.HttpContext.Items[EmailtemKey] = email;
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
using PostFundManagement.Application.Services.Shared;
|
||||||
|
using PostFundManagement.Domain.Attributes;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Interceptors;
|
||||||
|
|
||||||
|
public sealed class AuditInterceptor(AuditService auditService, IHttpContextAccessor httpContextAccessor) : IAsyncInterceptor
|
||||||
|
{
|
||||||
|
public void InterceptSynchronous(IInvocation invocation) => invocation.Proceed();
|
||||||
|
|
||||||
|
public void InterceptAsynchronous(IInvocation invocation) =>
|
||||||
|
invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
|
||||||
|
|
||||||
|
public void InterceptAsynchronous<TResult>(IInvocation invocation) =>
|
||||||
|
invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
|
||||||
|
|
||||||
|
private static async Task InternalInterceptAsynchronous(IInvocation invocation)
|
||||||
|
{
|
||||||
|
invocation.Proceed();
|
||||||
|
|
||||||
|
var task = (Task)invocation.ReturnValue;
|
||||||
|
|
||||||
|
await task.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
|
||||||
|
{
|
||||||
|
invocation.Proceed();
|
||||||
|
|
||||||
|
var task = (Task<TResult>)invocation.ReturnValue;
|
||||||
|
var resultObj = await task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
var method = invocation.MethodInvocationTarget ?? invocation.Method;
|
||||||
|
var auditAttr = method.GetCustomAttribute<AuditAttribute>()
|
||||||
|
?? invocation.Method.GetCustomAttribute<AuditAttribute>();
|
||||||
|
|
||||||
|
if (auditAttr == null) return resultObj;
|
||||||
|
|
||||||
|
if (resultObj is IResultBase result && result.IsSuccess)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var action = ResolveActionName(method.Name);
|
||||||
|
var entityName = ResolveEntityName(method, resultObj, invocation.Arguments);
|
||||||
|
var entityId = ResolveEntityId(resultObj, invocation.Arguments);
|
||||||
|
var changes = BuildPayloadSummary(invocation.Method.GetParameters(), invocation.Arguments);
|
||||||
|
var performedBy = ResolveUserId();
|
||||||
|
|
||||||
|
await auditService.LogActionAsync(
|
||||||
|
entityId: entityId,
|
||||||
|
entityName: entityName,
|
||||||
|
action: action,
|
||||||
|
changes: changes,
|
||||||
|
performedBy: performedBy).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { /* eat all exceptions, don't interrupt program flow */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveActionName(string methodName) =>
|
||||||
|
methodName.EndsWith("Async", StringComparison.Ordinal)
|
||||||
|
? methodName[..^5]
|
||||||
|
: methodName;
|
||||||
|
|
||||||
|
private static string ResolveEntityName(MethodInfo method, object? resultObj, object[] args)
|
||||||
|
{
|
||||||
|
var returnType = method.ReturnType;
|
||||||
|
|
||||||
|
Type? innerType = null;
|
||||||
|
|
||||||
|
if (returnType.IsGenericType)
|
||||||
|
{
|
||||||
|
var genericDef = returnType.GetGenericTypeDefinition();
|
||||||
|
|
||||||
|
if (genericDef == typeof(Task<>) || genericDef == typeof(ValueTask<>))
|
||||||
|
innerType = returnType.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerType != null && innerType.IsGenericType && typeof(IResultBase).IsAssignableFrom(innerType))
|
||||||
|
{
|
||||||
|
var valueType = innerType.GetGenericArguments().FirstOrDefault();
|
||||||
|
|
||||||
|
if (valueType != null && valueType != typeof(bool) && valueType != typeof(int) && valueType != typeof(string))
|
||||||
|
return valueType.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var arg in args)
|
||||||
|
{
|
||||||
|
if (arg == null || arg is CancellationToken or IFormFile) continue;
|
||||||
|
|
||||||
|
var argTypeName = arg.GetType().Name;
|
||||||
|
|
||||||
|
if (argTypeName.EndsWith("Request", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var trimmed = argTypeName[..^7];
|
||||||
|
|
||||||
|
return trimmed.Replace("Create", "").Replace("Update", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveActionName(method.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveEntityId(object? resultObj, object[] args)
|
||||||
|
{
|
||||||
|
if (resultObj != null)
|
||||||
|
{
|
||||||
|
var valueProp = resultObj.GetType().GetProperty("Value");
|
||||||
|
if (valueProp != null)
|
||||||
|
{
|
||||||
|
var value = valueProp.GetValue(resultObj);
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
var idProp = value.GetType().GetProperty("Id") ?? value.GetType().GetProperty("id");
|
||||||
|
if (idProp != null)
|
||||||
|
{
|
||||||
|
var idVal = idProp.GetValue(value);
|
||||||
|
if (idVal != null) return idVal.ToString()!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var arg in args)
|
||||||
|
{
|
||||||
|
if (arg is long or int or Guid or string)
|
||||||
|
return arg.ToString()!;
|
||||||
|
|
||||||
|
if (arg != null && arg is not CancellationToken and not IFormFile)
|
||||||
|
{
|
||||||
|
var idProp = arg.GetType().GetProperty("Id") ?? arg.GetType().GetProperty("id");
|
||||||
|
|
||||||
|
if (idProp != null)
|
||||||
|
{
|
||||||
|
var idVal = idProp.GetValue(arg);
|
||||||
|
|
||||||
|
if (idVal != null) return idVal.ToString()!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] BuildPayloadSummary(ParameterInfo[] parameters, object[] args)
|
||||||
|
{
|
||||||
|
var entries = new List<string>();
|
||||||
|
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
var param = parameters[i];
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
if (param.ParameterType == typeof(CancellationToken))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (arg == null)
|
||||||
|
{
|
||||||
|
entries.Add($"{param.Name}: null");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg is IFormFile file)
|
||||||
|
{
|
||||||
|
entries.Add($"{param.Name}: [File] Name='{file.FileName}', Size={file.Length} bytes, Type='{file.ContentType}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg is IEnumerable<IFormFile> files)
|
||||||
|
{
|
||||||
|
var fileSummaries = files.Select(f => $"'{f.FileName}' ({f.Length} bytes)");
|
||||||
|
|
||||||
|
entries.Add($"{param.Name}: [Files] count={fileSummaries.Count()} [{string.Join(", ", fileSummaries)}]");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg is Stream stream)
|
||||||
|
{
|
||||||
|
entries.Add($"{param.Name}: [Stream] Length={(stream.CanSeek ? stream.Length : -1)} bytes");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(arg);
|
||||||
|
|
||||||
|
entries.Add($"{param.Name}: {json}");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
entries.Add($"{param.Name}: [Unserializable]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. entries];
|
||||||
|
}
|
||||||
|
|
||||||
|
private Guid ResolveUserId()
|
||||||
|
{
|
||||||
|
var user = httpContextAccessor.HttpContext?.User;
|
||||||
|
|
||||||
|
var sub = user?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? user?.FindFirst("sub")?.Value;
|
||||||
|
|
||||||
|
return Guid.TryParse(sub, out var guid) ? guid : Guid.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
<!-- API Documentation -->
|
<!-- API Documentation -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Validation" Version="10.0.12" />
|
<PackageReference Include="Microsoft.Extensions.Validation" Version="10.0.12" />
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.17.4" />
|
<PackageReference Include="Scalar.AspNetCore" Version="2.17.4" />
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
<!-- Shared Usings -->
|
<!-- Shared Usings -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Humanizer" />
|
<Using Include="Humanizer" />
|
||||||
|
<Using Include="Castle.DynamicProxy" />
|
||||||
<Using Include="Microsoft.FeatureManagement" />
|
<Using Include="Microsoft.FeatureManagement" />
|
||||||
<Using Include="System.ComponentModel.DataAnnotations" />
|
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Using Include="Microsoft.Extensions.Caching.Distributed" />
|
<Using Include="Microsoft.Extensions.Caching.Distributed" />
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
using PostFundManagement.Domain.Errors;
|
||||||
|
using PostFundManagement.Infrastructure.Database;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Application.Services.Shared;
|
||||||
|
|
||||||
|
public sealed class AuditService(IDbContextFactory<ApplicationDbContext> contextFactory, ITenantContextService tenantContextService) : IService
|
||||||
|
{
|
||||||
|
public async ValueTask<Result> LogActionAsync(string entityId, string entityName, string action, string[] changes, Guid performedBy, CancellationToken cancellationToken = default) =>
|
||||||
|
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||||
|
{
|
||||||
|
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
|
context.Add(new Domain.Entities.AuditLog
|
||||||
|
{
|
||||||
|
EntityId = entityId,
|
||||||
|
Action = action,
|
||||||
|
Changes = changes,
|
||||||
|
EntityName = entityName,
|
||||||
|
PerformedBy = performedBy,
|
||||||
|
Timestamp = DateTime.UtcNow,
|
||||||
|
TenantId = tenantContextService.TenantId > 0 ? tenantContextService.TenantId : null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PostFundManagement.Domain.Abstractions;
|
||||||
|
|
||||||
|
public interface ITenantContextService
|
||||||
|
{
|
||||||
|
string? TenantName { get; }
|
||||||
|
long TenantId { get; }
|
||||||
|
bool IsAdmin { get; }
|
||||||
|
bool HasTenantFilter { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
namespace PostFundManagement.Domain.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||||
|
public sealed class AuditAttribute : Attribute;
|
||||||
@@ -5,16 +5,19 @@ namespace PostFundManagement.Domain.Entities;
|
|||||||
[EntityTypeConfiguration<AuditLogConfiguration, AuditLog>]
|
[EntityTypeConfiguration<AuditLogConfiguration, AuditLog>]
|
||||||
public class AuditLog : Models.AuditLog
|
public class AuditLog : Models.AuditLog
|
||||||
{
|
{
|
||||||
public virtual User? Performer { get; set; }
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
|
public virtual User? Performer { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class AuditLogConfiguration : IEntityTypeConfiguration<Domain.Entities.AuditLog>
|
public sealed class AuditLogConfiguration : IEntityTypeConfiguration<AuditLog>
|
||||||
{
|
{
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.AuditLog> builder)
|
public void Configure(EntityTypeBuilder<AuditLog> builder)
|
||||||
{
|
{
|
||||||
builder.ToTable(nameof(Domain.Entities.AuditLog).Pluralize());
|
builder.ToTable(nameof(AuditLog).Pluralize());
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
builder.HasKey(pk => pk.Id);
|
||||||
|
builder.Property(f => f.TenantId).IsRequired(false);
|
||||||
builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength);
|
builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength);
|
||||||
builder.Property(f => f.EntityId).IsRequired();
|
builder.Property(f => f.EntityId).IsRequired();
|
||||||
builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength);
|
builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength);
|
||||||
@@ -22,6 +25,12 @@ public sealed class AuditLogConfiguration : IEntityTypeConfiguration<Domain.Enti
|
|||||||
builder.Property(f => f.PerformedBy).IsRequired();
|
builder.Property(f => f.PerformedBy).IsRequired();
|
||||||
builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()");
|
builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()");
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired(false)
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
builder.HasOne(f => f.Performer)
|
builder.HasOne(f => f.Performer)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(fk => fk.PerformedBy)
|
.HasForeignKey(fk => fk.PerformedBy)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public class Award : Models.Award
|
|||||||
|
|
||||||
public virtual User? Approver { get; set; }
|
public virtual User? Approver { get; set; }
|
||||||
|
|
||||||
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
public virtual Organisation? Organisation { get; set; }
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
public virtual Programme? Programme { get; set; }
|
||||||
@@ -27,6 +29,7 @@ public sealed class AwardConfiguration : IEntityTypeConfiguration<Award>
|
|||||||
builder.HasKey(pk => pk.Id);
|
builder.HasKey(pk => pk.Id);
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
builder.Property(f => f.OrganisationId).IsRequired();
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
builder.Property(f => f.ProgrammeId).IsRequired();
|
||||||
|
builder.Property(f => f.TenantId).IsRequired();
|
||||||
builder.Property(f => f.ProjectId).IsRequired(false);
|
builder.Property(f => f.ProjectId).IsRequired(false);
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
builder.Property(f => f.CreatedBy).IsRequired();
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
||||||
@@ -37,6 +40,12 @@ public sealed class AwardConfiguration : IEntityTypeConfiguration<Award>
|
|||||||
.HasSentinel(ApprovalStatus.Pending);
|
.HasSentinel(ApprovalStatus.Pending);
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
builder.HasOne(f => f.Organisation)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
.HasForeignKey(fk => fk.OrganisationId)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace PostFundManagement.Domain.Entities;
|
|||||||
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
||||||
public class Notification : Models.Notification
|
public class Notification : Models.Notification
|
||||||
{
|
{
|
||||||
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
public virtual Organisation? Organisation { get; set; }
|
||||||
|
|
||||||
public virtual User? RecipientUser { get; set; }
|
public virtual User? RecipientUser { get; set; }
|
||||||
@@ -22,6 +24,7 @@ public sealed class NotificationConfiguration : IEntityTypeConfiguration<Notific
|
|||||||
builder.Property(f => f.SentAt).IsRequired(false);
|
builder.Property(f => f.SentAt).IsRequired(false);
|
||||||
builder.Property(f => f.Recipient).IsRequired();
|
builder.Property(f => f.Recipient).IsRequired();
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
builder.Property(f => f.OrganisationId).IsRequired();
|
||||||
|
builder.Property(f => f.TenantId).IsRequired();
|
||||||
builder.Property(f => f.Platform).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
builder.Property(f => f.Platform).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
||||||
.HasSentinel(NotificationPlatform.Email);
|
.HasSentinel(NotificationPlatform.Email);
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
||||||
@@ -31,6 +34,12 @@ public sealed class NotificationConfiguration : IEntityTypeConfiguration<Notific
|
|||||||
builder.Property(f => f.Destination).IsRequired().HasMaxLength(LabelLength);
|
builder.Property(f => f.Destination).IsRequired().HasMaxLength(LabelLength);
|
||||||
builder.Property(f => f.Error).IsRequired(false);
|
builder.Property(f => f.Error).IsRequired(false);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
builder.HasOne(f => f.Organisation)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
.HasForeignKey(fk => fk.OrganisationId)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public class Portfolio : Models.Portfolio
|
|||||||
|
|
||||||
public virtual User? Owner { get; set; }
|
public virtual User? Owner { get; set; }
|
||||||
|
|
||||||
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
public virtual Organisation? Organisation { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<Programme> Programmes { get; set; } = [];
|
public virtual ICollection<Programme> Programmes { get; set; } = [];
|
||||||
@@ -21,6 +23,7 @@ public sealed class PortfolioConfiguration : IEntityTypeConfiguration<Portfolio>
|
|||||||
builder.ToTable(nameof(Portfolio).Pluralize());
|
builder.ToTable(nameof(Portfolio).Pluralize());
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
builder.HasKey(pk => pk.Id);
|
||||||
|
builder.Property(f => f.TenantId).IsRequired();
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
builder.Property(f => f.OrganisationId).IsRequired();
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
builder.Property(f => f.CreatedBy).IsRequired();
|
||||||
builder.Property(f => f.OwnedBy).IsRequired();
|
builder.Property(f => f.OwnedBy).IsRequired();
|
||||||
@@ -28,6 +31,12 @@ public sealed class PortfolioConfiguration : IEntityTypeConfiguration<Portfolio>
|
|||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
||||||
builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength);
|
builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
builder.HasOne(f => f.Organisation)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
.HasForeignKey(fk => fk.OrganisationId)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ public class Programme : Models.Programme
|
|||||||
public virtual User? Creator { get; set; }
|
public virtual User? Creator { get; set; }
|
||||||
|
|
||||||
public virtual User? Owner { get; set; }
|
public virtual User? Owner { get; set; }
|
||||||
|
|
||||||
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
public virtual Portfolio? Portfolio { get; set; }
|
public virtual Portfolio? Portfolio { get; set; }
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ public sealed class ProgrammeConfiguration : IEntityTypeConfiguration<Programme>
|
|||||||
builder.ToTable(nameof(Programme).Pluralize());
|
builder.ToTable(nameof(Programme).Pluralize());
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
builder.HasKey(pk => pk.Id);
|
||||||
|
builder.Property(f => f.TenantId).IsRequired();
|
||||||
builder.Property(f => f.PortfolioId).IsRequired();
|
builder.Property(f => f.PortfolioId).IsRequired();
|
||||||
builder.Property(f => f.OwnedBy).IsRequired();
|
builder.Property(f => f.OwnedBy).IsRequired();
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
builder.Property(f => f.CreatedBy).IsRequired();
|
||||||
@@ -37,6 +40,12 @@ public sealed class ProgrammeConfiguration : IEntityTypeConfiguration<Programme>
|
|||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
||||||
.HasSentinel(ApprovalStatus.Pending);
|
.HasSentinel(ApprovalStatus.Pending);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
builder.HasOne(f => f.Portfolio)
|
builder.HasOne(f => f.Portfolio)
|
||||||
.WithMany(f => f.Programmes)
|
.WithMany(f => f.Programmes)
|
||||||
.HasForeignKey(fk => fk.PortfolioId)
|
.HasForeignKey(fk => fk.PortfolioId)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public class Project : Models.Project
|
|||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
public virtual User? Updator { get; set; }
|
||||||
|
|
||||||
|
public virtual Tenant? Tenant { get; set; }
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
public virtual Programme? Programme { get; set; }
|
||||||
|
|
||||||
public virtual ICollection<Award> Awards { get; set; } = [];
|
public virtual ICollection<Award> Awards { get; set; } = [];
|
||||||
@@ -21,6 +23,7 @@ public sealed class ProjectConfiguration : IEntityTypeConfiguration<Project>
|
|||||||
builder.ToTable(nameof(Project).Pluralize());
|
builder.ToTable(nameof(Project).Pluralize());
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
builder.HasKey(pk => pk.Id);
|
||||||
|
builder.Property(f => f.TenantId).IsRequired();
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
builder.Property(f => f.ProgrammeId).IsRequired();
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
builder.Property(f => f.CreatedBy).IsRequired();
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
||||||
@@ -37,6 +40,12 @@ public sealed class ProjectConfiguration : IEntityTypeConfiguration<Project>
|
|||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1")
|
||||||
.HasSentinel(ApprovalStatus.Pending);
|
.HasSentinel(ApprovalStatus.Pending);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.Tenant)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.TenantId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
builder.HasOne(f => f.Programme)
|
builder.HasOne(f => f.Programme)
|
||||||
.WithMany(f => f.Projects)
|
.WithMany(f => f.Projects)
|
||||||
.HasForeignKey(fk => fk.ProgrammeId)
|
.HasForeignKey(fk => fk.ProgrammeId)
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ public sealed class TenantUserConfiguration : IEntityTypeConfiguration<TenantUse
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
builder.HasOne(f => f.User)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(fk => fk.UserId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
builder.HasOne(f => f.Creator)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
.HasForeignKey(fk => fk.CreatedBy)
|
||||||
|
|||||||
@@ -4,6 +4,37 @@ namespace PostFundManagement.Domain.Extensions;
|
|||||||
|
|
||||||
public static class Mappers
|
public static class Mappers
|
||||||
{
|
{
|
||||||
|
public static TenantUser Map(this Entities.TenantUser entity) => new()
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
|
CreatedAt = entity.CreatedAt,
|
||||||
|
CreatedBy = entity.CreatedBy,
|
||||||
|
Status = entity.Status,
|
||||||
|
UserId = entity.UserId
|
||||||
|
};
|
||||||
|
|
||||||
|
public static TenantOrganisation Map(this Entities.TenantOrganisation entity) => new()
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
Status = entity.Status,
|
||||||
|
CreatedAt = entity.CreatedAt,
|
||||||
|
CreatedBy = entity.CreatedBy,
|
||||||
|
OrganisationId = entity.OrganisationId,
|
||||||
|
TenantId = entity.TenantId
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Tenant Map(this Entities.Tenant entity) => new()
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
Name = entity.Name,
|
||||||
|
CreatedAt = entity.CreatedAt,
|
||||||
|
CreatedBy = entity.CreatedBy,
|
||||||
|
Status = entity.Status,
|
||||||
|
UpdatedAt = entity.UpdatedAt,
|
||||||
|
UpdatedBy = entity.UpdatedBy
|
||||||
|
};
|
||||||
|
|
||||||
public static Activity Map(this Entities.Activity entity) => new()
|
public static Activity Map(this Entities.Activity entity) => new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
@@ -40,6 +71,7 @@ public static class Mappers
|
|||||||
ApprovedBy = entity.ApprovedBy,
|
ApprovedBy = entity.ApprovedBy,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
CreatedBy = entity.CreatedBy,
|
CreatedBy = entity.CreatedBy,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
OrganisationId = entity.OrganisationId,
|
OrganisationId = entity.OrganisationId,
|
||||||
ProgrammeId = entity.ProgrammeId,
|
ProgrammeId = entity.ProgrammeId,
|
||||||
ProjectId = entity.ProjectId,
|
ProjectId = entity.ProjectId,
|
||||||
@@ -162,7 +194,7 @@ public static class Mappers
|
|||||||
AwardId = entity.AwardId,
|
AwardId = entity.AwardId,
|
||||||
ContractId = entity.ContractId,
|
ContractId = entity.ContractId,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
CreatedBy = entity.CreatedBy,
|
CreatedBy = entity.CreatedBy,
|
||||||
InvitedOrganisationId = entity.InvitedOrganisationId,
|
InvitedOrganisationId = entity.InvitedOrganisationId,
|
||||||
OrganisationId = entity.OrganisationId,
|
OrganisationId = entity.OrganisationId,
|
||||||
Recipient = entity.Recipient,
|
Recipient = entity.Recipient,
|
||||||
@@ -202,7 +234,7 @@ public static class Mappers
|
|||||||
public static Organisation Map(this Entities.Organisation entity) => new()
|
public static Organisation Map(this Entities.Organisation entity) => new()
|
||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
CreatedBy = entity.CreatedBy,
|
CreatedBy = entity.CreatedBy,
|
||||||
Email = entity.Email,
|
Email = entity.Email,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
@@ -225,6 +257,7 @@ public static class Mappers
|
|||||||
{
|
{
|
||||||
Id = entity.Id,
|
Id = entity.Id,
|
||||||
CreatedAt = entity.CreatedAt,
|
CreatedAt = entity.CreatedAt,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
CreatedBy = entity.CreatedBy,
|
CreatedBy = entity.CreatedBy,
|
||||||
Description = entity.Description,
|
Description = entity.Description,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
@@ -239,6 +272,7 @@ public static class Mappers
|
|||||||
Destination = entity.Destination,
|
Destination = entity.Destination,
|
||||||
Error = entity.Error,
|
Error = entity.Error,
|
||||||
Message = entity.Message,
|
Message = entity.Message,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
OrganisationId = entity.OrganisationId,
|
OrganisationId = entity.OrganisationId,
|
||||||
Platform = entity.Platform,
|
Platform = entity.Platform,
|
||||||
Recipient = entity.Recipient,
|
Recipient = entity.Recipient,
|
||||||
@@ -255,6 +289,7 @@ public static class Mappers
|
|||||||
CreatedBy = entity.CreatedBy,
|
CreatedBy = entity.CreatedBy,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
OwnedBy = entity.OwnedBy,
|
OwnedBy = entity.OwnedBy,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
PortfolioId = entity.PortfolioId,
|
PortfolioId = entity.PortfolioId,
|
||||||
Status = entity.Status,
|
Status = entity.Status,
|
||||||
UpdatedAt = entity.UpdatedAt,
|
UpdatedAt = entity.UpdatedAt,
|
||||||
@@ -269,6 +304,7 @@ public static class Mappers
|
|||||||
Description = entity.Description,
|
Description = entity.Description,
|
||||||
EndedAt = entity.EndedAt,
|
EndedAt = entity.EndedAt,
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
|
TenantId = entity.TenantId,
|
||||||
ProgrammeId = entity.ProgrammeId,
|
ProgrammeId = entity.ProgrammeId,
|
||||||
StartedAt = entity.StartedAt,
|
StartedAt = entity.StartedAt,
|
||||||
Status = entity.Status,
|
Status = entity.Status,
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ public class AuditLog
|
|||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public long? TenantId { get; set; }
|
||||||
|
|
||||||
public string? EntityName { get; set; }
|
public string? EntityName { get; set; }
|
||||||
|
|
||||||
public long EntityId { get; set; }
|
public string? EntityId { get; set; }
|
||||||
|
|
||||||
public string? Action { get; set; }
|
public string? Action { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ public class Award
|
|||||||
|
|
||||||
public long OrganisationId { get; set; }
|
public long OrganisationId { get; set; }
|
||||||
|
|
||||||
|
public long TenantId { get; set; }
|
||||||
|
|
||||||
public long ProgrammeId { get; set; }
|
public long ProgrammeId { get; set; }
|
||||||
|
|
||||||
public long? ProjectId { get; set; }
|
public long? ProjectId { get; set; }
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ public class Notification
|
|||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public long TenantId { get; set; }
|
||||||
|
|
||||||
public long OrganisationId { get; set; }
|
public long OrganisationId { get; set; }
|
||||||
|
|
||||||
public Guid Recipient { get; set; }
|
public Guid Recipient { get; set; }
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ public class Portfolio
|
|||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public long TenantId { get; set; }
|
||||||
|
|
||||||
public long OrganisationId { get; set; }
|
public long OrganisationId { get; set; }
|
||||||
|
|
||||||
public Guid OwnedBy { get; set; }
|
public Guid OwnedBy { get; set; }
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ public class Programme
|
|||||||
|
|
||||||
public long PortfolioId { get; set; }
|
public long PortfolioId { get; set; }
|
||||||
|
|
||||||
|
public long TenantId { get; set; }
|
||||||
|
|
||||||
public Guid OwnedBy { get; set; }
|
public Guid OwnedBy { get; set; }
|
||||||
|
|
||||||
public Guid CreatedBy { get; set; }
|
public Guid CreatedBy { get; set; }
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ public class Project
|
|||||||
|
|
||||||
public long ProgrammeId { get; set; }
|
public long ProgrammeId { get; set; }
|
||||||
|
|
||||||
|
public long TenantId { get; set; }
|
||||||
|
|
||||||
public Guid CreatedBy { get; set; }
|
public Guid CreatedBy { get; set; }
|
||||||
|
|
||||||
public Guid? UpdatedBy { get; set; }
|
public Guid? UpdatedBy { get; set; }
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ public class Tenant
|
|||||||
|
|
||||||
public Guid CreatedBy { get; set; }
|
public Guid CreatedBy { get; set; }
|
||||||
|
|
||||||
public DateTime UpdatedAt { get; set; }
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
|
||||||
public Guid UpdatedBy { get; set; }
|
public Guid? UpdatedBy { get; set; }
|
||||||
|
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
using PostFundManagement.Domain.Entities;
|
using PostFundManagement.Domain.Entities;
|
||||||
|
|
||||||
namespace PostFundManagement.Infrastructure.Database;
|
namespace PostFundManagement.Infrastructure.Database;
|
||||||
|
|
||||||
public sealed class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : DbContext(options)
|
public sealed class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
|
||||||
|
ITenantContextService tenantService) : DbContext(options)
|
||||||
{
|
{
|
||||||
|
private bool BypassFilter => tenantService.IsAdmin;
|
||||||
|
private long CurrentTenantId => tenantService.TenantId;
|
||||||
|
|
||||||
public DbSet<TenantUser> TenantUsers => Set<TenantUser>();
|
public DbSet<TenantUser> TenantUsers => Set<TenantUser>();
|
||||||
|
|
||||||
public DbSet<TenantOrganisation> TenantOrganisations => Set<TenantOrganisation>();
|
public DbSet<TenantOrganisation> TenantOrganisations => Set<TenantOrganisation>();
|
||||||
@@ -17,13 +22,13 @@ public sealed class ApplicationDbContext(DbContextOptions<ApplicationDbContext>
|
|||||||
public DbSet<Activity> Activities => Set<Activity>();
|
public DbSet<Activity> Activities => Set<Activity>();
|
||||||
|
|
||||||
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||||
|
|
||||||
public DbSet<SiteVisit> SiteVisits => Set<SiteVisit>();
|
public DbSet<SiteVisit> SiteVisits => Set<SiteVisit>();
|
||||||
|
|
||||||
public DbSet<ChangeRequest> ChangeRequests => Set<ChangeRequest>();
|
public DbSet<ChangeRequest> ChangeRequests => Set<ChangeRequest>();
|
||||||
|
|
||||||
public DbSet<ComplianceItem> ComplianceItems => Set<ComplianceItem>();
|
public DbSet<ComplianceItem> ComplianceItems => Set<ComplianceItem>();
|
||||||
|
|
||||||
public DbSet<Issue> Issues => Set<Issue>();
|
public DbSet<Issue> Issues => Set<Issue>();
|
||||||
|
|
||||||
public DbSet<Outcome> Outcomes => Set<Outcome>();
|
public DbSet<Outcome> Outcomes => Set<Outcome>();
|
||||||
@@ -31,13 +36,13 @@ public sealed class ApplicationDbContext(DbContextOptions<ApplicationDbContext>
|
|||||||
public DbSet<Beneficiary> Beneficiaries => Set<Beneficiary>();
|
public DbSet<Beneficiary> Beneficiaries => Set<Beneficiary>();
|
||||||
|
|
||||||
public DbSet<Evidence> Evidences => Set<Evidence>();
|
public DbSet<Evidence> Evidences => Set<Evidence>();
|
||||||
|
|
||||||
public DbSet<Risk> Risks => Set<Risk>();
|
public DbSet<Risk> Risks => Set<Risk>();
|
||||||
|
|
||||||
public DbSet<Indicator> Indicators => Set<Indicator>();
|
public DbSet<Indicator> Indicators => Set<Indicator>();
|
||||||
|
|
||||||
public DbSet<Disbursement> Disbursements => Set<Disbursement>();
|
public DbSet<Disbursement> Disbursements => Set<Disbursement>();
|
||||||
|
|
||||||
public DbSet<Milestone> Milestones => Set<Milestone>();
|
public DbSet<Milestone> Milestones => Set<Milestone>();
|
||||||
|
|
||||||
public DbSet<Budget> Budgets => Set<Budget>();
|
public DbSet<Budget> Budgets => Set<Budget>();
|
||||||
@@ -57,6 +62,38 @@ public sealed class ApplicationDbContext(DbContextOptions<ApplicationDbContext>
|
|||||||
public DbSet<Portfolio> Portfolios => Set<Portfolio>();
|
public DbSet<Portfolio> Portfolios => Set<Portfolio>();
|
||||||
|
|
||||||
public DbSet<Instrument> Instruments => Set<Instrument>();
|
public DbSet<Instrument> Instruments => Set<Instrument>();
|
||||||
|
|
||||||
public DbSet<User> Users => Set<User>();
|
public DbSet<User> Users => Set<User>();
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Portfolio>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Programme>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Project>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Award>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Notification>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<TenantOrganisation>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<TenantUser>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<AuditLog>()
|
||||||
|
.HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Organisation>().HasQueryFilter(o => BypassFilter ||
|
||||||
|
Set<TenantOrganisation>().Any(to => to.TenantId == CurrentTenantId && to.OrganisationId == o.Id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ using static PostFundManagement.Domain.Extensions.Constants;
|
|||||||
|
|
||||||
namespace PostFundManagement.Infrastructure.Database.Factories;
|
namespace PostFundManagement.Infrastructure.Database.Factories;
|
||||||
|
|
||||||
public sealed class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
|
public sealed partial class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
|
||||||
{
|
{
|
||||||
public ApplicationDbContext CreateDbContext(string[] args)
|
public ApplicationDbContext CreateDbContext(string[] args)
|
||||||
{
|
{
|
||||||
@@ -14,11 +14,14 @@ public sealed class ApplicationDbContextFactory : IDesignTimeDbContextFactory<Ap
|
|||||||
|
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
|
||||||
|
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName)
|
var connectionString = configuration.GetConnectionString(DatabaseConfigName)
|
||||||
?? throw new InvalidOperationException($"Connection string '{DatabaseConfigName}' was not found in configuration.");
|
?? throw new InvalidOperationException($"Connection string '{DatabaseConfigName}' was not found in configuration.");
|
||||||
|
|
||||||
optionsBuilder.UseNpgsql(connectionString, npgsqlOptions => npgsqlOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName));
|
|
||||||
|
|
||||||
return new ApplicationDbContext(optionsBuilder.Options);
|
optionsBuilder.UseNpgsql(connectionString, npgsqlOptions =>
|
||||||
|
npgsqlOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName));
|
||||||
|
|
||||||
|
var designTimeTenantService = new DesignTimeTenantContextService();
|
||||||
|
|
||||||
|
return new ApplicationDbContext(optionsBuilder.Options, designTimeTenantService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+2233
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,370 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PostFundManagement.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedTenant : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Projects",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Programmes",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Portfolios",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Notifications",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Awards",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Tenants",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
UpdatedBy = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
Status = table.Column<int>(type: "integer", nullable: false, defaultValueSql: "1")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Tenants", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Tenants_Users_CreatedBy",
|
||||||
|
column: x => x.CreatedBy,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Tenants_Users_UpdatedBy",
|
||||||
|
column: x => x.UpdatedBy,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TenantOrganisations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TenantId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
OrganisationId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
Status = table.Column<int>(type: "integer", nullable: false, defaultValueSql: "1")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TenantOrganisations", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantOrganisations_Organisations_OrganisationId",
|
||||||
|
column: x => x.OrganisationId,
|
||||||
|
principalTable: "Organisations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantOrganisations_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantOrganisations_Users_CreatedBy",
|
||||||
|
column: x => x.CreatedBy,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TenantUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TenantId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Status = table.Column<int>(type: "integer", nullable: false, defaultValueSql: "1")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TenantUsers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantUsers_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantUsers_Users_CreatedBy",
|
||||||
|
column: x => x.CreatedBy,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TenantUsers_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Projects_TenantId",
|
||||||
|
table: "Projects",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Programmes_TenantId",
|
||||||
|
table: "Programmes",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Portfolios_TenantId",
|
||||||
|
table: "Portfolios",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Notifications_TenantId",
|
||||||
|
table: "Notifications",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Awards_TenantId",
|
||||||
|
table: "Awards",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantOrganisations_CreatedBy",
|
||||||
|
table: "TenantOrganisations",
|
||||||
|
column: "CreatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantOrganisations_OrganisationId",
|
||||||
|
table: "TenantOrganisations",
|
||||||
|
column: "OrganisationId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantOrganisations_TenantId",
|
||||||
|
table: "TenantOrganisations",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Tenants_CreatedBy",
|
||||||
|
table: "Tenants",
|
||||||
|
column: "CreatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Tenants_UpdatedBy",
|
||||||
|
table: "Tenants",
|
||||||
|
column: "UpdatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantUsers_CreatedBy",
|
||||||
|
table: "TenantUsers",
|
||||||
|
column: "CreatedBy");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantUsers_TenantId",
|
||||||
|
table: "TenantUsers",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantUsers_UserId",
|
||||||
|
table: "TenantUsers",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.Sql(@"
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM ""Users"" WHERE ""Id"" = '18caeb45-65f6-4103-9bda-ddf073a24de2'::uuid) THEN
|
||||||
|
INSERT INTO ""Tenants"" (""Id"", ""Name"", ""Status"", ""CreatedBy"", ""CreatedAt"")
|
||||||
|
OVERRIDING SYSTEM VALUE
|
||||||
|
VALUES (1, 'Default Tenant', 1, '18caeb45-65f6-4103-9bda-ddf073a24de2'::uuid, NOW())
|
||||||
|
ON CONFLICT (""Id"") DO NOTHING;
|
||||||
|
ELSE
|
||||||
|
RAISE EXCEPTION 'Target user 18caeb45-65f6-4103-9bda-ddf073a24de2 does not exist in Users table.';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
");
|
||||||
|
|
||||||
|
migrationBuilder.Sql(@"SELECT setval(pg_get_serial_sequence('""Tenants""', 'Id'), GREATEST(coalesce(max(""Id""), 1), 1)) FROM ""Tenants"";");
|
||||||
|
|
||||||
|
migrationBuilder.Sql("UPDATE \"Notifications\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;");
|
||||||
|
migrationBuilder.Sql("UPDATE \"Awards\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;");
|
||||||
|
migrationBuilder.Sql("UPDATE \"Portfolios\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;");
|
||||||
|
migrationBuilder.Sql("UPDATE \"Programmes\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;");
|
||||||
|
migrationBuilder.Sql("UPDATE \"Projects\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantOrganisations_TenantId_OrganisationId",
|
||||||
|
table: "TenantOrganisations",
|
||||||
|
columns: ["TenantId", "OrganisationId"],
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TenantUsers_TenantId_UserId",
|
||||||
|
table: "TenantUsers",
|
||||||
|
columns: ["TenantId", "UserId"],
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Awards_Tenants_TenantId",
|
||||||
|
table: "Awards",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Notifications_Tenants_TenantId",
|
||||||
|
table: "Notifications",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Portfolios_Tenants_TenantId",
|
||||||
|
table: "Portfolios",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Programmes_Tenants_TenantId",
|
||||||
|
table: "Programmes",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Projects_Tenants_TenantId",
|
||||||
|
table: "Projects",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Awards_Tenants_TenantId",
|
||||||
|
table: "Awards");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Notifications_Tenants_TenantId",
|
||||||
|
table: "Notifications");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Portfolios_Tenants_TenantId",
|
||||||
|
table: "Portfolios");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Programmes_Tenants_TenantId",
|
||||||
|
table: "Programmes");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Projects_Tenants_TenantId",
|
||||||
|
table: "Projects");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TenantOrganisations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TenantUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Tenants");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_TenantOrganisations_TenantId_OrganisationId",
|
||||||
|
table: "TenantOrganisations");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_TenantUsers_TenantId_UserId",
|
||||||
|
table: "TenantUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Projects_TenantId",
|
||||||
|
table: "Projects");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Programmes_TenantId",
|
||||||
|
table: "Programmes");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Portfolios_TenantId",
|
||||||
|
table: "Portfolios");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Notifications_TenantId",
|
||||||
|
table: "Notifications");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Awards_TenantId",
|
||||||
|
table: "Awards");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Projects");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Programmes");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Portfolios");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Notifications");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "Awards");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+2246
File diff suppressed because it is too large
Load Diff
+64
@@ -0,0 +1,64 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PostFundManagement.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class UpdateAuditLog : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "EntityId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(long),
|
||||||
|
oldType: "bigint");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_TenantId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_AuditLogs_Tenants_TenantId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "TenantId",
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_AuditLogs_Tenants_TenantId",
|
||||||
|
table: "AuditLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_AuditLogs_TenantId",
|
||||||
|
table: "AuditLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TenantId",
|
||||||
|
table: "AuditLogs");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<long>(
|
||||||
|
name: "EntityId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+270
-3
@@ -17,7 +17,7 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "10.0.11")
|
.HasAnnotation("ProductVersion", "10.0.12")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@@ -111,8 +111,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
b.PrimitiveCollection<string>("Changes")
|
b.PrimitiveCollection<string>("Changes")
|
||||||
.HasColumnType("jsonb");
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
b.Property<long>("EntityId")
|
b.Property<string>("EntityId")
|
||||||
.HasColumnType("bigint");
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("EntityName")
|
b.Property<string>("EntityName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@@ -122,6 +123,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
b.Property<Guid>("PerformedBy")
|
b.Property<Guid>("PerformedBy")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long?>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime>("Timestamp")
|
b.Property<DateTime>("Timestamp")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
@@ -131,6 +135,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("PerformedBy");
|
b.HasIndex("PerformedBy");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.ToTable("AuditLogs", (string)null);
|
b.ToTable("AuditLogs", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -171,6 +177,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasDefaultValueSql("1");
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
@@ -189,6 +198,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ProjectId");
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.HasIndex("UpdatedBy");
|
b.HasIndex("UpdatedBy");
|
||||||
|
|
||||||
b.ToTable("Awards", (string)null);
|
b.ToTable("Awards", (string)null);
|
||||||
@@ -883,6 +894,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("character varying(256)");
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
@@ -892,6 +906,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Recipient");
|
b.HasIndex("Recipient");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.ToTable("Notifications", (string)null);
|
b.ToTable("Notifications", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1012,6 +1028,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
b.Property<Guid>("OwnedBy")
|
b.Property<Guid>("OwnedBy")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("CreatedBy");
|
b.HasIndex("CreatedBy");
|
||||||
@@ -1020,6 +1039,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("OwnedBy");
|
b.HasIndex("OwnedBy");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.ToTable("Portfolios", (string)null);
|
b.ToTable("Portfolios", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1055,6 +1076,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasDefaultValueSql("1");
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
@@ -1069,6 +1093,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("PortfolioId");
|
b.HasIndex("PortfolioId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.ToTable("Programmes", (string)null);
|
b.ToTable("Programmes", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1122,6 +1148,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasDefaultValueSql("1");
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
@@ -1134,6 +1163,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ProgrammeId");
|
b.HasIndex("ProgrammeId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
b.HasIndex("UpdatedBy");
|
b.HasIndex("UpdatedBy");
|
||||||
|
|
||||||
b.ToTable("Projects", (string)null);
|
b.ToTable("Projects", (string)null);
|
||||||
@@ -1282,6 +1313,123 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
b.ToTable("SiteVisits", (string)null);
|
b.ToTable("SiteVisits", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasDefaultValueSql("now()");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedBy")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedBy")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("UpdatedBy");
|
||||||
|
|
||||||
|
b.ToTable("Tenants", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasDefaultValueSql("now()");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedBy")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<long>("OrganisationId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("OrganisationId");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("TenantOrganisations", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasDefaultValueSql("now()");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedBy")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasDefaultValueSql("1");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("TenantUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -1355,7 +1503,14 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
b.Navigation("Performer");
|
b.Navigation("Performer");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b =>
|
||||||
@@ -1388,6 +1543,12 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.HasForeignKey("ProjectId")
|
.HasForeignKey("ProjectId")
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("PostFundManagement.Domain.Entities.User", "Updator")
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Updator")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UpdatedBy")
|
.HasForeignKey("UpdatedBy")
|
||||||
@@ -1403,6 +1564,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.Navigation("Project");
|
b.Navigation("Project");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
|
||||||
b.Navigation("Updator");
|
b.Navigation("Updator");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1761,9 +1924,17 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Organisation");
|
b.Navigation("Organisation");
|
||||||
|
|
||||||
b.Navigation("RecipientUser");
|
b.Navigation("RecipientUser");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b =>
|
||||||
@@ -1816,11 +1987,19 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.NoAction)
|
.OnDelete(DeleteBehavior.NoAction)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Creator");
|
b.Navigation("Creator");
|
||||||
|
|
||||||
b.Navigation("Organisation");
|
b.Navigation("Organisation");
|
||||||
|
|
||||||
b.Navigation("Owner");
|
b.Navigation("Owner");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b =>
|
||||||
@@ -1843,11 +2022,19 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.NoAction)
|
.OnDelete(DeleteBehavior.NoAction)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.NoAction)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Creator");
|
b.Navigation("Creator");
|
||||||
|
|
||||||
b.Navigation("Owner");
|
b.Navigation("Owner");
|
||||||
|
|
||||||
b.Navigation("Portfolio");
|
b.Navigation("Portfolio");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b =>
|
||||||
@@ -1864,6 +2051,12 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("PostFundManagement.Domain.Entities.User", "Updator")
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Updator")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UpdatedBy")
|
.HasForeignKey("UpdatedBy")
|
||||||
@@ -1873,6 +2066,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
|
|
||||||
b.Navigation("Programme");
|
b.Navigation("Programme");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
|
||||||
b.Navigation("Updator");
|
b.Navigation("Updator");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1940,6 +2135,78 @@ namespace PostFundManagement.Infrastructure.Database.Migrations
|
|||||||
b.Navigation("Project");
|
b.Navigation("Project");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Creator")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Updator")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UpdatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
|
||||||
|
b.Navigation("Updator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Creator")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OrganisationId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
|
||||||
|
b.Navigation("Organisation");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.User", "Creator")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PostFundManagement.Domain.Entities.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b =>
|
modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Evidences");
|
b.Navigation("Evidences");
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Infrastructure.Database.Factories;
|
||||||
|
|
||||||
|
public sealed partial class ApplicationDbContextFactory
|
||||||
|
{
|
||||||
|
private sealed class DesignTimeTenantContextService : ITenantContextService
|
||||||
|
{
|
||||||
|
public string? TenantName => "Design Time";
|
||||||
|
public long TenantId => 0L;
|
||||||
|
public bool IsAdmin => true;
|
||||||
|
public bool HasTenantFilter => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using PostFundManagement.Domain.Abstractions;
|
||||||
|
|
||||||
|
namespace PostFundManagement.Infrastructure.Database.Services;
|
||||||
|
|
||||||
|
public sealed class TenantContextService : ITenantContextService
|
||||||
|
{
|
||||||
|
public string? TenantName { get; }
|
||||||
|
|
||||||
|
public long TenantId { get; }
|
||||||
|
|
||||||
|
public bool IsAdmin { get; }
|
||||||
|
|
||||||
|
public bool HasTenantFilter { get; }
|
||||||
|
|
||||||
|
public TenantContextService(IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
var user = httpContextAccessor.HttpContext?.User;
|
||||||
|
|
||||||
|
IsAdmin = user?.IsInRole("Admin") == true || user?.HasClaim("role", "Admin") == true;
|
||||||
|
|
||||||
|
HasTenantFilter = !IsAdmin;
|
||||||
|
|
||||||
|
var tenantIdClaimVal = user?.FindFirst("tenant_id")?.Value ?? user?.FindFirst("TenantId")?.Value;
|
||||||
|
TenantId = long.TryParse(tenantIdClaimVal, out var id) ? id : 0L;
|
||||||
|
|
||||||
|
var tenantNameClaimVal = user?.FindFirst("tenant")?.Value ?? user?.FindFirst("Tenant")?.Value;
|
||||||
|
TenantName = !string.IsNullOrWhiteSpace(tenantNameClaimVal) ? tenantNameClaimVal : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.12" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.12" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.12" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.12" />
|
||||||
|
|
||||||
|
<Using Include="Microsoft.AspNetCore.Http" />
|
||||||
<Using Include="Microsoft.Extensions.Configuration" />
|
<Using Include="Microsoft.Extensions.Configuration" />
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
<Using Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user