Skip to content

Commit

Permalink
Merge pull request #473 from Strongminds/release/11.2.4
Browse files Browse the repository at this point in the history
Release/11.2.4
  • Loading branch information
mrjsawdk authored Dec 19, 2022
2 parents eb46968 + 15352f1 commit 0cf3b0f
Show file tree
Hide file tree
Showing 165 changed files with 4,842 additions and 1,019 deletions.
4 changes: 4 additions & 0 deletions Core.ApplicationServices/Core.ApplicationServices.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<Compile Include="Extensions\IAuthorizationContextExtensions.cs" />
<Compile Include="Authorization\Policies\GlobalReadAccessPolicy.cs" />
<Compile Include="Extensions\OptionalValueChangeExtensions.cs" />
<Compile Include="Extensions\OrganizationTreeUpdateConsequencesExtensions.cs" />
<Compile Include="GDPR\DataProcessingRegistrationReadModelService.cs" />
<Compile Include="GDPR\DataProcessingRegistrationApplicationService.cs" />
<Compile Include="GDPR\DataProcessingRegistrationOptionsApplicationService.cs" />
Expand Down Expand Up @@ -149,6 +150,7 @@
<Compile Include="Model\Organizations\OrganizationRemovalConflicts.cs" />
<Compile Include="Model\Organizations\PaymentChangeParameters.cs" />
<Compile Include="Model\Organizations\StsOrganizationSynchronizationDetails.cs" />
<Compile Include="Model\Organizations\AuthorizedUpdateOrganizationFromFKOrganisationCommand.cs" />
<Compile Include="Model\RightsHolder\UserRoleAssociationDTO.cs" />
<Compile Include="Model\Shared\OptionalValueChange.cs" />
<Compile Include="Model\SystemUsage\Write\NamedLink.cs" />
Expand Down Expand Up @@ -183,6 +185,8 @@
<Compile Include="Notification\UserNotificationApplicationService.cs" />
<Compile Include="Organizations\Handlers\HandleOrganizationBeingDeleted.cs" />
<Compile Include="Organizations\Handlers\RemoveOrganizationUnitRegistrationsCommandHandler.cs" />
<Compile Include="Organizations\Handlers\AuthorizedUpdateOrganizationFromFKOrganisationCommandHandler.cs" />
<Compile Include="Organizations\Handlers\SendEmailToStakeholdersOnExternalOrganizationConnectionUpdatedHandler.cs" />
<Compile Include="Organizations\IOrganizationUnitService.cs" />
<Compile Include="Organizations\IStsOrganizationSynchronizationService.cs" />
<Compile Include="Organizations\OrganizationUnitService.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using Core.Abstractions.Types;
using Core.DomainModel.Organization;
using Core.DomainServices.Context;
using Core.DomainServices.Time;
using System.Collections.Generic;
using System.Linq;

namespace Core.ApplicationServices.Extensions
{
public static class OrganizationTreeUpdateConsequencesExtensions
{
public static ExternalConnectionAddNewLogInput ToLogEntries(this OrganizationTreeUpdateConsequences consequences, Maybe<ActiveUserIdContext> activeUserIdContext, IOperationClock operationClock)
{
var changeLogType = ExternalOrganizationChangeLogResponsible.Background;
int? changeLogUserId = null;
if (activeUserIdContext.HasValue)
{
var userId = activeUserIdContext.Value.ActiveUserId;
changeLogType = ExternalOrganizationChangeLogResponsible.User;
changeLogUserId = userId;
}

var changeLogEntries = consequences.ConvertConsequencesToConsequenceLogs().ToList();
var changeLogLogTime = operationClock.Now;

return new ExternalConnectionAddNewLogInput(changeLogUserId, changeLogType, changeLogLogTime, MapToExternalConnectionAddNewLogEntryInput(changeLogEntries));
}

private static IEnumerable<ExternalConnectionAddNewLogEntryInput> MapToExternalConnectionAddNewLogEntryInput(IEnumerable<StsOrganizationConsequenceLog> entry)
{
return entry
.Select(x => new ExternalConnectionAddNewLogEntryInput(x.ExternalUnitUuid, x.Name, x.Type, x.Description))
.ToList();
}

public static IEnumerable<StsOrganizationConsequenceLog> ConvertConsequencesToConsequenceLogs(this OrganizationTreeUpdateConsequences consequences)
{
var logs = new List<StsOrganizationConsequenceLog>();
logs.AddRange(MapAddedOrganizationUnits(consequences));
logs.AddRange(MapRenamedOrganizationUnits(consequences));
logs.AddRange(MapMovedOrganizationUnits(consequences));
logs.AddRange(MapRemovedOrganizationUnits(consequences));
logs.AddRange(MapConvertedOrganizationUnits(consequences));

return logs;
}

private static IEnumerable<StsOrganizationConsequenceLog> MapConvertedOrganizationUnits(OrganizationTreeUpdateConsequences consequences)
{
return consequences
.DeletedExternalUnitsBeingConvertedToNativeUnits
.Select(converted => new StsOrganizationConsequenceLog
{
Name = converted.organizationUnit.Name,
Type = ConnectionUpdateOrganizationUnitChangeType.Converted,
ExternalUnitUuid = converted.externalOriginUuid,
Description = $"'{converted.organizationUnit.Name}' er slettet i FK Organisation men konverteres til KITOS enhed, da den anvendes aktivt i KITOS."
})
.ToList();
}

private static IEnumerable<StsOrganizationConsequenceLog> MapRemovedOrganizationUnits(OrganizationTreeUpdateConsequences consequences)
{
return consequences
.DeletedExternalUnitsBeingDeleted
.Select(deleted => new StsOrganizationConsequenceLog
{
Name = deleted.organizationUnit.Name,
Type = ConnectionUpdateOrganizationUnitChangeType.Deleted,
ExternalUnitUuid = deleted.externalOriginUuid,
Description = $"'{deleted.organizationUnit.Name}' slettes."
})
.ToList();
}

private static IEnumerable<StsOrganizationConsequenceLog> MapMovedOrganizationUnits(OrganizationTreeUpdateConsequences consequences)
{
return consequences
.OrganizationUnitsBeingMoved
.Select(moved =>
{
var (movedUnit, oldParent, newParent) = moved;
return new StsOrganizationConsequenceLog
{
Name = movedUnit.Name,
Type = ConnectionUpdateOrganizationUnitChangeType.Moved,
ExternalUnitUuid = movedUnit.ExternalOriginUuid.GetValueOrDefault(),
Description = $"'{movedUnit.Name}' flyttes fra at være underenhed til '{oldParent.Name}' til fremover at være underenhed for {newParent.Name}"
};
})
.ToList();
}

private static IEnumerable<StsOrganizationConsequenceLog> MapRenamedOrganizationUnits(OrganizationTreeUpdateConsequences consequences)
{
return consequences
.OrganizationUnitsBeingRenamed
.Select(renamed =>
{
var (affectedUnit, oldName, newName) = renamed;
return new StsOrganizationConsequenceLog
{
Name = oldName,
Type = ConnectionUpdateOrganizationUnitChangeType.Renamed,
ExternalUnitUuid = affectedUnit.ExternalOriginUuid.GetValueOrDefault(),
Description = $"'{oldName}' omdøbes til '{newName}'"
};
})
.ToList();
}

private static IEnumerable<StsOrganizationConsequenceLog> MapAddedOrganizationUnits(OrganizationTreeUpdateConsequences consequences)
{
return consequences
.AddedExternalOrganizationUnits
.Select(added => new StsOrganizationConsequenceLog
{
Name = added.unitToAdd.Name,
Type = ConnectionUpdateOrganizationUnitChangeType.Added,
ExternalUnitUuid = added.unitToAdd.Uuid,
Description = $"'{added.unitToAdd.Name}' tilføjes som underenhed til '{added.parent?.Name}'"
}
)
.ToList();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Core.Abstractions.Types;
using Core.DomainModel.Commands;
using Core.DomainModel.Organization;

namespace Core.ApplicationServices.Model.Organizations
{
/// <summary>
/// Describes a pre-authorized update command for the FK Org synchronization.
/// Make sure to authorize the call prior to executing this command
/// </summary>
public class AuthorizedUpdateOrganizationFromFKOrganisationCommand : ICommand
{
public bool SubscribeToChanges { get; }
public Maybe<int> SynchronizationDepth { get; }
public Organization Organization { get; }
public Maybe<ExternalOrganizationUnit> PreloadedExternalTree { get; }

public AuthorizedUpdateOrganizationFromFKOrganisationCommand(Organization organization, Maybe<int> synchronizationDepth, bool subscribeToChanges, Maybe<ExternalOrganizationUnit> preloadedExternalTree)
{
SubscribeToChanges = subscribeToChanges;
PreloadedExternalTree = preloadedExternalTree;
SynchronizationDepth = synchronizationDepth;
Organization = organization;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Core.DomainServices.Model.StsOrganization;
using System;
using Core.DomainServices.Model.StsOrganization;

namespace Core.ApplicationServices.Model.Organizations
{
Expand All @@ -10,15 +11,19 @@ public class StsOrganizationSynchronizationDetails
public bool CanUpdateConnection { get; }
public bool CanDeleteConnection { get; }
public CheckConnectionError? CheckConnectionError { get; }
public bool SubscribesToUpdates { get; }
public DateTime? DateOfLatestCheckBySubscription { get; }

public StsOrganizationSynchronizationDetails(bool connected, int? synchronizationDepth, bool canCreateConnection, bool canUpdateConnection, bool canDeleteConnection, CheckConnectionError? checkConnectionError)
public StsOrganizationSynchronizationDetails(bool connected, int? synchronizationDepth, bool canCreateConnection, bool canUpdateConnection, bool canDeleteConnection, CheckConnectionError? checkConnectionError, bool subscribesToUpdates, DateTime? dateOfLatestCheckBySubscription)
{
Connected = connected;
SynchronizationDepth = synchronizationDepth;
CanCreateConnection = canCreateConnection;
CanUpdateConnection = canUpdateConnection;
CanDeleteConnection = canDeleteConnection;
CheckConnectionError = checkConnectionError;
SubscribesToUpdates = subscribesToUpdates;
DateOfLatestCheckBySubscription = dateOfLatestCheckBySubscription;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Core.Abstractions.Types;
using Core.ApplicationServices.Model.Organizations;
using Core.DomainModel.Commands;
using Core.DomainModel.Events;
using Core.DomainModel.Organization;
using System;
using System.Linq;
using Core.ApplicationServices.Extensions;
using Core.DomainServices;
using Core.DomainServices.Organizations;
using Infrastructure.Services.DataAccess;
using Serilog;
using Core.DomainServices.Context;
using Core.DomainServices.Time;

namespace Core.ApplicationServices.Organizations.Handlers
{
public class AuthorizedUpdateOrganizationFromFKOrganisationCommandHandler : ICommandHandler<AuthorizedUpdateOrganizationFromFKOrganisationCommand, Maybe<OperationError>>
{
private readonly IStsOrganizationUnitService _stsOrganizationUnitService;
private readonly IGenericRepository<OrganizationUnit> _organizationUnitRepository;
private readonly ILogger _logger;
private readonly IDomainEvents _domainEvents;
private readonly IDatabaseControl _databaseControl;
private readonly ITransactionManager _transactionManager;
private readonly Maybe<ActiveUserIdContext> _userContext;
private readonly IOperationClock _operationClock;
private readonly IGenericRepository<StsOrganizationChangeLog> _stsChangeLogRepository;

public AuthorizedUpdateOrganizationFromFKOrganisationCommandHandler(
IStsOrganizationUnitService stsOrganizationUnitService,
IGenericRepository<OrganizationUnit> organizationUnitRepository,
ILogger logger,
IDomainEvents domainEvents,
IDatabaseControl databaseControl,
ITransactionManager transactionManager,
Maybe<ActiveUserIdContext> userContext,
IOperationClock operationClock,
IGenericRepository<StsOrganizationChangeLog> stsChangeLogRepository)
{
_stsOrganizationUnitService = stsOrganizationUnitService;
_organizationUnitRepository = organizationUnitRepository;
_logger = logger;
_domainEvents = domainEvents;
_databaseControl = databaseControl;
_transactionManager = transactionManager;
_userContext = userContext;
_operationClock = operationClock;
_stsChangeLogRepository = stsChangeLogRepository;
}

public Maybe<OperationError> Execute(AuthorizedUpdateOrganizationFromFKOrganisationCommand command)
{
var organization = command.Organization;
using var transaction = _transactionManager.Begin();
try
{
//Load the external tree if not already provided
var organizationTree = command
.PreloadedExternalTree
.Match(tree => tree, () => _stsOrganizationUnitService.ResolveOrganizationTree(organization));

if (organizationTree.Failed)
{
var error = organizationTree.Error;
_logger.Error("Unable to resolve external org tree for organization with uuid {uuid}. Failed with: {code}:{detail}:{message}", command.Organization.Uuid, error.FailureType, error.Detail, error.Message);
return new OperationError($"Failed to resolve org tree:{error.Message.GetValueOrFallback("")}:{error.Detail:G}:{error.FailureType:G}", error.FailureType);
}

//Import the external tree into the organization
var updateResult = organization.UpdateConnectionToExternalOrganizationHierarchy(OrganizationUnitOrigin.STS_Organisation, organizationTree.Value, command.SynchronizationDepth, command.SubscribeToChanges);
if (updateResult.Failed)
{
var error = updateResult.Error;
_logger.Error("Failed importing org tree for organization with uuid {uuid}. Failed with: {code}:{message}", command.Organization.Uuid, error.FailureType, error.Message);
transaction.Rollback();
return new OperationError($"Failed to import org tree:{error.Message.GetValueOrFallback("")}:{error.FailureType:G}", error.FailureType);
}

//React on import consequences
var consequences = updateResult.Value;

if (consequences.DeletedExternalUnitsBeingDeleted.Any())
{
_organizationUnitRepository.RemoveRange(consequences.DeletedExternalUnitsBeingDeleted.Select(x => x.organizationUnit).ToList());
}
foreach (var (affectedUnit, _, _) in consequences.OrganizationUnitsBeingRenamed)
{
_domainEvents.Raise(new EntityUpdatedEvent<OrganizationUnit>(affectedUnit));
}

if (IsBackgroundImport())
{
organization.StsOrganizationConnection.DateOfLatestCheckBySubscription = DateTime.Now;
}

var logEntries = consequences.ToLogEntries(_userContext, _operationClock);

//We only add a change log entry if any changes were detected
if (logEntries.Entries.Any())
{
var addLogResult = organization.AddExternalImportLog(OrganizationUnitOrigin.STS_Organisation, logEntries);
if (addLogResult.Failed)
{
var error = addLogResult.Error;
_logger.Error("Failed adding change log while importing org tree for organization with uuid {uuid}. Failed with: {code}:{message}", command.Organization.Uuid, error.FailureType, error.Message);
transaction.Rollback();
return error;
}

var addNewLogsResult = addLogResult.Value;
var removedChangeLogs = addNewLogsResult.RemovedChangeLogs.OfType<StsOrganizationChangeLog>().ToList();
if (removedChangeLogs.Any())
{
_stsChangeLogRepository.RemoveRange(removedChangeLogs);
}
}

_domainEvents.Raise(new EntityUpdatedEvent<Organization>(organization));
_databaseControl.SaveChanges();
transaction.Commit();

_domainEvents.Raise(new ExternalOrganizationConnectionUpdated(organization, organization.StsOrganizationConnection, logEntries));

return Maybe<OperationError>.None;

}
catch (Exception e)
{
_logger.Error(e, "Exception during FK Org sync of organization with uuid:{uuid}", command.Organization.Uuid);
transaction.Rollback();
return new OperationError("Exception during import", OperationFailure.UnknownError);
}
}

private bool IsBackgroundImport()
{
return _userContext.IsNone;
}
}
}
Loading

0 comments on commit 0cf3b0f

Please sign in to comment.