From cced24eb435f43a38c0c35ef4c26f3b7982185c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Krch?= Date: Wed, 3 Jul 2024 02:31:35 +0200 Subject: [PATCH 1/3] MT-17 migration of documents with non-unique DocumentGuid --- .../Auxiliary/NodeXmlAdapter.cs | 2 + .../MigrateCategoriesCommandHandler.cs | 1 + .../Handlers/MigratePagesCommandHandler.cs | 5 +- .../KsCoreDiExtensions.cs | 1 + .../Mappers/ContentItemMapper.cs | 18 ++++- .../PageTemplateConfigurationMapper.cs | 1 + .../Services/Model/PageSelectorItem.cs | 1 + .../Services/PrimaryKeyLocatorService.cs | 3 + .../Services/SpoiledGuidContext.cs | 73 +++++++++++++++++++ .../Helpers/GuidHelper.cs | 2 + .../Auxiliary/NodeXmlAdapter.cs | 2 + .../MigrateContactManagementCommandHandler.cs | 1 + .../Services/CmsClass/PageSelectorItem.cs | 1 + .../Services/PrimaryKeyLocatorService.cs | 2 + .../Auxiliary/NodeXmlAdapter.cs | 2 + .../MigrateContactManagementCommandHandler.cs | 1 + .../Services/CmsClass/PageSelectorItem.cs | 1 + .../Services/PrimaryKeyLocatorService.cs | 2 + .../Auxiliary/NodeXmlAdapter.cs | 2 + .../MigrateContactManagementCommandHandler.cs | 1 + .../PageTemplateConfigurationMapper.cs | 1 + .../Services/CmsClass/PageSelectorItem.cs | 1 + .../Services/PrimaryKeyLocatorService.cs | 3 + 23 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs diff --git a/KVA/Migration.Toolkit.Source/Auxiliary/NodeXmlAdapter.cs b/KVA/Migration.Toolkit.Source/Auxiliary/NodeXmlAdapter.cs index e69ea8a2..c27e544a 100644 --- a/KVA/Migration.Toolkit.Source/Auxiliary/NodeXmlAdapter.cs +++ b/KVA/Migration.Toolkit.Source/Auxiliary/NodeXmlAdapter.cs @@ -41,6 +41,7 @@ public bool HasValueSet(string columnName) public int? NodeParentID => _xClass.Element(NodeXmlColumns.NODE_PARENT_ID)?.Value(); public int? NodeLevel => _xClass.Element(NodeXmlColumns.NODE_LEVEL)?.Value(); public int? NodeSiteID => _xClass.Element(NodeXmlColumns.NODE_SITE_ID)?.Value(); + [Obsolete("NodeGUID is not unique, use other means of node identification", true)] public Guid? NodeGUID => _xClass.Element(NodeXmlColumns.NODE_GUID)?.Value(); public int? NodeOrder => _xClass.Element(NodeXmlColumns.NODE_ORDER)?.Value(); public int? NodeOwner => _xClass.Element(NodeXmlColumns.NODE_OWNER)?.Value(); @@ -63,6 +64,7 @@ public bool HasValueSet(string columnName) public string? DocumentContent => _xClass.Element(NodeXmlColumns.DOCUMENT_CONTENT)?.Value; public string? DocumentLastVersionNumber => _xClass.Element(NodeXmlColumns.DOCUMENT_LAST_VERSION_NUMBER)?.Value; public bool? DocumentIsArchived => _xClass.Element(NodeXmlColumns.DOCUMENT_IS_ARCHIVED)?.ValueAsBool(); + [Obsolete("DocumentGUID is not unique, use other means of document identification", true)] public Guid? DocumentGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_GUID)?.Value(); public Guid? DocumentWorkflowCycleGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_WORKFLOW_CYCLE_GUID)?.Value(); public bool? DocumentCanBePublished => _xClass.Element(NodeXmlColumns.DOCUMENT_CAN_BE_PUBLISHED)?.ValueAsBool(); diff --git a/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs b/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs index ac2829ec..bf754b34 100644 --- a/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs +++ b/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs @@ -105,6 +105,7 @@ FROM View_CMS_Tree_Joined [TJ] .ImportAsync(umtModel) .AssertSuccess(logger) is {Success:true, Info: {} tag}) { +#error "Migration of taxonimies is broken due to DocumentGUID possible conflicts!!" query = """ SELECT TJ.DocumentGUID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID FROM View_CMS_Tree_Joined [TJ] diff --git a/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs b/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs index 6232df04..db2c2a42 100644 --- a/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs +++ b/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs @@ -90,6 +90,7 @@ public async Task Handle(MigratePagesCommand request, Cancellatio continue; } +#error "NodeGuid may not be unique, use other means of searching for node!" // materialize linked node & write to protocol var linkedNode = modelFacade.SelectWhere("NodeSiteID = @nodeSiteID AND NodeGUID = @nodeGuid", new SqlParameter("nodeSiteID", ksNode.NodeSiteID), @@ -109,6 +110,7 @@ public async Task Handle(MigratePagesCommand request, Cancellatio { var linkedDocument = linkedNodeDocuments[i]; var fixedDocumentGuid = GuidHelper.CreateDocumentGuid($"{linkedDocument.DocumentID}|{ksNode.NodeID}|{ksNode.NodeSiteID}"); //Guid.NewGuid(); +#error "NodeGuid may not be unique, use other means of searching for node!" if (ContentItemInfo.Provider.Get(ksNode.NodeGUID)?.ContentItemID is { } contentItemId) { if (cultureCodeToLanguageGuid.TryGetValue(linkedDocument.DocumentCulture, out var languageGuid) && @@ -177,6 +179,7 @@ public async Task Handle(MigratePagesCommand request, Cancellatio var ksNodeParent = modelFacade.SelectById(ksNode.NodeParentID); var nodeParentGuid = ksNodeParent?.NodeAliasPath == "/" || ksNodeParent == null ? (Guid?)null +#error "NodeGuid may not be unique, use other means of searching for node!" : ksNodeParent?.NodeGUID; var targetClass = DataClassInfoProvider.ProviderObject.Get(ksNodeClass.ClassGUID); @@ -415,7 +418,7 @@ private async Task MigratePageUrlPaths(Guid webPageItemGuid, int webPageItemId, VersionStatus.InitialDraft => false, VersionStatus.Draft => true, VersionStatus.Published => false, - VersionStatus.Archived => false, + VersionStatus.Unpublished => false, _ => throw new ArgumentOutOfRangeException() }, }; diff --git a/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs b/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs index 4a9fea6e..51010916 100644 --- a/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs +++ b/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs @@ -37,6 +37,7 @@ public static IServiceCollection UseKsToolkitCore(this IServiceCollection servic services.AddTransient(); services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs index 6f96e59c..c36441e9 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs @@ -49,7 +49,8 @@ public class ContentItemMapper( KxpMediaFileFacade mediaFileFacade, ModelFacade modelFacade, ReusableSchemaService reusableSchemaService, - DeferredPathService deferredPathService + DeferredPathService deferredPathService, + SpoiledGuidContext spoiledGuidContext ) : UmtMapperBase { private const string CLASS_FIELD_CONTROL_NAME = "controlname"; @@ -64,6 +65,7 @@ protected override IEnumerable MapInternal(CmsTreeMapperSource source throw new InvalidOperationException($"Fatal: node class is missing, class id '{cmsTree.NodeClassID}'"); } +#error "NodeGuid may not be unique, use other means of searching for node!" var contentItemGuid = cmsTree.NodeGUID; yield return new ContentItemModel { @@ -154,9 +156,16 @@ protected override IEnumerable MapInternal(CmsTreeMapperSource source PatchJsonDefinitions(source.CmsTree.NodeSiteID, ref contentItemCommonDataPageTemplateConfiguration, ref contentItemCommonDataPageBuilderWidgets, out var ndp); + var documentGuid = spoiledGuidContext.EnsureDocumentGuid( + cmsDocument.DocumentGUID ?? throw new InvalidOperationException($"DocumentGUID is null"), + cmsTree.NodeSiteID, + cmsTree.NodeID, + cmsDocument.DocumentID + ); + var commonDataModel = new ContentItemCommonDataModel { - ContentItemCommonDataGUID = cmsDocument.DocumentGUID ?? throw new InvalidOperationException($"DocumentGUID is null"), + ContentItemCommonDataGUID = documentGuid, ContentItemCommonDataContentItemGuid = contentItemGuid, ContentItemCommonDataContentLanguageGuid = languageGuid, // DocumentCulture -> language entity needs to be created and its ID used here ContentItemCommonDataVersionStatus = versionStatus, @@ -260,7 +269,7 @@ protected override IEnumerable MapInternal(CmsTreeMapperSource source var languageMetadataInfo = new ContentItemLanguageMetadataModel { - ContentItemLanguageMetadataGUID = cmsDocument.DocumentGUID, + ContentItemLanguageMetadataGUID = documentGuid, ContentItemLanguageMetadataContentItemGuid = contentItemGuid, ContentItemLanguageMetadataDisplayName = cmsDocument.DocumentName, // For the admin UI only ContentItemLanguageMetadataLatestVersionStatus = draftMigrated ? VersionStatus.Draft : versionStatus, // That's the latest status of th item for admin optimization @@ -574,6 +583,7 @@ private void WalkProperties(JObject properties, List? f { if (value?.ToObject>() is { Count: > 0 } items) { +#error "NodeGuid may not be unique, use other means of searching for node!" properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList()); } @@ -785,6 +795,7 @@ ICmsClass nodeClass { if (fieldMigration.Actions?.Contains(TcaDirective.ConvertToPages) ?? false) { +#error "NodeGuid may not be unique, use other means of searching for node!" // relation to other document var convertedRelation = relationshipService.GetNodeRelationships(cmsTree.NodeID) .Select(r => new WebPageRelatedItem { WebPageGuid = r.RightNode.NodeGUID }); @@ -801,6 +812,7 @@ ICmsClass nodeClass { if (value is string pageReferenceJson) { +#error "NodeGuid may not be unique, use other means of searching for node!" target[columnName] = pageReferenceJson.Replace("\"NodeGuid\"", "\"WebPageGuid\""); } } diff --git a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs index d18d9687..8d1d7fd6 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs @@ -192,6 +192,7 @@ private void WalkProperties(JObject properties, List? f { if (value?.ToObject>() is { Count: > 0 } items) { +#error "NodeGuid may not be unique, use other means of searching for node!" properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList()); } diff --git a/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs b/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs index 7711b333..f04c61b4 100644 --- a/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs +++ b/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs @@ -7,5 +7,6 @@ public class PageSelectorItem { /// Node Guid of a page. [JsonProperty("nodeGuid")] +#error "NodeGuid may not be unique, use other means of searching for node!" public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs index 5ecf5904..944ddb35 100644 --- a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs +++ b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs @@ -84,10 +84,12 @@ public IEnumerable SelectAll(Expression().Select(x => new { x.NodeID, x.NodeGUID }).ToList(); var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); var result = source.Join(target, +#error "NodeGuid may not be unique, use other means of searching for node!" a => a.NodeGUID, b => b.ChannelGuid, (a, b) => new SourceTargetKeyMapping(a.NodeID, b.ChannelId) @@ -217,6 +219,7 @@ public bool TryLocate(Expression> keyNameSelector, int source if (sourceType == typeof(ICmsTree)) { +#error "NodeGuid may not be unique, use other means of searching for node!" var sourceGuid = modelFacade.SelectById(sourceId)?.NodeGUID; targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == sourceGuid).Select(x => x.ChannelId).Single(); return true; diff --git a/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs b/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs new file mode 100644 index 00000000..305f335c --- /dev/null +++ b/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs @@ -0,0 +1,73 @@ +namespace Migration.Toolkit.Source.Services; + +using System.Collections.Frozen; +using System.Collections.Immutable; +using Microsoft.Extensions.Logging; +using Migration.Toolkit.Common; +using Migration.Toolkit.Common.Helpers; + +/// +/// in cases where consumer exported site with documents and created new site with that particular export, GUID conflicts will probably happen. This class aims to query and solve those conflicts +/// +/// +/// +public class SpoiledGuidContext(ModelFacade modelFacade, ILogger logger) +{ + internal record SpoiledDocumentGuidInfo(int SiteId, int NodeId); + + private IDictionary>? _spoiledDocumentGuids; + internal IDictionary> SpoiledDocumentGuids => + _spoiledDocumentGuids ??= modelFacade.Select(""" + SELECT DocumentGUID, STRING_AGG(CONCAT(NodeSiteID, '-', NodeID), '|') [SiteID-NodeID] + FROM View_CMS_Tree_Joined TJ + GROUP BY DocumentGUID + HAVING COUNT(DocumentID) > 1 + """, + (reader, version) => new { DocumentGUID = reader.Unbox("DocumentGuid"), Info = reader.Unbox("SiteID-NodeID") }) + .ToFrozenDictionary( + x => x.DocumentGUID, + x => x.Info.Split('|').Select(i => + { + var spl = i.Split('-'); + + return new SpoiledDocumentGuidInfo(int.Parse(spl[0]), int.Parse(spl[1])); + }).ToImmutableList() + ); + + internal record SpoiledNodeGuidInfo(int SiteId); + + private IDictionary>? _spoiledNodeGuids; + internal IDictionary> SpoiledNodeGuids => + _spoiledNodeGuids ??= modelFacade.Select(""" + SELECT NodeGUID, STRING_AGG(NodeSiteID, '|') [SiteID] + FROM View_CMS_Tree_Joined TJ + GROUP BY NodeGUID + HAVING COUNT(NodeGUID) > 1 + """, + (reader, version) => new { DocumentGUID = reader.Unbox("NodeGUID"), Info = reader.Unbox("SiteID") }) + .ToFrozenDictionary( + x => x.DocumentGUID, + x => x.Info.Split('|').Select(i => + { + var spl = i.Split('-'); + + return new SpoiledNodeGuidInfo(int.Parse(spl[0])); + }).ToImmutableList() + ); + + public Guid EnsureDocumentGuid(Guid documentGuid, int siteId, int nodeId, int documentId) + { + if (!SpoiledDocumentGuids.TryGetValue(documentGuid, out _)) return documentGuid; + var newGuid = GuidHelper.CreateDocumentGuid($"{documentId}|{nodeId}|{siteId}"); + logger.LogTrace("Spoiled document guid encountered '{OriginalGuid}', replaced by {NewGuid} {Details}", documentGuid, newGuid, new { siteId, nodeId, documentId }); + return newGuid; + } + + public Guid EnsureNodeGuid(Guid nodeGuid, int siteId, int nodeId) + { + if (!SpoiledNodeGuids.TryGetValue(nodeGuid, out _)) return nodeGuid; + var newGuid = GuidHelper.CreateNodeGuid($"{nodeId}|{siteId}"); + logger.LogTrace("Spoiled node guid encountered '{OriginalGuid}', replaced by {NewGuid} {Details}", nodeGuid, newGuid, new { siteId, nodeId }); + return newGuid; + } +} \ No newline at end of file diff --git a/Migration.Toolkit.Common/Helpers/GuidHelper.cs b/Migration.Toolkit.Common/Helpers/GuidHelper.cs index cfc8f2e3..ad04d584 100644 --- a/Migration.Toolkit.Common/Helpers/GuidHelper.cs +++ b/Migration.Toolkit.Common/Helpers/GuidHelper.cs @@ -5,12 +5,14 @@ public static class GuidHelper public static readonly Guid GuidNsWebPageUrlPathInfo = new Guid("436E024E-BA61-435F-96A7-EC7E34160DCE"); public static readonly Guid GuidNsReusableSchema = new("2702A9E7-D859-49F0-B620-FE4268A92596"); public static readonly Guid GuidNsDocument = new("DCBADED0-54FC-4EEC-BB50-D6E7110E499D"); + public static readonly Guid GuidNsNode = new("8691FEE4-FFFF-4642-8605-1B20B9D05360"); public static readonly Guid GuidNsTaxonomy = new("7F23EF23-F9AE-4DB8-914B-96964E6E78E6"); public static readonly Guid GuidNsDocumentNameField = new("8935FCE5-1BDC-4677-A4CA-6DFD32F65A0F"); public static Guid CreateWebPageUrlPathGuid(string hash) => GuidV5.NewNameBased(GuidNsWebPageUrlPathInfo, hash); public static Guid CreateReusableSchemaGuid(string name) => GuidV5.NewNameBased(GuidNsReusableSchema, name); public static Guid CreateDocumentGuid(string name) => GuidV5.NewNameBased(GuidNsDocument, name); + public static Guid CreateNodeGuid(string name) => GuidV5.NewNameBased(GuidNsNode, name); public static Guid CreateTaxonomyGuid(string name) => GuidV5.NewNameBased(GuidNsTaxonomy, name); public static Guid CreateDocumentNameFieldGuid(string name) => GuidV5.NewNameBased(GuidNsDocumentNameField, name); } \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Auxiliary/NodeXmlAdapter.cs b/Migration.Toolkit.Core.K11/Auxiliary/NodeXmlAdapter.cs index 61d14e22..247e7c33 100644 --- a/Migration.Toolkit.Core.K11/Auxiliary/NodeXmlAdapter.cs +++ b/Migration.Toolkit.Core.K11/Auxiliary/NodeXmlAdapter.cs @@ -41,6 +41,7 @@ public bool HasValueSet(string columnName) public int? NodeParentID => _xClass.Element(NodeXmlColumns.NODE_PARENT_ID)?.Value(); public int? NodeLevel => _xClass.Element(NodeXmlColumns.NODE_LEVEL)?.Value(); public int? NodeSiteID => _xClass.Element(NodeXmlColumns.NODE_SITE_ID)?.Value(); + [Obsolete("NodeGUID is not unique, use other means of node identification", true)] public Guid? NodeGUID => _xClass.Element(NodeXmlColumns.NODE_GUID)?.Value(); public int? NodeOrder => _xClass.Element(NodeXmlColumns.NODE_ORDER)?.Value(); public int? NodeOwner => _xClass.Element(NodeXmlColumns.NODE_OWNER)?.Value(); @@ -63,6 +64,7 @@ public bool HasValueSet(string columnName) public string? DocumentContent => _xClass.Element(NodeXmlColumns.DOCUMENT_CONTENT)?.Value; public string? DocumentLastVersionNumber => _xClass.Element(NodeXmlColumns.DOCUMENT_LAST_VERSION_NUMBER)?.Value; public bool? DocumentIsArchived => _xClass.Element(NodeXmlColumns.DOCUMENT_IS_ARCHIVED)?.ValueAsBool(); + [Obsolete("DocumentGUID is not unique, use other means of node identification", true)] public Guid? DocumentGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_GUID)?.Value(); public Guid? DocumentWorkflowCycleGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_WORKFLOW_CYCLE_GUID)?.Value(); public bool? DocumentCanBePublished => _xClass.Element(NodeXmlColumns.DOCUMENT_CAN_BE_PUBLISHED)?.ValueAsBool(); diff --git a/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs index f1432243..8fe9bb6c 100644 --- a/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs @@ -321,6 +321,7 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin { var result = keyMappingContext.MapSourceKey( s => s.NodeId, +#error "NodeGuid may not be unique, use other means of searching for node!" s => s.NodeGuid, activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); switch(result) diff --git a/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs index c5cf6a4c..91afaff7 100644 --- a/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs @@ -6,6 +6,7 @@ namespace Migration.Toolkit.Core.K11.Services.CmsClass; public class PageSelectorItem { /// Node Guid of a page. +#error "NodeGuid may not be unique, use other means of searching for node!" [JsonProperty("nodeGuid")] public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs index b0e9aaa5..4a408117 100644 --- a/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs @@ -78,6 +78,7 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); @@ -212,6 +213,7 @@ public bool TryLocate(Expression> keyNameSelector, int source if (sourceType == typeof(CmsTree)) { +#error "NodeGuid may not be unique, use other means of searching for node!" // careful - cms.root will have different guid var k11Guid = KX12Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == k11Guid).Select(x => x.ChannelId).Single(); diff --git a/Migration.Toolkit.Core.KX12/Auxiliary/NodeXmlAdapter.cs b/Migration.Toolkit.Core.KX12/Auxiliary/NodeXmlAdapter.cs index 2371291a..fece3926 100644 --- a/Migration.Toolkit.Core.KX12/Auxiliary/NodeXmlAdapter.cs +++ b/Migration.Toolkit.Core.KX12/Auxiliary/NodeXmlAdapter.cs @@ -41,6 +41,7 @@ public bool HasValueSet(string columnName) public int? NodeParentID => _xClass.Element(NodeXmlColumns.NODE_PARENT_ID)?.Value(); public int? NodeLevel => _xClass.Element(NodeXmlColumns.NODE_LEVEL)?.Value(); public int? NodeSiteID => _xClass.Element(NodeXmlColumns.NODE_SITE_ID)?.Value(); + [Obsolete("NodeGUID is not unique, use other means of node identification", true)] public Guid? NodeGUID => _xClass.Element(NodeXmlColumns.NODE_GUID)?.Value(); public int? NodeOrder => _xClass.Element(NodeXmlColumns.NODE_ORDER)?.Value(); public int? NodeOwner => _xClass.Element(NodeXmlColumns.NODE_OWNER)?.Value(); @@ -63,6 +64,7 @@ public bool HasValueSet(string columnName) public string? DocumentContent => _xClass.Element(NodeXmlColumns.DOCUMENT_CONTENT)?.Value; public string? DocumentLastVersionNumber => _xClass.Element(NodeXmlColumns.DOCUMENT_LAST_VERSION_NUMBER)?.Value; public bool? DocumentIsArchived => _xClass.Element(NodeXmlColumns.DOCUMENT_IS_ARCHIVED)?.ValueAsBool(); + [Obsolete("DocumentGUID is not unique, use other means of document identification", true)] public Guid? DocumentGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_GUID)?.Value(); public Guid? DocumentWorkflowCycleGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_WORKFLOW_CYCLE_GUID)?.Value(); public bool? DocumentCanBePublished => _xClass.Element(NodeXmlColumns.DOCUMENT_CAN_BE_PUBLISHED)?.ValueAsBool(); diff --git a/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs index c01b6e43..479cd676 100644 --- a/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs @@ -323,6 +323,7 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin { var result = keyMappingContext.MapSourceKey( s => s.NodeId, +#error "NodeGuid may not be unique, use other means of searching for node!" s => s.NodeGuid, activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); switch(result) diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs index 94f35678..dc00396b 100644 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs @@ -7,5 +7,6 @@ public class PageSelectorItem { /// Node Guid of a page. [JsonProperty("nodeGuid")] +#error "NodeGuid may not be unique, use other means of searching for node!" public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs index a0feae36..32431688 100644 --- a/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs @@ -96,6 +96,7 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); @@ -231,6 +232,7 @@ public bool TryLocate(Expression> keyNameSelector, int source if (sourceType == typeof(KX12M.CmsTree)) { // careful - cms.root will have different guid +#error "NodeGuid may not be unique, use other means of searching for node!" var k12Guid = KX12Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == k12Guid).Select(x => x.ChannelId).Single(); return true; diff --git a/Migration.Toolkit.Core.KX13/Auxiliary/NodeXmlAdapter.cs b/Migration.Toolkit.Core.KX13/Auxiliary/NodeXmlAdapter.cs index a07dec96..471e8c48 100644 --- a/Migration.Toolkit.Core.KX13/Auxiliary/NodeXmlAdapter.cs +++ b/Migration.Toolkit.Core.KX13/Auxiliary/NodeXmlAdapter.cs @@ -41,6 +41,7 @@ public bool HasValueSet(string columnName) public int? NodeParentID => _xClass.Element(NodeXmlColumns.NODE_PARENT_ID)?.Value(); public int? NodeLevel => _xClass.Element(NodeXmlColumns.NODE_LEVEL)?.Value(); public int? NodeSiteID => _xClass.Element(NodeXmlColumns.NODE_SITE_ID)?.Value(); + [Obsolete("NodeGUID is not unique, use other means of node identification", true)] public Guid? NodeGUID => _xClass.Element(NodeXmlColumns.NODE_GUID)?.Value(); public int? NodeOrder => _xClass.Element(NodeXmlColumns.NODE_ORDER)?.Value(); public int? NodeOwner => _xClass.Element(NodeXmlColumns.NODE_OWNER)?.Value(); @@ -63,6 +64,7 @@ public bool HasValueSet(string columnName) public string? DocumentContent => _xClass.Element(NodeXmlColumns.DOCUMENT_CONTENT)?.Value; public string? DocumentLastVersionNumber => _xClass.Element(NodeXmlColumns.DOCUMENT_LAST_VERSION_NUMBER)?.Value; public bool? DocumentIsArchived => _xClass.Element(NodeXmlColumns.DOCUMENT_IS_ARCHIVED)?.ValueAsBool(); + [Obsolete("DocumentGUID is not unique, use other means of document identification", true)] public Guid? DocumentGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_GUID)?.Value(); public Guid? DocumentWorkflowCycleGUID => _xClass.Element(NodeXmlColumns.DOCUMENT_WORKFLOW_CYCLE_GUID)?.Value(); public bool? DocumentCanBePublished => _xClass.Element(NodeXmlColumns.DOCUMENT_CAN_BE_PUBLISHED)?.ValueAsBool(); diff --git a/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs index 0508946d..d3c8217f 100644 --- a/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs @@ -343,6 +343,7 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin { var result = _keyMappingContext.MapSourceKey( s => s.NodeId, +#error "NodeGuid may not be unique, use other means of searching for node!" s => s.NodeGuid, activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); switch(result) diff --git a/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs b/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs index 26ce14f8..1289e3ea 100644 --- a/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs +++ b/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs @@ -192,6 +192,7 @@ private void WalkProperties(JObject properties, List? f { properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { +#error "NodeGuid may not be unique, use other means of searching for node!" WebPageGuid = x.NodeGuid }).ToList()); } diff --git a/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs index 7b6941b0..35d53cf9 100644 --- a/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs @@ -7,5 +7,6 @@ public class PageSelectorItem { /// Node Guid of a page. [JsonProperty("nodeGuid")] +#error "NodeGuid may not be unique, use other means of searching for node!" public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs index 17ed7b66..a351770b 100644 --- a/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs @@ -95,10 +95,12 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); var result = source.Join(target, +#error "NodeGuid may not be unique, use other means of searching for node!" a => a.NodeGuid, b => b.ChannelGuid, (a, b) => new SourceTargetKeyMapping(a.NodeId, b.ChannelId) @@ -228,6 +230,7 @@ public bool TryLocate(Expression> keyNameSelector, int source if (sourceType == typeof(Toolkit.KX13.Models.CmsTree)) { // careful - cms.root will have different guid +#error "NodeGuid may not be unique, use other means of searching for node!" var kx13Guid = kx13Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); targetId = kxpContext.CmsWebPageItems.Where(x => x.WebPageItemGuid == kx13Guid).Select(x => x.WebPageItemId).Single(); return true; From 1c007a4612dba0c3d01c5c7ae900098bc9c4f98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Krch?= Date: Tue, 9 Jul 2024 14:02:26 +0200 Subject: [PATCH 2/3] MT-17 non-unique DocumentGUID & NodeGUID support --- .../MigrateCategoriesCommandHandler.cs | 16 +- .../Handlers/MigratePagesCommandHandler.cs | 11 +- .../KsCoreDiExtensions.cs | 3 +- .../Mappers/ContentItemMapper.cs | 38 +- .../PageTemplateConfigurationMapper.cs | 21 +- .../Services/Model/PageSelectorItem.cs | 1 - .../Services/PrimaryKeyLocatorService.cs | 54 +-- .../Services/SpoiledGuidContext.cs | 38 +- .../ConfigurationValidator.cs | 2 + .../ConfigurationNames.cs | 1 + .../Services/ISpoiledGuidContext.cs | 9 + .../Contexts/SourceInstanceContext.cs | 99 ----- .../MigrateContactManagementCommandHandler.cs | 54 ++- .../K11CoreDiExtensions.cs | 2 - .../CmsClass/EditableAreasConfiguration.cs | 374 +++++++++--------- .../Services/CmsClass/PageSelectorItem.cs | 23 +- .../CmsClass/PageTemplateConfiguration.cs | 58 +-- .../Services/PrimaryKeyLocatorService.cs | 29 -- .../Contexts/SourceInstanceContext.cs | 112 ------ .../MigrateContactManagementCommandHandler.cs | 50 ++- .../KX12CoreDiExtensions.cs | 2 - .../CmsClass/EditableAreasConfiguration.cs | 374 +++++++++--------- .../Services/CmsClass/PageSelectorItem.cs | 24 +- .../CmsClass/PageTemplateConfiguration.cs | 58 +-- .../Services/PrimaryKeyLocatorService.cs | 29 -- .../Contexts/SourceInstanceContext.cs | 110 ------ .../DependencyInjectionExtensions.cs | 2 - .../MigrateContactManagementCommandHandler.cs | 167 ++++---- .../PageTemplateConfigurationMapper.cs | 224 ----------- .../Services/CmsClass/PageSelectorItem.cs | 1 - .../Services/PrimaryKeyLocatorService.cs | 81 +--- 31 files changed, 720 insertions(+), 1347 deletions(-) create mode 100644 Migration.Toolkit.Common/Services/ISpoiledGuidContext.cs delete mode 100644 Migration.Toolkit.Core.K11/Contexts/SourceInstanceContext.cs delete mode 100644 Migration.Toolkit.Core.KX12/Contexts/SourceInstanceContext.cs delete mode 100644 Migration.Toolkit.Core.KX13/Contexts/SourceInstanceContext.cs delete mode 100644 Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs diff --git a/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs b/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs index bf754b34..d78322e3 100644 --- a/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs +++ b/KVA/Migration.Toolkit.Source/Handlers/MigrateCategoriesCommandHandler.cs @@ -23,7 +23,8 @@ public class MigrateCategoriesCommandHandler( ModelFacade modelFacade, IImporter importer, ReusableSchemaService reusableSchemaService, - IUmtMapper tagModelMapper + IUmtMapper tagModelMapper, + SpoiledGuidContext spoiledGuidContext ) : IRequestHandler { public async Task Handle(MigrateCategoriesCommand request, CancellationToken cancellationToken) @@ -105,9 +106,8 @@ FROM View_CMS_Tree_Joined [TJ] .ImportAsync(umtModel) .AssertSuccess(logger) is {Success:true, Info: {} tag}) { -#error "Migration of taxonimies is broken due to DocumentGUID possible conflicts!!" query = """ - SELECT TJ.DocumentGUID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID + SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID FROM View_CMS_Tree_Joined [TJ] JOIN dbo.CMS_DocumentCategory [CDC] on [TJ].DocumentID = [CDC].DocumentID JOIN dbo.CMS_Category CC on CDC.CategoryID = CC.CategoryID AND CC.CategoryUserID IS NULL @@ -116,10 +116,16 @@ FROM View_CMS_Tree_Joined [TJ] var docsWithCategories = modelFacade.Select(query, (reader, _) => new { - DocumentGUID = reader.Unbox("DocumentGUID"), CategoryID = reader.Unbox("CategoryID"), DocumentCheckedOutVersionHistoryID = reader.Unbox("DocumentCheckedOutVersionHistoryID"), - NodeClassID = reader.Unbox("NodeClassID") + NodeClassID = reader.Unbox("NodeClassID"), + NodeSiteID = reader.Unbox("NodeSiteID"), + DocumentGUID = spoiledGuidContext.EnsureDocumentGuid( + reader.Unbox("DocumentGUID"), + reader.Unbox("NodeSiteID"), + reader.Unbox("NodeID"), + reader.Unbox("DocumentID") + ) }, new SqlParameter("categoryId", cmsCategory.CategoryID)); foreach (var dwc in docsWithCategories) diff --git a/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs b/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs index db2c2a42..cd99aa73 100644 --- a/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs +++ b/KVA/Migration.Toolkit.Source/Handlers/MigratePagesCommandHandler.cs @@ -35,7 +35,8 @@ public class MigratePagesCommandHandler( IImporter importer, IUmtMapper mapper, ModelFacade modelFacade, - DeferredPathService deferredPathService + DeferredPathService deferredPathService, + SpoiledGuidContext spoiledGuidContext ) : IRequestHandler { @@ -90,7 +91,6 @@ public async Task Handle(MigratePagesCommand request, Cancellatio continue; } -#error "NodeGuid may not be unique, use other means of searching for node!" // materialize linked node & write to protocol var linkedNode = modelFacade.SelectWhere("NodeSiteID = @nodeSiteID AND NodeGUID = @nodeGuid", new SqlParameter("nodeSiteID", ksNode.NodeSiteID), @@ -110,8 +110,8 @@ public async Task Handle(MigratePagesCommand request, Cancellatio { var linkedDocument = linkedNodeDocuments[i]; var fixedDocumentGuid = GuidHelper.CreateDocumentGuid($"{linkedDocument.DocumentID}|{ksNode.NodeID}|{ksNode.NodeSiteID}"); //Guid.NewGuid(); -#error "NodeGuid may not be unique, use other means of searching for node!" - if (ContentItemInfo.Provider.Get(ksNode.NodeGUID)?.ContentItemID is { } contentItemId) + var patchedNodeGuid = spoiledGuidContext.EnsureNodeGuid(ksNode.NodeGUID, ksNode.NodeSiteID, ksNode.NodeID); + if (ContentItemInfo.Provider.Get(patchedNodeGuid)?.ContentItemID is { } contentItemId) { if (cultureCodeToLanguageGuid.TryGetValue(linkedDocument.DocumentCulture, out var languageGuid) && ContentLanguageInfoProvider.ProviderObject.Get(languageGuid) is { } languageInfo) @@ -179,8 +179,7 @@ public async Task Handle(MigratePagesCommand request, Cancellatio var ksNodeParent = modelFacade.SelectById(ksNode.NodeParentID); var nodeParentGuid = ksNodeParent?.NodeAliasPath == "/" || ksNodeParent == null ? (Guid?)null -#error "NodeGuid may not be unique, use other means of searching for node!" - : ksNodeParent?.NodeGUID; + : spoiledGuidContext.EnsureNodeGuid(ksNodeParent); var targetClass = DataClassInfoProvider.ProviderObject.Get(ksNodeClass.ClassGUID); diff --git a/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs b/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs index 51010916..a1ba04de 100644 --- a/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs +++ b/KVA/Migration.Toolkit.Source/KsCoreDiExtensions.cs @@ -37,7 +37,8 @@ public static IServiceCollection UseKsToolkitCore(this IServiceCollection servic services.AddTransient(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(s => s.GetRequiredService() as SpoiledGuidContext ?? throw new InvalidOperationException()); services.AddTransient(); services.AddTransient(); diff --git a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs index c36441e9..4176d097 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs @@ -8,6 +8,7 @@ namespace Migration.Toolkit.Source.Mappers; using CMS.Websites; using Kentico.Components.Web.Mvc.FormComponents; using Kentico.Xperience.UMT.Model; +using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; using Migration.Toolkit.Common.Abstractions; @@ -65,8 +66,7 @@ protected override IEnumerable MapInternal(CmsTreeMapperSource source throw new InvalidOperationException($"Fatal: node class is missing, class id '{cmsTree.NodeClassID}'"); } -#error "NodeGuid may not be unique, use other means of searching for node!" - var contentItemGuid = cmsTree.NodeGUID; + var contentItemGuid = spoiledGuidContext.EnsureNodeGuid(cmsTree.NodeGUID, cmsTree.NodeSiteID, cmsTree.NodeID); yield return new ContentItemModel { ContentItemGUID = contentItemGuid, @@ -317,7 +317,7 @@ private void PatchJsonDefinitions(int sourceSiteId, ref string? pageTemplateConf sourceInstanceContext.GetPageTemplateFormComponents(sourceSiteId, pageTemplateConfigurationObj?.Identifier); if (pageTemplateConfigurationObj.Properties is { Count: > 0 }) { - WalkProperties(pageTemplateConfigurationObj.Properties, pageTemplateConfigurationFcs, out var ndp); + WalkProperties(sourceSiteId, pageTemplateConfigurationObj.Properties, pageTemplateConfigurationFcs, out var ndp); needsDeferredPatch = ndp || needsDeferredPatch; } @@ -475,6 +475,8 @@ private IEnumerable MigrateDraft(ICmsVersionHistory checkoutVersion, #region "Page template & page widget walkers" + private record WalkerContext(int SiteId); + private void WalkAreas(int siteId, List areas, out bool needsDeferredPatch) { needsDeferredPatch = false; @@ -499,7 +501,7 @@ private void WalkSections(int siteId, List sections, out b // TODO tk: 2022-09-14 find other acronym for FormComponents var sectionFcs = sourceInstanceContext.GetSectionFormComponents(siteId, section.TypeIdentifier); - WalkProperties(section.Properties, sectionFcs, out var ndp1); + WalkProperties(siteId, section.Properties, sectionFcs, out var ndp1); needsDeferredPatch = ndp1 || needsDeferredPatch; if (section.Zones is { Count: > 0 }) @@ -539,14 +541,14 @@ private void WalkWidgets(int siteId, List widgets, out bool if (variant.Properties is { Count: > 0 }) { - WalkProperties(variant.Properties, widgetFcs, out var ndp); + WalkProperties(siteId, variant.Properties, widgetFcs, out var ndp); needsDeferredPatch = ndp || needsDeferredPatch; } } } } - private void WalkProperties(JObject properties, List? formControlModels, out bool needsDeferredPatch) + private void WalkProperties(int siteId, JObject properties, List? formControlModels, out bool needsDeferredPatch) { needsDeferredPatch = false; foreach (var (key, value) in properties) @@ -583,8 +585,10 @@ private void WalkProperties(JObject properties, List? f { if (value?.ToObject>() is { Count: > 0 } items) { -#error "NodeGuid may not be unique, use other means of searching for node!" - properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList()); + properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem + { + WebPageGuid = spoiledGuidContext.EnsureNodeGuid(x.NodeGuid, siteId) + }).ToList()); } logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent); @@ -795,10 +799,11 @@ ICmsClass nodeClass { if (fieldMigration.Actions?.Contains(TcaDirective.ConvertToPages) ?? false) { -#error "NodeGuid may not be unique, use other means of searching for node!" // relation to other document var convertedRelation = relationshipService.GetNodeRelationships(cmsTree.NodeID) - .Select(r => new WebPageRelatedItem { WebPageGuid = r.RightNode.NodeGUID }); + .Select(r => new WebPageRelatedItem { + WebPageGuid = spoiledGuidContext.EnsureNodeGuid(r.RightNode.NodeGUID, r.RightNode.NodeSiteID, r.RightNode.NodeID) + }); target.SetValueAsJson(columnName, convertedRelation); } @@ -812,8 +817,17 @@ ICmsClass nodeClass { if (value is string pageReferenceJson) { -#error "NodeGuid may not be unique, use other means of searching for node!" - target[columnName] = pageReferenceJson.Replace("\"NodeGuid\"", "\"WebPageGuid\""); +#warning [PATCHED] - [VERIFY] "NodeGuid may not be unique, use other means of searching for node!" + var parsed = JObject.Parse(pageReferenceJson); + foreach (var jToken in parsed.DescendantsAndSelf()) + { + if (jToken.Path.EndsWith("NodeGUID", StringComparison.InvariantCultureIgnoreCase)) + { + var patchedGuid = spoiledGuidContext.EnsureNodeGuid(jToken.Value(), cmsTree.NodeSiteID); + jToken.Replace(JToken.FromObject(patchedGuid)); + } + } + target[columnName] = parsed.ToString().Replace("\"NodeGuid\"", "\"WebPageGuid\""); } } } diff --git a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs index 8d1d7fd6..6d010017 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs @@ -15,6 +15,7 @@ namespace Migration.Toolkit.Source.Mappers; using Migration.Toolkit.KXP.Models; using Migration.Toolkit.Source.Contexts; using Migration.Toolkit.Source.Model; +using Migration.Toolkit.Source.Services; using Migration.Toolkit.Source.Services.Model; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -23,7 +24,8 @@ public class PageTemplateConfigurationMapper( ILogger logger, PrimaryKeyMappingContext pkContext, IProtocol protocol, - SourceInstanceContext sourceInstanceContext) + SourceInstanceContext sourceInstanceContext, + SpoiledGuidContext spoiledGuidContext) : EntityMapperBase(logger, pkContext, protocol) { protected override PageTemplateConfigurationInfo? CreateNewInstance(ICmsPageTemplateConfiguration source, MappingHelper mappingHelper, AddFailure addFailure) @@ -64,7 +66,7 @@ protected override PageTemplateConfigurationInfo MapInternal(ICmsPageTemplateCon sourceInstanceContext.GetPageTemplateFormComponents(source.PageTemplateConfigurationSiteID, pageTemplateConfiguration.Identifier); if (pageTemplateConfiguration.Properties is { Count: > 0 }) { - WalkProperties(pageTemplateConfiguration.Properties, pageTemplateConfigurationFcs); + WalkProperties(source.PageTemplateConfigurationSiteID, pageTemplateConfiguration.Properties, pageTemplateConfigurationFcs); } target.PageTemplateConfigurationTemplate = JsonConvert.SerializeObject(pageTemplateConfiguration); @@ -119,7 +121,7 @@ private void WalkSections(int siteId, List 0 }) { @@ -154,13 +156,13 @@ private void WalkWidgets(int siteId, List 0 }) { - WalkProperties(variant.Properties, widgetFcs); + WalkProperties(siteId, variant.Properties, widgetFcs); } } } } - private void WalkProperties(JObject properties, List? formControlModels) + private void WalkProperties(int siteId, JObject properties, List? formControlModels) { foreach (var (key, value) in properties) { @@ -192,8 +194,11 @@ private void WalkProperties(JObject properties, List? f { if (value?.ToObject>() is { Count: > 0 } items) { -#error "NodeGuid may not be unique, use other means of searching for node!" - properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = x.NodeGuid }).ToList()); +#warning [PATCHED] - [CHECK] "NodeGuid may not be unique, use other means of searching for node!" + properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem + { + WebPageGuid = spoiledGuidContext.EnsureNodeGuid(x.NodeGuid, siteId) + }).ToList()); } logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent); @@ -208,8 +213,6 @@ private void WalkProperties(JObject properties, List? f } else if (FieldMappingInstance.BuiltInModel.SupportedInKxpLegacyMode.Contains(editingFcm.FormComponentIdentifier)) { - // TODO tomas.krch 2024-03-27: nothing is supported in legacy mode (no legacy mode) - // OK logger.LogTrace("Editing form component found {FormComponentName} => supported in legacy mode", editingFcm.FormComponentIdentifier); diff --git a/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs b/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs index f04c61b4..7711b333 100644 --- a/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs +++ b/KVA/Migration.Toolkit.Source/Services/Model/PageSelectorItem.cs @@ -7,6 +7,5 @@ public class PageSelectorItem { /// Node Guid of a page. [JsonProperty("nodeGuid")] -#error "NodeGuid may not be unique, use other means of searching for node!" public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs index 944ddb35..745007f3 100644 --- a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs +++ b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs @@ -82,26 +82,26 @@ public IEnumerable SelectAll(Expression().Select(x => new { x.NodeID, x.NodeGUID }).ToList(); - var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); - - var result = source.Join(target, -#error "NodeGuid may not be unique, use other means of searching for node!" - a => a.NodeGUID, - b => b.ChannelGuid, - (a, b) => new SourceTargetKeyMapping(a.NodeID, b.ChannelId) - ); - - foreach (var resultingMapping in result) - { - yield return resultingMapping; - } - - yield break; - } +// if (sourceType == typeof(ICmsTree) && memberName == nameof(ICmsTree.NodeID)) +// { +// #error "NodeGuid may not be unique, use other means of searching for node!" +// var source = modelFacade.SelectAll().Select(x => new { x.NodeID, x.NodeGUID, x.NodeSiteID }).ToList(); +// var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); +// +// var result = source.Join(target, +// #error "NodeGuid may not be unique, use other means of searching for node!" +// a => a.NodeGUID, +// b => b.ChannelGuid, +// (a, b) => new SourceTargetKeyMapping(a.NodeID, b.ChannelId) +// ); +// +// foreach (var resultingMapping in result) +// { +// yield return resultingMapping; +// } +// +// yield break; +// } if (sourceType == typeof(ICmsState) && memberName == nameof(ICmsState.StateID)) { @@ -217,13 +217,13 @@ public bool TryLocate(Expression> keyNameSelector, int source return true; } - if (sourceType == typeof(ICmsTree)) - { -#error "NodeGuid may not be unique, use other means of searching for node!" - var sourceGuid = modelFacade.SelectById(sourceId)?.NodeGUID; - targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == sourceGuid).Select(x => x.ChannelId).Single(); - return true; - } +// if (sourceType == typeof(ICmsTree)) +// { +// #error "NodeGuid may not be unique, use other means of searching for node!" +// var sourceGuid = modelFacade.SelectById(sourceId)?.NodeGUID; +// targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == sourceGuid).Select(x => x.ChannelId).Single(); +// return true; +// } } catch (InvalidOperationException ioex) { diff --git a/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs b/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs index 305f335c..bfa17146 100644 --- a/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs +++ b/KVA/Migration.Toolkit.Source/Services/SpoiledGuidContext.cs @@ -2,16 +2,19 @@ namespace Migration.Toolkit.Source.Services; using System.Collections.Frozen; using System.Collections.Immutable; +using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; using Migration.Toolkit.Common.Helpers; +using Migration.Toolkit.Common.Services; +using Migration.Toolkit.Source.Model; /// /// in cases where consumer exported site with documents and created new site with that particular export, GUID conflicts will probably happen. This class aims to query and solve those conflicts /// /// /// -public class SpoiledGuidContext(ModelFacade modelFacade, ILogger logger) +public class SpoiledGuidContext(ModelFacade modelFacade, ILogger logger) : ISpoiledGuidContext { internal record SpoiledDocumentGuidInfo(int SiteId, int NodeId); @@ -70,4 +73,37 @@ public Guid EnsureNodeGuid(Guid nodeGuid, int siteId, int nodeId) logger.LogTrace("Spoiled node guid encountered '{OriginalGuid}', replaced by {NewGuid} {Details}", nodeGuid, newGuid, new { siteId, nodeId }); return newGuid; } + + public Guid EnsureNodeGuid(Guid nodeGuid, int siteId) + { + if (!SpoiledNodeGuids.TryGetValue(nodeGuid, out _)) return nodeGuid; + + var nodeId = modelFacade.Select(""" + SELECT NodeID FROM CMS_Tree WHERE NodeSiteID = @siteId AND NodeGUID = @nodeGuid + """, (reader, version) => reader.Unbox("NodeID"), + new SqlParameter("siteId", siteId), new SqlParameter("nodeGuid", nodeGuid) + ).Single(); + + var newGuid = GuidHelper.CreateNodeGuid($"{nodeId}|{siteId}"); + logger.LogTrace("Spoiled node guid encountered '{OriginalGuid}', replaced by {NewGuid} {Details}", nodeGuid, newGuid, new { siteId, nodeId }); + return newGuid; + } + + public Guid? GetNodeGuid(int nodeId, int siteId) + { + var nodeGuid = modelFacade.Select(""" + SELECT NodeGUID FROM CMS_Tree WHERE NodeSiteID = @siteId AND NodeId = @nodeId + """, (reader, version) => reader.Unbox("NodeGUID"), + new SqlParameter("siteId", siteId), new SqlParameter("nodeId", nodeId) + ).FirstOrDefault(); + + if(nodeGuid is not {} sNodeGuid) return null; + if (!SpoiledNodeGuids.TryGetValue(sNodeGuid, out _)) return sNodeGuid; + + var newGuid = GuidHelper.CreateNodeGuid($"{nodeId}|{siteId}"); + logger.LogTrace("Spoiled node guid encountered '{OriginalGuid}', replaced by {NewGuid} {Details}", nodeGuid, newGuid, new { siteId, nodeId }); + return newGuid; + } + + public Guid EnsureNodeGuid(ICmsTree node) => EnsureNodeGuid(node.NodeGUID, node.NodeSiteID, node.NodeID); } \ No newline at end of file diff --git a/Migration.Toolkit.CLI/ConfigurationValidator.cs b/Migration.Toolkit.CLI/ConfigurationValidator.cs index 9b98473e..1dac9638 100644 --- a/Migration.Toolkit.CLI/ConfigurationValidator.cs +++ b/Migration.Toolkit.CLI/ConfigurationValidator.cs @@ -89,6 +89,8 @@ public static IEnumerable GetValidationErrors(IConfigurationR var connections = querySourceInstanceApi.GetSection(ConfigurationNames.Connections).GetChildren(); foreach (var connection in connections) { +#warning EXTEND SITENAME + // var siteName = connection.GetValue(ConfigurationNames.SiteName); var siteUri = connection.GetValue(ConfigurationNames.SourceInstanceUri); var secret = connection.GetValue(ConfigurationNames.Secret); diff --git a/Migration.Toolkit.Common/ConfigurationNames.cs b/Migration.Toolkit.Common/ConfigurationNames.cs index 4656d71e..e6a5ae5f 100644 --- a/Migration.Toolkit.Common/ConfigurationNames.cs +++ b/Migration.Toolkit.Common/ConfigurationNames.cs @@ -20,6 +20,7 @@ public class ConfigurationNames public const string ExcludeCodeNames = "ExcludeCodeNames"; public const string ExplicitPrimaryKeyMapping = "ExplicitPrimaryKeyMapping"; + public const string SiteName = "SiteName"; public const string SourceInstanceUri = "SourceInstanceUri"; public const string Secret = "Secret"; diff --git a/Migration.Toolkit.Common/Services/ISpoiledGuidContext.cs b/Migration.Toolkit.Common/Services/ISpoiledGuidContext.cs new file mode 100644 index 00000000..82c6e172 --- /dev/null +++ b/Migration.Toolkit.Common/Services/ISpoiledGuidContext.cs @@ -0,0 +1,9 @@ +namespace Migration.Toolkit.Common.Services; + +public interface ISpoiledGuidContext +{ + Guid EnsureDocumentGuid(Guid documentGuid, int siteId, int nodeId, int documentId); + Guid EnsureNodeGuid(Guid nodeGuid, int siteId, int nodeId); + Guid EnsureNodeGuid(Guid nodeGuid, int siteId); + Guid? GetNodeGuid(int nodeId, int siteId); +} \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Contexts/SourceInstanceContext.cs b/Migration.Toolkit.Core.K11/Contexts/SourceInstanceContext.cs deleted file mode 100644 index 540c3f3e..00000000 --- a/Migration.Toolkit.Core.K11/Contexts/SourceInstanceContext.cs +++ /dev/null @@ -1,99 +0,0 @@ -namespace Migration.Toolkit.Core.K11.Contexts; - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Migration.Toolkit.Common; -using Migration.Toolkit.Common.Services.Ipc; -using Migration.Toolkit.K11; - -public class SourceInstanceContext(IpcService ipcService, IDbContextFactory k11ContextFactory, ILogger logger, - ToolkitConfiguration configuration) -{ - private readonly Dictionary _cachedInfos = new(StringComparer.InvariantCultureIgnoreCase); - - private bool _sourceInfoLoaded; - - public bool HasInfo => _cachedInfos.Count > 0 && _sourceInfoLoaded; - - public bool IsQuerySourceInstanceEnabled() - { - return configuration.OptInFeatures?.QuerySourceInstanceApi?.Enabled ?? false; - } - - public async Task RequestSourceInstanceInfo() - { - if (!_sourceInfoLoaded) - { - var result = await ipcService.GetSourceInstanceDiscoveredInfos(); - foreach (var (key, value) in result) - { - _cachedInfos.Add(key, value); - logger.LogInformation("Source instance info loaded for site '{SiteName}' successfully", key); - } - - _sourceInfoLoaded = true; - } - - return _sourceInfoLoaded; - } - - public List? GetWidgetPropertyFormComponents(string siteName, string widgetIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.WidgetProperties != null && info.WidgetProperties.TryGetValue(widgetIdentifier, out var widgetProperties) - ? widgetProperties - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetPageTemplateFormComponents(string siteName, string pageTemplateIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.PageTemplateProperties != null && info.PageTemplateProperties.TryGetValue(pageTemplateIdentifier, out var pageTemplate) - ? pageTemplate - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetWidgetPropertyFormComponents(int siteId, string widgetIdentifier) - { - var context = k11ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetWidgetPropertyFormComponents(siteName, widgetIdentifier); - } - - public List? GetPageTemplateFormComponents(int siteId, string pageTemplateIdentifier) - { - var context = k11ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetPageTemplateFormComponents(siteName, pageTemplateIdentifier); - } - - public List? GetSectionFormComponents(int siteId, string sectionIdentifier) - { - var context = k11ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.SectionProperties != null && info.SectionProperties.TryGetValue(sectionIdentifier, out var sectionFcs) - ? sectionFcs - : null; - } - else - { - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - } -} \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs index 8fe9bb6c..0a325bfc 100644 --- a/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.K11/Handlers/MigrateContactManagementCommandHandler.cs @@ -3,13 +3,13 @@ namespace Migration.Toolkit.Core.K11.Handlers; using CMS.Activities; using CMS.ContactManagement; using CMS.ContentEngine; -using CMS.Websites.Internal; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; using Migration.Toolkit.Common.Abstractions; using Migration.Toolkit.Common.MigrationProtocol; +using Migration.Toolkit.Common.Services; using Migration.Toolkit.Common.Services.BulkCopy; using Migration.Toolkit.Core.K11.Contexts; using Migration.Toolkit.Core.K11.Helpers; @@ -26,6 +26,7 @@ public class MigrateContactManagementCommandHandler(ILogger, IDisposable { @@ -214,7 +215,7 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum #region "Migrate contact activities" - private CommandResult? MigrateContactActivities() //(List migratedSiteIds) + private CommandResult? MigrateContactActivities() { var requiredColumnsForContactMigration = new Dictionary { @@ -251,10 +252,6 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum return new CommandFailureResult(); } - // _primaryKeyMappingContext.PreloadDependencies(u => u.NodeId); - // no need to preload contact, ID should stay same - // _primaryKeyMappingContext.PreloadDependencies(u => u.ContactId); - var bulkCopyRequest = new BulkCopyRequestExtended("OM_Activity", s => true,// s => s != "ActivityID", reader => true, @@ -319,35 +316,30 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin if (columnName.Equals(nameof(OmActivity.ActivityNodeId), StringComparison.InvariantCultureIgnoreCase) && value is int activityNodeId) { - var result = keyMappingContext.MapSourceKey( - s => s.NodeId, -#error "NodeGuid may not be unique, use other means of searching for node!" - s => s.NodeGuid, - activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); - switch(result) + if (currentRow.TryGetValue(nameof(OmActivity.ActivitySiteId), out var mSiteId) && mSiteId is int siteId) { - case (true, var guid): - return ValueInterceptorResult.ReplaceValue(guid); - case { Success: false }: + if (spoiledGuidContext.GetNodeGuid(siteId, activityNodeId) is { } nodeGuid) { - switch (toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) - { - case AutofixEnum.DiscardData: - logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); - return ValueInterceptorResult.SkipRow; - case AutofixEnum.AttemptFix: - logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); - return ValueInterceptorResult.ReplaceValue(null); - case AutofixEnum.Error: - default: //error - protocol.Append(HandbookReferences - .MissingRequiredDependency(columnName, value) - .WithData(currentRow) - ); - return ValueInterceptorResult.SkipRow; - } + return ValueInterceptorResult.ReplaceValue(nodeGuid); } } + + switch (toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) + { + case AutofixEnum.DiscardData: + logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); + return ValueInterceptorResult.SkipRow; + case AutofixEnum.AttemptFix: + logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); + return ValueInterceptorResult.ReplaceValue(null); + case AutofixEnum.Error: + default: //error + protocol.Append(HandbookReferences + .MissingRequiredDependency(columnName, value) + .WithData(currentRow) + ); + return ValueInterceptorResult.SkipRow; + } } if (columnName.Equals( nameof(KXP.Models.OmActivity.ActivityLanguageId), StringComparison.InvariantCultureIgnoreCase) && value is string cultureCode) diff --git a/Migration.Toolkit.Core.K11/K11CoreDiExtensions.cs b/Migration.Toolkit.Core.K11/K11CoreDiExtensions.cs index 67ab7f3d..6d65a53b 100644 --- a/Migration.Toolkit.Core.K11/K11CoreDiExtensions.cs +++ b/Migration.Toolkit.Core.K11/K11CoreDiExtensions.cs @@ -45,8 +45,6 @@ public static IServiceCollection UseK11ToolkitCore(this IServiceCollection servi services.AddTransient(typeof(IPipelineBehavior<,>), typeof(XbKApiContextBehavior<,>)); services.AddSingleton(s => new TableReflectionService(s.GetRequiredService>())); - services.AddSingleton(); - services.AddTransient(); services.AddScoped(); services.AddSingleton(); diff --git a/Migration.Toolkit.Core.K11/Services/CmsClass/EditableAreasConfiguration.cs b/Migration.Toolkit.Core.K11/Services/CmsClass/EditableAreasConfiguration.cs index 2b537ab9..db7f0f01 100644 --- a/Migration.Toolkit.Core.K11/Services/CmsClass/EditableAreasConfiguration.cs +++ b/Migration.Toolkit.Core.K11/Services/CmsClass/EditableAreasConfiguration.cs @@ -1,187 +1,187 @@ -namespace Migration.Toolkit.Core.K11.Services.CmsClass; - -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -#region Copied from Kentico assembly - -[DataContract(Name = "Configuration", Namespace = "")] -public sealed class EditableAreasConfiguration -{ - /// Editable areas within the page. - [DataMember] - [JsonProperty("editableAreas")] - public List EditableAreas { get; private set; } - - /// - /// Creates an instance of class. - /// - public EditableAreasConfiguration() => this.EditableAreas = new List(); -} - -/// -/// Represents configuration of editable area within the instance. -/// -[DataContract(Name = "EditableArea", Namespace = "")] -public sealed class EditableAreaConfiguration -{ - /// Identifier of the editable area. - [DataMember] - [JsonProperty("identifier")] - public string Identifier { get; set; } - - /// Sections within editable area. - [DataMember] - [JsonProperty("sections")] - public List Sections { get; private set; } - - /// - /// A flag indicating whether the output of the individual widgets within the editable area can be cached. The default value is false. - /// - public bool AllowWidgetOutputCache { get; set; } - - /// - /// An absolute expiration date for the cached output of the individual widgets. - /// - public DateTimeOffset? WidgetOutputCacheExpiresOn { get; set; } - - /// - /// The length of time from the first request to cache the output of the individual widgets. - /// - public TimeSpan? WidgetOutputCacheExpiresAfter { get; set; } - - /// - /// The time after which the cached output of the individual widgets should be evicted if it has not been accessed. - /// - public TimeSpan? WidgetOutputCacheExpiresSliding { get; set; } - - /// - /// Creates an instance of class. - /// - public EditableAreaConfiguration() => this.Sections = new List(); -} - -/// -/// Represents configuration of section within the instance. -/// -[DataContract(Name = "Section", Namespace = "")] -public sealed class SectionConfiguration -{ - /// Identifier of the section. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Type section identifier. - [DataMember] - [JsonProperty("type")] - public string TypeIdentifier { get; set; } - - /// Section properties. - [DataMember] - [JsonProperty("properties")] - // public ISectionProperties Properties { get; set; } - public JObject Properties { get; set; } - - /// Zones within the section. - [DataMember] - [JsonProperty("zones")] - public List Zones { get; private set; } - - /// - /// Creates an instance of class. - /// - public SectionConfiguration() => this.Zones = new List(); -} - -/// -/// Represents the zone within the configuration class. -/// -[DataContract(Name = "Zone", Namespace = "")] -public sealed class ZoneConfiguration -{ - /// Identifier of the widget zone. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Name of the widget zone. - [DataMember] - [JsonProperty("name")] - public string Name { get; set; } - - /// List of widgets within the zone. - [DataMember] - [JsonProperty("widgets")] - public List Widgets { get; private set; } - - /// - /// Creates an instance of class. - /// - public ZoneConfiguration() => this.Widgets = new List(); -} - -/// -/// Represents the configuration of a widget within the list. -/// -[DataContract(Name = "Widget", Namespace = "")] -public sealed class WidgetConfiguration -{ - /// Identifier of the widget instance. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Type widget identifier. - [DataMember] - [JsonProperty("type")] - public string TypeIdentifier { get; set; } - - /// Personalization condition type identifier. - [DataMember] - [JsonProperty("conditionType")] - public string PersonalizationConditionTypeIdentifier { get; set; } - - /// List of widget variants. - [DataMember] - [JsonProperty("variants")] - public List Variants { get; set; } - - /// - /// Creates an instance of class. - /// - public WidgetConfiguration() => this.Variants = new List(); -} - -/// -/// Represents the configuration variant of a widget within the list. -/// -[DataContract(Name = "Variant", Namespace = "")] -public sealed class WidgetVariantConfiguration -{ - /// Identifier of the variant instance. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Widget variant name. - [DataMember] - [JsonProperty("name")] - public string Name { get; set; } - - /// Widget variant properties. - [DataMember] - [JsonProperty("properties")] - // public IWidgetProperties Properties { get; set; } - public JObject Properties { get; set; } - - /// Widget variant personalization condition type. - /// Only personalization condition type parameters are serialized to JSON. - [DataMember] - [JsonProperty("conditionTypeParameters")] - public JObject PersonalizationConditionType { get; set; } -} - -#endregion - +// namespace Migration.Toolkit.Core.K11.Services.CmsClass; +// +// using System.Runtime.Serialization; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// #region Copied from Kentico assembly +// +// [DataContract(Name = "Configuration", Namespace = "")] +// public sealed class EditableAreasConfiguration +// { +// /// Editable areas within the page. +// [DataMember] +// [JsonProperty("editableAreas")] +// public List EditableAreas { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public EditableAreasConfiguration() => this.EditableAreas = new List(); +// } +// +// /// +// /// Represents configuration of editable area within the instance. +// /// +// [DataContract(Name = "EditableArea", Namespace = "")] +// public sealed class EditableAreaConfiguration +// { +// /// Identifier of the editable area. +// [DataMember] +// [JsonProperty("identifier")] +// public string Identifier { get; set; } +// +// /// Sections within editable area. +// [DataMember] +// [JsonProperty("sections")] +// public List Sections { get; private set; } +// +// /// +// /// A flag indicating whether the output of the individual widgets within the editable area can be cached. The default value is false. +// /// +// public bool AllowWidgetOutputCache { get; set; } +// +// /// +// /// An absolute expiration date for the cached output of the individual widgets. +// /// +// public DateTimeOffset? WidgetOutputCacheExpiresOn { get; set; } +// +// /// +// /// The length of time from the first request to cache the output of the individual widgets. +// /// +// public TimeSpan? WidgetOutputCacheExpiresAfter { get; set; } +// +// /// +// /// The time after which the cached output of the individual widgets should be evicted if it has not been accessed. +// /// +// public TimeSpan? WidgetOutputCacheExpiresSliding { get; set; } +// +// /// +// /// Creates an instance of class. +// /// +// public EditableAreaConfiguration() => this.Sections = new List(); +// } +// +// /// +// /// Represents configuration of section within the instance. +// /// +// [DataContract(Name = "Section", Namespace = "")] +// public sealed class SectionConfiguration +// { +// /// Identifier of the section. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Type section identifier. +// [DataMember] +// [JsonProperty("type")] +// public string TypeIdentifier { get; set; } +// +// /// Section properties. +// [DataMember] +// [JsonProperty("properties")] +// // public ISectionProperties Properties { get; set; } +// public JObject Properties { get; set; } +// +// /// Zones within the section. +// [DataMember] +// [JsonProperty("zones")] +// public List Zones { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public SectionConfiguration() => this.Zones = new List(); +// } +// +// /// +// /// Represents the zone within the configuration class. +// /// +// [DataContract(Name = "Zone", Namespace = "")] +// public sealed class ZoneConfiguration +// { +// /// Identifier of the widget zone. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Name of the widget zone. +// [DataMember] +// [JsonProperty("name")] +// public string Name { get; set; } +// +// /// List of widgets within the zone. +// [DataMember] +// [JsonProperty("widgets")] +// public List Widgets { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public ZoneConfiguration() => this.Widgets = new List(); +// } +// +// /// +// /// Represents the configuration of a widget within the list. +// /// +// [DataContract(Name = "Widget", Namespace = "")] +// public sealed class WidgetConfiguration +// { +// /// Identifier of the widget instance. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Type widget identifier. +// [DataMember] +// [JsonProperty("type")] +// public string TypeIdentifier { get; set; } +// +// /// Personalization condition type identifier. +// [DataMember] +// [JsonProperty("conditionType")] +// public string PersonalizationConditionTypeIdentifier { get; set; } +// +// /// List of widget variants. +// [DataMember] +// [JsonProperty("variants")] +// public List Variants { get; set; } +// +// /// +// /// Creates an instance of class. +// /// +// public WidgetConfiguration() => this.Variants = new List(); +// } +// +// /// +// /// Represents the configuration variant of a widget within the list. +// /// +// [DataContract(Name = "Variant", Namespace = "")] +// public sealed class WidgetVariantConfiguration +// { +// /// Identifier of the variant instance. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Widget variant name. +// [DataMember] +// [JsonProperty("name")] +// public string Name { get; set; } +// +// /// Widget variant properties. +// [DataMember] +// [JsonProperty("properties")] +// // public IWidgetProperties Properties { get; set; } +// public JObject Properties { get; set; } +// +// /// Widget variant personalization condition type. +// /// Only personalization condition type parameters are serialized to JSON. +// [DataMember] +// [JsonProperty("conditionTypeParameters")] +// public JObject PersonalizationConditionType { get; set; } +// } +// +// #endregion +// diff --git a/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs index 91afaff7..890354aa 100644 --- a/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.K11/Services/CmsClass/PageSelectorItem.cs @@ -1,12 +1,11 @@ -namespace Migration.Toolkit.Core.K11.Services.CmsClass; - -using Newtonsoft.Json; - -/// Represents an item for a page selector. -public class PageSelectorItem -{ - /// Node Guid of a page. -#error "NodeGuid may not be unique, use other means of searching for node!" - [JsonProperty("nodeGuid")] - public Guid NodeGuid { get; set; } -} \ No newline at end of file +// namespace Migration.Toolkit.Core.K11.Services.CmsClass; +// +// using Newtonsoft.Json; +// +// /// Represents an item for a page selector. +// public class PageSelectorItem +// { +// /// Node Guid of a page. +// [JsonProperty("nodeGuid")] +// public Guid NodeGuid { get; set; } +// } \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Services/CmsClass/PageTemplateConfiguration.cs b/Migration.Toolkit.Core.K11/Services/CmsClass/PageTemplateConfiguration.cs index b1b0d3a7..0242bdf3 100644 --- a/Migration.Toolkit.Core.K11/Services/CmsClass/PageTemplateConfiguration.cs +++ b/Migration.Toolkit.Core.K11/Services/CmsClass/PageTemplateConfiguration.cs @@ -1,29 +1,29 @@ -namespace Migration.Toolkit.Core.K11.Services.CmsClass; - -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -/// -/// Page template configuration for the instance. -/// -[DataContract(Name = "PageTemplate", Namespace = "")] -public class PageTemplateConfiguration -{ - /// Identifier of the page template. - [DataMember] - [JsonProperty("identifier")] - public string Identifier { get; set; } - - /// - /// Identifier of the page template configuration based on which the page was created. - /// - [DataMember] - [JsonProperty("configurationIdentifier")] - public Guid ConfigurationIdentifier { get; set; } - - /// Page template properties. - [DataMember] - [JsonProperty("properties")] - public JObject Properties { get; set; } -} \ No newline at end of file +// namespace Migration.Toolkit.Core.K11.Services.CmsClass; +// +// using System.Runtime.Serialization; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// /// +// /// Page template configuration for the instance. +// /// +// [DataContract(Name = "PageTemplate", Namespace = "")] +// public class PageTemplateConfiguration +// { +// /// Identifier of the page template. +// [DataMember] +// [JsonProperty("identifier")] +// public string Identifier { get; set; } +// +// /// +// /// Identifier of the page template configuration based on which the page was created. +// /// +// [DataMember] +// [JsonProperty("configurationIdentifier")] +// public Guid ConfigurationIdentifier { get; set; } +// +// /// Page template properties. +// [DataMember] +// [JsonProperty("properties")] +// public JObject Properties { get; set; } +// } \ No newline at end of file diff --git a/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs index 4a408117..013e35d1 100644 --- a/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.K11/Services/PrimaryKeyLocatorService.cs @@ -76,26 +76,6 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); - var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); - - var result = source.Join(target, - a => a.NodeGuid, - b => b.ChannelGuid, - (a, b) => new SourceTargetKeyMapping(a.NodeId, b.ChannelId) - ); - - foreach (var resultingMapping in result) - { - yield return resultingMapping; - } - - yield break; - } - if (sourceType == typeof(CmsState) && memberName == nameof(CmsState.StateId)) { var source = k11Context.CmsStates.Select(x => new { x.StateId, x.StateName }).ToList(); @@ -210,15 +190,6 @@ public bool TryLocate(Expression> keyNameSelector, int source targetId = kxpContext.OmContacts.Where(x => x.ContactGuid == k11Guid).Select(x => x.ContactId).Single(); return true; } - - if (sourceType == typeof(CmsTree)) - { -#error "NodeGuid may not be unique, use other means of searching for node!" - // careful - cms.root will have different guid - var k11Guid = KX12Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); - targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == k11Guid).Select(x => x.ChannelId).Single(); - return true; - } } catch (InvalidOperationException ioex) { diff --git a/Migration.Toolkit.Core.KX12/Contexts/SourceInstanceContext.cs b/Migration.Toolkit.Core.KX12/Contexts/SourceInstanceContext.cs deleted file mode 100644 index ac055bc0..00000000 --- a/Migration.Toolkit.Core.KX12/Contexts/SourceInstanceContext.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace Migration.Toolkit.Core.KX12.Contexts; - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Migration.Toolkit.Common; -using Migration.Toolkit.Common.Services.Ipc; -using Migration.Toolkit.KX12.Context; - -public class SourceInstanceContext -{ - private readonly IpcService _ipcService; - private readonly IDbContextFactory _kx12ContextFactory; - private readonly ILogger _logger; - private readonly ToolkitConfiguration _configuration; - - private readonly Dictionary _cachedInfos = new(StringComparer.InvariantCultureIgnoreCase); - - private bool _sourceInfoLoaded; - - public bool HasInfo => _cachedInfos.Count > 0 && _sourceInfoLoaded; - - public SourceInstanceContext(IpcService ipcService, IDbContextFactory kx12ContextFactory, ILogger logger, - ToolkitConfiguration configuration) - { - _ipcService = ipcService; - _kx12ContextFactory = kx12ContextFactory; - _logger = logger; - _configuration = configuration; - } - - public bool IsQuerySourceInstanceEnabled() - { - return _configuration.OptInFeatures?.QuerySourceInstanceApi?.Enabled ?? false; - } - - public async Task RequestSourceInstanceInfo() - { - if (!_sourceInfoLoaded) - { - var result = await _ipcService.GetSourceInstanceDiscoveredInfos(); - foreach (var (key, value) in result) - { - _cachedInfos.Add(key, value); - _logger.LogInformation("Source instance info loaded for site '{SiteName}' successfully", key); - } - - _sourceInfoLoaded = true; - } - - return _sourceInfoLoaded; - } - - public List? GetWidgetPropertyFormComponents(string siteName, string widgetIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.WidgetProperties != null && info.WidgetProperties.TryGetValue(widgetIdentifier, out var widgetProperties) - ? widgetProperties - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetPageTemplateFormComponents(string siteName, string pageTemplateIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.PageTemplateProperties != null && info.PageTemplateProperties.TryGetValue(pageTemplateIdentifier, out var pageTemplate) - ? pageTemplate - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetWidgetPropertyFormComponents(int siteId, string widgetIdentifier) - { - var context = _kx12ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetWidgetPropertyFormComponents(siteName, widgetIdentifier); - } - - public List? GetPageTemplateFormComponents(int siteId, string pageTemplateIdentifier) - { - var context = _kx12ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetPageTemplateFormComponents(siteName, pageTemplateIdentifier); - } - - public List? GetSectionFormComponents(int siteId, string sectionIdentifier) - { - var context = _kx12ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.SectionProperties != null && info.SectionProperties.TryGetValue(sectionIdentifier, out var sectionFcs) - ? sectionFcs - : null; - } - else - { - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - } -} \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs index 479cd676..bb898bb3 100644 --- a/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.KX12/Handlers/MigrateContactManagementCommandHandler.cs @@ -3,13 +3,13 @@ namespace Migration.Toolkit.Core.KX12.Handlers; using CMS.Activities; using CMS.ContactManagement; using CMS.ContentEngine; -using CMS.Websites.Internal; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; using Migration.Toolkit.Common.Abstractions; using Migration.Toolkit.Common.MigrationProtocol; +using Migration.Toolkit.Common.Services; using Migration.Toolkit.Common.Services.BulkCopy; using Migration.Toolkit.Core.KX12.Contexts; using Migration.Toolkit.Core.KX12.Helpers; @@ -27,6 +27,7 @@ public class MigrateContactManagementCommandHandler( KeyMappingContext keyMappingContext, CountryMigrator countryMigrator, KxpClassFacade kxpClassFacade, + ISpoiledGuidContext spoiledGuidContext, IProtocol protocol ) : IRequestHandler, IDisposable @@ -319,37 +320,32 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin } } - if (columnName.Equals(nameof(KX12M.OmActivity.ActivityNodeId), StringComparison.InvariantCultureIgnoreCase) && value is int activityNodeId) + if (columnName.Equals(nameof(OmActivity.ActivityNodeId), StringComparison.InvariantCultureIgnoreCase) && value is int activityNodeId) { - var result = keyMappingContext.MapSourceKey( - s => s.NodeId, -#error "NodeGuid may not be unique, use other means of searching for node!" - s => s.NodeGuid, - activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); - switch(result) + if (currentRow.TryGetValue(nameof(OmActivity.ActivitySiteId), out var mSiteId) && mSiteId is int siteId) { - case (true, var guid): - return ValueInterceptorResult.ReplaceValue(guid); - case { Success: false }: + if (spoiledGuidContext.GetNodeGuid(siteId, activityNodeId) is { } nodeGuid) { - switch (toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) - { - case AutofixEnum.DiscardData: - logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); - return ValueInterceptorResult.SkipRow; - case AutofixEnum.AttemptFix: - logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); - return ValueInterceptorResult.ReplaceValue(null); - case AutofixEnum.Error: - default: //error - protocol.Append(HandbookReferences - .MissingRequiredDependency(columnName, value) - .WithData(currentRow) - ); - return ValueInterceptorResult.SkipRow; - } + return ValueInterceptorResult.ReplaceValue(nodeGuid); } } + + switch (toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) + { + case AutofixEnum.DiscardData: + logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); + return ValueInterceptorResult.SkipRow; + case AutofixEnum.AttemptFix: + logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); + return ValueInterceptorResult.ReplaceValue(null); + case AutofixEnum.Error: + default: //error + protocol.Append(HandbookReferences + .MissingRequiredDependency(columnName, value) + .WithData(currentRow) + ); + return ValueInterceptorResult.SkipRow; + } } if (columnName.Equals( nameof(KXP.Models.OmActivity.ActivityLanguageId), StringComparison.InvariantCultureIgnoreCase) && value is string cultureCode) diff --git a/Migration.Toolkit.Core.KX12/KX12CoreDiExtensions.cs b/Migration.Toolkit.Core.KX12/KX12CoreDiExtensions.cs index e2e91f78..933a9c21 100644 --- a/Migration.Toolkit.Core.KX12/KX12CoreDiExtensions.cs +++ b/Migration.Toolkit.Core.KX12/KX12CoreDiExtensions.cs @@ -45,8 +45,6 @@ public static IServiceCollection UseKx12ToolkitCore(this IServiceCollection serv services.AddTransient(typeof(IPipelineBehavior<,>), typeof(XbKApiContextBehavior<,>)); services.AddSingleton(s => new TableReflectionService(s.GetRequiredService>())); - services.AddSingleton(); - services.AddTransient(); services.AddScoped(); services.AddSingleton(); diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs index 390be54f..e6d6f4ba 100644 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs +++ b/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs @@ -1,187 +1,187 @@ -namespace Migration.Toolkit.Core.KX12.Services.CmsClass; - -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -#region Copied from Kentico assembly - -[DataContract(Name = "Configuration", Namespace = "")] -public sealed class EditableAreasConfiguration -{ - /// Editable areas within the page. - [DataMember] - [JsonProperty("editableAreas")] - public List EditableAreas { get; private set; } - - /// - /// Creates an instance of class. - /// - public EditableAreasConfiguration() => this.EditableAreas = new List(); -} - -/// -/// Represents configuration of editable area within the instance. -/// -[DataContract(Name = "EditableArea", Namespace = "")] -public sealed class EditableAreaConfiguration -{ - /// Identifier of the editable area. - [DataMember] - [JsonProperty("identifier")] - public string Identifier { get; set; } - - /// Sections within editable area. - [DataMember] - [JsonProperty("sections")] - public List Sections { get; private set; } - - /// - /// A flag indicating whether the output of the individual widgets within the editable area can be cached. The default value is false. - /// - public bool AllowWidgetOutputCache { get; set; } - - /// - /// An absolute expiration date for the cached output of the individual widgets. - /// - public DateTimeOffset? WidgetOutputCacheExpiresOn { get; set; } - - /// - /// The length of time from the first request to cache the output of the individual widgets. - /// - public TimeSpan? WidgetOutputCacheExpiresAfter { get; set; } - - /// - /// The time after which the cached output of the individual widgets should be evicted if it has not been accessed. - /// - public TimeSpan? WidgetOutputCacheExpiresSliding { get; set; } - - /// - /// Creates an instance of class. - /// - public EditableAreaConfiguration() => this.Sections = new List(); -} - -/// -/// Represents configuration of section within the instance. -/// -[DataContract(Name = "Section", Namespace = "")] -public sealed class SectionConfiguration -{ - /// Identifier of the section. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Type section identifier. - [DataMember] - [JsonProperty("type")] - public string TypeIdentifier { get; set; } - - /// Section properties. - [DataMember] - [JsonProperty("properties")] - // public ISectionProperties Properties { get; set; } - public JObject Properties { get; set; } - - /// Zones within the section. - [DataMember] - [JsonProperty("zones")] - public List Zones { get; private set; } - - /// - /// Creates an instance of class. - /// - public SectionConfiguration() => this.Zones = new List(); -} - -/// -/// Represents the zone within the configuration class. -/// -[DataContract(Name = "Zone", Namespace = "")] -public sealed class ZoneConfiguration -{ - /// Identifier of the widget zone. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Name of the widget zone. - [DataMember] - [JsonProperty("name")] - public string Name { get; set; } - - /// List of widgets within the zone. - [DataMember] - [JsonProperty("widgets")] - public List Widgets { get; private set; } - - /// - /// Creates an instance of class. - /// - public ZoneConfiguration() => this.Widgets = new List(); -} - -/// -/// Represents the configuration of a widget within the list. -/// -[DataContract(Name = "Widget", Namespace = "")] -public sealed class WidgetConfiguration -{ - /// Identifier of the widget instance. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Type widget identifier. - [DataMember] - [JsonProperty("type")] - public string TypeIdentifier { get; set; } - - /// Personalization condition type identifier. - [DataMember] - [JsonProperty("conditionType")] - public string PersonalizationConditionTypeIdentifier { get; set; } - - /// List of widget variants. - [DataMember] - [JsonProperty("variants")] - public List Variants { get; set; } - - /// - /// Creates an instance of class. - /// - public WidgetConfiguration() => this.Variants = new List(); -} - -/// -/// Represents the configuration variant of a widget within the list. -/// -[DataContract(Name = "Variant", Namespace = "")] -public sealed class WidgetVariantConfiguration -{ - /// Identifier of the variant instance. - [DataMember] - [JsonProperty("identifier")] - public Guid Identifier { get; set; } - - /// Widget variant name. - [DataMember] - [JsonProperty("name")] - public string Name { get; set; } - - /// Widget variant properties. - [DataMember] - [JsonProperty("properties")] - // public IWidgetProperties Properties { get; set; } - public JObject Properties { get; set; } - - /// Widget variant personalization condition type. - /// Only personalization condition type parameters are serialized to JSON. - [DataMember] - [JsonProperty("conditionTypeParameters")] - public JObject PersonalizationConditionType { get; set; } -} - -#endregion - +// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; +// +// using System.Runtime.Serialization; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// #region Copied from Kentico assembly +// +// [DataContract(Name = "Configuration", Namespace = "")] +// public sealed class EditableAreasConfiguration +// { +// /// Editable areas within the page. +// [DataMember] +// [JsonProperty("editableAreas")] +// public List EditableAreas { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public EditableAreasConfiguration() => this.EditableAreas = new List(); +// } +// +// /// +// /// Represents configuration of editable area within the instance. +// /// +// [DataContract(Name = "EditableArea", Namespace = "")] +// public sealed class EditableAreaConfiguration +// { +// /// Identifier of the editable area. +// [DataMember] +// [JsonProperty("identifier")] +// public string Identifier { get; set; } +// +// /// Sections within editable area. +// [DataMember] +// [JsonProperty("sections")] +// public List Sections { get; private set; } +// +// /// +// /// A flag indicating whether the output of the individual widgets within the editable area can be cached. The default value is false. +// /// +// public bool AllowWidgetOutputCache { get; set; } +// +// /// +// /// An absolute expiration date for the cached output of the individual widgets. +// /// +// public DateTimeOffset? WidgetOutputCacheExpiresOn { get; set; } +// +// /// +// /// The length of time from the first request to cache the output of the individual widgets. +// /// +// public TimeSpan? WidgetOutputCacheExpiresAfter { get; set; } +// +// /// +// /// The time after which the cached output of the individual widgets should be evicted if it has not been accessed. +// /// +// public TimeSpan? WidgetOutputCacheExpiresSliding { get; set; } +// +// /// +// /// Creates an instance of class. +// /// +// public EditableAreaConfiguration() => this.Sections = new List(); +// } +// +// /// +// /// Represents configuration of section within the instance. +// /// +// [DataContract(Name = "Section", Namespace = "")] +// public sealed class SectionConfiguration +// { +// /// Identifier of the section. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Type section identifier. +// [DataMember] +// [JsonProperty("type")] +// public string TypeIdentifier { get; set; } +// +// /// Section properties. +// [DataMember] +// [JsonProperty("properties")] +// // public ISectionProperties Properties { get; set; } +// public JObject Properties { get; set; } +// +// /// Zones within the section. +// [DataMember] +// [JsonProperty("zones")] +// public List Zones { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public SectionConfiguration() => this.Zones = new List(); +// } +// +// /// +// /// Represents the zone within the configuration class. +// /// +// [DataContract(Name = "Zone", Namespace = "")] +// public sealed class ZoneConfiguration +// { +// /// Identifier of the widget zone. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Name of the widget zone. +// [DataMember] +// [JsonProperty("name")] +// public string Name { get; set; } +// +// /// List of widgets within the zone. +// [DataMember] +// [JsonProperty("widgets")] +// public List Widgets { get; private set; } +// +// /// +// /// Creates an instance of class. +// /// +// public ZoneConfiguration() => this.Widgets = new List(); +// } +// +// /// +// /// Represents the configuration of a widget within the list. +// /// +// [DataContract(Name = "Widget", Namespace = "")] +// public sealed class WidgetConfiguration +// { +// /// Identifier of the widget instance. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Type widget identifier. +// [DataMember] +// [JsonProperty("type")] +// public string TypeIdentifier { get; set; } +// +// /// Personalization condition type identifier. +// [DataMember] +// [JsonProperty("conditionType")] +// public string PersonalizationConditionTypeIdentifier { get; set; } +// +// /// List of widget variants. +// [DataMember] +// [JsonProperty("variants")] +// public List Variants { get; set; } +// +// /// +// /// Creates an instance of class. +// /// +// public WidgetConfiguration() => this.Variants = new List(); +// } +// +// /// +// /// Represents the configuration variant of a widget within the list. +// /// +// [DataContract(Name = "Variant", Namespace = "")] +// public sealed class WidgetVariantConfiguration +// { +// /// Identifier of the variant instance. +// [DataMember] +// [JsonProperty("identifier")] +// public Guid Identifier { get; set; } +// +// /// Widget variant name. +// [DataMember] +// [JsonProperty("name")] +// public string Name { get; set; } +// +// /// Widget variant properties. +// [DataMember] +// [JsonProperty("properties")] +// // public IWidgetProperties Properties { get; set; } +// public JObject Properties { get; set; } +// +// /// Widget variant personalization condition type. +// /// Only personalization condition type parameters are serialized to JSON. +// [DataMember] +// [JsonProperty("conditionTypeParameters")] +// public JObject PersonalizationConditionType { get; set; } +// } +// +// #endregion +// diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs index dc00396b..d72c3d73 100644 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs @@ -1,12 +1,12 @@ -namespace Migration.Toolkit.Core.KX12.Services.CmsClass; - -using Newtonsoft.Json; - -/// Represents an item for a page selector. -public class PageSelectorItem -{ - /// Node Guid of a page. - [JsonProperty("nodeGuid")] -#error "NodeGuid may not be unique, use other means of searching for node!" - public Guid NodeGuid { get; set; } -} \ No newline at end of file +// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; +// +// using Newtonsoft.Json; +// +// /// Represents an item for a page selector. +// public class PageSelectorItem +// { +// /// Node Guid of a page. +// [JsonProperty("nodeGuid")] +// #error "NodeGuid may not be unique, use other means of searching for node!" +// public Guid NodeGuid { get; set; } +// } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs index ee813b8d..5679916d 100644 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs +++ b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs @@ -1,29 +1,29 @@ -namespace Migration.Toolkit.Core.KX12.Services.CmsClass; - -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -/// -/// Page template configuration for the instance. -/// -[DataContract(Name = "PageTemplate", Namespace = "")] -public class PageTemplateConfiguration -{ - /// Identifier of the page template. - [DataMember] - [JsonProperty("identifier")] - public string Identifier { get; set; } - - /// - /// Identifier of the page template configuration based on which the page was created. - /// - [DataMember] - [JsonProperty("configurationIdentifier")] - public Guid ConfigurationIdentifier { get; set; } - - /// Page template properties. - [DataMember] - [JsonProperty("properties")] - public JObject Properties { get; set; } -} \ No newline at end of file +// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; +// +// using System.Runtime.Serialization; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// /// +// /// Page template configuration for the instance. +// /// +// [DataContract(Name = "PageTemplate", Namespace = "")] +// public class PageTemplateConfiguration +// { +// /// Identifier of the page template. +// [DataMember] +// [JsonProperty("identifier")] +// public string Identifier { get; set; } +// +// /// +// /// Identifier of the page template configuration based on which the page was created. +// /// +// [DataMember] +// [JsonProperty("configurationIdentifier")] +// public Guid ConfigurationIdentifier { get; set; } +// +// /// Page template properties. +// [DataMember] +// [JsonProperty("properties")] +// public JObject Properties { get; set; } +// } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs index 32431688..51430aca 100644 --- a/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.KX12/Services/PrimaryKeyLocatorService.cs @@ -94,26 +94,6 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); - var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); - - var result = source.Join(target, - a => a.NodeGuid, - b => b.ChannelGuid, - (a, b) => new SourceTargetKeyMapping(a.NodeId, b.ChannelId) - ); - - foreach (var resultingMapping in result) - { - yield return resultingMapping; - } - - yield break; - } - if (sourceType == typeof(KX12M.CmsState) && memberName == nameof(KX12M.CmsState.StateId)) { var source = kx12Context.CmsStates.Select(x => new { x.StateId, x.StateName }).ToList(); @@ -228,15 +208,6 @@ public bool TryLocate(Expression> keyNameSelector, int source targetId = kxpContext.OmContacts.Where(x => x.ContactGuid == k12Guid).Select(x => x.ContactId).Single(); return true; } - - if (sourceType == typeof(KX12M.CmsTree)) - { - // careful - cms.root will have different guid -#error "NodeGuid may not be unique, use other means of searching for node!" - var k12Guid = KX12Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); - targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == k12Guid).Select(x => x.ChannelId).Single(); - return true; - } } catch (InvalidOperationException ioex) { diff --git a/Migration.Toolkit.Core.KX13/Contexts/SourceInstanceContext.cs b/Migration.Toolkit.Core.KX13/Contexts/SourceInstanceContext.cs deleted file mode 100644 index 6d996e9c..00000000 --- a/Migration.Toolkit.Core.KX13/Contexts/SourceInstanceContext.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace Migration.Toolkit.Core.KX13.Contexts; - -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Migration.Toolkit.Common; -using Migration.Toolkit.Common.Services.Ipc; -using Migration.Toolkit.KX13.Context; - -public class SourceInstanceContext -{ - private readonly IpcService _ipcService; - private readonly IDbContextFactory _kx13ContextFactory; - private readonly ILogger _logger; - private readonly ToolkitConfiguration _configuration; - - private readonly Dictionary _cachedInfos = new(StringComparer.InvariantCultureIgnoreCase); - - private bool _sourceInfoLoaded; - - public bool HasInfo => _cachedInfos.Count > 0 && _sourceInfoLoaded; - - public SourceInstanceContext(IpcService ipcService, IDbContextFactory kx13ContextFactory, ILogger logger, - ToolkitConfiguration configuration) - { - _ipcService = ipcService; - _kx13ContextFactory = kx13ContextFactory; - _logger = logger; - _configuration = configuration; - } - - public bool IsQuerySourceInstanceEnabled() - { - return _configuration.OptInFeatures?.QuerySourceInstanceApi?.Enabled ?? false; - } - - public async Task RequestSourceInstanceInfo() - { - if (!_sourceInfoLoaded) - { - var result = await _ipcService.GetSourceInstanceDiscoveredInfos(); - foreach (var (key, value) in result) - { - _cachedInfos.Add(key, value); - _logger.LogInformation("Source instance info loaded for site '{SiteName}' successfully", key); - } - - _sourceInfoLoaded = true; - } - - return _sourceInfoLoaded; - } - - public List? GetWidgetPropertyFormComponents(string siteName, string widgetIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.WidgetProperties != null && info.WidgetProperties.TryGetValue(widgetIdentifier, out var widgetProperties) - ? widgetProperties - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetPageTemplateFormComponents(string siteName, string pageTemplateIdentifier) - { - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.PageTemplateProperties != null && info.PageTemplateProperties.TryGetValue(pageTemplateIdentifier, out var pageTemplate) - ? pageTemplate - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } - - public List? GetWidgetPropertyFormComponents(int siteId, string widgetIdentifier) - { - var context = _kx13ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetWidgetPropertyFormComponents(siteName, widgetIdentifier); - } - - public List? GetPageTemplateFormComponents(int siteId, string pageTemplateIdentifier) - { - var context = _kx13ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - return GetPageTemplateFormComponents(siteName, pageTemplateIdentifier); - } - - public List? GetSectionFormComponents(int siteId, string sectionIdentifier) - { - var context = _kx13ContextFactory.CreateDbContext(); - var siteName = context.CmsSites.FirstOrDefault(s => s.SiteId == siteId)?.SiteName - ?? throw new InvalidOperationException($"Source site with SiteID '{siteId}' not exists"); - - if (_cachedInfos.TryGetValue(siteName, out var info)) - { - return info.SectionProperties != null && info.SectionProperties.TryGetValue(sectionIdentifier, out var sectionFcs) - ? sectionFcs - : null; - } - - throw new InvalidOperationException($"No info was loaded for site '{siteName}'"); - } -} \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX13/DependencyInjectionExtensions.cs b/Migration.Toolkit.Core.KX13/DependencyInjectionExtensions.cs index f58a1fb5..bc3d4f49 100644 --- a/Migration.Toolkit.Core.KX13/DependencyInjectionExtensions.cs +++ b/Migration.Toolkit.Core.KX13/DependencyInjectionExtensions.cs @@ -45,8 +45,6 @@ public static IServiceCollection UseKx13ToolkitCore(this IServiceCollection serv services.AddTransient(typeof(IPipelineBehavior<,>), typeof(XbKApiContextBehavior<,>)); services.AddSingleton(s => new TableReflectionService(s.GetRequiredService>())); - services.AddSingleton(); - services.AddTransient(); services.AddScoped(); services.AddSingleton(); diff --git a/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs b/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs index d3c8217f..f78a2fd9 100644 --- a/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs +++ b/Migration.Toolkit.Core.KX13/Handlers/MigrateContactManagementCommandHandler.cs @@ -3,13 +3,13 @@ namespace Migration.Toolkit.Core.KX13.Handlers; using CMS.Activities; using CMS.ContactManagement; using CMS.ContentEngine; -using CMS.Websites.Internal; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; using Migration.Toolkit.Common.Abstractions; using Migration.Toolkit.Common.MigrationProtocol; +using Migration.Toolkit.Common.Services; using Migration.Toolkit.Common.Services.BulkCopy; using Migration.Toolkit.Core.KX13.Contexts; using Migration.Toolkit.Core.KX13.Helpers; @@ -18,44 +18,24 @@ namespace Migration.Toolkit.Core.KX13.Handlers; using Migration.Toolkit.KXP.Api; using Migration.Toolkit.KXP.Context; -public class MigrateContactManagementCommandHandler : IRequestHandler, IDisposable +public class MigrateContactManagementCommandHandler( + ILogger logger, + IDbContextFactory kxpContextFactory, + BulkDataCopyService bulkDataCopyService, + ToolkitConfiguration toolkitConfiguration, + PrimaryKeyMappingContext primaryKeyMappingContext, + KeyMappingContext keyMappingContext, + CountryMigrator countryMigrator, + KxpClassFacade kxpClassFacade, + ISpoiledGuidContext spoiledGuidContext, + IProtocol protocol) + : IRequestHandler, IDisposable { - private readonly ILogger _logger; - private readonly BulkDataCopyService _bulkDataCopyService; - private readonly ToolkitConfiguration _toolkitConfiguration; - private readonly PrimaryKeyMappingContext _primaryKeyMappingContext; - private readonly KeyMappingContext _keyMappingContext; - private readonly CountryMigrator _countryMigrator; - private readonly KxpClassFacade _kxpClassFacade; - private readonly IProtocol _protocol; - private readonly KxpContext _kxpContext; - - public MigrateContactManagementCommandHandler( - ILogger logger, - IDbContextFactory kxpContextFactory, - BulkDataCopyService bulkDataCopyService, - ToolkitConfiguration toolkitConfiguration, - PrimaryKeyMappingContext primaryKeyMappingContext, - KeyMappingContext keyMappingContext, - CountryMigrator countryMigrator, - KxpClassFacade kxpClassFacade, - IProtocol protocol - ) - { - _logger = logger; - _kxpContext = kxpContextFactory.CreateDbContext(); - _bulkDataCopyService = bulkDataCopyService; - _toolkitConfiguration = toolkitConfiguration; - _primaryKeyMappingContext = primaryKeyMappingContext; - _keyMappingContext = keyMappingContext; - _countryMigrator = countryMigrator; - _kxpClassFacade = kxpClassFacade; - _protocol = protocol; - } + private readonly KxpContext _kxpContext = kxpContextFactory.CreateDbContext(); public Task Handle(MigrateContactManagementCommand request, CancellationToken cancellationToken) { - _countryMigrator.MigrateCountriesAndStates(); + countryMigrator.MigrateCountriesAndStates(); if (MigrateContacts() is { } ccr) return Task.FromResult(ccr); if (MigrateContactActivities() is { } acr) return Task.FromResult(acr); @@ -101,34 +81,34 @@ public Task Handle(MigrateContactManagementCommand request, Cance // No support 2022-07-07 { nameof(OmContact.ContactSalesForceLeadReplicationRequired), nameof(KXO.Models.OmContact.ContactSalesForceLeadReplicationRequired) }, }; - foreach (var cfi in _kxpClassFacade.GetCustomizedFieldInfos(ContactInfo.TYPEINFO.ObjectClassName)) + foreach (var cfi in kxpClassFacade.GetCustomizedFieldInfos(ContactInfo.TYPEINFO.ObjectClassName)) { requiredColumnsForContactMigration.Add(cfi.FieldName, cfi.FieldName); } - if (_bulkDataCopyService.CheckIfDataExistsInTargetTable("OM_Contact")) + if (bulkDataCopyService.CheckIfDataExistsInTargetTable("OM_Contact")) { - _protocol.Append(HandbookReferences.DataMustNotExistInTargetInstanceTable("OM_Contact")); - _logger.LogError("Data must not exist in target instance table, remove data before proceeding"); + protocol.Append(HandbookReferences.DataMustNotExistInTargetInstanceTable("OM_Contact")); + logger.LogError("Data must not exist in target instance table, remove data before proceeding"); return new CommandFailureResult(); } - if (_bulkDataCopyService.CheckForTableColumnsDifferences("OM_Contact", requiredColumnsForContactMigration, out var differences)) + if (bulkDataCopyService.CheckForTableColumnsDifferences("OM_Contact", requiredColumnsForContactMigration, out var differences)) { - _protocol.Append(HandbookReferences + protocol.Append(HandbookReferences .BulkCopyColumnMismatch("OM_Contact") .NeedsManualAction() .WithData(differences) ); - _logger.LogError("Table {TableName} columns do not match, fix columns before proceeding", "OM_Contact"); + logger.LogError("Table {TableName} columns do not match, fix columns before proceeding", "OM_Contact"); { return new CommandFailureResult(); } } - _primaryKeyMappingContext.PreloadDependencies(u => u.UserId); - _primaryKeyMappingContext.PreloadDependencies(u => u.StateId); - _primaryKeyMappingContext.PreloadDependencies(u => u.CountryId); + primaryKeyMappingContext.PreloadDependencies(u => u.UserId); + primaryKeyMappingContext.PreloadDependencies(u => u.StateId); + primaryKeyMappingContext.PreloadDependencies(u => u.CountryId); var bulkCopyRequest = new BulkCopyRequest("OM_Contact", s => true,// s => s != "ContactID", @@ -136,18 +116,18 @@ public Task Handle(MigrateContactManagementCommand request, Cance 50000, requiredColumnsForContactMigration.Keys.ToList(), ContactValueInterceptor, - current => { _logger.LogError("Contact skipped due error, contact: {Contact}", PrintHelper.PrintDictionary(current)); }, + current => { logger.LogError("Contact skipped due error, contact: {Contact}", PrintHelper.PrintDictionary(current)); }, "ContactID" ); - _logger.LogTrace("Bulk data copy request: {Request}", bulkCopyRequest); + logger.LogTrace("Bulk data copy request: {Request}", bulkCopyRequest); try { - _bulkDataCopyService.CopyTableToTable(bulkCopyRequest); + bulkDataCopyService.CopyTableToTable(bulkCopyRequest); } catch(Exception ex) { - _logger.LogError(ex, "Failed to migrate contacts"); + logger.LogError(ex, "Failed to migrate contacts"); return new CommandFailureResult(); } @@ -164,7 +144,7 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum if (value is string { Length: > 100 } s) { - _protocol.Append(HandbookReferences.ValueTruncationSkip("OM_Contact") + protocol.Append(HandbookReferences.ValueTruncationSkip("OM_Contact") .WithData(new { value, maxLength = 100, s.Length, columnName, contact = PrintHelper.PrintDictionary(currentRow) }) ); return ValueInterceptorResult.SkipRow; @@ -173,14 +153,14 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum if (columnName.Equals(nameof(KXP.Models.OmContact.ContactOwnerUserId), StringComparison.InvariantCultureIgnoreCase) && value is int sourceUserId) { - switch (_primaryKeyMappingContext.MapSourceId(u => u.UserId, sourceUserId)) + switch (primaryKeyMappingContext.MapSourceId(u => u.UserId, sourceUserId)) { case (true, var id): return ValueInterceptorResult.ReplaceValue(id); case { Success: false }: { // try search member - if (_keyMappingContext.MapSourceKey( + if (keyMappingContext.MapSourceKey( s => s.UserId, s => s.UserGuid, sourceUserId, @@ -190,7 +170,7 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum { return ValueInterceptorResult.ReplaceValue(memberId); } - _protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) + protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) .WithData(currentRow)); return ValueInterceptorResult.SkipRow; } @@ -199,13 +179,13 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum if (columnName.Equals(nameof(KXP.Models.OmContact.ContactStateId), StringComparison.InvariantCultureIgnoreCase) && value is int sourceStateId) { - switch (_primaryKeyMappingContext.MapSourceId(u => u.StateId, sourceStateId.NullIfZero())) + switch (primaryKeyMappingContext.MapSourceId(u => u.StateId, sourceStateId.NullIfZero())) { case (true, var id): return ValueInterceptorResult.ReplaceValue(id); case { Success: false }: { - _protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) + protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) .WithData(currentRow)); return ValueInterceptorResult.SkipRow; } @@ -214,13 +194,13 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum if (columnName.Equals(nameof(KXP.Models.OmContact.ContactCountryId), StringComparison.InvariantCultureIgnoreCase) && value is int sourceCountryId) { - switch (_primaryKeyMappingContext.MapSourceId(u => u.CountryId, sourceCountryId.NullIfZero())) + switch (primaryKeyMappingContext.MapSourceId(u => u.CountryId, sourceCountryId.NullIfZero())) { case (true, var id): return ValueInterceptorResult.ReplaceValue(id); case { Success: false }: { - _protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) + protocol.Append(HandbookReferences.MissingRequiredDependency(columnName, value) .WithData(currentRow)); return ValueInterceptorResult.SkipRow; } @@ -261,15 +241,15 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum { nameof(OmActivity.ActivityUtmcontent), nameof(KXP.Models.OmActivity.ActivityUtmcontent) }, }; - foreach (var cfi in _kxpClassFacade.GetCustomizedFieldInfos(ActivityInfo.TYPEINFO.ObjectClassName)) + foreach (var cfi in kxpClassFacade.GetCustomizedFieldInfos(ActivityInfo.TYPEINFO.ObjectClassName)) { requiredColumnsForContactMigration.Add(cfi.FieldName, cfi.FieldName); } - if (_bulkDataCopyService.CheckIfDataExistsInTargetTable("OM_Activity")) + if (bulkDataCopyService.CheckIfDataExistsInTargetTable("OM_Activity")) { - _protocol.Append(HandbookReferences.DataMustNotExistInTargetInstanceTable("OM_Activity")); - _logger.LogError("Data must not exist in target instance table, remove data before proceeding"); + protocol.Append(HandbookReferences.DataMustNotExistInTargetInstanceTable("OM_Activity")); + logger.LogError("Data must not exist in target instance table, remove data before proceeding"); return new CommandFailureResult(); } @@ -283,19 +263,19 @@ private ValueInterceptorResult ContactValueInterceptor(int ordinal, string colum 50000, requiredColumnsForContactMigration, ActivityValueInterceptor, - current => { _logger.LogError("Contact activity skipped due error, activity: {Activity}", PrintHelper.PrintDictionary(current)); }, + current => { logger.LogError("Contact activity skipped due error, activity: {Activity}", PrintHelper.PrintDictionary(current)); }, "ActivityID" ); - _logger.LogTrace("Bulk data copy request: {Request}", bulkCopyRequest); + logger.LogTrace("Bulk data copy request: {Request}", bulkCopyRequest); try { - _bulkDataCopyService.CopyTableToTable(bulkCopyRequest); + bulkDataCopyService.CopyTableToTable(bulkCopyRequest); } catch(Exception ex) { - _logger.LogError(ex, "Failed to migrate activities"); + logger.LogError(ex, "Failed to migrate activities"); return new CommandFailureResult(); } return null; @@ -306,7 +286,7 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin if (columnName.Equals(nameof(KX13M.OmActivity.ActivitySiteId), StringComparison.InvariantCultureIgnoreCase) && value is int sourceActivitySiteId) { - var result = _keyMappingContext.MapSourceKey( + var result = keyMappingContext.MapSourceKey( s => s.SiteId, s => s.SiteGuid, sourceActivitySiteId.NullIfZero(), @@ -319,17 +299,17 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin return ValueInterceptorResult.ReplaceValue(id ?? 0); case { Success: false }: { - switch (_toolkitConfiguration.UseOmActivitySiteRelationAutofix ?? AutofixEnum.Error) + switch (toolkitConfiguration.UseOmActivitySiteRelationAutofix ?? AutofixEnum.Error) { case AutofixEnum.DiscardData: - _logger.LogTrace("Autofix (ActivitySiteId={ActivitySiteId} not exists) => discard data", sourceActivitySiteId); + logger.LogTrace("Autofix (ActivitySiteId={ActivitySiteId} not exists) => discard data", sourceActivitySiteId); return ValueInterceptorResult.SkipRow; case AutofixEnum.AttemptFix: - _logger.LogTrace("Autofix (ActivitySiteId={ActivitySiteId} not exists) => ActivityNodeId=0", sourceActivitySiteId); + logger.LogTrace("Autofix (ActivitySiteId={ActivitySiteId} not exists) => ActivityNodeId=0", sourceActivitySiteId); return ValueInterceptorResult.ReplaceValue(0); case AutofixEnum.Error: default: //error - _protocol.Append(HandbookReferences + protocol.Append(HandbookReferences .MissingRequiredDependency(columnName, value) .WithData(currentRow) ); @@ -339,37 +319,32 @@ private ValueInterceptorResult ActivityValueInterceptor(int columnOrdinal, strin } } - if (columnName.Equals(nameof(KX13M.OmActivity.ActivityNodeId), StringComparison.InvariantCultureIgnoreCase) && value is int activityNodeId) + if (columnName.Equals(nameof(OmActivity.ActivityNodeId), StringComparison.InvariantCultureIgnoreCase) && value is int activityNodeId) { - var result = _keyMappingContext.MapSourceKey( - s => s.NodeId, -#error "NodeGuid may not be unique, use other means of searching for node!" - s => s.NodeGuid, - activityNodeId.NullIfZero(), t => t.WebPageItemGuid, t => t.WebPageItemGuid); - switch(result) + if (currentRow.TryGetValue(nameof(OmActivity.ActivitySiteId), out var mSiteId) && mSiteId is int siteId) { - case (true, var guid): - return ValueInterceptorResult.ReplaceValue(guid); - case { Success: false }: + if (spoiledGuidContext.GetNodeGuid(siteId, activityNodeId) is { } nodeGuid) { - switch (_toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) - { - case AutofixEnum.DiscardData: - _logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); - return ValueInterceptorResult.SkipRow; - case AutofixEnum.AttemptFix: - _logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); - return ValueInterceptorResult.ReplaceValue(null); - case AutofixEnum.Error: - default: //error - _protocol.Append(HandbookReferences - .MissingRequiredDependency(columnName, value) - .WithData(currentRow) - ); - return ValueInterceptorResult.SkipRow; - } + return ValueInterceptorResult.ReplaceValue(nodeGuid); } } + + switch (toolkitConfiguration.UseOmActivityNodeRelationAutofix ?? AutofixEnum.Error) + { + case AutofixEnum.DiscardData: + logger.LogTrace("Autofix (ActivitySiteId={NodeId} not exists) => discard data", activityNodeId); + return ValueInterceptorResult.SkipRow; + case AutofixEnum.AttemptFix: + logger.LogTrace("Autofix (ActivityNodeId={NodeId} not exists) => ActivityNodeId=0", activityNodeId); + return ValueInterceptorResult.ReplaceValue(null); + case AutofixEnum.Error: + default: //error + protocol.Append(HandbookReferences + .MissingRequiredDependency(columnName, value) + .WithData(currentRow) + ); + return ValueInterceptorResult.SkipRow; + } } if (columnName.Equals( nameof(KX13M.OmActivity.ActivityCulture), StringComparison.InvariantCultureIgnoreCase) && value is string cultureCode) diff --git a/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs b/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs deleted file mode 100644 index 1289e3ea..00000000 --- a/Migration.Toolkit.Core.KX13/Mappers/PageTemplateConfigurationMapper.cs +++ /dev/null @@ -1,224 +0,0 @@ -namespace Migration.Toolkit.Core.KX13.Mappers; - -using AngleSharp.Text; -using CMS.MediaLibrary; -using CMS.Websites; -using Microsoft.Extensions.Logging; -using Migration.Toolkit.Common.Abstractions; -using Migration.Toolkit.Common.Enumerations; -using Migration.Toolkit.Common.MigrationProtocol; -using Migration.Toolkit.Common.Services.Ipc; -using Migration.Toolkit.Core.KX13.Contexts; -using Migration.Toolkit.Core.KX13.Services.CmsClass; -using Migration.Toolkit.KX13.Models; -using Migration.Toolkit.KXP.Api.Auxiliary; -using Migration.Toolkit.KXP.Api.Services.CmsClass; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -public class PageTemplateConfigurationMapper : EntityMapperBase -{ - private readonly ILogger _logger; - private readonly SourceInstanceContext _sourceInstanceContext; - - public PageTemplateConfigurationMapper( - ILogger logger, - PrimaryKeyMappingContext pkContext, - IProtocol protocol, - SourceInstanceContext sourceInstanceContext - ) : base(logger, pkContext, protocol) - { - _logger = logger; - _sourceInstanceContext = sourceInstanceContext; - } - - protected override PageTemplateConfigurationInfo? CreateNewInstance(CmsPageTemplateConfiguration source, MappingHelper mappingHelper, - AddFailure addFailure) - => PageTemplateConfigurationInfo.New(); - - protected override PageTemplateConfigurationInfo MapInternal(KX13M.CmsPageTemplateConfiguration source, PageTemplateConfigurationInfo target, - bool newInstance, MappingHelper mappingHelper, AddFailure addFailure) - { - target.PageTemplateConfigurationDescription = source.PageTemplateConfigurationDescription; - target.PageTemplateConfigurationName = source.PageTemplateConfigurationName; - target.PageTemplateConfigurationLastModified = source.PageTemplateConfigurationLastModified; - target.PageTemplateConfigurationIcon = "xp-custom-element"; // TODO tomas.krch: 2023-11-27 some better default icon pick? - - if (newInstance) - { - target.PageTemplateConfigurationGUID = source.PageTemplateConfigurationGuid; - } - - if (_sourceInstanceContext.HasInfo) - { - if (source.PageTemplateConfigurationTemplate != null) - { - var pageTemplateConfiguration = JsonConvert.DeserializeObject(source.PageTemplateConfigurationTemplate); - if (pageTemplateConfiguration?.Identifier != null) - { - _logger.LogTrace("Walk page template configuration {Identifier}", pageTemplateConfiguration.Identifier); - - - var pageTemplateConfigurationFcs = - _sourceInstanceContext.GetPageTemplateFormComponents(source.PageTemplateConfigurationSiteId, pageTemplateConfiguration.Identifier); - if (pageTemplateConfiguration.Properties is { Count: > 0 }) - { - WalkProperties(pageTemplateConfiguration.Properties, pageTemplateConfigurationFcs); - } - - target.PageTemplateConfigurationTemplate = JsonConvert.SerializeObject(pageTemplateConfiguration); - } - } - - if (source.PageTemplateConfigurationWidgets != null) - { - var areas = JsonConvert.DeserializeObject(source.PageTemplateConfigurationWidgets); - if (areas?.EditableAreas is { Count : > 0 }) - { - WalkAreas(source.PageTemplateConfigurationSiteId, areas.EditableAreas); - } - - target.PageTemplateConfigurationWidgets = JsonConvert.SerializeObject(areas); - } - } - else - { - // simply copy if no info is available - target.PageTemplateConfigurationTemplate = source.PageTemplateConfigurationTemplate; - target.PageTemplateConfigurationWidgets = source.PageTemplateConfigurationWidgets; - } - - return target; - } - - // TODO tk: 2022-09-14 move walker logic to separate class - #region "Page template & page widget walkers" - - private void WalkAreas(int siteId, List areas) - { - foreach (var area in areas) - { - _logger.LogTrace("Walk area {Identifier}", area.Identifier); - - if (area.Sections is { Count: > 0 }) - { - WalkSections(siteId, area.Sections); - } - } - } - - private void WalkSections(int siteId, List sections) - { - foreach (var section in sections) - { - _logger.LogTrace("Walk section {TypeIdentifier}|{Identifier}", section.TypeIdentifier, section.Identifier); - - var sectionFcs = _sourceInstanceContext.GetSectionFormComponents(siteId, section.TypeIdentifier); - WalkProperties(section.Properties, sectionFcs); - - if (section.Zones is { Count: > 0 }) - { - WalkZones(siteId, section.Zones); - } - } - } - - private void WalkZones(int siteId, List zones) - { - foreach (var zone in zones) - { - _logger.LogTrace("Walk zone {Name}|{Identifier}", zone.Name, zone.Identifier); - - if (zone.Widgets is { Count: > 0 }) - { - WalkWidgets(siteId, zone.Widgets); - } - } - } - - private void WalkWidgets(int siteId, List widgets) - { - foreach (var widget in widgets) - { - _logger.LogTrace("Walk widget {TypeIdentifier}|{Identifier}", widget.TypeIdentifier, widget.Identifier); - - var widgetFcs = _sourceInstanceContext.GetWidgetPropertyFormComponents(siteId, widget.TypeIdentifier); - foreach (var variant in widget.Variants) - { - _logger.LogTrace("Walk widget variant {Name}|{Identifier}", variant.Name, variant.Identifier); - - if (variant.Properties is { Count: > 0 }) - { - WalkProperties(variant.Properties, widgetFcs); - } - } - } - } - - private void WalkProperties(JObject properties, List? formControlModels) - { - foreach (var (key, value) in properties) - { - _logger.LogTrace("Walk property {Name}|{Identifier}", key, value?.ToString()); - - var editingFcm = formControlModels?.FirstOrDefault(x => x.PropertyName.Equals(key, StringComparison.InvariantCultureIgnoreCase)); - if (editingFcm != null) - { - if (FieldMappingInstance.BuiltInModel.NotSupportedInKxpLegacyMode - .SingleOrDefault(x => x.OldFormComponent == editingFcm.FormComponentIdentifier) is var (oldFormComponent, newFormComponent)) - { - Protocol.Append(HandbookReferences.FormComponentNotSupportedInLegacyMode(oldFormComponent, newFormComponent)); - _logger.LogTrace("Editing form component found {FormComponentName} => no longer supported {Replacement}", - editingFcm.FormComponentIdentifier, newFormComponent); - - switch (oldFormComponent) - { - case Kx13FormComponents.Kentico_AttachmentSelector when newFormComponent == FormComponents.AdminAssetSelectorComponent: - { - if (value?.ToObject>() is { Count: > 0 } items) - { - properties[key] = JToken.FromObject(items.Select(x => new AssetRelatedItem - { - Identifier = x.FileGuid - }).ToList()); - } - - _logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent); - break; - } - case Kx13FormComponents.Kentico_PageSelector when newFormComponent == FormComponents.Kentico_Xperience_Admin_Websites_WebPageSelectorComponent: - { - if (value?.ToObject>() is { Count: > 0 } items) - { - properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem - { -#error "NodeGuid may not be unique, use other means of searching for node!" - WebPageGuid = x.NodeGuid - }).ToList()); - } - - _logger.LogTrace("Value migrated from {Old} model to {New} model", oldFormComponent, newFormComponent); - break; - } - } - } - else if (FieldMappingInstance.BuiltInModel.SupportedInKxpLegacyMode.Contains(editingFcm.FormComponentIdentifier)) - { - // OK - _logger.LogTrace("Editing form component found {FormComponentName} => supported in legacy mode", - editingFcm.FormComponentIdentifier); - } - else - { - // unknown control, probably custom - Protocol.Append(HandbookReferences.FormComponentCustom(editingFcm.FormComponentIdentifier)); - _logger.LogTrace( - "Editing form component found {FormComponentName} => custom or inlined component, don't forget to migrate code accordingly", - editingFcm.FormComponentIdentifier); - } - } - } - } - - #endregion -} \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs index 35d53cf9..7b6941b0 100644 --- a/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs +++ b/Migration.Toolkit.Core.KX13/Services/CmsClass/PageSelectorItem.cs @@ -7,6 +7,5 @@ public class PageSelectorItem { /// Node Guid of a page. [JsonProperty("nodeGuid")] -#error "NodeGuid may not be unique, use other means of searching for node!" public Guid NodeGuid { get; set; } } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs b/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs index a351770b..77f7f6a8 100644 --- a/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs +++ b/Migration.Toolkit.Core.KX13/Services/PrimaryKeyLocatorService.cs @@ -4,36 +4,17 @@ namespace Migration.Toolkit.Core.KX13.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Migration.Toolkit.Common; -using Migration.Toolkit.Common.Services; using Migration.Toolkit.KXP.Context; -public class PrimaryKeyLocatorService : IPrimaryKeyLocatorService +public class PrimaryKeyLocatorService( + ILogger logger, + IDbContextFactory kxpContextFactory, + IDbContextFactory kx13ContextFactory) + : IPrimaryKeyLocatorService { - private readonly ILogger _logger; - private readonly IDbContextFactory _kxpContextFactory; - private readonly IDbContextFactory _kx13ContextFactory; - - public PrimaryKeyLocatorService( - ILogger logger, - IDbContextFactory kxpContextFactory, - IDbContextFactory kx13ContextFactory - ) + private class KeyEqualityComparerWithLambda(Func equalityComparer) : IEqualityComparer { - _logger = logger; - _kxpContextFactory = kxpContextFactory; - _kx13ContextFactory = kx13ContextFactory; - } - - private class KeyEqualityComparerWithLambda : IEqualityComparer - { - private readonly Func _equalityComparer; - - public KeyEqualityComparerWithLambda(Func equalityComparer) - { - _equalityComparer = equalityComparer; - } - - public bool Equals(T? x, T? y) => _equalityComparer.Invoke(x, y); + public bool Equals(T? x, T? y) => equalityComparer.Invoke(x, y); public int GetHashCode(T obj) => obj?.GetHashCode() ?? 0; } @@ -42,13 +23,13 @@ private record CmsUserKey(Guid UserGuid, string UserName); public IEnumerable SelectAll(Expression> keyNameSelector) { - using var kxpContext = _kxpContextFactory.CreateDbContext(); - using var kx13Context = _kx13ContextFactory.CreateDbContext(); + using var kxpContext = kxpContextFactory.CreateDbContext(); + using var kx13Context = kx13ContextFactory.CreateDbContext(); var sourceType = typeof(T); var memberName = keyNameSelector.GetMemberName(); - _logger.LogTrace("Preload of entity {Entity} member {MemberName} mapping requested", sourceType.Name, memberName); + logger.LogTrace("Preload of entity {Entity} member {MemberName} mapping requested", sourceType.Name, memberName); if (sourceType == typeof(Toolkit.KX13.Models.CmsUser) && memberName == nameof(KX13M.CmsUser.UserId)) { @@ -93,27 +74,6 @@ public IEnumerable SelectAll(Expression new { x.NodeId, x.NodeGuid }).ToList(); - var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); - - var result = source.Join(target, -#error "NodeGuid may not be unique, use other means of searching for node!" - a => a.NodeGuid, - b => b.ChannelGuid, - (a, b) => new SourceTargetKeyMapping(a.NodeId, b.ChannelId) - ); - - foreach (var resultingMapping in result) - { - yield return resultingMapping; - } - - yield break; - } - if (sourceType == typeof(Toolkit.KX13.Models.CmsState) && memberName == nameof(KX13M.CmsState.StateId)) { var source = kx13Context.CmsStates.Select(x => new { x.StateId, x.StateName }).ToList(); @@ -157,8 +117,8 @@ public IEnumerable SelectAll(Expression(Expression> keyNameSelector, int sourceId, out int targetId) { - using var kxpContext = _kxpContextFactory.CreateDbContext(); - using var kx13Context = _kx13ContextFactory.CreateDbContext(); + using var kxpContext = kxpContextFactory.CreateDbContext(); + using var kx13Context = kx13ContextFactory.CreateDbContext(); var sourceType = typeof(T); targetId = -1; @@ -226,25 +186,16 @@ public bool TryLocate(Expression> keyNameSelector, int source targetId = kxpContext.OmContacts.Where(x => x.ContactGuid == kx13Guid).Select(x => x.ContactId).Single(); return true; } - - if (sourceType == typeof(Toolkit.KX13.Models.CmsTree)) - { - // careful - cms.root will have different guid -#error "NodeGuid may not be unique, use other means of searching for node!" - var kx13Guid = kx13Context.CmsTrees.Where(c => c.NodeId == sourceId).Select(x => x.NodeGuid).Single(); - targetId = kxpContext.CmsWebPageItems.Where(x => x.WebPageItemGuid == kx13Guid).Select(x => x.WebPageItemId).Single(); - return true; - } } catch (InvalidOperationException ioex) { if (ioex.Message.StartsWith("Sequence contains no elements")) { - _logger.LogDebug("Mapping {SourceFullType} primary key: {SourceId} failed, {Message}", sourceType.FullName, sourceId, ioex.Message); + logger.LogDebug("Mapping {SourceFullType} primary key: {SourceId} failed, {Message}", sourceType.FullName, sourceId, ioex.Message); } else { - _logger.LogWarning("Mapping {SourceFullType} primary key: {SourceId} failed, {Message}", sourceType.FullName, sourceId, ioex.Message); + logger.LogWarning("Mapping {SourceFullType} primary key: {SourceId} failed, {Message}", sourceType.FullName, sourceId, ioex.Message); } return false; } @@ -252,11 +203,11 @@ public bool TryLocate(Expression> keyNameSelector, int source { if (targetId != -1) { - _logger.LogTrace("Mapping {SourceFullType} primary key: {SourceId} to {TargetId}", sourceType.FullName, sourceId, targetId); + logger.LogTrace("Mapping {SourceFullType} primary key: {SourceId} to {TargetId}", sourceType.FullName, sourceId, targetId); } } - _logger.LogError("Mapping {SourceFullType} primary key is not supported", sourceType.FullName); + logger.LogError("Mapping {SourceFullType} primary key is not supported", sourceType.FullName); targetId = -1; return false; } From 4e3c0577a4b56c1b5026a38a6d944c4f24298cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Krch?= Date: Tue, 9 Jul 2024 14:08:20 +0200 Subject: [PATCH 3/3] MT-17 cleanup --- .../Mappers/ContentItemMapper.cs | 1 - .../PageTemplateConfigurationMapper.cs | 1 - .../Services/PrimaryKeyLocatorService.cs | 29 --- .../CmsClass/EditableAreasConfiguration.cs | 187 ------------------ .../Services/CmsClass/PageSelectorItem.cs | 12 -- .../CmsClass/PageTemplateConfiguration.cs | 29 --- 6 files changed, 259 deletions(-) delete mode 100644 Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs delete mode 100644 Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs delete mode 100644 Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs diff --git a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs index 4176d097..9923daa7 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/ContentItemMapper.cs @@ -817,7 +817,6 @@ ICmsClass nodeClass { if (value is string pageReferenceJson) { -#warning [PATCHED] - [VERIFY] "NodeGuid may not be unique, use other means of searching for node!" var parsed = JObject.Parse(pageReferenceJson); foreach (var jToken in parsed.DescendantsAndSelf()) { diff --git a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs index 6d010017..77e9436a 100644 --- a/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs +++ b/KVA/Migration.Toolkit.Source/Mappers/PageTemplateConfigurationMapper.cs @@ -194,7 +194,6 @@ private void WalkProperties(int siteId, JObject properties, List>() is { Count: > 0 } items) { -#warning [PATCHED] - [CHECK] "NodeGuid may not be unique, use other means of searching for node!" properties[key] = JToken.FromObject(items.Select(x => new WebPageRelatedItem { WebPageGuid = spoiledGuidContext.EnsureNodeGuid(x.NodeGuid, siteId) diff --git a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs index 745007f3..58e09482 100644 --- a/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs +++ b/KVA/Migration.Toolkit.Source/Services/PrimaryKeyLocatorService.cs @@ -82,27 +82,6 @@ public IEnumerable SelectAll(Expression().Select(x => new { x.NodeID, x.NodeGUID, x.NodeSiteID }).ToList(); -// var target = kxpContext.CmsChannels.Select(x => new { x.ChannelId, x.ChannelGuid }).ToList(); -// -// var result = source.Join(target, -// #error "NodeGuid may not be unique, use other means of searching for node!" -// a => a.NodeGUID, -// b => b.ChannelGuid, -// (a, b) => new SourceTargetKeyMapping(a.NodeID, b.ChannelId) -// ); -// -// foreach (var resultingMapping in result) -// { -// yield return resultingMapping; -// } -// -// yield break; -// } - if (sourceType == typeof(ICmsState) && memberName == nameof(ICmsState.StateID)) { var source = modelFacade.SelectAll().Select(x => new { x.StateID, x.StateName }).ToList(); @@ -216,14 +195,6 @@ public bool TryLocate(Expression> keyNameSelector, int source targetId = kxpContext.OmContacts.Where(x => x.ContactGuid == sourceGuid).Select(x => x.ContactId).Single(); return true; } - -// if (sourceType == typeof(ICmsTree)) -// { -// #error "NodeGuid may not be unique, use other means of searching for node!" -// var sourceGuid = modelFacade.SelectById(sourceId)?.NodeGUID; -// targetId = kxpContext.CmsChannels.Where(x => x.ChannelGuid == sourceGuid).Select(x => x.ChannelId).Single(); -// return true; -// } } catch (InvalidOperationException ioex) { diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs deleted file mode 100644 index e6d6f4ba..00000000 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/EditableAreasConfiguration.cs +++ /dev/null @@ -1,187 +0,0 @@ -// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; -// -// using System.Runtime.Serialization; -// using Newtonsoft.Json; -// using Newtonsoft.Json.Linq; -// -// #region Copied from Kentico assembly -// -// [DataContract(Name = "Configuration", Namespace = "")] -// public sealed class EditableAreasConfiguration -// { -// /// Editable areas within the page. -// [DataMember] -// [JsonProperty("editableAreas")] -// public List EditableAreas { get; private set; } -// -// /// -// /// Creates an instance of class. -// /// -// public EditableAreasConfiguration() => this.EditableAreas = new List(); -// } -// -// /// -// /// Represents configuration of editable area within the instance. -// /// -// [DataContract(Name = "EditableArea", Namespace = "")] -// public sealed class EditableAreaConfiguration -// { -// /// Identifier of the editable area. -// [DataMember] -// [JsonProperty("identifier")] -// public string Identifier { get; set; } -// -// /// Sections within editable area. -// [DataMember] -// [JsonProperty("sections")] -// public List Sections { get; private set; } -// -// /// -// /// A flag indicating whether the output of the individual widgets within the editable area can be cached. The default value is false. -// /// -// public bool AllowWidgetOutputCache { get; set; } -// -// /// -// /// An absolute expiration date for the cached output of the individual widgets. -// /// -// public DateTimeOffset? WidgetOutputCacheExpiresOn { get; set; } -// -// /// -// /// The length of time from the first request to cache the output of the individual widgets. -// /// -// public TimeSpan? WidgetOutputCacheExpiresAfter { get; set; } -// -// /// -// /// The time after which the cached output of the individual widgets should be evicted if it has not been accessed. -// /// -// public TimeSpan? WidgetOutputCacheExpiresSliding { get; set; } -// -// /// -// /// Creates an instance of class. -// /// -// public EditableAreaConfiguration() => this.Sections = new List(); -// } -// -// /// -// /// Represents configuration of section within the instance. -// /// -// [DataContract(Name = "Section", Namespace = "")] -// public sealed class SectionConfiguration -// { -// /// Identifier of the section. -// [DataMember] -// [JsonProperty("identifier")] -// public Guid Identifier { get; set; } -// -// /// Type section identifier. -// [DataMember] -// [JsonProperty("type")] -// public string TypeIdentifier { get; set; } -// -// /// Section properties. -// [DataMember] -// [JsonProperty("properties")] -// // public ISectionProperties Properties { get; set; } -// public JObject Properties { get; set; } -// -// /// Zones within the section. -// [DataMember] -// [JsonProperty("zones")] -// public List Zones { get; private set; } -// -// /// -// /// Creates an instance of class. -// /// -// public SectionConfiguration() => this.Zones = new List(); -// } -// -// /// -// /// Represents the zone within the configuration class. -// /// -// [DataContract(Name = "Zone", Namespace = "")] -// public sealed class ZoneConfiguration -// { -// /// Identifier of the widget zone. -// [DataMember] -// [JsonProperty("identifier")] -// public Guid Identifier { get; set; } -// -// /// Name of the widget zone. -// [DataMember] -// [JsonProperty("name")] -// public string Name { get; set; } -// -// /// List of widgets within the zone. -// [DataMember] -// [JsonProperty("widgets")] -// public List Widgets { get; private set; } -// -// /// -// /// Creates an instance of class. -// /// -// public ZoneConfiguration() => this.Widgets = new List(); -// } -// -// /// -// /// Represents the configuration of a widget within the list. -// /// -// [DataContract(Name = "Widget", Namespace = "")] -// public sealed class WidgetConfiguration -// { -// /// Identifier of the widget instance. -// [DataMember] -// [JsonProperty("identifier")] -// public Guid Identifier { get; set; } -// -// /// Type widget identifier. -// [DataMember] -// [JsonProperty("type")] -// public string TypeIdentifier { get; set; } -// -// /// Personalization condition type identifier. -// [DataMember] -// [JsonProperty("conditionType")] -// public string PersonalizationConditionTypeIdentifier { get; set; } -// -// /// List of widget variants. -// [DataMember] -// [JsonProperty("variants")] -// public List Variants { get; set; } -// -// /// -// /// Creates an instance of class. -// /// -// public WidgetConfiguration() => this.Variants = new List(); -// } -// -// /// -// /// Represents the configuration variant of a widget within the list. -// /// -// [DataContract(Name = "Variant", Namespace = "")] -// public sealed class WidgetVariantConfiguration -// { -// /// Identifier of the variant instance. -// [DataMember] -// [JsonProperty("identifier")] -// public Guid Identifier { get; set; } -// -// /// Widget variant name. -// [DataMember] -// [JsonProperty("name")] -// public string Name { get; set; } -// -// /// Widget variant properties. -// [DataMember] -// [JsonProperty("properties")] -// // public IWidgetProperties Properties { get; set; } -// public JObject Properties { get; set; } -// -// /// Widget variant personalization condition type. -// /// Only personalization condition type parameters are serialized to JSON. -// [DataMember] -// [JsonProperty("conditionTypeParameters")] -// public JObject PersonalizationConditionType { get; set; } -// } -// -// #endregion -// diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs deleted file mode 100644 index d72c3d73..00000000 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageSelectorItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; -// -// using Newtonsoft.Json; -// -// /// Represents an item for a page selector. -// public class PageSelectorItem -// { -// /// Node Guid of a page. -// [JsonProperty("nodeGuid")] -// #error "NodeGuid may not be unique, use other means of searching for node!" -// public Guid NodeGuid { get; set; } -// } \ No newline at end of file diff --git a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs b/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs deleted file mode 100644 index 5679916d..00000000 --- a/Migration.Toolkit.Core.KX12/Services/CmsClass/PageTemplateConfiguration.cs +++ /dev/null @@ -1,29 +0,0 @@ -// namespace Migration.Toolkit.Core.KX12.Services.CmsClass; -// -// using System.Runtime.Serialization; -// using Newtonsoft.Json; -// using Newtonsoft.Json.Linq; -// -// /// -// /// Page template configuration for the instance. -// /// -// [DataContract(Name = "PageTemplate", Namespace = "")] -// public class PageTemplateConfiguration -// { -// /// Identifier of the page template. -// [DataMember] -// [JsonProperty("identifier")] -// public string Identifier { get; set; } -// -// /// -// /// Identifier of the page template configuration based on which the page was created. -// /// -// [DataMember] -// [JsonProperty("configurationIdentifier")] -// public Guid ConfigurationIdentifier { get; set; } -// -// /// Page template properties. -// [DataMember] -// [JsonProperty("properties")] -// public JObject Properties { get; set; } -// } \ No newline at end of file