Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: refactoring and various improvements #848

Merged
merged 11 commits into from
Nov 5, 2024
3 changes: 3 additions & 0 deletions .changeset/pr-848-2155869305.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

---
---
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ namespace Equinor.ProjectExecutionPortal.Application.Cache;

public class CacheManager : ICacheManager
{
private readonly IMemoryCache _cache;

public CacheManager() => _cache = new MemoryCache(new MemoryCacheOptions
private readonly IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions
{
Clock = new CacheClock()
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Equinor.ProjectExecutionPortal.Application.Cache;

public class CacheOptions
{
public int FusionAppsCacheMinutes { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
using Fusion.Integration.Apps.Abstractions.Abstractions;
using Fusion.Integration.Apps.Abstractions.Models;
using Microsoft.Extensions.Options;

namespace Equinor.ProjectExecutionPortal.Application.Cache;

public class FusionAppsCache : IFusionAppsCache
{
private readonly ICacheManager _cacheManager;
private readonly IAppsClient _fusionAppsClient;
private readonly IOptions<CacheOptions> _cacheOptions;

public FusionAppsCache(ICacheManager cacheManager, IAppsClient fusionAppsClient)
private const string FusionAppCacheKey = "FUSION_APP";

public FusionAppsCache(ICacheManager cacheManager, IAppsClient fusionAppsClient, IOptions<CacheOptions> cacheOptions)
{
_cacheManager = cacheManager;
_fusionAppsClient = fusionAppsClient;
_cacheOptions = cacheOptions;
}

// TODO: Move cache duration to app settings

public async Task<List<App>> GetFusionApps()
{
return await _cacheManager.GetOrCreateAsync("FUSION_APP",
return await _cacheManager.GetOrCreateAsync(FusionAppCacheKey,
async () =>
{
var fusionApps = await _fusionAppsClient.GetAppsAsync();

return fusionApps.ToList();
},
CacheDuration.Minutes,
60);
CacheDuration.Minutes, _cacheOptions.Value.FusionAppsCacheMinutes);
}

public async Task<App?> GetFusionApp(string appKey)
{
return await _cacheManager.GetOrCreateAsync("FUSION_APP",
return await _cacheManager.GetOrCreateAsync(FusionAppCacheKey,
async () => await _fusionAppsClient.GetAppAsync(appKey),
CacheDuration.Minutes,
60);
CacheDuration.Minutes, _cacheOptions.Value.FusionAppsCacheMinutes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private void ApplyMappingsFromAssembly(Assembly assembly)
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1")!.GetMethod("Mapping");

methodInfo?.Invoke(instance, new object[] { this });
methodInfo?.Invoke(instance, [this]);

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ public abstract class EntityBase
{
private List<INotification>? _domainEvents;

public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly() ?? (_domainEvents = new List<INotification>()).AsReadOnly();
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly() ?? (_domainEvents = []).AsReadOnly();

public virtual Guid Id { get; protected set; }

public void AddDomainEvent(INotification eventItem)
{
_domainEvents ??= new List<INotification>();
_domainEvents ??= [];
_domainEvents.Add(eventItem);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@

namespace Equinor.ProjectExecutionPortal.Domain.Infrastructure
{
public abstract class QueryBase<T> : IRequest<T>
{
}
public abstract class QueryBase<T> : IRequest<T>;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Equinor.ProjectExecutionPortal.Infrastructure;

Expand All @@ -8,6 +7,4 @@ public interface IReadWriteContext : IDisposable
DbSet<TEntity> Set<TEntity>() where TEntity : class;

Task<int> SaveChangesAsync(CancellationToken cancellationToken);

DatabaseFacade Database { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Equinor.ProjectExecutionPortal.Infrastructure;

public static class InfrastructureModule
{
public static IServiceCollection AddInfrastructureModules(this IServiceCollection services, IConfiguration configuration)
public static void AddInfrastructureModules(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("ProjectPortalContext");

Expand All @@ -15,7 +15,5 @@ public static IServiceCollection AddInfrastructureModules(this IServiceCollectio
b => b.MigrationsAssembly(typeof(ProjectExecutionPortalContext).Assembly.FullName)
)
);

return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

public static DateTimeKindConverter DateTimeKindConverter { get; } = new();

public DbSet<Portal> Portals { get; set; }
public DbSet<PortalApp> PortalApps { get; set; }
public DbSet<OnboardedApp> OnboardedApps { get; set; }
public DbSet<OnboardedContext> OnboardedContexts { get; set; }
public DbSet<ContextType> ContextTypes { get; set; }
public DbSet<Portal> Portals { get; init; }
public DbSet<PortalApp> PortalApps { get; init; }
public DbSet<OnboardedApp> OnboardedApps { get; init; }
public DbSet<OnboardedContext> OnboardedContexts { get; init; }
public DbSet<ContextType> ContextTypes { get; init; }

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
await DispatchEventsAsync(cancellationToken);
await SetAuditDataAsync();
SetAuditData();

try
{
Expand All @@ -63,12 +63,13 @@ private async Task DispatchEventsAsync(CancellationToken cancellationToken = def
await _eventDispatcher.DispatchAsync(entities, cancellationToken);
}

private async Task SetAuditDataAsync()
private void SetAuditData()
{
var addedEntries = ChangeTracker
.Entries<ICreationAuditable>()
.Where(x => x.State == EntityState.Added)
.ToList();

var modifiedEntries = ChangeTracker
.Entries<IModificationAuditable>()
.Where(x => x.State == EntityState.Modified)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection;
using Equinor.ProjectExecutionPortal.Application;
using Equinor.ProjectExecutionPortal.Application.Cache;
using Equinor.ProjectExecutionPortal.Application.Events.Common;
using Equinor.ProjectExecutionPortal.Domain.Common.Events.Common;
using Equinor.ProjectExecutionPortal.Domain.Common.Time;
Expand All @@ -11,7 +12,6 @@
using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Client;

namespace Equinor.ProjectExecutionPortal.WebApi.DiModules;

Expand All @@ -23,7 +23,7 @@ public static void AddApplicationModules(this IServiceCollection services, IConf

var applicationAssembly = typeof(IApplicationMarker).GetTypeInfo().Assembly;

services.Configure<CacheOptions>(configuration.GetSection("CacheOptions"));
services.Configure<CacheOptions>(configuration.GetSection("Cache"));

services.AddAuthorization(options =>
{
Expand Down
19 changes: 9 additions & 10 deletions backend/src/Equinor.ProjectExecutionPortal.WebApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text.Json.Serialization;
using Equinor.ProjectExecutionPortal.WebApi.DiModules;
using Equinor.ProjectExecutionPortal.WebApi.Middleware;
using FluentValidation;
using FluentValidation.AspNetCore;
using Fusion.Integration.Apps.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
Expand Down Expand Up @@ -44,13 +45,14 @@
// Add fusion integration
builder.Services.AddFusionIntegration(f =>
{
var environment = builder.Configuration.GetValue<string>("Fusion:Environment" ?? "ci");
var environment = builder.Configuration.GetValue<string>("Fusion:Environment")!;

f.UseServiceInformation("Fusion.Project.Portal", environment);
f.UseDefaultEndpointResolver(environment);
f.AddAppsClient();
f.UseDefaultTokenProvider(opts =>
{
opts.ClientId = builder.Configuration.GetValue<string>("AzureAd:ClientId");
opts.ClientId = builder.Configuration.GetValue<string>("AzureAd:ClientId")!;
opts.ClientSecret = builder.Configuration.GetValue<string>("AzureAd:ClientSecret");
});
f.DisableClaimsTransformation();
Expand All @@ -65,10 +67,8 @@
})
.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

builder.Services.AddFluentValidation(c =>
{
c.RegisterValidatorsFromAssemblyContaining<Program>();
});
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining<Program>();

builder.Services.AddEndpointsApiExplorer();

Expand All @@ -89,8 +89,8 @@
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(builder.Configuration["Swagger:AuthorizationUrl"]),
TokenUrl = new Uri(builder.Configuration["Swagger:TokenUrl"]),
AuthorizationUrl = new Uri(builder.Configuration["Swagger:AuthorizationUrl"]!),
TokenUrl = new Uri(builder.Configuration["Swagger:TokenUrl"]!),
Scopes = scopes
}
}
Expand Down Expand Up @@ -148,5 +148,4 @@
app.Run();

// Used for tests
public partial class Program
{ }
public abstract partial class Program;
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"Scope": "access_as_user"
},
"Fusion": {
"Environment": "ci"
"Environment": "ci"
},
"AzureAd": {
"ClientId": "guid", // This app's AppReg key
Expand All @@ -25,6 +25,9 @@
"ApplicationInsights": {
"ConnectionString": ""
},
"Cache": {
"FusionAppsCacheMinutes": 60
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class ContextTypeData
internal static class ContextTypeData
{
public class InitialDbSeedData
public static class InitialDbSeedData
{
public static ContextType ContextType1 = new(ValidContextTypes.ProjectMasterContextTypeKey);
public static ContextType ContextType2 = new(ValidContextTypes.FacilityContextTypeKey);
public static readonly ContextType ContextType1 = new(ValidContextTypes.ProjectMasterContextTypeKey);
public static readonly ContextType ContextType2 = new(ValidContextTypes.FacilityContextTypeKey);
}

public class ValidContextTypes
public static class ValidContextTypes
{
public static string ProjectMasterContextTypeKey = "ProjectMaster";
public static string FacilityContextTypeKey = "Facility";
public static string ContractContextTypeKey = "Contract";
public const string ProjectMasterContextTypeKey = "ProjectMaster";
public const string FacilityContextTypeKey = "Facility";
public const string ContractContextTypeKey = "Contract";
}

public class InvalidContextTypes
public static class InvalidContextTypes
{
public static string InvalidContextTypeKey = "SuperContext";
public const string InvalidContextTypeKey = "SuperContext";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class FusionAppApiData
internal static class FusionAppApiData
{
public static App MeetingsFusion => new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class FusionContextApiData
internal static class FusionContextApiData
{
public static string JcaContextExternalId = "fc5ffcbc-392f-4d7e-bb14-79a006579337";
public const string JcaContextExternalId = "fc5ffcbc-392f-4d7e-bb14-79a006579337";
public static Guid JcaContextId = new("94dd5f4d-17f1-4312-bf75-ad75f4d9572c");
public static string OgpContextExternalId = "91dd6653-a364-40c7-af26-7af516d66c42";
public const string OgpContextExternalId = "91dd6653-a364-40c7-af26-7af516d66c42";
public static Guid OgpContextId = new("ce31b83a-b6cd-4267-89f3-db308edf721e");
public static string MongstadContextExternalId = "09206ca3-02ac-4c65-adbf-caa7b66364ea";
public const string MongstadContextExternalId = "09206ca3-02ac-4c65-adbf-caa7b66364ea";
public static Guid MongstadContextId = new("4CB78175-46CF-41A3-58DB-08DC74C80D40");
public static string InvalidContextExternalId = "11111111-1111-1111-1111-111111111111";
public const string InvalidContextExternalId = "11111111-1111-1111-1111-111111111111";
public static Guid InvalidContextId = new("11111111-1111-1111-1111-111111111111");

public static FusionContext JcaFusionContext => new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class OnboardedAppData
internal static class OnboardedAppData
{
public class InitialDbSeedData
public static class InitialDbSeedData
{
public static OnboardedApp MeetingsApp = new("meetings");
public static OnboardedApp ReviewsApp = new("reviews");
public static OnboardedApp TasksApp = new("tasks");
public static OnboardedApp OrgChartApp = new("one-equinor");
public static OnboardedApp HandoverGardenApp = new("handover-garden");
public static OnboardedApp WorkOrderGardenApp = new("workorder-garden");
public static readonly OnboardedApp MeetingsApp = new("meetings");
public static readonly OnboardedApp ReviewsApp = new("reviews");
public static readonly OnboardedApp TasksApp = new("tasks");
public static readonly OnboardedApp OrgChartApp = new("one-equinor");
public static readonly OnboardedApp HandoverGardenApp = new("handover-garden");
public static readonly OnboardedApp WorkOrderGardenApp = new("workorder-garden");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class OnboardedContextData
internal static class OnboardedContextData
{
public class InitialDbSeedData
public static class InitialDbSeedData
{
public static OnboardedContext JcaContext = new(FusionContextApiData.JcaContextExternalId, ContextTypeData.ValidContextTypes.ProjectMasterContextTypeKey, "desc");
public static OnboardedContext OgpContext = new(FusionContextApiData.OgpContextExternalId, ContextTypeData.ValidContextTypes.ProjectMasterContextTypeKey, "desc");
public static readonly OnboardedContext JcaContext = new(FusionContextApiData.JcaContextExternalId, ContextTypeData.ValidContextTypes.ProjectMasterContextTypeKey, "desc");
public static readonly OnboardedContext OgpContext = new(FusionContextApiData.OgpContextExternalId, ContextTypeData.ValidContextTypes.ProjectMasterContextTypeKey, "desc");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Equinor.ProjectExecutionPortal.Tests.WebApi.Data
{
internal class PortalConfigurationData
internal static class PortalConfigurationData
{
public class InitialDbSeedData
public static class InitialDbSeedData
{
public static PortalConfiguration GenericPortalConfiguration = new(
public static readonly PortalConfiguration GenericPortalConfiguration = new(
"routerConfig",
"extensionConfig",
"environmentConfig"
Expand Down
Loading