Skip to content

Commit

Permalink
feat: add Configuration to Portal domain (#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
kjetilhau authored Sep 3, 2024
1 parent 0351d2c commit a4a6ebc
Show file tree
Hide file tree
Showing 21 changed files with 743 additions and 7 deletions.
21 changes: 21 additions & 0 deletions .changeset/pr-723.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"fusion-project-portal": minor
---

feat: add Configuration to Portal domain

To enable the configuration of pages such as the project page, facility page, and root page, it should be possible to add a route configuration to the portal.

A configuration model as been added, currently only with a router property (but designed to be exteded if needed in the future)

The following endpoints has been added:

- GET /portal/ID/configuration
- PUT /portal/ID/configuration

In addition the data is now included in the following endpoint:

- GET /portal/ID

> [!IMPORTANT]
> This change requires database migration.
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public UpdatePortalCommand(Guid id, string name, string shortName, string subTex
public string Name { get; }
public string ShortName { get; }
public string SubText { get; }
public string? Description { get; set; }
public string? Description { get; }
public string Icon { get; }
public IList<string>? ContextTypes { get; set; }
public IList<string>? ContextTypes { get; }

public class Handler : IRequestHandler<UpdatePortalCommand, Guid>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Equinor.ProjectExecutionPortal.Domain.Common.Exceptions;
using Equinor.ProjectExecutionPortal.Domain.Entities;
using Equinor.ProjectExecutionPortal.Infrastructure;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Equinor.ProjectExecutionPortal.Application.Commands.Portals.UpdatePortalConfiguration;

public class UpdatePortalConfigurationCommand : IRequest<Guid>
{
public UpdatePortalConfigurationCommand(Guid portalId, string? router)
{
PortalId = portalId;
Router = router;
}

public Guid PortalId { get; }
public string? Router { get; }

public class Handler : IRequestHandler<UpdatePortalConfigurationCommand, Guid>
{
private readonly IReadWriteContext _readWriteContext;

public Handler(IReadWriteContext readWriteContext)
{
_readWriteContext = readWriteContext;
}

public async Task<Guid> Handle(UpdatePortalConfigurationCommand command, CancellationToken cancellationToken)
{
var entity = await _readWriteContext.Set<Portal>()
.Include(portal => portal.Configuration)
.FirstOrDefaultAsync(portal => portal.Id == command.PortalId, cancellationToken);

if (entity == null)
{
throw new NotFoundException(nameof(PortalConfiguration), command.PortalId);
}

entity.Configuration.Update(command.Router);

await _readWriteContext.SaveChangesAsync(cancellationToken);

return entity.Id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public Handler(IReadWriteContext readWriteContext, IMapper mapper)
var entity = await _readWriteContext.Set<Portal>()
.AsNoTracking()
.Include(x => x.ContextTypes)
.Include(x => x.Configuration)
.FirstOrDefaultAsync(x => x.Id == request.PortalId, cancellationToken);

var portal = _mapper.Map<Portal?, PortalDto?>(entity);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using AutoMapper;
using Equinor.ProjectExecutionPortal.Domain.Entities;
using Equinor.ProjectExecutionPortal.Domain.Infrastructure;
using Equinor.ProjectExecutionPortal.Infrastructure;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Equinor.ProjectExecutionPortal.Application.Queries.Portals.GetPortalConfiguration;

public class GetPortalConfigurationQuery : QueryBase<PortalConfigurationDto?>
{
public GetPortalConfigurationQuery(Guid portalId)
{
PortalId = portalId;
}

public Guid PortalId { get; }

public class Handler : IRequestHandler<GetPortalConfigurationQuery, PortalConfigurationDto?>
{
private readonly IReadWriteContext _readWriteContext;
private readonly IMapper _mapper;

public Handler(IReadWriteContext readWriteContext, IMapper mapper)
{
_readWriteContext = readWriteContext;
_mapper = mapper;
}

public async Task<PortalConfigurationDto?> Handle(GetPortalConfigurationQuery request, CancellationToken cancellationToken)
{
var entity = await _readWriteContext.Set<PortalConfiguration>()
.AsNoTracking()
.FirstOrDefaultAsync(x => x.PortalId == request.PortalId, cancellationToken);

var portal = _mapper.Map<PortalConfiguration?, PortalConfigurationDto?>(entity);

return portal;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Equinor.ProjectExecutionPortal.Application.Infrastructure.Mappings;

namespace Equinor.ProjectExecutionPortal.Application.Queries.Portals
{
public class PortalConfigurationDto : IMapFrom<Domain.Entities.PortalConfiguration>
{
public Guid Id { get; set; }
public string? Router { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ public class PortalDto : IMapFrom<Domain.Entities.Portal>
public string Icon { get; set; }
public IList<ContextTypeDto> ContextTypes { get; set; }
public List<PortalAppDto> Apps { get; set; }
public PortalConfigurationDto? Configuration { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace Equinor.ProjectExecutionPortal.Domain.Entities;

/// <summary>
/// The Work Surface functions as a container for all apps and related information about a specific phase
/// The Portal functions as a container for enabled apps and contexts
/// </summary>
public class Portal : AuditableEntityBase, ICreationAuditable, IModificationAuditable
{
Expand All @@ -25,6 +25,7 @@ public Portal(string key, string name, string shortName, string subText, string?
SubText = subText;
Description = description;
Icon = icon;
Configuration = CreateDefaultPortalConfiguration();
}

public string Key { get; set; }
Expand All @@ -34,6 +35,8 @@ public Portal(string key, string name, string shortName, string subText, string?
public string? Description { get; set; }
public string Icon { get; set; }

public PortalConfiguration Configuration { get; set; }

public IReadOnlyCollection<PortalApp> Apps => _apps.AsReadOnly();
public IReadOnlyCollection<ContextType> ContextTypes => _contextTypes.AsReadOnly();

Expand All @@ -47,6 +50,11 @@ public void Update(string key, string name, string shortName, string subText, st
Icon = icon;
}

private static PortalConfiguration CreateDefaultPortalConfiguration()
{
return new PortalConfiguration(null);
}

public void AddApp(PortalApp app)
{
_apps.Add(app);
Expand All @@ -57,9 +65,10 @@ public void AddContextTypes(IList<ContextType> contextTypes)
_contextTypes.Clear();
_contextTypes.AddRange(contextTypes);
}

public void AddContextType(ContextType contextType)
{
_contextTypes.Add(contextType);
_contextTypes.Add(contextType);
}

public void RemoveContextType(ContextType contextType)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Equinor.ProjectExecutionPortal.Domain.Common;
using Equinor.ProjectExecutionPortal.Domain.Common.Audit;

namespace Equinor.ProjectExecutionPortal.Domain.Entities;

/// <summary>
/// Each portal has their own configuration
/// </summary>
public class PortalConfiguration : AuditableEntityBase, ICreationAuditable, IModificationAuditable
{
public const int RouterLengthMax = 8000;

public PortalConfiguration(string? router)
{
Router = router;
}

public string? Router { get; set; }

public Guid PortalId { get; set; }
public Portal Portal { get; set; } = null!;

public void Update(string? router)
{
Router = router;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,21 @@ public void Configure(EntityTypeBuilder<Portal> builder)
builder.Property(t => t.Icon)
.IsRequired();

builder.HasOne(x => x.Configuration)
.WithOne(x => x.Portal)
.OnDelete(DeleteBehavior.Cascade);

builder.HasMany(x => x.Apps)
.WithOne(x => x.Portal)
.OnDelete(DeleteBehavior.Restrict);

builder.HasMany(x => x.ContextTypes).WithMany(x => x.Portals).UsingEntity(join => join.ToTable("PortalContextTypes"));
builder.HasMany(x => x.ContextTypes)
.WithMany(x => x.Portals)
.UsingEntity(join => join.ToTable("PortalContextTypes"));

builder.HasOne(x => x.Configuration)
.WithOne(x => x.Portal)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Equinor.ProjectExecutionPortal.Infrastructure.EntityConfigurations.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace Equinor.ProjectExecutionPortal.Infrastructure.EntityConfigurations;

public class PortalConfigurationConfiguration : IEntityTypeConfiguration<Domain.Entities.PortalConfiguration>
{
public void Configure(EntityTypeBuilder<Domain.Entities.PortalConfiguration> builder)
{
builder.ConfigureCreationAudit();
builder.ConfigureModificationAudit();

builder.Property(t => t.Router)
.HasMaxLength(Domain.Entities.PortalConfiguration.RouterLengthMax);
}
}
Loading

0 comments on commit a4a6ebc

Please sign in to comment.