diff --git a/Core.ApplicationServices/Contract/Write/ItContractWriteService.cs b/Core.ApplicationServices/Contract/Write/ItContractWriteService.cs index f1b6038d67..ced04af825 100644 --- a/Core.ApplicationServices/Contract/Write/ItContractWriteService.cs +++ b/Core.ApplicationServices/Contract/Write/ItContractWriteService.cs @@ -274,7 +274,7 @@ private Maybe UpdateNoticePeriodMonthsUuid(ItContract contract, private Maybe UpdateExternalReferences(ItContract contract, IEnumerable externalReferences) { return _referenceService - .BatchUpdateExternalReferences( + .UpdateExternalReferences( ReferenceRootType.Contract, contract.Id, externalReferences.ToList()) diff --git a/Core.ApplicationServices/GDPR/Write/DataProcessingRegistrationWriteService.cs b/Core.ApplicationServices/GDPR/Write/DataProcessingRegistrationWriteService.cs index a2537880e2..c046ea5580 100644 --- a/Core.ApplicationServices/GDPR/Write/DataProcessingRegistrationWriteService.cs +++ b/Core.ApplicationServices/GDPR/Write/DataProcessingRegistrationWriteService.cs @@ -129,7 +129,7 @@ private Result PerformUpdates(DataPr private Result PerformReferencesUpdate(DataProcessingRegistration dpr, IEnumerable externalReferences) { - var updateResult = _referenceService.BatchUpdateExternalReferences( + var updateResult = _referenceService.UpdateExternalReferences( ReferenceRootType.DataProcessingRegistration, dpr.Id, externalReferences.ToList()); diff --git a/Core.ApplicationServices/Model/Shared/Write/UpdatedExternalReferenceProperties.cs b/Core.ApplicationServices/Model/Shared/Write/UpdatedExternalReferenceProperties.cs index 70badf5ce1..4168df6a0f 100644 --- a/Core.ApplicationServices/Model/Shared/Write/UpdatedExternalReferenceProperties.cs +++ b/Core.ApplicationServices/Model/Shared/Write/UpdatedExternalReferenceProperties.cs @@ -1,7 +1,10 @@ -namespace Core.ApplicationServices.Model.Shared.Write +using System; + +namespace Core.ApplicationServices.Model.Shared.Write { public class UpdatedExternalReferenceProperties { + public Guid? Uuid { get; set; } public string Title { get; set; } public string DocumentId { get; set; } public string Url { get; set; } diff --git a/Core.ApplicationServices/References/IReferenceService.cs b/Core.ApplicationServices/References/IReferenceService.cs index 63438601e2..22df457b11 100644 --- a/Core.ApplicationServices/References/IReferenceService.cs +++ b/Core.ApplicationServices/References/IReferenceService.cs @@ -15,6 +15,6 @@ public interface IReferenceService Result, OperationFailure> DeleteBySystemUsageId(int usageId); Result, OperationFailure> DeleteByContractId(int contractId); Result, OperationFailure> DeleteByDataProcessingRegistrationId(int id); - Maybe BatchUpdateExternalReferences(ReferenceRootType rootType, int rootId, IEnumerable externalReferences); + Maybe UpdateExternalReferences(ReferenceRootType rootType, int rootId, IEnumerable externalReferences); } } diff --git a/Core.ApplicationServices/References/ReferenceService.cs b/Core.ApplicationServices/References/ReferenceService.cs index dfc0107dd8..0fca34b443 100644 --- a/Core.ApplicationServices/References/ReferenceService.cs +++ b/Core.ApplicationServices/References/ReferenceService.cs @@ -148,30 +148,30 @@ private void RaiseRootUpdated(IEntityWithExternalReferences owner) public Result, OperationFailure> DeleteBySystemId(int systemId) { var system = _itSystemRepository.GetSystem(systemId); - return DeleteExternalReferences(system); + return DeleteExternalReferencesByRoot(system); } public Result, OperationFailure> DeleteBySystemUsageId(int usageId) { var itSystemUsage = _systemUsageRepository.GetSystemUsage(usageId); - return DeleteExternalReferences(itSystemUsage); + return DeleteExternalReferencesByRoot(itSystemUsage); } public Result, OperationFailure> DeleteByContractId(int contractId) { var contract = _contractRepository.GetById(contractId); - return DeleteExternalReferences(contract); + return DeleteExternalReferencesByRoot(contract); } public Result, OperationFailure> DeleteByDataProcessingRegistrationId(int id) { return _dataProcessingRegistrationRepository .GetById(id) - .Select(DeleteExternalReferences) + .Select(DeleteExternalReferencesByRoot) .Match(r => r, () => OperationFailure.NotFound); } - public Maybe BatchUpdateExternalReferences( + public Maybe UpdateExternalReferences( ReferenceRootType rootType, int rootId, IEnumerable externalReferences) @@ -182,16 +182,26 @@ public Maybe BatchUpdateExternalReferences( .GetRootEntity(rootId, rootType) .Match(root => { - //Clear existing state - root.ClearMasterReference(); - var deleteResult = DeleteExternalReferences(root); + var referenceList = externalReferences.ToList(); + + //External references with uuids not included in the update are going to be deleted + var uuidsToUpdateHashSet = referenceList + .Where(referenceToUpdate => referenceToUpdate.Uuid.HasValue) + .Select(referenceToUpdate => referenceToUpdate.Uuid) + .ToHashSet(); + + var referencesToDelete = root + .ExternalReferences + .Where(externalReference => !uuidsToUpdateHashSet.Contains(externalReference.Uuid)); + + //Delete all references not explicitly referenced by the input parameters + var deleteResult = DeleteExternalReferences(root, referencesToDelete); if (deleteResult.Failed) return new OperationError("Failed to delete old references", deleteResult.Error); - var newReferences = externalReferences.ToList(); - if (newReferences.Any()) + if (referenceList.Any()) { - var masterReferencesCount = newReferences.Count(x => x.MasterReference); + var masterReferencesCount = referenceList.Count(x => x.MasterReference); switch (masterReferencesCount) { @@ -201,60 +211,98 @@ public Maybe BatchUpdateExternalReferences( return new OperationError("Only one reference can be master reference", OperationFailure.BadInput); } - foreach (var referenceProperties in newReferences) + foreach (var externalReferenceProperties in referenceList) { - var result = AddReference(rootId, rootType, referenceProperties.Title, referenceProperties.DocumentId, referenceProperties.Url); - - if (result.Failed) - return new OperationError($"Failed to add reference with data:{JsonConvert.SerializeObject(referenceProperties)}. Error:{result.Error.Message.GetValueOrEmptyString()}", result.Error.FailureType); + ExternalReference externalReference; + //Replace references, which are identified by the update using uuids + //If no existing reference is found by uuid, it is considered an input error + if (externalReferenceProperties.Uuid.HasValue) + { + var uuid = externalReferenceProperties.Uuid.Value; + var existingReferenceResult = _referenceRepository.GetByUuid(uuid); + if (existingReferenceResult.IsNone) + return new OperationError($"External reference with uuid: {uuid} was not found in the {rootType} with id: {root.Id}", OperationFailure.BadInput); - if (referenceProperties.MasterReference) + externalReference = existingReferenceResult.Value; + MapExternalReference(externalReferenceProperties, externalReference); + } + //If uuid is null a new reference is going to be created + else { - var masterReferenceResult = root.SetMasterReference(result.Value); - if (masterReferenceResult.Failed) - return new OperationError($"Failed while setting the master reference:{masterReferenceResult.Error.Message.GetValueOrEmptyString()}", masterReferenceResult.Error.FailureType); + var addReferenceResult = AddReference(rootId, rootType, externalReferenceProperties.Title, externalReferenceProperties.DocumentId, externalReferenceProperties.Url); + + if (addReferenceResult.Failed) + return new OperationError($"Failed to add reference with data:{JsonConvert.SerializeObject(externalReferenceProperties)}. Error:{addReferenceResult.Error.Message.GetValueOrEmptyString()}", addReferenceResult.Error.FailureType); + + externalReference = addReferenceResult.Value; } + + if (!externalReferenceProperties.MasterReference) + continue; + + var masterReferenceResult = root.SetMasterReference(externalReference); + if (masterReferenceResult.Failed) + return new OperationError($"Failed while setting the master reference:{masterReferenceResult.Error.Message.GetValueOrEmptyString()}", masterReferenceResult.Error.FailureType); } + + _referenceRepository.SaveRootEntity(root); } + return Maybe.None; }, () => new OperationError(OperationFailure.NotFound)); if (error.IsNone) transaction.Commit(); + else + transaction.Rollback(); return error; } - private Result, OperationFailure> DeleteExternalReferences(IEntityWithExternalReferences root) + private static void MapExternalReference(UpdatedExternalReferenceProperties updatedProperties, + ExternalReference externalReference) + { + externalReference.Title = updatedProperties.Title; + externalReference.ExternalReferenceId = updatedProperties.DocumentId; + externalReference.URL = updatedProperties.Url; + } + + private Result, OperationFailure> DeleteExternalReferencesByRoot(IEntityWithExternalReferences root) { if (root == null) { return OperationFailure.NotFound; } + return DeleteExternalReferences(root, root.ExternalReferences); + } + + private Result, OperationFailure> DeleteExternalReferences(IEntityWithExternalReferences root, IEnumerable externalReferences) + { if (!_authorizationContext.AllowModify(root)) { return OperationFailure.Forbidden; } using var transaction = _transactionManager.Begin(); - var systemExternalReferences = root.ExternalReferences.ToList(); + var externalReferenceList = externalReferences.ToList(); - if (systemExternalReferences.Count == 0) + if (externalReferenceList.Count == 0) { - return systemExternalReferences; + return externalReferenceList; } - foreach (var reference in systemExternalReferences) + foreach (var reference in externalReferenceList) { _domainEvents.Raise(new EntityBeingDeletedEvent(reference)); _referenceRepository.Delete(reference); } - RaiseRootUpdated(root); transaction.Commit(); - return systemExternalReferences; + + return Result, OperationFailure>.Success(externalReferenceList); } + } } diff --git a/Core.ApplicationServices/SystemUsage/Write/ItSystemUsageWriteService.cs b/Core.ApplicationServices/SystemUsage/Write/ItSystemUsageWriteService.cs index 76d494f724..744d605363 100644 --- a/Core.ApplicationServices/SystemUsage/Write/ItSystemUsageWriteService.cs +++ b/Core.ApplicationServices/SystemUsage/Write/ItSystemUsageWriteService.cs @@ -312,7 +312,7 @@ private Maybe UpdateSensitivePersonDataIds(ItSystemUsage systemU private Result PerformReferencesUpdate(ItSystemUsage systemUsage, IEnumerable externalReferences) { //Clear existing state - var updateResult = _referenceService.BatchUpdateExternalReferences( + var updateResult = _referenceService.UpdateExternalReferences( ReferenceRootType.SystemUsage, systemUsage.Id, externalReferences); diff --git a/Core.DomainModel/ExternalReference.cs b/Core.DomainModel/ExternalReference.cs index 71ea7af7d0..e4e7c4f09c 100644 --- a/Core.DomainModel/ExternalReference.cs +++ b/Core.DomainModel/ExternalReference.cs @@ -7,13 +7,16 @@ namespace Core.DomainModel { - public class ExternalReference : Entity, ISystemModule, IContractModule, IDataProcessingModule + public class ExternalReference : Entity, ISystemModule, IContractModule, IDataProcessingModule, IHasUuid { public ExternalReference() { BrokenLinkReports = new List(); + Uuid = Guid.NewGuid(); } + public Guid Uuid { get; set; } + public int? Itcontract_Id { get; set; } public virtual ItContract.ItContract ItContract { get; set; } diff --git a/Core.DomainServices/Repositories/Reference/IReferenceRepository.cs b/Core.DomainServices/Repositories/Reference/IReferenceRepository.cs index a6ea58a515..4ddbdcac10 100644 --- a/Core.DomainServices/Repositories/Reference/IReferenceRepository.cs +++ b/Core.DomainServices/Repositories/Reference/IReferenceRepository.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Core.Abstractions.Types; using Core.DomainModel; using Core.DomainModel.References; @@ -9,6 +10,7 @@ namespace Core.DomainServices.Repositories.Reference public interface IReferenceRepository { Maybe Get(int referenceId); + Maybe GetByUuid(Guid uuid); Maybe GetRootEntity(int id, ReferenceRootType rootType); IQueryable GetByRootType(ReferenceRootType rootType); void SaveRootEntity(IEntityWithExternalReferences root); diff --git a/Core.DomainServices/Repositories/Reference/ReferenceRepository.cs b/Core.DomainServices/Repositories/Reference/ReferenceRepository.cs index 64c2706f0e..f1a9640552 100644 --- a/Core.DomainServices/Repositories/Reference/ReferenceRepository.cs +++ b/Core.DomainServices/Repositories/Reference/ReferenceRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Core.Abstractions.Types; using Core.DomainModel; @@ -7,6 +8,7 @@ using Core.DomainModel.ItSystem; using Core.DomainModel.ItSystemUsage; using Core.DomainModel.References; +using Core.DomainServices.Extensions; namespace Core.DomainServices.Repositories.Reference @@ -53,6 +55,11 @@ public Maybe Get(int referenceId) return _referenceRepository.GetByKey(referenceId); } + public Maybe GetByUuid(Guid uuid) + { + return _referenceRepository.AsQueryable().ByUuid(uuid); + } + public Maybe GetRootEntity(int id, ReferenceRootType rootType) { return ResolveRepositoryOperations(rootType).Get(id); diff --git a/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj b/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj index 3d7870185e..4b2c5d85c9 100644 --- a/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj +++ b/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj @@ -995,10 +995,15 @@ 202301030910462_change_dpr_sub_data_processors.cs + + + 202301120832587_added_uuid_to_external_reference.cs + + @@ -1012,6 +1017,9 @@ 202301030910462_change_dpr_sub_data_processors.cs + + 202301120832587_added_uuid_to_external_reference.cs + diff --git a/Infrastructure.DataAccess/KitosContext.cs b/Infrastructure.DataAccess/KitosContext.cs index ff9e8caf57..c7344ed5ae 100644 --- a/Infrastructure.DataAccess/KitosContext.cs +++ b/Infrastructure.DataAccess/KitosContext.cs @@ -264,6 +264,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) modelBuilder.Configurations.Add(new StsOrganizationChangeLogMap()); modelBuilder.Configurations.Add(new StsOrganizationConsequenceLogMap()); modelBuilder.Configurations.Add(new SubDataProcessorMap()); + modelBuilder.Configurations.Add(new ExternalReferenceMap()); } } } diff --git a/Infrastructure.DataAccess/Mapping/ExternalReferenceMap.cs b/Infrastructure.DataAccess/Mapping/ExternalReferenceMap.cs index 2f1fe7d44e..ff9ab8dc0e 100644 --- a/Infrastructure.DataAccess/Mapping/ExternalReferenceMap.cs +++ b/Infrastructure.DataAccess/Mapping/ExternalReferenceMap.cs @@ -5,10 +5,15 @@ namespace Infrastructure.DataAccess.Mapping public class ExternalReferenceMap : EntityMap { public ExternalReferenceMap() { - this.ToTable("ExternalReferences"); - this.Property(t => t.Itcontract_Id).HasColumnName("ItContract_Id").IsOptional(); - this.Property(t => t.ItSystemUsage_Id).HasColumnName("ItSystemUsage_Id").IsOptional(); - this.Property(t => t.ItSystem_Id).HasColumnName("ItSystem_Id").IsOptional(); + ToTable("ExternalReferences"); + + Property(t => t.Itcontract_Id).HasColumnName("ItContract_Id").IsOptional(); + Property(t => t.ItSystemUsage_Id).HasColumnName("ItSystemUsage_Id").IsOptional(); + Property(t => t.ItSystem_Id).HasColumnName("ItSystem_Id").IsOptional(); + + Property(x => x.Uuid) + .IsRequired() + .HasUniqueIndexAnnotation("UX_ExternalReference_Uuid", 0); } } } diff --git a/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.Designer.cs b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.Designer.cs new file mode 100644 index 0000000000..3fb0996542 --- /dev/null +++ b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.Designer.cs @@ -0,0 +1,29 @@ +// +namespace Infrastructure.DataAccess.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.4.4")] + public sealed partial class added_uuid_to_external_reference : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(added_uuid_to_external_reference)); + + string IMigrationMetadata.Id + { + get { return "202301120832587_added_uuid_to_external_reference"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.cs b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.cs new file mode 100644 index 0000000000..4b4a19b57a --- /dev/null +++ b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.cs @@ -0,0 +1,39 @@ +using Infrastructure.DataAccess.Tools; + +namespace Infrastructure.DataAccess.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class added_uuid_to_external_reference : DbMigration + { + public override void Up() + { + DropIndex("dbo.ExternalReferences", new[] { "Itcontract_Id" }); + DropIndex("dbo.ExternalReferences", new[] { "ObjectOwnerId" }); + DropIndex("dbo.ExternalReferences", new[] { "LastChangedByUserId" }); + AddColumn("dbo.ExternalReferences", "Uuid", c => c.Guid(nullable: false)); + SqlResource(SqlMigrationScriptRepository.GetResourceName("Patch_Uuid_ExternalReference.sql")); + AlterColumn("dbo.ExternalReferences", "ObjectOwnerId", c => c.Int(nullable: false)); + AlterColumn("dbo.ExternalReferences", "LastChangedByUserId", c => c.Int(nullable: false)); + CreateIndex("dbo.ExternalReferences", "Uuid", unique: true, name: "UX_ExternalReference_Uuid"); + CreateIndex("dbo.ExternalReferences", "ItContract_Id"); + CreateIndex("dbo.ExternalReferences", "ObjectOwnerId"); + CreateIndex("dbo.ExternalReferences", "LastChangedByUserId"); + } + + public override void Down() + { + DropIndex("dbo.ExternalReferences", new[] { "LastChangedByUserId" }); + DropIndex("dbo.ExternalReferences", new[] { "ObjectOwnerId" }); + DropIndex("dbo.ExternalReferences", new[] { "ItContract_Id" }); + DropIndex("dbo.ExternalReferences", "UX_ExternalReference_Uuid"); + AlterColumn("dbo.ExternalReferences", "LastChangedByUserId", c => c.Int()); + AlterColumn("dbo.ExternalReferences", "ObjectOwnerId", c => c.Int()); + DropColumn("dbo.ExternalReferences", "Uuid"); + CreateIndex("dbo.ExternalReferences", "LastChangedByUserId"); + CreateIndex("dbo.ExternalReferences", "ObjectOwnerId"); + CreateIndex("dbo.ExternalReferences", "Itcontract_Id"); + } + } +} diff --git a/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.resx b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.resx new file mode 100644 index 0000000000..b15954e7f6 --- /dev/null +++ b/Infrastructure.DataAccess/Migrations/202301120832587_added_uuid_to_external_reference.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/Infrastructure.DataAccess/Migrations/SQLScripts/Patch_Uuid_ExternalReference.sql b/Infrastructure.DataAccess/Migrations/SQLScripts/Patch_Uuid_ExternalReference.sql new file mode 100644 index 0000000000..2ef621e857 --- /dev/null +++ b/Infrastructure.DataAccess/Migrations/SQLScripts/Patch_Uuid_ExternalReference.sql @@ -0,0 +1,11 @@ +/* +Content: + We added Uuid to External Reference as part of https://os2web.atlassian.net/browse/KITOSUDV-3812 + As they are not generated automatically when adding the column on existing values we use this to initialize their value. +*/ + +BEGIN + UPDATE[ExternalReferences] + SET Uuid = NEWID() + WHERE Uuid = '00000000-0000-0000-0000-000000000000' OR Uuid IS NULL +END diff --git a/Presentation.Web/App_Start/MappingConfig.cs b/Presentation.Web/App_Start/MappingConfig.cs index 1089f19264..af8cd45ad4 100644 --- a/Presentation.Web/App_Start/MappingConfig.cs +++ b/Presentation.Web/App_Start/MappingConfig.cs @@ -53,6 +53,7 @@ public DropdownProfile() CreateMap() .ReverseMap() + .ForMember(x => x.Uuid, opt => opt.Ignore()) .IgnoreDestinationEntityFields(); CreateMap() diff --git a/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationResponseMapper.cs b/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationResponseMapper.cs index c4b7686fb8..fd998f4bc4 100644 --- a/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationResponseMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationResponseMapper.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; -using Core.DomainModel; using Core.DomainModel.GDPR; using Core.DomainModel.Shared; +using Presentation.Web.Controllers.API.V2.External.Generic; using Presentation.Web.Controllers.API.V2.Mapping; using Presentation.Web.Models.API.V2.Response.DataProcessing; using Presentation.Web.Models.API.V2.Response.Generic.Identity; @@ -14,6 +14,13 @@ namespace Presentation.Web.Controllers.API.V2.External.DataProcessingRegistratio { public class DataProcessingRegistrationResponseMapper : IDataProcessingRegistrationResponseMapper { + private readonly IExternalReferenceResponseMapper _externalReferenceResponseMapper; + + public DataProcessingRegistrationResponseMapper(IExternalReferenceResponseMapper externalReferenceResponseMapper) + { + _externalReferenceResponseMapper = externalReferenceResponseMapper; + } + public DataProcessingRegistrationResponseDTO MapDataProcessingRegistrationDTO(DataProcessingRegistration dataProcessingRegistration) { return new DataProcessingRegistrationResponseDTO @@ -28,7 +35,7 @@ public DataProcessingRegistrationResponseDTO MapDataProcessingRegistrationDTO(Da SystemUsages = MapSystemUsages(dataProcessingRegistration), Oversight = MapOversight(dataProcessingRegistration), Roles = MapRoles(dataProcessingRegistration), - ExternalReferences = MapExternalReferences(dataProcessingRegistration) + ExternalReferences = _externalReferenceResponseMapper.MapExternalReferences(dataProcessingRegistration.ExternalReferences, dataProcessingRegistration.Reference) }; } @@ -112,22 +119,6 @@ private static DataProcessorRegistrationSubDataProcessorResponseDTO ToSubDataPro return isAgreementConcluded?.ToYesNoIrrelevantChoice(); } - private static IEnumerable MapExternalReferences(DataProcessingRegistration dataProcessingRegistration) - { - return dataProcessingRegistration.ExternalReferences.Select(reference => MapExternalReferenceDTO(dataProcessingRegistration, reference)).ToList(); - } - - private static ExternalReferenceDataDTO MapExternalReferenceDTO(DataProcessingRegistration dataProcessingRegistration, ExternalReference reference) - { - return new() - { - DocumentId = reference.ExternalReferenceId, - Title = reference.Title, - Url = reference.URL, - MasterReference = dataProcessingRegistration.Reference?.Id.Equals(reference.Id) == true - }; - } - private static IEnumerable MapRoles(DataProcessingRegistration dataProcessingRegistration) { return dataProcessingRegistration.Rights.Select(ToRoleResponseDTO).ToList(); @@ -135,7 +126,7 @@ private static IEnumerable MapRoles(DataProcessingReg private static RoleAssignmentResponseDTO ToRoleResponseDTO(DataProcessingRegistrationRight right) { - return new() + return new RoleAssignmentResponseDTO { Role = right.Role.MapIdentityNamePairDTO(), User = right.User.MapIdentityNamePairDTO() diff --git a/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationWriteModelMapper.cs b/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationWriteModelMapper.cs index 2078375f41..0cb33eb569 100644 --- a/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationWriteModelMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/DataProcessingRegistrations/Mapping/DataProcessingRegistrationWriteModelMapper.cs @@ -13,8 +13,8 @@ using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.DataProcessing; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.SharedProperties; -using Presentation.Web.Models.API.V2.Types.Shared; namespace Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping { @@ -27,29 +27,49 @@ public DataProcessingRegistrationWriteModelMapper(ICurrentHttpRequest currentHtt public DataProcessingRegistrationModificationParameters FromPOST(CreateDataProcessingRegistrationRequestDTO dto) { - return Map(dto, false); + return MapCreate(dto, false); } public DataProcessingRegistrationModificationParameters FromPUT(UpdateDataProcessingRegistrationRequestDTO dto) { - return Map(dto, true); + return MapUpdate(dto, true); } public DataProcessingRegistrationModificationParameters FromPATCH(UpdateDataProcessingRegistrationRequestDTO dto) { - return Map(dto, false); + return MapUpdate(dto, false); } - private DataProcessingRegistrationModificationParameters Map(T dto, bool enforceFallbackIfNotProvided) where T : DataProcessingRegistrationWriteRequestDTO, IHasNameExternal + private DataProcessingRegistrationModificationParameters MapCreate( + CreateDataProcessingRegistrationRequestDTO dto, bool enforceFallbackIfNotProvided) { - TSection WithResetDataIfSectionIsNotDefined(TSection deserializedValue, Expression> propertySelection) where TSection : new() => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, enforceFallbackIfNotProvided); - TSection WithResetDataIfSectionIsNotDefinedWithFallback(TSection deserializedValue, Expression> propertySelection, Func fallbackFactory) => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, fallbackFactory, enforceFallbackIfNotProvided); + var parameters = Map(dto, enforceFallbackIfNotProvided); + parameters.ExternalReferences = MapCreateReferences(dto); + + return parameters; + } + + private DataProcessingRegistrationModificationParameters MapUpdate( + UpdateDataProcessingRegistrationRequestDTO dto, bool enforceFallbackIfNotProvided) + { + var parameters = Map(dto, enforceFallbackIfNotProvided); + parameters.ExternalReferences = MapUpdateReferences(dto); + + return parameters; + } + + private DataProcessingRegistrationModificationParameters Map(TDto dto, bool enforceFallbackIfNotProvided) + where TDto: DataProcessingRegistrationWriteRequestDTO, IHasNameExternal, IHasExternalReference + where TExternalReferenceDto: ExternalReferenceDataWriteRequestDTO + { + TSection WithResetDataIfSectionIsNotDefined(TSection deserializedValue, Expression> propertySelection) where TSection : new() => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, enforceFallbackIfNotProvided); + TSection WithResetDataIfSectionIsNotDefinedWithFallback(TSection deserializedValue, Expression> propertySelection, Func fallbackFactory) => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, fallbackFactory, enforceFallbackIfNotProvided); dto.General = WithResetDataIfSectionIsNotDefined(dto.General, x => x.General); dto.SystemUsageUuids = WithResetDataIfSectionIsNotDefinedWithFallback(dto.SystemUsageUuids, x => x.SystemUsageUuids, () => new List()); dto.Oversight = WithResetDataIfSectionIsNotDefined(dto.Oversight, x => x.Oversight); dto.Roles = WithResetDataIfSectionIsNotDefinedWithFallback(dto.Roles, x => x.Roles, Array.Empty); - dto.ExternalReferences = WithResetDataIfSectionIsNotDefinedWithFallback(dto.ExternalReferences, x => x.ExternalReferences, Array.Empty); + dto.ExternalReferences = WithResetDataIfSectionIsNotDefinedWithFallback(dto.ExternalReferences, x => x.ExternalReferences, Array.Empty); return new DataProcessingRegistrationModificationParameters { @@ -57,14 +77,18 @@ private DataProcessingRegistrationModificationParameters Map(T dto, bool enfo General = dto.General.FromNullable().Select(generalData => MapGeneral(generalData, enforceFallbackIfNotProvided)), SystemUsageUuids = dto.SystemUsageUuids.FromNullable(), Oversight = dto.Oversight.FromNullable().Select(oversight => MapOversight(oversight, enforceFallbackIfNotProvided)), - Roles = dto.Roles.FromNullable().Select(MapRoles), - ExternalReferences = dto.ExternalReferences.FromNullable().Select(MapReferences) + Roles = dto.Roles.FromNullable().Select(MapRoles) }; } + + private Maybe> MapCreateReferences(CreateDataProcessingRegistrationRequestDTO dto) + { + return dto.ExternalReferences.FromNullable().Select(BaseMapCreateReferences); + } - private IEnumerable MapReferences(IEnumerable references) + private Maybe> MapUpdateReferences(UpdateDataProcessingRegistrationRequestDTO dto) { - return BaseMapReferences(references); + return dto.ExternalReferences.FromNullable().Select(BaseMapUpdateReferences); } private UpdatedDataProcessingRegistrationGeneralDataParameters MapGeneral(DataProcessingRegistrationGeneralDataWriteRequestDTO dto, bool enforceFallbackIfNotProvided) diff --git a/Presentation.Web/Controllers/API/V2/External/Generic/ExternalReferenceResponseMapper.cs b/Presentation.Web/Controllers/API/V2/External/Generic/ExternalReferenceResponseMapper.cs new file mode 100644 index 0000000000..f84503ca7e --- /dev/null +++ b/Presentation.Web/Controllers/API/V2/External/Generic/ExternalReferenceResponseMapper.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using Core.DomainModel; +using Presentation.Web.Models.API.V2.Response.Shared; + +namespace Presentation.Web.Controllers.API.V2.External.Generic +{ + public class ExternalReferenceResponseMapper: IExternalReferenceResponseMapper + { + public IEnumerable MapExternalReferences(IEnumerable externalReferences, + ExternalReference masterReference) + { + return externalReferences.Select(externalReference => MapExternalReferenceDto(externalReference, masterReference)).ToList(); + } + + private static ExternalReferenceDataResponseDTO MapExternalReferenceDto(ExternalReference externalReference, ExternalReference masterReference) + { + return new ExternalReferenceDataResponseDTO + { + Uuid = externalReference.Uuid, + DocumentId = externalReference.ExternalReferenceId, + Title = externalReference.Title, + Url = externalReference.URL, + MasterReference = masterReference?.Id.Equals(externalReference.Id) == true + }; + } + } +} \ No newline at end of file diff --git a/Presentation.Web/Controllers/API/V2/External/Generic/IExternalReferenceResponseMapper.cs b/Presentation.Web/Controllers/API/V2/External/Generic/IExternalReferenceResponseMapper.cs new file mode 100644 index 0000000000..eca194fde0 --- /dev/null +++ b/Presentation.Web/Controllers/API/V2/External/Generic/IExternalReferenceResponseMapper.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Core.DomainModel; +using Presentation.Web.Models.API.V2.Response.Shared; + +namespace Presentation.Web.Controllers.API.V2.External.Generic +{ + public interface IExternalReferenceResponseMapper + { + IEnumerable MapExternalReferences(IEnumerable externalReferences, ExternalReference masterReference); + } +} diff --git a/Presentation.Web/Controllers/API/V2/External/Generic/WriteModelMapperBase.cs b/Presentation.Web/Controllers/API/V2/External/Generic/WriteModelMapperBase.cs index b61dd24e5f..8c789cf44f 100644 --- a/Presentation.Web/Controllers/API/V2/External/Generic/WriteModelMapperBase.cs +++ b/Presentation.Web/Controllers/API/V2/External/Generic/WriteModelMapperBase.cs @@ -9,7 +9,7 @@ using Newtonsoft.Json.Linq; using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.Generic.Roles; -using Presentation.Web.Models.API.V2.Types.Shared; +using Presentation.Web.Models.API.V2.Request.Shared; namespace Presentation.Web.Controllers.API.V2.External.Generic { @@ -158,15 +158,14 @@ HashSet UpdateProperties(IEnumerable pathTokensToLeafLevel) return isPartOfScopedReset; } - protected IEnumerable BaseMapReferences(IEnumerable references) + protected IEnumerable BaseMapCreateReferences(IEnumerable references) { - return references.Select(x => new UpdatedExternalReferenceProperties - { - Title = x.Title, - DocumentId = x.DocumentId, - Url = x.Url, - MasterReference = x.MasterReference - }).ToList(); + return references.Select(MapCommonReference).ToList(); + } + + protected IEnumerable BaseMapUpdateReferences(IEnumerable references) + { + return references.Select(MapUpdateReference).ToList(); } protected static ChangedValue>> BaseMapRoleAssignments(IReadOnlyCollection roleAssignmentResponseDtos) @@ -179,5 +178,26 @@ protected static ChangedValue>> BaseMapRoleAssig }).ToList()) : Maybe>.None).AsChangedValue(); } + + + private static UpdatedExternalReferenceProperties MapUpdateReference(UpdateExternalReferenceDataWriteRequestDTO reference) + { + var updateProperties = MapCommonReference(reference); + updateProperties.Uuid = reference.Uuid; + + return updateProperties; + } + + private static UpdatedExternalReferenceProperties MapCommonReference(T reference) + where T : ExternalReferenceDataWriteRequestDTO + { + return new UpdatedExternalReferenceProperties + { + Title = reference.Title, + DocumentId = reference.DocumentId, + Url = reference.Url, + MasterReference = reference.MasterReference + }; + } } } \ No newline at end of file diff --git a/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractResponseMapper.cs b/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractResponseMapper.cs index d33d892e9b..9b61b2d190 100644 --- a/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractResponseMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractResponseMapper.cs @@ -1,20 +1,26 @@ using System; using System.Collections.Generic; using System.Linq; -using Core.DomainModel; using Core.DomainModel.ItContract; using Presentation.Web.Controllers.API.V2.Mapping; using Presentation.Web.Models.API.V2.Response.Contract; using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; using Presentation.Web.Models.API.V2.Types.Contract; -using Presentation.Web.Models.API.V2.Types.Shared; using Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping; +using Presentation.Web.Controllers.API.V2.External.Generic; namespace Presentation.Web.Controllers.API.V2.External.ItContracts.Mapping { public class ItContractResponseMapper : IItContractResponseMapper { + private readonly IExternalReferenceResponseMapper _externalReferenceResponseMapper; + + public ItContractResponseMapper(IExternalReferenceResponseMapper externalReferenceResponseMapper) + { + _externalReferenceResponseMapper = externalReferenceResponseMapper; + } + public ItContractResponseDTO MapContractDTO(ItContract contract) { return new ItContractResponseDTO @@ -37,7 +43,7 @@ public ItContractResponseDTO MapContractDTO(ItContract contract) Termination = MapTermination(contract), Payments = MapPayments(contract), Roles = MapRoles(contract), - ExternalReferences = MapExternalReferences(contract) + ExternalReferences = _externalReferenceResponseMapper.MapExternalReferences(contract.ExternalReferences, contract.Reference) }; } @@ -102,11 +108,6 @@ private static List MapRoles(ItContract contract) return contract.Rights?.Select(ToRoleResponseDTO).ToList() ?? new List(); } - private static List MapExternalReferences(ItContract contract) - { - return contract.ExternalReferences?.Select(x => MapExternalReferenceDTO(contract, x)).ToList() ?? new List(); - } - private static ContractPaymentModelDataResponseDTO MapPaymentModel(ItContract contract) { return new () @@ -186,17 +187,6 @@ private static ContractGeneralDataResponseDTO MapGeneral(ItContract contract) }; } - private static ExternalReferenceDataDTO MapExternalReferenceDTO(ItContract contract, ExternalReference reference) - { - return new() - { - DocumentId = reference.ExternalReferenceId, - Title = reference.Title, - Url = reference.URL, - MasterReference = contract.Reference?.Id.Equals(reference.Id) == true - }; - } - private static RoleAssignmentResponseDTO ToRoleResponseDTO(ItContractRight right) { return new() diff --git a/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractWriteModelMapper.cs b/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractWriteModelMapper.cs index e39096395f..14f129ce14 100644 --- a/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractWriteModelMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/ItContracts/Mapping/ItContractWriteModelMapper.cs @@ -9,15 +9,15 @@ using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.Contract; using Presentation.Web.Models.API.V2.Request.Generic.Roles; -using Presentation.Web.Models.API.V2.SharedProperties; using Presentation.Web.Models.API.V2.Types.Contract; -using Presentation.Web.Models.API.V2.Types.Shared; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Core.DomainModel.Shared; using Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping; +using Presentation.Web.Models.API.V2.Request.Shared; +using Presentation.Web.Models.API.V2.SharedProperties; namespace Presentation.Web.Controllers.API.V2.External.ItContracts.Mapping { @@ -30,29 +30,49 @@ public ItContractWriteModelMapper(ICurrentHttpRequest currentHttpRequest) public ItContractModificationParameters FromPOST(CreateNewContractRequestDTO dto) { - return Map(dto, false); + return MapCreate(dto, false); } public ItContractModificationParameters FromPATCH(UpdateContractRequestDTO dto) { - return Map(dto, false); + return MapUpdate(dto, false); } public ItContractModificationParameters FromPUT(UpdateContractRequestDTO dto) { - return Map(dto, true); + return MapUpdate(dto, true); } - private ItContractModificationParameters Map(T dto, bool enforceFallbackIfNotProvided) where T : ContractWriteRequestDTO, IHasNameExternal + private ItContractModificationParameters MapCreate( + CreateNewContractRequestDTO dto, bool enforceFallbackIfNotProvided) { - var rule = CreateChangeRule(enforceFallbackIfNotProvided); + var parameters = Map(dto, enforceFallbackIfNotProvided); + parameters.ExternalReferences = MapCreateReferences(dto); + + return parameters; + } + + private ItContractModificationParameters MapUpdate( + UpdateContractRequestDTO dto, bool enforceFallbackIfNotProvided) + { + var parameters = Map(dto, enforceFallbackIfNotProvided); + parameters.ExternalReferences = MapUpdateReferences(dto); + + return parameters; + } + + private ItContractModificationParameters Map(TDto dto, bool enforceFallbackIfNotProvided) + where TDto : ContractWriteRequestDTO, IHasNameExternal, IHasExternalReference + where TExternalReferenceDto : ExternalReferenceDataWriteRequestDTO + { + var rule = CreateChangeRule(enforceFallbackIfNotProvided); TSection WithResetDataIfSectionIsNotDefined(TSection deserializedValue, - Expression> propertySelection) where TSection : new() => + Expression> propertySelection) where TSection : new() => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, enforceFallbackIfNotProvided); TSection WithResetDataIfSectionIsNotDefinedWithFallback(TSection deserializedValue, - Expression> propertySelection, + Expression> propertySelection, Func fallbackFactory) => WithResetDataIfPropertyIsDefined(deserializedValue, propertySelection, fallbackFactory, enforceFallbackIfNotProvided); @@ -61,8 +81,7 @@ TSection WithResetDataIfSectionIsNotDefinedWithFallback(TSection deser dto.Responsible = WithResetDataIfSectionIsNotDefined(dto.Responsible, x => x.Responsible); dto.Procurement = WithResetDataIfSectionIsNotDefined(dto.Procurement, x => x.Procurement); dto.Supplier = WithResetDataIfSectionIsNotDefined(dto.Supplier, x => x.Supplier); - dto.ExternalReferences = WithResetDataIfSectionIsNotDefinedWithFallback(dto.ExternalReferences, - x => x.ExternalReferences, Array.Empty); + dto.ExternalReferences = WithResetDataIfSectionIsNotDefinedWithFallback(dto.ExternalReferences, x => x.ExternalReferences, Array.Empty); dto.SystemUsageUuids = WithResetDataIfSectionIsNotDefinedWithFallback(dto.SystemUsageUuids, x => x.SystemUsageUuids, () => new List()); dto.Roles = WithResetDataIfSectionIsNotDefinedWithFallback(dto.Roles, x => x.Roles, @@ -85,7 +104,6 @@ TSection WithResetDataIfSectionIsNotDefinedWithFallback(TSection deser SystemUsageUuids = dto.SystemUsageUuids.FromNullable(), Responsible = dto.Responsible.FromNullable().Select(responsible => MapResponsible(responsible, rule)), Supplier = dto.Supplier.FromNullable().Select(supplier => MapSupplier(supplier, rule)), - ExternalReferences = dto.ExternalReferences.FromNullable().Select(MapReferences), Roles = dto.Roles.FromNullable().Select(MapRoles), DataProcessingRegistrationUuids = dto.DataProcessingRegistrationUuids.FromNullable(), PaymentModel = dto.PaymentModel.FromNullable().Select(payment => MapPaymentModel(payment, rule)), @@ -310,9 +328,14 @@ private static ItContractProcurementModificationParameters MapProcurement MapReferences(IEnumerable dtos) + private Maybe> MapCreateReferences(CreateNewContractRequestDTO dto) + { + return dto.ExternalReferences.FromNullable().Select(BaseMapCreateReferences); + } + + private Maybe> MapUpdateReferences(UpdateContractRequestDTO dto) { - return BaseMapReferences(dtos); + return dto.ExternalReferences.FromNullable().Select(BaseMapUpdateReferences); } private static Maybe<(byte quarter, int year)> MapProcurementPlan(ProcurementPlanDTO plan) diff --git a/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageResponseMapper.cs b/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageResponseMapper.cs index d53cee3764..59f2e51233 100644 --- a/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageResponseMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageResponseMapper.cs @@ -8,8 +8,10 @@ using Core.DomainModel.ItSystemUsage.GDPR; using Core.DomainServices; using Core.DomainServices.Repositories.GDPR; +using Presentation.Web.Controllers.API.V2.External.Generic; using Presentation.Web.Controllers.API.V2.Mapping; using Presentation.Web.Models.API.V2.Response.Generic.Roles; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.Response.SystemUsage; using Presentation.Web.Models.API.V2.Types.Shared; using Presentation.Web.Models.API.V2.Types.SystemUsage; @@ -21,15 +23,18 @@ public class ItSystemUsageResponseMapper : IItSystemUsageResponseMapper private readonly IItSystemUsageAttachedOptionRepository _itSystemUsageAttachedOptionRepository; private readonly ISensitivePersonalDataTypeRepository _sensitivePersonalDataTypeRepository; private readonly IGenericRepository _registerTypesRepository; + private readonly IExternalReferenceResponseMapper _externalReferenceResponseMapper; public ItSystemUsageResponseMapper( IItSystemUsageAttachedOptionRepository itSystemUsageAttachedOptionRepository, ISensitivePersonalDataTypeRepository sensitivePersonalDataTypeRepository, - IGenericRepository registerTypesRepository) + IGenericRepository registerTypesRepository, + IExternalReferenceResponseMapper externalReferenceResponseMapper) { _itSystemUsageAttachedOptionRepository = itSystemUsageAttachedOptionRepository; _sensitivePersonalDataTypeRepository = sensitivePersonalDataTypeRepository; _registerTypesRepository = registerTypesRepository; + _externalReferenceResponseMapper = externalReferenceResponseMapper; } public ItSystemUsageResponseDTO MapSystemUsageDTO(ItSystemUsage systemUsage) @@ -46,7 +51,7 @@ public ItSystemUsageResponseDTO MapSystemUsageDTO(ItSystemUsage systemUsage) Roles = MapRoles(systemUsage), LocalKLEDeviations = MapKle(systemUsage), OrganizationUsage = MapOrganizationUsage(systemUsage), - ExternalReferences = MapExternalReferences(systemUsage), + ExternalReferences = _externalReferenceResponseMapper.MapExternalReferences(systemUsage.ExternalReferences, systemUsage.Reference), OutgoingSystemRelations = MapOutgoingSystemRelations(systemUsage), Archiving = MapArchiving(systemUsage), GDPR = MapGDPR(systemUsage) @@ -131,11 +136,6 @@ private IEnumerable MapOutgoingSystemRelations(ItSyst return systemUsage.UsageRelations.Select(MapSystemRelationDTO).ToList(); } - private static IEnumerable MapExternalReferences(ItSystemUsage systemUsage) - { - return systemUsage.ExternalReferences.Select(reference => MapExternalReferenceDTO(systemUsage, reference)).ToList(); - } - private static OrganizationUsageResponseDTO MapOrganizationUsage(ItSystemUsage systemUsage) { return new OrganizationUsageResponseDTO @@ -241,17 +241,6 @@ public SystemRelationResponseDTO MapSystemRelationDTO(SystemRelation systemRelat }; } - private static ExternalReferenceDataDTO MapExternalReferenceDTO(ItSystemUsage systemUsage, ExternalReference reference) - { - return new() - { - DocumentId = reference.ExternalReferenceId, - Title = reference.Title, - Url = reference.URL, - MasterReference = systemUsage.Reference?.Id.Equals(reference.Id) == true - }; - } - private static RoleAssignmentResponseDTO ToRoleResponseDTO(ItSystemRight right) { return new() diff --git a/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageWriteModelMapper.cs b/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageWriteModelMapper.cs index e634553cc0..bcbb241ee3 100644 --- a/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageWriteModelMapper.cs +++ b/Presentation.Web/Controllers/API/V2/External/ItSystemUsages/Mapping/ItSystemUsageWriteModelMapper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using Core.Abstractions.Extensions; using Core.Abstractions.Types; using Core.ApplicationServices.Extensions; @@ -15,6 +14,7 @@ using Presentation.Web.Controllers.API.V2.External.Generic; using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Request.SystemUsage; using Presentation.Web.Models.API.V2.Types.Shared; using Presentation.Web.Models.API.V2.Types.SystemUsage; @@ -55,7 +55,7 @@ private SystemUsageUpdateParameters MapUpdate(UpdateItSystemUsageRequestDTO requ var orgUsageInput = WithResetDataIfPropertyIsDefined(request.OrganizationUsage, nameof(UpdateItSystemUsageRequestDTO.OrganizationUsage), enforceFallbackOnUndefinedProperties); var kleInput = WithResetDataIfPropertyIsDefined(request.LocalKleDeviations, nameof(UpdateItSystemUsageRequestDTO.LocalKleDeviations), enforceFallbackOnUndefinedProperties); var roles = WithResetDataIfPropertyIsDefined(request.Roles, nameof(UpdateItSystemUsageRequestDTO.Roles), () => new List(), enforceFallbackOnUndefinedProperties); - var externalReferenceDataDtos = WithResetDataIfPropertyIsDefined(request.ExternalReferences, nameof(UpdateItSystemUsageRequestDTO.ExternalReferences), () => new List(), enforceFallbackOnUndefinedProperties); + var externalReferenceDataDtos = WithResetDataIfPropertyIsDefined(request.ExternalReferences, nameof(UpdateItSystemUsageRequestDTO.ExternalReferences), () => new List(), enforceFallbackOnUndefinedProperties); var gdpr = WithResetDataIfPropertyIsDefined(request.GDPR, nameof(UpdateItSystemUsageRequestDTO.GDPR), enforceFallbackOnUndefinedProperties); var archiving = WithResetDataIfPropertyIsDefined(request.Archiving, nameof(UpdateItSystemUsageRequestDTO.Archiving), enforceFallbackOnUndefinedProperties); @@ -64,7 +64,7 @@ private SystemUsageUpdateParameters MapUpdate(UpdateItSystemUsageRequestDTO requ GeneralProperties = generalDataInput.FromNullable().Select(general => MapGeneralDataUpdate(general, enforceFallbackOnUndefinedProperties)), OrganizationalUsage = orgUsageInput.FromNullable().Select(orgUsage => MapOrganizationalUsage(orgUsage, enforceFallbackOnUndefinedProperties)), KLE = kleInput.FromNullable().Select(kle => MapKle(kle, enforceFallbackOnUndefinedProperties)), - ExternalReferences = externalReferenceDataDtos.FromNullable().Select(MapReferences), + ExternalReferences = externalReferenceDataDtos.FromNullable().Select(MapUpdateReferences), Roles = roles.FromNullable().Select(MapRoles), GDPR = gdpr.FromNullable().Select(gdpr => MapGDPR(gdpr, enforceFallbackOnUndefinedProperties)), Archiving = archiving.FromNullable().Select(archiving => MapArchiving(archiving, enforceFallbackOnUndefinedProperties)) @@ -246,9 +246,14 @@ private static SystemUsageJournalPeriod MapJournalPeriod(JournalPeriodDTO journa }; } - private IEnumerable MapReferences(IEnumerable references) + private IEnumerable MapReferences(IEnumerable references) { - return BaseMapReferences(references); + return BaseMapCreateReferences(references); + } + + private IEnumerable MapUpdateReferences(IEnumerable references) + { + return BaseMapUpdateReferences(references); } private UpdatedSystemUsageKLEDeviationParameters MapKle(LocalKLEDeviationsRequestDTO source, bool enforceFallbackIfNotProvided) diff --git a/Presentation.Web/Models/API/V1/ExternalReferenceDTO.cs b/Presentation.Web/Models/API/V1/ExternalReferenceDTO.cs index d078f86ce3..eb28d21fe6 100644 --- a/Presentation.Web/Models/API/V1/ExternalReferenceDTO.cs +++ b/Presentation.Web/Models/API/V1/ExternalReferenceDTO.cs @@ -5,6 +5,7 @@ namespace Presentation.Web.Models.API.V1 public class ExternalReferenceDTO { public int Id { get; set; } + public Guid Uuid { get; set; } public string Title { get; set; } public string ExternalReferenceId { get; set; } public string URL { get; set; } diff --git a/Presentation.Web/Models/API/V2/Request/Contract/ContractWriteRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/Contract/ContractWriteRequestDTO.cs index 04ae7caa67..c922495a7d 100644 --- a/Presentation.Web/Models/API/V2/Request/Contract/ContractWriteRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/Contract/ContractWriteRequestDTO.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Presentation.Web.Infrastructure.Attributes; using Presentation.Web.Models.API.V2.Request.Generic.Roles; -using Presentation.Web.Models.API.V2.Types.Shared; namespace Presentation.Web.Models.API.V2.Request.Contract { @@ -47,6 +46,5 @@ public class ContractWriteRequestDTO /// public IEnumerable Roles { get; set; } - public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/Contract/CreateNewContractRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/Contract/CreateNewContractRequestDTO.cs index 37cc74b97e..49a284ebee 100644 --- a/Presentation.Web/Models/API/V2/Request/Contract/CreateNewContractRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/Contract/CreateNewContractRequestDTO.cs @@ -1,12 +1,14 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Core.DomainModel.ItContract; using Presentation.Web.Infrastructure.Attributes; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.SharedProperties; namespace Presentation.Web.Models.API.V2.Request.Contract { - public class CreateNewContractRequestDTO : ContractWriteRequestDTO, IHasNameExternal + public class CreateNewContractRequestDTO : ContractWriteRequestDTO, IHasNameExternal, IHasExternalReference { /// /// UUID of the organization in which the contract will be created @@ -24,5 +26,12 @@ public class CreateNewContractRequestDTO : ContractWriteRequestDTO, IHasNameExte [Required(AllowEmptyStrings = false)] [MaxLength(ItContractConstraints.MaxNameLength)] public string Name { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraints: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/Contract/UpdateContractRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/Contract/UpdateContractRequestDTO.cs index 4a773516e3..31cd6201fa 100644 --- a/Presentation.Web/Models/API/V2/Request/Contract/UpdateContractRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/Contract/UpdateContractRequestDTO.cs @@ -1,10 +1,12 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Core.DomainModel.ItContract; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.SharedProperties; namespace Presentation.Web.Models.API.V2.Request.Contract { - public class UpdateContractRequestDTO : ContractWriteRequestDTO, IHasNameExternal + public class UpdateContractRequestDTO : ContractWriteRequestDTO, IHasNameExternal, IHasExternalReference { /// /// Name of the contract. @@ -14,5 +16,15 @@ public class UpdateContractRequestDTO : ContractWriteRequestDTO, IHasNameExterna /// [MaxLength(ItContractConstraints.MaxNameLength)] public string Name { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraints: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// - If the reference has a uuid it will update an existing reference (with the same uuid), uuid must exist + /// - If the reference has no uuid, a new External Reference will be created + /// - Existing references will be replaced by the input data, so unless identified using uuid in the updates, the existing references will be removed. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/DataProcessing/CreateDataProcessingRegistrationRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/DataProcessing/CreateDataProcessingRegistrationRequestDTO.cs index e50769aeea..12c4ed4186 100644 --- a/Presentation.Web/Models/API/V2/Request/DataProcessing/CreateDataProcessingRegistrationRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/DataProcessing/CreateDataProcessingRegistrationRequestDTO.cs @@ -1,12 +1,14 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Core.DomainModel.GDPR; using Presentation.Web.Infrastructure.Attributes; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.SharedProperties; namespace Presentation.Web.Models.API.V2.Request.DataProcessing { - public class CreateDataProcessingRegistrationRequestDTO : DataProcessingRegistrationWriteRequestDTO, IHasNameExternal + public class CreateDataProcessingRegistrationRequestDTO : DataProcessingRegistrationWriteRequestDTO, IHasNameExternal, IHasExternalReference { /// /// UUID of the organization the data processing registration will be created in @@ -24,5 +26,12 @@ public class CreateDataProcessingRegistrationRequestDTO : DataProcessingRegistra [Required(AllowEmptyStrings = false)] [MaxLength(DataProcessingRegistrationConstraints.MaxNameLength)] public string Name { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraint: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/DataProcessing/DataProcessingRegistrationWriteRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/DataProcessing/DataProcessingRegistrationWriteRequestDTO.cs index 97fd3ff21e..7c7fe1eb7d 100644 --- a/Presentation.Web/Models/API/V2/Request/DataProcessing/DataProcessingRegistrationWriteRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/DataProcessing/DataProcessingRegistrationWriteRequestDTO.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Presentation.Web.Models.API.V2.Request.Generic.Roles; -using Presentation.Web.Models.API.V2.Types.Shared; namespace Presentation.Web.Models.API.V2.Request.DataProcessing { @@ -23,9 +22,5 @@ public class DataProcessingRegistrationWriteRequestDTO /// - Role options must be available in the organization of the data processing registration /// public IEnumerable Roles { get; set; } - /// - /// External reference definitions - /// - public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/DataProcessing/UpdateDataProcessingRegistrationRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/DataProcessing/UpdateDataProcessingRegistrationRequestDTO.cs index bc9b0c1713..24d6638e2f 100644 --- a/Presentation.Web/Models/API/V2/Request/DataProcessing/UpdateDataProcessingRegistrationRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/DataProcessing/UpdateDataProcessingRegistrationRequestDTO.cs @@ -1,10 +1,12 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Core.DomainModel.GDPR; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.SharedProperties; namespace Presentation.Web.Models.API.V2.Request.DataProcessing { - public class UpdateDataProcessingRegistrationRequestDTO : DataProcessingRegistrationWriteRequestDTO, IHasNameExternal + public class UpdateDataProcessingRegistrationRequestDTO : DataProcessingRegistrationWriteRequestDTO, IHasNameExternal, IHasExternalReference { /// /// Name of the registration @@ -14,5 +16,15 @@ public class UpdateDataProcessingRegistrationRequestDTO : DataProcessingRegistra /// [MaxLength(DataProcessingRegistrationConstraints.MaxNameLength)] public string Name { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraints: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// - If the reference has a uuid it will update an existing reference (with the same uuid), uuid must exist + /// - If the reference has no uuid, a new External Reference will be created + /// - Existing references will be replaced by the input data, so unless identified using uuid in the updates, the existing references will be removed. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Types/Shared/ExternalReferenceDataDTO.cs b/Presentation.Web/Models/API/V2/Request/Shared/ExternalReferenceDataWriteRequestDTO.cs similarity index 90% rename from Presentation.Web/Models/API/V2/Types/Shared/ExternalReferenceDataDTO.cs rename to Presentation.Web/Models/API/V2/Request/Shared/ExternalReferenceDataWriteRequestDTO.cs index 90382f24dc..a0d51046b5 100644 --- a/Presentation.Web/Models/API/V2/Types/Shared/ExternalReferenceDataDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/Shared/ExternalReferenceDataWriteRequestDTO.cs @@ -1,11 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace Presentation.Web.Models.API.V2.Types.Shared +namespace Presentation.Web.Models.API.V2.Request.Shared { /// /// User defined external references attached to a KITOS entity /// - public class ExternalReferenceDataDTO + public class ExternalReferenceDataWriteRequestDTO { /// /// Reference title as shown in KITOS UI diff --git a/Presentation.Web/Models/API/V2/Request/Shared/UpdateExternalReferenceDataWriteRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/Shared/UpdateExternalReferenceDataWriteRequestDTO.cs new file mode 100644 index 0000000000..6023ceaf41 --- /dev/null +++ b/Presentation.Web/Models/API/V2/Request/Shared/UpdateExternalReferenceDataWriteRequestDTO.cs @@ -0,0 +1,15 @@ +using System; + +namespace Presentation.Web.Models.API.V2.Request.Shared +{ + public class UpdateExternalReferenceDataWriteRequestDTO : ExternalReferenceDataWriteRequestDTO + { + /// + /// The UUID of the External Reference + /// Constrains: + /// - If the reference has a uuid it the update points to an existing reference (with the same uuid). + /// - If the reference has no uuid, it will be considered anonymous and be added as such (and KITOS will assign a uuid to it automatically) + /// + public Guid? Uuid { get; set; } + } +} \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/SystemUsage/BaseItSystemUsageWriteRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/SystemUsage/BaseItSystemUsageWriteRequestDTO.cs index a47ae98037..35aec0deab 100644 --- a/Presentation.Web/Models/API/V2/Request/SystemUsage/BaseItSystemUsageWriteRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/SystemUsage/BaseItSystemUsageWriteRequestDTO.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Presentation.Web.Models.API.V2.Request.Generic.Roles; -using Presentation.Web.Models.API.V2.Types.Shared; +using Presentation.Web.Models.API.V2.Request.Shared; namespace Presentation.Web.Models.API.V2.Request.SystemUsage { @@ -27,12 +27,5 @@ public abstract class BaseItSystemUsageWriteRequestDTO /// GDPR-specific registrations /// public GDPRWriteRequestDTO GDPR { get; set; } - /// - /// User defined external references. - /// The external reference marked as "master reference" will be shown in overviews and on the system front page in KITOS - /// Constraint: - /// - If the list is not empty one (and only one) must be marked as the master reference. - /// - public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/SystemUsage/CreateItSystemUsageRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/SystemUsage/CreateItSystemUsageRequestDTO.cs index 2b688ef455..a8d7a2722b 100644 --- a/Presentation.Web/Models/API/V2/Request/SystemUsage/CreateItSystemUsageRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/SystemUsage/CreateItSystemUsageRequestDTO.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Presentation.Web.Infrastructure.Attributes; +using Presentation.Web.Models.API.V2.Request.Shared; namespace Presentation.Web.Models.API.V2.Request.SystemUsage { @@ -22,5 +24,12 @@ public class CreateItSystemUsageRequestDTO : BaseItSystemUsageWriteRequestDTO public Guid OrganizationUuid { get; set; } public GeneralDataWriteRequestDTO General { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraint: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Request/SystemUsage/UpdateItSystemUsageRequestDTO.cs b/Presentation.Web/Models/API/V2/Request/SystemUsage/UpdateItSystemUsageRequestDTO.cs index c6984aa15e..aeb70afde4 100644 --- a/Presentation.Web/Models/API/V2/Request/SystemUsage/UpdateItSystemUsageRequestDTO.cs +++ b/Presentation.Web/Models/API/V2/Request/SystemUsage/UpdateItSystemUsageRequestDTO.cs @@ -1,7 +1,20 @@ -namespace Presentation.Web.Models.API.V2.Request.SystemUsage +using Presentation.Web.Models.API.V2.Request.Shared; +using System.Collections.Generic; + +namespace Presentation.Web.Models.API.V2.Request.SystemUsage { public class UpdateItSystemUsageRequestDTO : BaseItSystemUsageWriteRequestDTO { public GeneralDataUpdateRequestDTO General { get; set; } + /// + /// User defined external references. + /// The external reference marked as "master reference" will be shown in overviews + /// Constraints: + /// - If the list is not empty one (and only one) must be marked as the master reference. + /// - If the reference has a uuid it will update an existing reference (with the same uuid), uuid must exist + /// - If the reference has no uuid, a new External Reference will be created + /// - Existing references will be replaced by the input data, so unless identified using uuid in the updates, the existing references will be removed. + /// + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Response/Contract/ItContractResponseDTO.cs b/Presentation.Web/Models/API/V2/Response/Contract/ItContractResponseDTO.cs index 99fa428936..aad1fc6ce9 100644 --- a/Presentation.Web/Models/API/V2/Response/Contract/ItContractResponseDTO.cs +++ b/Presentation.Web/Models/API/V2/Response/Contract/ItContractResponseDTO.cs @@ -5,7 +5,7 @@ using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; using Presentation.Web.Models.API.V2.Response.Organization; -using Presentation.Web.Models.API.V2.Types.Shared; +using Presentation.Web.Models.API.V2.Response.Shared; namespace Presentation.Web.Models.API.V2.Response.Contract { @@ -76,7 +76,10 @@ public class ItContractResponseDTO : IHasNameExternal, IHasUuidExternal, IHasLas /// [Required] public IEnumerable Roles { get; set; } + /// + /// External reference definitions + /// [Required] - public IEnumerable ExternalReferences { get; set; } + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Response/DataProcessing/DataProcessingRegistrationResponseDTO.cs b/Presentation.Web/Models/API/V2/Response/DataProcessing/DataProcessingRegistrationResponseDTO.cs index c28adec689..9f7faaf8f8 100644 --- a/Presentation.Web/Models/API/V2/Response/DataProcessing/DataProcessingRegistrationResponseDTO.cs +++ b/Presentation.Web/Models/API/V2/Response/DataProcessing/DataProcessingRegistrationResponseDTO.cs @@ -4,6 +4,7 @@ using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; using Presentation.Web.Models.API.V2.Response.Organization; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.SharedProperties; using Presentation.Web.Models.API.V2.Types.Shared; @@ -43,6 +44,6 @@ public class DataProcessingRegistrationResponseDTO : IHasNameExternal, IHasUuidE /// External reference definitions /// [Required] - public IEnumerable ExternalReferences { get; set; } + public IEnumerable ExternalReferences { get; set; } } } \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Response/Shared/ExternalReferenceDataResponseDTO.cs b/Presentation.Web/Models/API/V2/Response/Shared/ExternalReferenceDataResponseDTO.cs new file mode 100644 index 0000000000..f47f6afe14 --- /dev/null +++ b/Presentation.Web/Models/API/V2/Response/Shared/ExternalReferenceDataResponseDTO.cs @@ -0,0 +1,10 @@ +using System; +using Presentation.Web.Models.API.V2.Request.Shared; + +namespace Presentation.Web.Models.API.V2.Response.Shared +{ + public class ExternalReferenceDataResponseDTO : ExternalReferenceDataWriteRequestDTO + { + public Guid Uuid { get; set; } + } +} \ No newline at end of file diff --git a/Presentation.Web/Models/API/V2/Response/SystemUsage/ItSystemUsageResponseDTO.cs b/Presentation.Web/Models/API/V2/Response/SystemUsage/ItSystemUsageResponseDTO.cs index cab62a88d8..f7d27094e3 100644 --- a/Presentation.Web/Models/API/V2/Response/SystemUsage/ItSystemUsageResponseDTO.cs +++ b/Presentation.Web/Models/API/V2/Response/SystemUsage/ItSystemUsageResponseDTO.cs @@ -4,8 +4,8 @@ using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; using Presentation.Web.Models.API.V2.Response.Organization; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.SharedProperties; -using Presentation.Web.Models.API.V2.Types.Shared; namespace Presentation.Web.Models.API.V2.Response.SystemUsage { @@ -67,7 +67,7 @@ public class ItSystemUsageResponseDTO : IHasUuidExternal, IHasLastModified /// User defined external references /// [Required] - public IEnumerable ExternalReferences { get; set; } + public IEnumerable ExternalReferences { get; set; } /// /// Archiving-specific registrations /// diff --git a/Presentation.Web/Models/API/V2/SharedProperties/IHasExternalReference.cs b/Presentation.Web/Models/API/V2/SharedProperties/IHasExternalReference.cs new file mode 100644 index 0000000000..c671f55085 --- /dev/null +++ b/Presentation.Web/Models/API/V2/SharedProperties/IHasExternalReference.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Presentation.Web.Models.API.V2.Request.Shared; + +namespace Presentation.Web.Models.API.V2.SharedProperties +{ + public interface IHasExternalReference where T : ExternalReferenceDataWriteRequestDTO + { + IEnumerable ExternalReferences{ get; set; } + } +} \ No newline at end of file diff --git a/Presentation.Web/Ninject/KernelBuilder.cs b/Presentation.Web/Ninject/KernelBuilder.cs index 3392bb4728..0e3ccdca36 100644 --- a/Presentation.Web/Ninject/KernelBuilder.cs +++ b/Presentation.Web/Ninject/KernelBuilder.cs @@ -120,6 +120,7 @@ using Core.ApplicationServices.Users.Handlers; using Core.DomainModel.Commands; using Infrastructure.Services.Types; +using Presentation.Web.Controllers.API.V2.External.Generic; namespace Presentation.Web.Ninject { @@ -307,6 +308,9 @@ private void RegisterMappers(IKernel kernel) //Interfaces kernel.Bind().To().InCommandScope(Mode); + + //External references + kernel.Bind().To().InCommandScope(Mode); } private void RegisterSSO(IKernel kernel) diff --git a/Presentation.Web/Presentation.Web.csproj b/Presentation.Web/Presentation.Web.csproj index 8af7774aeb..1707b353d9 100644 --- a/Presentation.Web/Presentation.Web.csproj +++ b/Presentation.Web/Presentation.Web.csproj @@ -422,6 +422,8 @@ + + @@ -448,7 +450,11 @@ + + + + @@ -618,7 +624,6 @@ - diff --git a/Tests.Integration.Presentation.Web/Contract/V2/ItContractsApiV2Test.cs b/Tests.Integration.Presentation.Web/Contract/V2/ItContractsApiV2Test.cs index 5232c0be2d..8c635fd045 100644 --- a/Tests.Integration.Presentation.Web/Contract/V2/ItContractsApiV2Test.cs +++ b/Tests.Integration.Presentation.Web/Contract/V2/ItContractsApiV2Test.cs @@ -14,10 +14,12 @@ using Presentation.Web.Models.API.V2.Request.Contract; using Presentation.Web.Models.API.V2.Request.Generic.Roles; using Presentation.Web.Models.API.V2.Request.DataProcessing; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Request.SystemUsage; using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; using Presentation.Web.Models.API.V2.Response.Organization; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.SharedProperties; using Presentation.Web.Models.API.V2.Types.Contract; using Presentation.Web.Models.API.V2.Types.Shared; @@ -914,7 +916,7 @@ public async Task Can_POST_With_ExternalReferences() //Arrange var (token, user, organization) = await CreatePrerequisitesAsync(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var inputs = Many().Transform(WithRandomMaster).ToList(); + var inputs = Many().Transform(WithRandomMaster).ToList(); var request = new CreateNewContractRequestDTO { @@ -945,17 +947,17 @@ public async Task Can_PATCH_ExternalReferences() }; var newContract = await ItContractV2Helper.PostContractAsync(token, request); - var inputs1 = Many().Transform(WithRandomMaster).ToList(); + var inputs1 = CreateUpdateExternalReferences(); //Act using var response1 = await ItContractV2Helper.SendPatchExternalReferences(token, newContract.Uuid, inputs1).WithExpectedResponseCode(HttpStatusCode.OK); //Assert var dto = await ItContractV2Helper.GetItContractAsync(token, newContract.Uuid); - AssertExternalReferenceResults(inputs1, dto); + AssertExternalReferenceResults(inputs1, dto, true); //Act - reset - var inputs2 = Enumerable.Empty().ToList(); + var inputs2 = CreateNewExternalReferenceDataWithOldUuid(dto.ExternalReferences); using var response2 = await ItContractV2Helper.SendPatchExternalReferences(token, newContract.Uuid, inputs2).WithExpectedResponseCode(HttpStatusCode.OK); //Assert @@ -1589,7 +1591,7 @@ public async Task Can_POST_Full_Contract() var (contractType, contractTemplateType, agreementElements, criticalityType, generalDataWriteRequest) = await CreateGeneralDataRequestDTO(organization, true, true, true, true); var contractResponsibleDataWriteRequest = await CreateContractResponsibleDataRequestDTO(token, organization, true, true, true); var supplierRequest = await CreateContractSupplierDataRequestDTO(true, true, true); - var externalReferences = Many().Transform(WithRandomMaster).ToList(); + var externalReferences = Many().Transform(WithRandomMaster).ToList(); var systemUsageUuids = await CreateSystemUsageUuids(token, organization); var roles = await CreateRoles(organization); var dataProcessingRegistrationUuids = await CreateDataProcessingRegistrationUuids(token, organization); @@ -1654,7 +1656,7 @@ public async Task Can_PUT_All() var (contractType1, contractTemplateType1, agreementElements1, criticalityType1, generalDataWriteRequest1) = await CreateGeneralDataRequestDTO(organization, true, true, true, true); var contractResponsibleDataWriteRequest1 = await CreateContractResponsibleDataRequestDTO(token, organization, true, true, true); var supplierRequest1 = await CreateContractSupplierDataRequestDTO(true, true, true); - var externalReferences1 = Many().Transform(WithRandomMaster).ToList(); + var externalReferences1 = CreateUpdateExternalReferences(); var systemUsageUuids1 = await CreateSystemUsageUuids(token, organization); var roles1 = await CreateRoles(organization); var dataProcessingRegistrationUuids1 = await CreateDataProcessingRegistrationUuids(token, organization); @@ -1662,7 +1664,7 @@ public async Task Can_PUT_All() var paymentsRequest1 = await CreatePaymentsInput(token, organization, true, true); var (paymentModelRequest1, paymentFrequencyType1, paymentModelType1, priceRegulationType1) = await CreatePaymentModelRequestAsync(organization.Uuid, true, true, true, true); var (terminationRequest1, noticePeriodMonthsType1) = await CreateTerminationRequest(organization.Uuid, true); - var requestDto1 = new UpdateContractRequestDTO() + var requestDto1 = new UpdateContractRequestDTO { Name = CreateName(), ParentContractUuid = parent1.Uuid, @@ -1693,7 +1695,7 @@ public async Task Can_PUT_All() AssertGeneralDataSection(generalDataWriteRequest1, contractType1, contractTemplateType1, agreementElements1, criticalityType1, contractDTO1); AssertResponsible(contractResponsibleDataWriteRequest1, contractDTO1); AssertSupplier(supplierRequest1, contractDTO1); - AssertExternalReferenceResults(externalReferences1, contractDTO1); + AssertExternalReferenceResults(externalReferences1, contractDTO1, true); AssertMultiAssignment(systemUsageUuids1, contractDTO1.SystemUsages); AssertRoleAssignments(roles1, contractDTO1); AssertMultiAssignment(dataProcessingRegistrationUuids1, contractDTO1.DataProcessingRegistrations); @@ -1708,7 +1710,7 @@ public async Task Can_PUT_All() var (contractType2, contractTemplateType2, agreementElements2, criticalityType2, generalDataWriteRequest2) = await CreateGeneralDataRequestDTO(organization, true, true, true, true); var contractResponsibleDataWriteRequest2 = await CreateContractResponsibleDataRequestDTO(token, organization, true, true, true); var supplierRequest2 = await CreateContractSupplierDataRequestDTO(true, true, true); - var externalReferences2 = Many().Transform(WithRandomMaster).ToList(); + var externalReferences2 = CreateNewExternalReferenceDataWithOldUuid(contractDTO1.ExternalReferences); var systemUsageUuids2 = await CreateSystemUsageUuids(token, organization); var roles2 = await CreateRoles(organization); var dataProcessingRegistrationUuids2 = await CreateDataProcessingRegistrationUuids(token, organization); @@ -1819,7 +1821,7 @@ public async Task Can_DELETE_Contract() //Act using var response = await ItContractV2Helper.SendDeleteContractAsync(token, contractDTO.Uuid); - + var res = await response.Content.ReadAsStringAsync(); //Assert Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); using var getResponse = await ItContractV2Helper.SendGetItContractAsync(token, contractDTO.Uuid); @@ -1897,6 +1899,30 @@ private async Task> CreateRoles(OrganizationDTO o return roles; } + private List CreateUpdateExternalReferences() + { + return Many() + .Transform(WithRandomMaster) + .Select(x => + { + x.Uuid = null; + return x; + }).ToList(); + } + + private List CreateNewExternalReferenceDataWithOldUuid(IEnumerable createExternalReferences) + { + return createExternalReferences.Select(externalReference => new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = externalReference.Uuid, + Title = A(), + DocumentId = A(), + Url = A(), + MasterReference = externalReference.MasterReference + }) + .ToList(); + } + private async Task> CreateSystemUsageUuids(string token, OrganizationDTO organization) { var system1 = await ItSystemHelper.CreateItSystemInOrganizationAsync(CreateName(), organization.Id, AccessModifier.Public); @@ -2127,7 +2153,16 @@ private async Task CreateContractAsync(int orgId, string name = n var user = DatabaseAccess.MapFromEntitySet(x => x.AsQueryable().ById(userAndGetToken.userId)); return (user, userAndGetToken.token); } - private IEnumerable WithRandomMaster(IEnumerable references) + private IEnumerable WithRandomMaster(IEnumerable references) + { + var orderedRandomly = references.OrderBy(x => A()).ToList(); + orderedRandomly.First().MasterReference = true; + foreach (var externalReferenceDataDto in orderedRandomly.Skip(1)) + externalReferenceDataDto.MasterReference = false; + + return orderedRandomly; + } + private IEnumerable WithRandomMaster(IEnumerable references) { var orderedRandomly = references.OrderBy(x => A()).ToList(); orderedRandomly.First().MasterReference = true; @@ -2137,7 +2172,23 @@ private IEnumerable WithRandomMaster(IEnumerable expected, ItContractResponseDTO actual) + private static void AssertExternalReferenceResults(IReadOnlyCollection expected, ItContractResponseDTO actual, bool ignoreUuid = false) + { + Assert.Equal(expected.Count, actual.ExternalReferences.Count()); + + expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() + .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).Select(x => + new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = ignoreUuid ? null : x.Uuid, + DocumentId = x.DocumentId, + MasterReference = x.MasterReference, + Title = x.Title, + Url = x.Url + }).ToList()); + } + + private static void AssertExternalReferenceResults(IReadOnlyCollection expected, ItContractResponseDTO actual) { Assert.Equal(expected.Count, actual.ExternalReferences.Count()); expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() diff --git a/Tests.Integration.Presentation.Web/GDPR/V2/DataProcessingRegistrationApiV2Test.cs b/Tests.Integration.Presentation.Web/GDPR/V2/DataProcessingRegistrationApiV2Test.cs index 0829841117..ce7a24e652 100644 --- a/Tests.Integration.Presentation.Web/GDPR/V2/DataProcessingRegistrationApiV2Test.cs +++ b/Tests.Integration.Presentation.Web/GDPR/V2/DataProcessingRegistrationApiV2Test.cs @@ -28,6 +28,8 @@ using ExpectedObjects; using Presentation.Web.Models.API.V1; using Presentation.Web.Models.API.V2.Request.Contract; +using Presentation.Web.Models.API.V2.Request.Shared; +using Presentation.Web.Models.API.V2.Response.Shared; namespace Tests.Integration.Presentation.Web.GDPR.V2 { @@ -979,7 +981,7 @@ public async Task Can_POST_With_ExternalReferences() //Arrange var (token, user, organization) = await CreatePrerequisitesAsync(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var inputs = Many().Transform(WithRandomMaster).ToList(); + var inputs = Many().Transform(WithRandomMaster).ToList(); var request = new CreateDataProcessingRegistrationRequestDTO { @@ -1011,17 +1013,17 @@ public async Task Can_PATCH_ExternalReferences() }; var newRegistration = await DataProcessingRegistrationV2Helper.PostAsync(token, request); - var inputs1 = Many().Transform(WithRandomMaster).ToList(); + var inputs1 = CreateUpdateExternalReferences(); //Act using var response1 = await DataProcessingRegistrationV2Helper.SendPatchExternalReferences(token, newRegistration.Uuid, inputs1).WithExpectedResponseCode(HttpStatusCode.OK); //Assert var dto = await DataProcessingRegistrationV2Helper.GetDPRAsync(token, newRegistration.Uuid); - AssertExternalReferenceResults(inputs1, dto); + AssertExternalReferenceResults(inputs1, dto, true); //Act - reset - var inputs2 = Enumerable.Empty().ToList(); + var inputs2 = Enumerable.Empty().ToList(); using var response2 = await DataProcessingRegistrationV2Helper.SendPatchExternalReferences(token, newRegistration.Uuid, inputs2).WithExpectedResponseCode(HttpStatusCode.OK); //Assert @@ -1044,7 +1046,7 @@ public async Task Can_POST_Full_DataProcessingRegistration() var oversightOption = (await OptionV2ApiHelper.GetOptionsAsync(OptionV2ApiHelper.ResourceName.DataProcessingRegistrationOversight, organization.Uuid, 10, 0)).RandomItem(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var externalReferenceInputs = Many().Transform(WithRandomMaster).ToList(); + var externalReferenceInputs = Many().Transform(WithRandomMaster).ToList(); var oversightInput = CreateOversightRequest(new[] { oversightOption.Uuid }, YesNoUndecidedChoice.Yes, new[] { oversightDate1, oversightDate2 }); @@ -1103,7 +1105,7 @@ public async Task Can_Put_All() var rolesRequest1 = new List { new() { RoleUuid = role1.Uuid, UserUuid = user1.Uuid } }; - var referencesRequest1 = Many().Transform(WithRandomMaster).ToList(); + var referencesRequest1 = CreateUpdateExternalReferences(); var modifyRequest1 = new UpdateDataProcessingRegistrationRequestDTO() { @@ -1125,7 +1127,7 @@ public async Task Can_Put_All() AssertMultiAssignment(systemUsagesRequest1, dto1.SystemUsages); AssertOversight(oversightRequest1, dto1.Oversight); AssertSingleRight(role1, user1, dto1.Roles); - AssertExternalReferenceResults(referencesRequest1, dto1); + AssertExternalReferenceResults(referencesRequest1, dto1, true); //Act - Put on filled var (dataResponsible2, basisForTransfer2, generalRequest2) = await CreateGeneralDataInput(true, true, true, true, true, organization); @@ -1145,7 +1147,7 @@ public async Task Can_Put_All() var rolesRequest2 = new List { new() { RoleUuid = role2.Uuid, UserUuid = user2.Uuid } }; - var referencesRequest2 = Many().Transform(WithRandomMaster).ToList(); + var referencesRequest2 = CreateNewExternalReferenceDataWithOldUuid(dto1.ExternalReferences); var modifyRequest2 = new UpdateDataProcessingRegistrationRequestDTO() { @@ -1172,7 +1174,7 @@ public async Task Can_Put_All() var generalRequest3 = new DataProcessingRegistrationGeneralDataWriteRequestDTO(); var systemUsagesRequest3 = Array.Empty(); - var referencesRequest3 = Enumerable.Empty().ToList(); + var referencesRequest3 = Enumerable.Empty().ToList(); var modifyRequest3 = new UpdateDataProcessingRegistrationRequestDTO() { @@ -1206,12 +1208,28 @@ public async Task Can_Put_All() #region Asserters - private static void AssertExternalReferenceResults(List expected, DataProcessingRegistrationResponseDTO actual) + private static void AssertExternalReferenceResults(List expected, DataProcessingRegistrationResponseDTO actual) { expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).ToList()); } + private static void AssertExternalReferenceResults(List expected, DataProcessingRegistrationResponseDTO actual, bool ignoreUuid = false) + { + Assert.Equal(expected.Count, actual.ExternalReferences.Count()); + + expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() + .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).Select(x => + new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = ignoreUuid ? null : x.Uuid, + DocumentId = x.DocumentId, + MasterReference = x.MasterReference, + Title = x.Title, + Url = x.Url + }).ToList()); + } + private void AssertEmptiedOversight(DataProcessingRegistrationOversightResponseDTO actual) { Assert.Empty(actual.OversightOptions); @@ -1349,7 +1367,41 @@ private DataProcessingRegistrationOversightWriteRequestDTO CreateOversightReques }; } - private IEnumerable WithRandomMaster(IEnumerable references) + private List CreateUpdateExternalReferences() + { + return Many() + .Transform(WithRandomMaster) + .Select(x => + { + x.Uuid = null; + return x; + }).ToList(); + } + + private List CreateNewExternalReferenceDataWithOldUuid(IEnumerable createExternalReferences) + { + return createExternalReferences.Select(externalReference => new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = externalReference.Uuid, + Title = A(), + DocumentId = A(), + Url = A(), + MasterReference = externalReference.MasterReference + }) + .ToList(); + } + + private IEnumerable WithRandomMaster(IEnumerable references) + { + var orderedRandomly = references.OrderBy(x => A()).ToList(); + orderedRandomly.First().MasterReference = true; + foreach (var externalReferenceDataDto in orderedRandomly.Skip(1)) + externalReferenceDataDto.MasterReference = false; + + return orderedRandomly; + } + + private IEnumerable WithRandomMaster(IEnumerable references) { var orderedRandomly = references.OrderBy(x => A()).ToList(); orderedRandomly.First().MasterReference = true; diff --git a/Tests.Integration.Presentation.Web/SystemUsage/V2/ItSystemUsageApiV2Test.cs b/Tests.Integration.Presentation.Web/SystemUsage/V2/ItSystemUsageApiV2Test.cs index 6dbae3bc1d..9c6fd87aa1 100644 --- a/Tests.Integration.Presentation.Web/SystemUsage/V2/ItSystemUsageApiV2Test.cs +++ b/Tests.Integration.Presentation.Web/SystemUsage/V2/ItSystemUsageApiV2Test.cs @@ -15,9 +15,11 @@ using Presentation.Web.Models.API.V1; using Presentation.Web.Models.API.V1.SystemRelations; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Request.SystemUsage; using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Generic.Roles; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.Response.SystemUsage; using Presentation.Web.Models.API.V2.Types.Shared; using Presentation.Web.Models.API.V2.Types.SystemUsage; @@ -654,7 +656,7 @@ public async Task Can_POST_With_ExternalReferences() //Arrange var (token, user, organization, system) = await CreatePrerequisitesAsync(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var inputs = Many().Transform(WithRandomMaster).ToList(); + var inputs = Many().Transform(WithRandomMaster).ToList(); var request = CreatePostRequest(organization.Uuid, system.Uuid, referenceDataDtos: inputs); @@ -676,17 +678,17 @@ public async Task Can_PATCH_ExternalReferences() var request = CreatePostRequest(organization.Uuid, system.Uuid); var newUsage = await ItSystemUsageV2Helper.PostAsync(token, request); - var inputs1 = Many().Transform(WithRandomMaster).ToList(); + var inputs1 = CreateUpdateExternalReferenceDataWriteRequestDTOs().ToList(); //Act using var response1 = await ItSystemUsageV2Helper.SendPatchExternalReferences(token, newUsage.Uuid, inputs1).WithExpectedResponseCode(HttpStatusCode.OK); //Assert var dto = await ItSystemUsageV2Helper.GetSingleAsync(token, newUsage.Uuid); - AssertExternalReferenceResults(inputs1, dto); + AssertExternalReferenceResults(inputs1, dto, true); //Act - reset - var inputs2 = Enumerable.Empty().ToList(); + var inputs2 = Enumerable.Empty().ToList(); using var response2 = await ItSystemUsageV2Helper.SendPatchExternalReferences(token, newUsage.Uuid, inputs2).WithExpectedResponseCode(HttpStatusCode.OK); //Assert @@ -1055,7 +1057,7 @@ public async Task Can_PUT_With_All_Data() var (token, user, organization, system) = await CreatePrerequisitesAsync(); var newUsage = await ItSystemUsageV2Helper.PostAsync(token, CreatePostRequest(organization.Uuid, system.Uuid)); - var (generalData1, orgUnit1, organizationUsageData1, addedTaskRefs1, removedTaskRefs1, kleDeviations1, externalReferences1, roles1, gdpr1, archiving1) = await CreateFullDataRequestDTO(organization, system); + var (generalData1, orgUnit1, organizationUsageData1, addedTaskRefs1, removedTaskRefs1, kleDeviations1, externalReferences1, roles1, gdpr1, archiving1) = await CreateUpdateFullDataRequestDTO(organization, system); var updateRequest1 = CreatePutRequest( generalSection: generalData1, organizationalUsageSection: organizationUsageData1, @@ -1071,12 +1073,12 @@ public async Task Can_PUT_With_All_Data() //Assert - PUT on empty system usage AssertGeneralData(updateRequest1.General, updatedUsage1.General); - await AssertOrganizationalUsage(token, updatedUsage1.Uuid, new OrgUnitDTO[] { orgUnit1 }, orgUnit1); + await AssertOrganizationalUsage(token, updatedUsage1.Uuid, new[] { orgUnit1 }, orgUnit1); AssertKLEDeviation(true, addedTaskRefs1, updatedUsage1.LocalKLEDeviations.AddedKLE); AssertKLEDeviation(true, removedTaskRefs1, updatedUsage1.LocalKLEDeviations.RemovedKLE); - AssertExternalReferenceResults(updateRequest1.ExternalReferences.ToList(), updatedUsage1); + AssertExternalReferenceResults(updateRequest1.ExternalReferences.ToList(), updatedUsage1, true); AssertRoles(updateRequest1.Roles, updatedUsage1.Roles); @@ -1085,7 +1087,8 @@ public async Task Can_PUT_With_All_Data() AssertArchivingParametersSet(updateRequest1.Archiving, updatedUsage1.Archiving); //Act - PUT on filled system usage - var (generalData2, orgUnit2, organizationUsageData2, addedTaskRefs2, removedTaskRefs2, kleDeviations2, externalReferences2, roles2, gdpr2, archiving2) = await CreateFullDataRequestDTO(organization, system); + var (generalData2, orgUnit2, organizationUsageData2, addedTaskRefs2, removedTaskRefs2, kleDeviations2, + externalReferences2, roles2, gdpr2, archiving2) = await CreateUpdateFullDataRequestDTO(organization, system, updatedUsage1.ExternalReferences); var updateRequest2 = CreatePutRequest( generalSection: generalData2, organizationalUsageSection: organizationUsageData2, @@ -1118,7 +1121,7 @@ public async Task Can_PUT_With_All_Data() generalSection: new GeneralDataWriteRequestDTO(), organizationalUsageSection: new OrganizationUsageWriteRequestDTO(), kleDeviationsRequest: new LocalKLEDeviationsRequestDTO(), - referenceDataDtos: new List(), + referenceDataDtos: new List(), roles: new List(), gdpr: new GDPRWriteRequestDTO(), archiving: new ArchivingWriteRequestDTO()); @@ -1706,16 +1709,43 @@ private static void AssertRelation(SystemRelationWriteRequestDTO expected, strin return (targetInterface.Uuid, targetInterface.Name); } - private async Task<(GeneralDataWriteRequestDTO, - OrgUnitDTO, - OrganizationUsageWriteRequestDTO, - Guid[], + private async Task<(GeneralDataWriteRequestDTO, + OrgUnitDTO, + OrganizationUsageWriteRequestDTO, Guid[], - LocalKLEDeviationsRequestDTO, - IEnumerable, + Guid[], + LocalKLEDeviationsRequestDTO, + IEnumerable, IEnumerable, GDPRWriteRequestDTO, - ArchivingWriteRequestDTO)> CreateFullDataRequestDTO(OrganizationDTO organization, ItSystemDTO system) + ArchivingWriteRequestDTO)> CreateUpdateFullDataRequestDTO(OrganizationDTO organization, ItSystemDTO system, IEnumerable existingExternalReferences = null) + { + var fullData = await CreateFullDataRequestDTO(organization, system); + + var mappedFullData = ( + fullData.generalData, + fullData.unit1, + fullData.organizationUsageData, + fullData.addedTaskRefs, + fullData.removedTaskRefs, + fullData.kleDeviations, + existingExternalReferences != null ? CreateNewExternalReferenceDataWithOldUuid(existingExternalReferences) : MapExternalReferenceDtosToUpdateDtos(fullData.externalReferences), + fullData.roles, + fullData.gdpr, + fullData.archiving); + return mappedFullData; + } + + private async Task<(GeneralDataWriteRequestDTO generalData, + OrgUnitDTO unit1, + OrganizationUsageWriteRequestDTO organizationUsageData, + Guid[] addedTaskRefs, + Guid[] removedTaskRefs, + LocalKLEDeviationsRequestDTO kleDeviations, + IEnumerable externalReferences, + IEnumerable roles, + GDPRWriteRequestDTO gdpr, + ArchivingWriteRequestDTO archiving)> CreateFullDataRequestDTO(OrganizationDTO organization, ItSystemDTO system) { var dataClassification = (await OptionV2ApiHelper.GetOptionsAsync(OptionV2ApiHelper.ResourceName.ItSystemUsageDataClassification, organization.Uuid, 1, 0)).First(); var generalData = CreateGeneralDataWriteRequestDTO(dataClassification.Uuid); @@ -1735,8 +1765,7 @@ private static void AssertRelation(SystemRelationWriteRequestDTO expected, strin } var kleDeviations = CreateLocalKLEDeviationsRequestDTO(addedTaskRefs, removedTaskRefs); - - var externalReferences = CreateExternalReferenceDataDTOs(); + var externalReferences = CreateExternalReferenceDataDTOs(); var userToGainRole = await CreateUser(organization); var role = DatabaseAccess.MapFromEntitySet(x => x.AsQueryable().First()); @@ -1801,12 +1830,36 @@ private IEnumerable CreateRoleAssignmentRequestDTOs(Gu }; } - private IEnumerable CreateExternalReferenceDataDTOs() + private IEnumerable CreateNewExternalReferenceDataWithOldUuid(IEnumerable createExternalReferences) { - Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - return Many().Transform(WithRandomMaster).ToList(); + return createExternalReferences.Select(externalReference => new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = externalReference.Uuid, + Title = A(), + DocumentId = A(), + Url = A(), + MasterReference = externalReference.MasterReference + }) + .ToList(); } + private IEnumerable CreateUpdateExternalReferenceDataWriteRequestDTOs() + { + return CreateExternalReferenceDataDTOs() + .Select( + x => + { + x.Uuid = null; + return x; + }); + } + + private IEnumerable CreateExternalReferenceDataDTOs() where T: ExternalReferenceDataWriteRequestDTO + { + Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs + return Many().Transform(WithRandomMaster).ToList(); + } + private LocalKLEDeviationsRequestDTO CreateLocalKLEDeviationsRequestDTO(Guid[] addedUuids, Guid[] removedUuids) { return new LocalKLEDeviationsRequestDTO @@ -2003,7 +2056,17 @@ private ArchivingWriteRequestDTO CreateArchivingWriteRequestDTO(Guid archiveType }; } - private IEnumerable WithRandomMaster(IEnumerable references) + private IEnumerable WithRandomMaster(IEnumerable references) where T: ExternalReferenceDataWriteRequestDTO + { + var orderedRandomly = references.OrderBy(x => A()).ToList(); + orderedRandomly.First().MasterReference = true; + foreach (var externalReferenceDataDto in orderedRandomly.Skip(1)) + externalReferenceDataDto.MasterReference = false; + + return orderedRandomly; + } + + private IEnumerable WithRandomMaster(IEnumerable references) { var orderedRandomly = references.OrderBy(x => A()).ToList(); orderedRandomly.First().MasterReference = true; @@ -2013,10 +2076,35 @@ private IEnumerable WithRandomMaster(IEnumerable expected, ItSystemUsageResponseDTO actual) + private static void AssertExternalReferenceResults(IReadOnlyCollection expected, ItSystemUsageResponseDTO actual, bool ignoreUuid = false) { + Assert.Equal(expected.Count, actual.ExternalReferences.Count()); + expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() - .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).ToList()); + .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).Select(x => + new UpdateExternalReferenceDataWriteRequestDTO + { + Uuid = ignoreUuid ? null : x.Uuid, + DocumentId = x.DocumentId, + MasterReference = x.MasterReference, + Title = x.Title, + Url = x.Url + }).ToList()); + } + + private static void AssertExternalReferenceResults(IReadOnlyCollection expected, ItSystemUsageResponseDTO actual) + { + Assert.Equal(expected.Count, actual.ExternalReferences.Count()); + + expected.OrderBy(x => x.DocumentId).ToList().ToExpectedObject() + .ShouldMatch(actual.ExternalReferences.OrderBy(x => x.DocumentId).Select(x => + new ExternalReferenceDataWriteRequestDTO + { + DocumentId = x.DocumentId, + MasterReference = x.MasterReference, + Title = x.Title, + Url = x.Url + }).ToList()); } private static void AssertKLEDeviation(bool withDeviation, IEnumerable expectedDeviation, IEnumerable actualDeviation) @@ -2117,7 +2205,7 @@ private static CreateItSystemUsageRequestDTO CreatePostRequest( GeneralDataWriteRequestDTO generalSection = null, OrganizationUsageWriteRequestDTO organizationalUsageSection = null, LocalKLEDeviationsRequestDTO kleDeviationsRequest = null, - IEnumerable referenceDataDtos = null, + IEnumerable referenceDataDtos = null, IEnumerable roles = null, GDPRWriteRequestDTO gdpr = null, ArchivingWriteRequestDTO archiving = null) @@ -2140,7 +2228,7 @@ private static UpdateItSystemUsageRequestDTO CreatePutRequest( GeneralDataWriteRequestDTO generalSection = null, OrganizationUsageWriteRequestDTO organizationalUsageSection = null, LocalKLEDeviationsRequestDTO kleDeviationsRequest = null, - IEnumerable referenceDataDtos = null, + IEnumerable referenceDataDtos = null, IEnumerable roles = null, GDPRWriteRequestDTO gdpr = null, ArchivingWriteRequestDTO archiving = null) @@ -2238,5 +2326,24 @@ private static int GetUsageIdByUuid(Guid uuid) { return DatabaseAccess.MapFromEntitySet(all => all.AsQueryable().ByUuid(uuid).Id); } + + private IEnumerable MapExternalReferenceDtosToUpdateDtos( + IEnumerable references) + { + return references.Select(MapExternalReferenceDtoToUpdateDto).ToList(); + } + + private UpdateExternalReferenceDataWriteRequestDTO MapExternalReferenceDtoToUpdateDto( + ExternalReferenceDataWriteRequestDTO reference) + { + return new UpdateExternalReferenceDataWriteRequestDTO + { + DocumentId = reference.DocumentId, + Title = reference.Title, + MasterReference = reference.MasterReference, + Url = reference.Url, + Uuid = null + }; + } } } diff --git a/Tests.Integration.Presentation.Web/Tools/External/DataProcessingRegistrationV2Helper.cs b/Tests.Integration.Presentation.Web/Tools/External/DataProcessingRegistrationV2Helper.cs index b50a79b880..c45bde7e2d 100644 --- a/Tests.Integration.Presentation.Web/Tools/External/DataProcessingRegistrationV2Helper.cs +++ b/Tests.Integration.Presentation.Web/Tools/External/DataProcessingRegistrationV2Helper.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Presentation.Web.Models.API.V2.Request.DataProcessing; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Response.DataProcessing; using Presentation.Web.Models.API.V2.Response.Options; using Presentation.Web.Models.API.V2.Types.Shared; @@ -131,9 +132,9 @@ public static async Task SendPatchRolesAsync(string token, return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/data-processing-registrations/{uuid}"), token, CreatePatchPayload(nameof(DataProcessingRegistrationWriteRequestDTO.Roles), payload)); } - public static async Task SendPatchExternalReferences(string token, Guid uuid, IEnumerable payload) + public static async Task SendPatchExternalReferences(string token, Guid uuid, IEnumerable payload) { - return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/data-processing-registrations/{uuid}"), token, CreatePatchPayload(nameof(DataProcessingRegistrationWriteRequestDTO.ExternalReferences), payload)); + return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/data-processing-registrations/{uuid}"), token, CreatePatchPayload(nameof(UpdateDataProcessingRegistrationRequestDTO.ExternalReferences), payload)); } public static async Task> GetRolesAsync(string token, Guid organizationUuid, int page = 0, int pageSize = 10) diff --git a/Tests.Integration.Presentation.Web/Tools/External/ItContractV2Helper.cs b/Tests.Integration.Presentation.Web/Tools/External/ItContractV2Helper.cs index 336042903d..f5db72bfa3 100644 --- a/Tests.Integration.Presentation.Web/Tools/External/ItContractV2Helper.cs +++ b/Tests.Integration.Presentation.Web/Tools/External/ItContractV2Helper.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Presentation.Web.Models.API.V2.Request.Contract; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Types.Shared; using Xunit; @@ -114,7 +115,7 @@ public static async Task SendPatchSystemUsagesAsync(string return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/it-contracts/{contractUuid}"), token, CreatePatchPayload(nameof(UpdateContractRequestDTO.SystemUsageUuids), dto)); } - public static async Task SendPatchExternalReferences(string token, Guid contractUuid, List request) + public static async Task SendPatchExternalReferences(string token, Guid contractUuid, List request) { return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/it-contracts/{contractUuid}"), token, CreatePatchPayload(nameof(UpdateContractRequestDTO.ExternalReferences), request)); } diff --git a/Tests.Integration.Presentation.Web/Tools/External/ItSystemUsageV2Helper.cs b/Tests.Integration.Presentation.Web/Tools/External/ItSystemUsageV2Helper.cs index ed178ee4b1..af247810d3 100644 --- a/Tests.Integration.Presentation.Web/Tools/External/ItSystemUsageV2Helper.cs +++ b/Tests.Integration.Presentation.Web/Tools/External/ItSystemUsageV2Helper.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading.Tasks; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Request.SystemUsage; using Presentation.Web.Models.API.V2.Response.SystemUsage; using Presentation.Web.Models.API.V2.Types.Shared; @@ -127,9 +128,9 @@ public static async Task SendPatchKle(string token, Guid uu return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/it-system-usages/{uuid}"), token, dto.AsPatchPayloadOfProperty(nameof(UpdateItSystemUsageRequestDTO.LocalKleDeviations))); } - public static async Task SendPatchExternalReferences(string token, Guid uuid, IEnumerable payload) + public static async Task SendPatchExternalReferences(string token, Guid uuid, IEnumerable payload) { - return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/it-system-usages/{uuid}"), token, (payload ?? new List()).AsPatchPayloadOfProperty(nameof(UpdateItSystemUsageRequestDTO.ExternalReferences))); + return await HttpApi.PatchWithTokenAsync(TestEnvironment.CreateUrl($"api/v2/it-system-usages/{uuid}"), token, (payload ?? new List()).AsPatchPayloadOfProperty(nameof(UpdateItSystemUsageRequestDTO.ExternalReferences))); } public static async Task SendPatchRoles(string token, Guid uuid, IEnumerable dto) diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/Contract/ItContractWriteServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/Contract/ItContractWriteServiceTest.cs index 3e4e02ad7e..8d8e992818 100644 --- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/Contract/ItContractWriteServiceTest.cs +++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/Contract/ItContractWriteServiceTest.cs @@ -1457,7 +1457,7 @@ private ItContractPaymentModelModificationParameters CreatePaymentModel(bool wit private void ExpectBatchUpdateExternalReferencesReturns(ItContract contract, IEnumerable externalReferences, Maybe value) { _referenceServiceMock - .Setup(x => x.BatchUpdateExternalReferences(ReferenceRootType.Contract, contract.Id, externalReferences)) + .Setup(x => x.UpdateExternalReferences(ReferenceRootType.Contract, contract.Id, externalReferences)) .Returns(value); } diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/GDPR/DataProcessingRegistrationWriteServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/GDPR/DataProcessingRegistrationWriteServiceTest.cs index cf129d0ad5..c81139fc0b 100644 --- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/GDPR/DataProcessingRegistrationWriteServiceTest.cs +++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/GDPR/DataProcessingRegistrationWriteServiceTest.cs @@ -2140,7 +2140,7 @@ public void Cannot_Create_With_ExternalReferences_If_BatchUpdate_Fails() private void ExpectBatchUpdateExternalReferencesReturns(DataProcessingRegistration dpr, IEnumerable externalReferences, Maybe value) { _referenceServiceMock - .Setup(x => x.BatchUpdateExternalReferences(ReferenceRootType.DataProcessingRegistration, dpr.Id, externalReferences)) + .Setup(x => x.UpdateExternalReferences(ReferenceRootType.DataProcessingRegistration, dpr.Id, externalReferences)) .Returns(value); } diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/SystemUsage/ItSystemUsageWriteServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/SystemUsage/ItSystemUsageWriteServiceTest.cs index 526d3c59f3..03267c55ca 100644 --- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/SystemUsage/ItSystemUsageWriteServiceTest.cs +++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/SystemUsage/ItSystemUsageWriteServiceTest.cs @@ -783,7 +783,7 @@ public void Cannot_Create_With_ExternalReferences_If_BatchUpdate_Fails() private void ExpectBatchUpdateExternalReferencesReturns(ItSystemUsage systemUsage, IEnumerable externalReferences, Maybe value) { _referenceServiceMock - .Setup(x => x.BatchUpdateExternalReferences(ReferenceRootType.SystemUsage, systemUsage.Id, externalReferences)) + .Setup(x => x.UpdateExternalReferences(ReferenceRootType.SystemUsage, systemUsage.Id, externalReferences)) .Returns(value); } diff --git a/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationResponseMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationResponseMapperTest.cs index c2582d0b7d..b35ed3e4bb 100644 --- a/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationResponseMapperTest.cs +++ b/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationResponseMapperTest.cs @@ -8,10 +8,13 @@ using Core.DomainModel.ItSystemUsage; using Core.DomainModel.Organization; using Core.DomainModel.Shared; +using Moq; using Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping; +using Presentation.Web.Controllers.API.V2.External.Generic; using Presentation.Web.Models.API.V2.Response.DataProcessing; using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Organization; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.Types.DataProcessing; using Presentation.Web.Models.API.V2.Types.Shared; using Tests.Toolkit.Extensions; @@ -23,10 +26,12 @@ namespace Tests.Unit.Presentation.Web.Models.V2 public class DataProcessingRegistrationResponseMapperTest : WithAutoFixture { private readonly DataProcessingRegistrationResponseMapper _sut; + private readonly Mock _externalReferenceResponseMapperMock; public DataProcessingRegistrationResponseMapperTest() { - _sut = new DataProcessingRegistrationResponseMapper(); + _externalReferenceResponseMapperMock = new Mock(); + _sut = new DataProcessingRegistrationResponseMapper(_externalReferenceResponseMapperMock.Object); } [Fact] @@ -242,12 +247,17 @@ public void MapDataProcessingRegistrationDTO_Maps_ExternalReferences_Properties_ var dpr = new DataProcessingRegistration(); AssignBasicProperties(dpr); AssignExternalReferences(dpr); + var mappedReferences = Many(); + _externalReferenceResponseMapperMock + .Setup(x => x.MapExternalReferences(dpr.ExternalReferences, dpr.Reference)) + .Returns(mappedReferences); //Act var dto = _sut.MapDataProcessingRegistrationDTO(dpr); //Assert - AssertExternalReferences(dpr, dto.ExternalReferences.ToList()); + _externalReferenceResponseMapperMock.Verify(x => x.MapExternalReferences(dpr.ExternalReferences, dpr.Reference), Times.Once); + Assert.Equivalent(mappedReferences, dto.ExternalReferences); } #region Creates @@ -395,6 +405,7 @@ private void AssignExternalReferences(DataProcessingRegistration dpr) { dpr.ExternalReferences = Many().Select((title, i) => new ExternalReference { + Uuid = A(), Title = title, URL = A(), ExternalReferenceId = A(), @@ -407,7 +418,7 @@ private void AssignExternalReferences(DataProcessingRegistration dpr) #region Asserts - private void AssertOversightDates(ICollection expectedOversightDates, IEnumerable actualOversightDates) + private static void AssertOversightDates(ICollection expectedOversightDates, IEnumerable actualOversightDates) { var orderedExpected = expectedOversightDates.OrderBy(x => x.OversightDate).ToList(); var orderedActual = actualOversightDates.OrderBy(x => x.CompletedAt).ToList(); @@ -434,7 +445,7 @@ private void AssertSystemUsages(DataProcessingRegistration dpr, List(T optionalExpectedIdentity, Identi AssertIdentity(optionalExpectedIdentity, actualIdentity); } - private static void AssertExternalReferences(DataProcessingRegistration dpr, List dtoExternalReferences) - { - var actualMaster = Assert.Single(dtoExternalReferences, reference => reference.MasterReference); - AssertExternalReference(dpr.Reference, actualMaster); - Assert.Equal(dpr.ExternalReferences.Count, dtoExternalReferences.Count); - - foreach (var comparison in dpr.ExternalReferences.OrderBy(x => x.Title) - .Zip(dtoExternalReferences.OrderBy(x => x.Title), (expected, actual) => new { expected, actual }) - .ToList()) - { - AssertExternalReference(comparison.expected, comparison.actual); - } - } - - private static void AssertExternalReference(ExternalReference reference, ExternalReferenceDataDTO actualMaster) - { - Assert.Equal(reference.Title, actualMaster.Title); - Assert.Equal(reference.URL, actualMaster.Url); - Assert.Equal(reference.ExternalReferenceId, actualMaster.DocumentId); - } - private static void AssertUser(User user, IdentityNamePairResponseDTO dtoValue) { Assert.Equal((user.GetFullName(), user.Uuid), (dtoValue.Name, dtoValue.Uuid)); diff --git a/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationWriteModelMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationWriteModelMapperTest.cs index 1f93044826..d30b60337b 100644 --- a/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationWriteModelMapperTest.cs +++ b/Tests.Unit.Presentation.Web/Models/V2/DataProcessingRegistrationWriteModelMapperTest.cs @@ -12,6 +12,7 @@ using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.DataProcessing; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Types.DataProcessing; using Presentation.Web.Models.API.V2.Types.Shared; using Tests.Toolkit.Extensions; @@ -495,7 +496,7 @@ public void MapRoles_Returns_UpdatedDataProcessingRegistrationRoles() public void Can_Map_ExternalReferences() { //Arrange - var references = Many().OrderBy(x => x.Url).ToList(); + var references = Many().OrderBy(x => x.Url).ToList(); //Act var output = _sut.FromPATCH(new UpdateDataProcessingRegistrationRequestDTO() { ExternalReferences = references }); @@ -504,7 +505,7 @@ public void Can_Map_ExternalReferences() AssertReferences(references, AssertPropertyContainsDataChange(output.ExternalReferences).ToList()); } - private static void AssertReferences(IReadOnlyList references, IReadOnlyList mappedReferences) + private static void AssertReferences(IReadOnlyList references, IReadOnlyList mappedReferences) where T: ExternalReferenceDataWriteRequestDTO { Assert.Equal(mappedReferences.Count, mappedReferences.Count); for (var i = 0; i < mappedReferences.Count; i++) @@ -515,6 +516,11 @@ private static void AssertReferences(IReadOnlyList ref Assert.Equal(expected.Title, actual.Title); Assert.Equal(expected.DocumentId, actual.DocumentId); Assert.Equal(expected.MasterReference, actual.MasterReference); + + if (expected is UpdateExternalReferenceDataWriteRequestDTO expectedUpdateReference) + { + Assert.Equal(expectedUpdateReference.Uuid, actual.Uuid); + } } } @@ -650,7 +656,8 @@ private void ConfigureRootProperties(bool noName, bool noGeneralData, bool noSys if (noSystems) properties.Remove(nameof(DataProcessingRegistrationWriteRequestDTO.SystemUsageUuids)); if (noOversight) properties.Remove(nameof(DataProcessingRegistrationWriteRequestDTO.Oversight)); if (noRoles) properties.Remove(nameof(DataProcessingRegistrationWriteRequestDTO.Roles)); - if (noReferences) properties.Remove(nameof(DataProcessingRegistrationWriteRequestDTO.ExternalReferences)); + if (noReferences) properties.Remove(nameof(UpdateDataProcessingRegistrationRequestDTO.ExternalReferences)); + _currentHttpRequestMock.Setup(x => x.GetDefinedJsonProperties(Enumerable.Empty().AsParameterMatch())).Returns(properties); } diff --git a/Tests.Unit.Presentation.Web/Models/V2/ExternalReferenceResponseMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/ExternalReferenceResponseMapperTest.cs new file mode 100644 index 0000000000..9923f9f8e2 --- /dev/null +++ b/Tests.Unit.Presentation.Web/Models/V2/ExternalReferenceResponseMapperTest.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Linq; +using Core.Abstractions.Extensions; +using Core.Abstractions.Types; +using Core.DomainModel; +using Presentation.Web.Controllers.API.V2.External.Generic; +using Presentation.Web.Models.API.V2.Response.Shared; +using Tests.Toolkit.Extensions; +using Tests.Toolkit.Patterns; +using Xunit; + +namespace Tests.Unit.Presentation.Web.Models.V2 +{ + public class ExternalReferenceResponseMapperTest : WithAutoFixture + { + private readonly ExternalReferenceResponseMapper _sut; + + public ExternalReferenceResponseMapperTest() + { + _sut = new ExternalReferenceResponseMapper(); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void MapExternalReferences_Maps_References(bool withMaster) + { + //Arrange + var externalReferences = CreateExternalReferences(); + var masterReference = withMaster ? externalReferences.RandomItem() : null; + + //Act + var dto = _sut.MapExternalReferences(externalReferences, masterReference); + + //Assert + AssertExternalReferences(externalReferences, masterReference.FromNullable(), dto.ToList()); + } + + private List CreateExternalReferences() + { + return Many().Select((title, i) => new ExternalReference + { + Title = title, + URL = A(), + ExternalReferenceId = A(), + Id = i + }).ToList(); + } + + private static void AssertExternalReferences( + IReadOnlyCollection expectedReferences, + Maybe expectedMasterReference, + IReadOnlyCollection dtoExternalReferences) + { + if (expectedMasterReference.HasValue) + { + var actualMaster = Assert.Single(dtoExternalReferences, reference => reference.MasterReference); + AssertExternalReference(expectedMasterReference.Value, actualMaster); + } + else + { + //None can be master then + Assert.All(dtoExternalReferences, reference => Assert.False(reference.MasterReference)); + } + + Assert.Equal(expectedReferences.Count, dtoExternalReferences.Count); + + foreach (var comparison in expectedReferences.OrderBy(x => x.Title) + .Zip(dtoExternalReferences.OrderBy(x => x.Title), (expected, actual) => new { expected, actual }) + .ToList()) + { + AssertExternalReference(comparison.expected, comparison.actual); + } + } + + private static void AssertExternalReference(ExternalReference reference, ExternalReferenceDataResponseDTO actualMaster) + { + Assert.Equal(reference.Uuid, actualMaster.Uuid); + Assert.Equal(reference.Title, actualMaster.Title); + Assert.Equal(reference.URL, actualMaster.Url); + Assert.Equal(reference.ExternalReferenceId, actualMaster.DocumentId); + } + } +} diff --git a/Tests.Unit.Presentation.Web/Models/V2/ItContractResponseMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/ItContractResponseMapperTest.cs index e800d20e58..690d89c1fa 100644 --- a/Tests.Unit.Presentation.Web/Models/V2/ItContractResponseMapperTest.cs +++ b/Tests.Unit.Presentation.Web/Models/V2/ItContractResponseMapperTest.cs @@ -8,13 +8,15 @@ using Core.DomainModel.ItSystemUsage; using Core.DomainModel.Organization; using Core.DomainModel.Shared; +using Moq; using Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping; +using Presentation.Web.Controllers.API.V2.External.Generic; using Presentation.Web.Controllers.API.V2.External.ItContracts.Mapping; using Presentation.Web.Models.API.V2.Response.Contract; using Presentation.Web.Models.API.V2.Response.Generic.Identity; using Presentation.Web.Models.API.V2.Response.Organization; +using Presentation.Web.Models.API.V2.Response.Shared; using Presentation.Web.Models.API.V2.Types.Contract; -using Presentation.Web.Models.API.V2.Types.Shared; using Tests.Toolkit.Patterns; using Xunit; @@ -23,10 +25,12 @@ namespace Tests.Unit.Presentation.Web.Models.V2 public class ItContractResponseMapperTest : WithAutoFixture { private readonly ItContractResponseMapper _sut; + private readonly Mock _externalReferenceResponseMapperMock; public ItContractResponseMapperTest() { - _sut = new ItContractResponseMapper(); + _externalReferenceResponseMapperMock = new Mock(); + _sut = new ItContractResponseMapper(_externalReferenceResponseMapperMock.Object); } [Fact] @@ -444,13 +448,19 @@ public void MapContractDTO_Maps_ExternalReferences_Properties_Section() AssignBasicProperties(contract); AssignExternalReferences(contract); + var mappedReferences = Many(); + _externalReferenceResponseMapperMock + .Setup(x => x.MapExternalReferences(contract.ExternalReferences, contract.Reference)) + .Returns(mappedReferences); + //Act var dto = _sut.MapContractDTO(contract); //Assert - AssertExternalReferences(contract, dto.ExternalReferences.ToList()); + Assert.Equivalent(mappedReferences, dto.ExternalReferences); + _externalReferenceResponseMapperMock.Verify(x => x.MapExternalReferences(contract.ExternalReferences, contract.Reference), Times.Once); } - + #region Creaters private DataProcessingRegistration CreateDPR() @@ -511,7 +521,7 @@ private ItContract CreateContract() private EconomyStream CreateEconomyStream() { - return new () + return new() { AccountingEntry = A(), Acquisition = A(), @@ -581,7 +591,7 @@ private void AssignAgreementPeriodProperties(ItContract contract, bool withOptio contract.DurationYears = A(); contract.DurationMonths = A() % 11; contract.DurationOngoing = A(); - contract.OptionExtend = withOptionalCrossReferences + contract.OptionExtend = withOptionalCrossReferences ? new OptionExtendType() { Uuid = A(), Name = A() } : null; contract.ExtendMultiplier = A(); @@ -609,7 +619,7 @@ private void AssignDPRs(ItContract contract, DataProcessingRegistration[] dprs) private void AssignSystemUsages(ItContract contract, ItSystemUsage[] usages) { - contract.AssociatedSystemUsages = usages.Select(usage => new ItContractItSystemUsage() {ItContract = contract, ItSystemUsage = usage} ).ToList(); + contract.AssociatedSystemUsages = usages.Select(usage => new ItContractItSystemUsage() { ItContract = contract, ItSystemUsage = usage }).ToList(); } private void AssignResponsiblePropertiesSection(ItContract contract, bool withOptionalCrossReferences) @@ -627,7 +637,7 @@ private void AssignSupplierPropertiesSection(ItContract contract, bool withOptio contract.SupplierContractSigner = A(); contract.HasSupplierSigned = A(); contract.SupplierSignedDate = A(); - contract.Supplier = withOptionalCrossReferences + contract.Supplier = withOptionalCrossReferences ? CreateOrganization() : null; } @@ -640,14 +650,14 @@ private void AssignProcurementPropertiesSection(ItContract contract, bool withOp contract.PurchaseForm = withOptionalCrossReferences ? new PurchaseFormType() { Uuid = A(), Name = A() } : null; - contract.ProcurementPlanQuarter = withOptionalCrossReferences - ? A() % 2 + contract.ProcurementPlanQuarter = withOptionalCrossReferences + ? A() % 2 : null; - contract.ProcurementPlanYear = withOptionalCrossReferences - ? A() + contract.ProcurementPlanYear = withOptionalCrossReferences + ? A() : null; - contract.ProcurementInitiated = withOptionalCrossReferences - ? A() + contract.ProcurementInitiated = withOptionalCrossReferences + ? A() : null; } @@ -672,7 +682,7 @@ private void AssignGeneralPropertiesSection(ItContract contract, bool withOption } : null; contract.Criticality = withOptionalCrossReferences - ? new CriticalityType {Uuid = A(), Name = A()} + ? new CriticalityType { Uuid = A(), Name = A() } : null; contract.Active = A(); contract.Concluded = A(); @@ -692,27 +702,6 @@ private void AssignBasicProperties(ItContract contract) #region Asserters - private static void AssertExternalReferences(ItContract contract, List dtoExternalReferences) - { - var actualMaster = Assert.Single(dtoExternalReferences, reference => reference.MasterReference); - AssertExternalReference(contract.Reference, actualMaster); - Assert.Equal(contract.ExternalReferences.Count, dtoExternalReferences.Count); - - foreach (var comparison in contract.ExternalReferences.OrderBy(x => x.Title) - .Zip(dtoExternalReferences.OrderBy(x => x.Title), (expected, actual) => new { expected, actual }) - .ToList()) - { - AssertExternalReference(comparison.expected, comparison.actual); - } - } - - private static void AssertExternalReference(ExternalReference reference, ExternalReferenceDataDTO actualMaster) - { - Assert.Equal(reference.Title, actualMaster.Title); - Assert.Equal(reference.URL, actualMaster.Url); - Assert.Equal(reference.ExternalReferenceId, actualMaster.DocumentId); - } - private void AssertPayments(ICollection expecteds, List actuals) { Assert.Equal(expecteds.Count, actuals.Count); diff --git a/Tests.Unit.Presentation.Web/Models/V2/ItContractWriteModelMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/ItContractWriteModelMapperTest.cs index 3b0e14ad72..5786f25599 100644 --- a/Tests.Unit.Presentation.Web/Models/V2/ItContractWriteModelMapperTest.cs +++ b/Tests.Unit.Presentation.Web/Models/V2/ItContractWriteModelMapperTest.cs @@ -16,6 +16,7 @@ using Tests.Toolkit.Extensions; using Xunit; using Presentation.Web.Controllers.API.V2.External.DataProcessingRegistrations.Mapping; +using Presentation.Web.Models.API.V2.Request.Shared; namespace Tests.Unit.Presentation.Web.Models.V2 { @@ -989,7 +990,7 @@ public void Can_Map_Procurement_From_Patch(bool hasValues) public void Can_Map_ExternalReferences_FromPUT() { //Arrange - var references = Many().OrderBy(x => x.Url).ToList(); + var references = Many().OrderBy(x => x.Url).ToList(); //Act var mappedReferences = _sut.FromPUT(new UpdateContractRequestDTO { ExternalReferences = references }).ExternalReferences.Value.OrderBy(x => x.Url).ToList(); @@ -1002,7 +1003,7 @@ public void Can_Map_ExternalReferences_FromPUT() public void Can_Map_ExternalReferences_FromPATCH() { //Arrange - var references = Many().OrderBy(x => x.Url).ToList(); + var references = Many().OrderBy(x => x.Url).ToList(); //Act var mappedReferences = _sut.FromPATCH(new UpdateContractRequestDTO { ExternalReferences = references }).ExternalReferences.Value.OrderBy(x => x.Url).ToList(); @@ -1015,7 +1016,7 @@ public void Can_Map_ExternalReferences_FromPATCH() public void Can_Map_ExternalReferences_FromPOST() { //Arrange - var references = Many().OrderBy(x => x.Url).ToList(); + var references = Many().OrderBy(x => x.Url).ToList(); //Act var mappedReferences = _sut.FromPOST(new CreateNewContractRequestDTO { ExternalReferences = references }).ExternalReferences.Value.OrderBy(x => x.Url).ToList(); @@ -1402,7 +1403,7 @@ private static void AssertUuids(IEnumerable expected, IEnumerable ac } } - private static void AssertExternalReferences(List mappedReferences, List references) + private static void AssertExternalReferences(IReadOnlyList mappedReferences, IReadOnlyList references) where T : ExternalReferenceDataWriteRequestDTO { Assert.Equal(mappedReferences.Count, mappedReferences.Count); for (var i = 0; i < mappedReferences.Count; i++) @@ -1413,6 +1414,11 @@ private static void AssertExternalReferences(List _attachedOptionsRepositoryMock; private readonly Mock _sensitivePersonalDataTypeRepositoryMock; private readonly Mock> _registerTypeRepositoryMock; + private readonly Mock _externalReferenceResponseMapperMock; public ItSystemUsageResponseMapperTest() { _attachedOptionsRepositoryMock = new Mock(); _sensitivePersonalDataTypeRepositoryMock = new Mock(); _registerTypeRepositoryMock = new Mock>(); + _externalReferenceResponseMapperMock = new Mock(); _sut = new ItSystemUsageResponseMapper( _attachedOptionsRepositoryMock.Object, _sensitivePersonalDataTypeRepositoryMock.Object, - _registerTypeRepositoryMock.Object + _registerTypeRepositoryMock.Object, + _externalReferenceResponseMapperMock.Object ); } @@ -174,11 +179,17 @@ public void MapSystemUsageDTO_Maps_ExternalReferences_Properties_Section() AssignBasicProperties(itSystemUsage); AssignExternalReferences(itSystemUsage); + var mappedReferences = Many(); + _externalReferenceResponseMapperMock + .Setup(x => x.MapExternalReferences(itSystemUsage.ExternalReferences, itSystemUsage.Reference)) + .Returns(mappedReferences); + //Act var dto = _sut.MapSystemUsageDTO(itSystemUsage); //Assert - AssertExternalReferences(itSystemUsage, dto.ExternalReferences.ToList()); + _externalReferenceResponseMapperMock.Verify(x => x.MapExternalReferences(itSystemUsage.ExternalReferences, itSystemUsage.Reference), Times.Once); + Assert.Equivalent(mappedReferences, dto.ExternalReferences); } [Fact] @@ -474,27 +485,6 @@ private SystemRelation CreateSystemRelation(ItSystemUsage itSystemUsage, bool wi }; } - private static void AssertExternalReferences(ItSystemUsage itSystemUsage, List dtoExternalReferences) - { - var actualMaster = Assert.Single(dtoExternalReferences, reference => reference.MasterReference); - AssertExternalReference(itSystemUsage.Reference, actualMaster); - Assert.Equal(itSystemUsage.ExternalReferences.Count, dtoExternalReferences.Count); - - foreach (var comparison in itSystemUsage.ExternalReferences.OrderBy(x => x.Title) - .Zip(dtoExternalReferences.OrderBy(x => x.Title), (expected, actual) => new { expected, actual }) - .ToList()) - { - AssertExternalReference(comparison.expected, comparison.actual); - } - } - - private static void AssertExternalReference(ExternalReference reference, ExternalReferenceDataDTO actualMaster) - { - Assert.Equal(reference.Title, actualMaster.Title); - Assert.Equal(reference.URL, actualMaster.Url); - Assert.Equal(reference.ExternalReferenceId, actualMaster.DocumentId); - } - private void AssignExternalReferences(ItSystemUsage itSystemUsage) { itSystemUsage.ExternalReferences = Many().Select((title, i) => new ExternalReference diff --git a/Tests.Unit.Presentation.Web/Models/V2/ItSystemUsageWriteModelMapperTest.cs b/Tests.Unit.Presentation.Web/Models/V2/ItSystemUsageWriteModelMapperTest.cs index 060d40cc43..877c7211c9 100644 --- a/Tests.Unit.Presentation.Web/Models/V2/ItSystemUsageWriteModelMapperTest.cs +++ b/Tests.Unit.Presentation.Web/Models/V2/ItSystemUsageWriteModelMapperTest.cs @@ -10,6 +10,7 @@ using Presentation.Web.Controllers.API.V2.External.ItSystemUsages.Mapping; using Presentation.Web.Infrastructure.Model.Request; using Presentation.Web.Models.API.V2.Request.Generic.Roles; +using Presentation.Web.Models.API.V2.Request.Shared; using Presentation.Web.Models.API.V2.Request.SystemUsage; using Presentation.Web.Models.API.V2.Types.Shared; using Tests.Toolkit.Extensions; @@ -62,7 +63,7 @@ public void Can_Map_Roles() public void Can_Map_ExternalReferences() { //Arrange - var references = Many().OrderBy(x => x.Url).ToList(); + var references = Many().OrderBy(x => x.Url).ToList(); //Act var mappedReferences = _sut.FromPATCH(new UpdateItSystemUsageRequestDTO() { ExternalReferences = references }).ExternalReferences.Value.OrderBy(x => x.Url).ToList(); @@ -73,6 +74,7 @@ public void Can_Map_ExternalReferences() { var expected = references[i]; var actual = mappedReferences[i]; + Assert.Equal(expected.Uuid, actual.Uuid); Assert.Equal(expected.Url, actual.Url); Assert.Equal(expected.Title, actual.Title); Assert.Equal(expected.DocumentId, actual.DocumentId); @@ -496,7 +498,7 @@ public void FromPOST_Maps_All_Defined_Sections_And_Undefined_Are_Mapped_As_Uncha Roles = rolesNull ? null : Many(), LocalKleDeviations = kleNull ? null : A(), OrganizationUsage = orgUsageNull ? null : A(), - ExternalReferences = referencesNull ? null : Many(), + ExternalReferences = referencesNull ? null : Many(), Archiving = archivingNull ? null : A(), GDPR = gdprNull ? null : A(), }; @@ -532,7 +534,7 @@ public void FromPUT_Maps_All_Sections_Including_Null_Sections( Roles = rolesNull ? null : Many(), LocalKleDeviations = kleNull ? null : A(), OrganizationUsage = orgUsageNull ? null : A(), - ExternalReferences = referencesNull ? null : Many(), + ExternalReferences = referencesNull ? null : Many(), Archiving = archivingNull ? null : A(), GDPR = gdprNull ? null : A(), }; diff --git a/Tests.Unit.Presentation.Web/Services/ReferenceServiceTest.cs b/Tests.Unit.Presentation.Web/Services/ReferenceServiceTest.cs index 7e02fb85d9..e52d9f9062 100644 --- a/Tests.Unit.Presentation.Web/Services/ReferenceServiceTest.cs +++ b/Tests.Unit.Presentation.Web/Services/ReferenceServiceTest.cs @@ -432,61 +432,104 @@ public void AddReference_Does_Not_Save_Root_If_Add_Fails() } [Fact] - public void Can_BatchUpdateExternalReferences_With_No_Previous_References() + public void UpdateExternalReferences_Creates_ExternalReferences() { //Arrange var rootType = A(); var rootId = A(); var root = new Mock(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var externalReferences = Many().ToList(); - var expectedMaster = externalReferences.RandomItem(); + var externalReferencePropertiesList = CreateExternalReferenceProperties(false).ToList(); + var expectedMaster = externalReferencePropertiesList.RandomItem(); expectedMaster.MasterReference = true; + var entity = root.Object.FromNullable(); var masterReference = CreateExternalReference(expectedMaster); ExpectMasterReference(root, masterReference); - ExpectRootDeleteAndAdd(root, externalReferences, new List()); + ExpectRootDeleteAndAdd(root, externalReferencePropertiesList, new List()); ExpectAllowModifyReturns(root.Object, true); ExpectTransactionToBeSet(); - ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); + ExpectGetRootEntityReturns(rootId, rootType, entity); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferencePropertiesList); //Assert Assert.True(result.IsNone); _dbTransaction.Verify(x => x.Commit()); + foreach (var externalReference in externalReferencePropertiesList) + { + root.Verify(x => x.AddExternalReference + ( + It.Is + ( + reference => reference.Title == externalReference.Title + && reference.ExternalReferenceId == externalReference.DocumentId + && reference.URL == externalReference.Url) + ), + Times.Once + ); + } } [Fact] - public void Can_BatchUpdateExternalReferences_With_Previous_References() + public void UpdateExternalReferences_Updates_Existing_ExternalReferences() { //Arrange var rootType = A(); var rootId = A(); + var externalReference = new ExternalReference + { + Uuid = Guid.NewGuid() + }; + var externalReferenceList = new List + { + externalReference + }; var root = new Mock(); + root.SetupGet(x => x.ExternalReferences).Returns(externalReferenceList); + var rootEntity = root.Object; + Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var externalReferences = Many().ToList(); - var expectedMaster = externalReferences.RandomItem(); - expectedMaster.MasterReference = true; + var updatedReference = new UpdatedExternalReferenceProperties() + { + Uuid = externalReference.Uuid, + DocumentId = A(), + Title = A(), + Url = A(), + MasterReference = true + }; + var externalReferencePropertiesList = new List() + { + updatedReference + }; - var masterReference = CreateExternalReference(expectedMaster); - ExpectMasterReference(root, masterReference); - ExpectRootDeleteAndAdd(root, externalReferences, externalReferences.Select(CreateExternalReference).ToList()); - ExpectAllowModifyReturns(root.Object, true); + ExpectRootDeleteAndAdd(root, externalReferencePropertiesList, externalReferenceList); + ExpectMasterReference(root, externalReference); + ExpectAllowModifyReturns(rootEntity, true); ExpectTransactionToBeSet(); - ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); + ExpectGetRootEntityReturns(rootId, rootType, rootEntity.FromNullable()); + ExpectGetByUuidForExternalReferences(externalReferenceList); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferencePropertiesList); //Assert Assert.True(result.IsNone); _dbTransaction.Verify(x => x.Commit()); + Assert.Equal(updatedReference.DocumentId, externalReference.ExternalReferenceId); + Assert.Equal(updatedReference.Title, externalReference.Title); + Assert.Equal(updatedReference.Url, externalReference.URL); + root.Verify(x => x.SetMasterReference(externalReference)); + _referenceRepository.Verify + ( + repository => repository.SaveRootEntity(It.Is(x => x.Id == rootEntity.Id)), + Times.Once + ); } [Fact] - public void Cannot_BatchUpdateExternalReferences_With_Multiple_Masters() + public void Cannot_UpdateExternalReferences_With_Multiple_Masters() { //Arrange var rootType = A(); @@ -503,7 +546,7 @@ public void Cannot_BatchUpdateExternalReferences_With_Multiple_Masters() ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferences); //Assert Assert.True(result.HasValue); @@ -511,7 +554,7 @@ public void Cannot_BatchUpdateExternalReferences_With_Multiple_Masters() } [Fact] - public void Cannot_BatchUpdateExternalReferences_With_No_Masters() + public void Cannot_UpdateExternalReferences_With_No_Masters() { //Arrange var rootType = A(); @@ -526,7 +569,7 @@ public void Cannot_BatchUpdateExternalReferences_With_No_Masters() ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferences); //Assert Assert.True(result.HasValue); @@ -534,14 +577,14 @@ public void Cannot_BatchUpdateExternalReferences_With_No_Masters() } [Fact] - public void Cannot_BatchUpdateExternalReferences_If_Set_Master_Fails() + public void Cannot_UpdateExternalReferences_If_Set_Master_Fails() { //Arrange var rootType = A(); var rootId = A(); var root = new Mock(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var externalReferences = Many().ToList(); + var externalReferences = CreateExternalReferenceProperties(false).ToList(); var expectedMaster = externalReferences.RandomItem(); expectedMaster.MasterReference = true; @@ -554,7 +597,7 @@ public void Cannot_BatchUpdateExternalReferences_If_Set_Master_Fails() ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferences); //Assert Assert.True(result.HasValue); @@ -563,22 +606,24 @@ public void Cannot_BatchUpdateExternalReferences_If_Set_Master_Fails() } [Fact] - public void Cannot_BatchUpdateExternalReferences_If_Not_Allowed_To_Modify() + public void Cannot_UpdateExternalReferences_If_Not_Allowed_To_Modify() { //Arrange var rootType = A(); var rootId = A(); + var externalReferenceList = CreateExternalReferences(); + var root = new Mock(); - var externalReferences = Many().ToList(); + var externalReferencePropertiesList = Many().ToList(); - ExpectRootDeleteAndAdd(root, externalReferences, new List()); + ExpectRootDeleteAndAdd(root, externalReferencePropertiesList, externalReferenceList); ExpectTransactionToBeSet(); ExpectGetRootEntityReturns(rootId, rootType, root.Object.FromNullable()); ExpectAllowModifyReturns(root.Object, false); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferencePropertiesList); //Assert Assert.True(result.HasValue); @@ -587,7 +632,7 @@ public void Cannot_BatchUpdateExternalReferences_If_Not_Allowed_To_Modify() } [Fact] - public void Cannot_BatchUpdateExternalReferences_If_Not_Found() + public void Cannot_UpdateExternalReferences_If_Not_Found() { //Arrange var rootType = A(); @@ -602,7 +647,7 @@ public void Cannot_BatchUpdateExternalReferences_If_Not_Found() ExpectGetRootEntityReturns(rootId, rootType, Maybe.None); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferences); //Assert Assert.True(result.HasValue); @@ -610,14 +655,14 @@ public void Cannot_BatchUpdateExternalReferences_If_Not_Found() } [Fact] - public void Cannot_BatchUpdateExternalReferences_If_Add_Fails() + public void Cannot_UpdateExternalReferences_If_Add_Fails() { //Arrange var rootType = A(); var rootId = A(); var root = new Mock(); Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs - var externalReferences = Many().ToList(); + var externalReferences = CreateExternalReferenceProperties(false).ToList(); var expectedMaster = externalReferences.RandomItem(); expectedMaster.MasterReference = true; @@ -631,7 +676,7 @@ public void Cannot_BatchUpdateExternalReferences_If_Add_Fails() root.Setup(x => x.AddExternalReference(It.IsAny())).Returns(operationError); //Act - var result = _sut.BatchUpdateExternalReferences(rootType, rootId, externalReferences); + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferences); //Assert Assert.True(result.HasValue); @@ -639,7 +684,35 @@ public void Cannot_BatchUpdateExternalReferences_If_Add_Fails() Assert.Contains("Failed to add reference with data:", result.Value.Message.GetValueOrEmptyString()); } - private void ExpectMasterReference(Mock root, ExternalReference masterReference) + [Fact] + public void Cannot_UpdateExternalReferences_If_Reference_With_Uuid_Was_Not_Found_Returns_BadInput() + { + //Arrange + var rootType = A(); + var rootId = A(); + var externalReferenceList = CreateExternalReferences().ToList(); + var root = new Mock(); + root.SetupGet(x => x.ExternalReferences).Returns(externalReferenceList); + var rootEntity = root.Object; + + Configure(f => f.Inject(false)); //Make sure no master is added when faking the inputs + var externalReferencePropertiesList = CreateExternalReferenceProperties(true).ToList(); + + ExpectRootDeleteAndAdd(root, externalReferencePropertiesList, externalReferenceList); + ExpectAllowModifyReturns(rootEntity, true); + ExpectTransactionToBeSet(); + ExpectGetRootEntityReturns(rootId, rootType, rootEntity.FromNullable()); + ExpectGetByUuidForExternalReferences(externalReferenceList); + + //Act + var result = _sut.UpdateExternalReferences(rootType, rootId, externalReferencePropertiesList); + + //Assert + Assert.True(result.HasValue); + Assert.Equal(OperationFailure.BadInput, result.Value.FailureType); + } + + private static void ExpectMasterReference(Mock root, ExternalReference masterReference) { root.Setup(x => x.SetMasterReference(It.Is(er => er.Title == masterReference.Title && @@ -680,6 +753,19 @@ private static void ExpectAddReferenceReturns(Mock references) + { + foreach (var externalReference in references) + { + ExpectGetByUuid(externalReference.Uuid, externalReference); + } + } + + private void ExpectGetByUuid(Guid uuid, Maybe value) + { + _referenceRepository.Setup(x => x.GetByUuid(uuid)).Returns(value); + } + private void ExpectGetRootEntityReturns(int id, ReferenceRootType rootType, Maybe value) { _referenceRepository.Setup(x => x.GetRootEntity(id, rootType)).Returns(value); @@ -743,5 +829,26 @@ private static T AddExternalReference(T system, ExternalReference reference) system.ExternalReferences.Add(reference); return system; } + + private IEnumerable CreateExternalReferenceProperties(bool hasUuid) + { + return Many().Select(x => + { + x.Uuid = hasUuid ? x.Uuid : null; + return x; + }); + } + + private IEnumerable CreateExternalReferences() + { + return new List + { + new() + { + Uuid = Guid.NewGuid() + } + }; + } + } } \ No newline at end of file diff --git a/Tests.Unit.Presentation.Web/Tests.Unit.Presentation.Web.csproj b/Tests.Unit.Presentation.Web/Tests.Unit.Presentation.Web.csproj index a30e973f1e..edc5901e41 100644 --- a/Tests.Unit.Presentation.Web/Tests.Unit.Presentation.Web.csproj +++ b/Tests.Unit.Presentation.Web/Tests.Unit.Presentation.Web.csproj @@ -287,6 +287,7 @@ +