Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Rls/v1.6.0 #313

Merged
merged 29 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fd75d40
fix(sln): remove unreferenced Migration.Tool.Model project
seangwright Nov 6, 2024
ca3ba19
docs(README): simplify/re-organize content
seangwright Nov 6, 2024
b352f47
docs(CLI): format README
seangwright Nov 6, 2024
f69f17a
docs(CLI): remove typo
seangwright Nov 6, 2024
c2c473b
docs(sln): format, restructure, grammar/typos
seangwright Nov 6, 2024
a64aaa5
build(github): issue template format, instructions
seangwright Nov 6, 2024
737b82c
docs(README): add links to migration guides
seangwright Nov 6, 2024
a2c6606
Merge branch 'master' into fix/remove-old-docs-and-projects
seangwright Nov 15, 2024
4efd444
#290 treenode context for mapping convertor
tkrch Nov 12, 2024
7f1bc8c
#290 sample update
tkrch Nov 22, 2024
254714b
Merge branch 'rls/v1.6.0' into fix/remove-old-docs-and-projects
seangwright Nov 22, 2024
3e3cb22
Merge pull request #287 from Kentico/fix/remove-old-docs-and-projects
seangwright Nov 22, 2024
6cfa16f
UMT upgrade v1.10.2 (content item with asset image preview)
tkrch Nov 25, 2024
695eb6d
291 truncated content item name fix
tkrch Nov 26, 2024
28d434f
Adjust naming conventions per official document
akfakmot Nov 27, 2024
33fea92
Change XbK* appsettings keys to XbyK* (backwards compatible)
akfakmot Nov 27, 2024
72ea517
Use "Kentico Migration Tool" phrase instead of just "Migration Tool"
akfakmot Nov 28, 2024
77fd690
Merge pull request #311 from Kentico/chore/naming-conventions
akfakmot Nov 28, 2024
d93af1d
Support category migration for mapped classes
akfakmot Nov 29, 2024
f63fd49
Fix formatting
akfakmot Nov 29, 2024
a23cba6
Merge pull request #316 from Kentico/feat/mapped-class-category-mig
akfakmot Dec 2, 2024
260397a
#291 long asset name correctly sanitized
tkrch Nov 28, 2024
8b80317
#291 warning message update
tkrch Dec 4, 2024
d4728aa
version matrix update
tkrch Dec 9, 2024
b129251
#284 ability to define and process custom table mapping to reusable c…
tkrch Dec 8, 2024
e8f262b
#284 default config update ConvertClassesToContentHub
tkrch Dec 8, 2024
3f6afc8
#300 copy field sample
tkrch Dec 8, 2024
deb5c8f
#284 better identifier for custom table item
tkrch Dec 9, 2024
e57fb23
using optimization
tkrch Dec 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
---
name: Bug report
about: Create a report to help us improve

---

### Brief bug description

What went wrong?

## Output logs

Please include the command line output log file and migration protocol generated for your `Migration.Tool.CLI.exe migrate` command.

### Repro steps

1. Go to '...'
Expand All @@ -21,9 +24,9 @@ What is the correct behavior?

### Test environment

- Platform/OS: [e.g. .NET Core 2.1, iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
- Platform/OS: [e.g. .NET Core 2.1, iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

### Additional context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

namespace Migration.Tool.Source.Behaviors;

public class XbKApiContextBehavior<TRequest, TResponse>(
ILogger<XbKApiContextBehavior<TRequest, TResponse>> logger,
public class XbyKApiContextBehavior<TRequest, TResponse>(
ILogger<XbyKApiContextBehavior<TRequest, TResponse>> logger,
IMigrationProtocol protocol,
KxpApiInitializer initializer)
: IPipelineBehavior<TRequest, TResponse>
Expand All @@ -28,9 +28,9 @@ public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TRe
{
protocol.Append(HandbookReferences
.MissingRequiredDependency<UserInfo>()
.WithMessage($"Target XbK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}'). Default administrator account is required for migration.")
.WithMessage($"Target XbyK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}'). Default administrator account is required for migration.")
);
throw new InvalidOperationException($"Target XbK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}')");
throw new InvalidOperationException($"Target XbyK doesn't contain default administrator account ('{UserInfoProvider.DEFAULT_ADMIN_USERNAME}')");
}

using (new CMSActionContext(defaultAdmin) { User = defaultAdmin, UseGlobalAdminContext = true })
Expand Down
2 changes: 2 additions & 0 deletions KVA/Migration.Tool.Source/Contexts/SourceObjectContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
namespace Migration.Tool.Source.Contexts;

public record DocumentSourceObjectContext(ICmsTree CmsTree, ICmsClass NodeClass, ICmsSite Site, FormInfo OldFormInfo, FormInfo NewFormInfo, int? DocumentId) : ISourceObjectContext;

public record CustomTableSourceObjectContext : ISourceObjectContext;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Collections;

using System.Diagnostics;
using CMS.ContentEngine;
using CMS.ContentEngine.Internal;
using CMS.DataEngine;
Expand All @@ -12,12 +12,14 @@

using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;

using Migration.Tool.Common;
using Migration.Tool.Common.Abstractions;
using Migration.Tool.Common.Builders;
using Migration.Tool.Common.Helpers;
using Migration.Tool.KXP.Api;
using Migration.Tool.Source.Mappers;
using Migration.Tool.Source.Model;
using Migration.Tool.Source.Providers;
using Migration.Tool.Source.Services;

using Newtonsoft.Json;
Expand All @@ -30,7 +32,9 @@ public class MigrateCategoriesCommandHandler(
IImporter importer,
ReusableSchemaService reusableSchemaService,
IUmtMapper<TagModelSource> tagModelMapper,
SpoiledGuidContext spoiledGuidContext
SpoiledGuidContext spoiledGuidContext,
KxpClassFacade kxpClassFacade,
ClassMappingProvider classMappingProvider
) : IRequestHandler<MigrateCategoriesCommand, CommandResult>
{
public async Task<CommandResult> Handle(MigrateCategoriesCommand request, CancellationToken cancellationToken)
Expand All @@ -48,26 +52,42 @@ public async Task<CommandResult> Handle(MigrateCategoriesCommand request, Cancel
if (result.Imported is TaxonomyInfo taxonomy)
{
string query = """
SELECT C.ClassName, C.ClassGuid, C.ClassID
SELECT C.ClassName, C.ClassGuid, C.ClassID, CC.CategoryID
FROM View_CMS_Tree_Joined [TJ]
JOIN dbo.CMS_DocumentCategory [CDC] on [TJ].DocumentID = [CDC].DocumentID
JOIN CMS_Class [C] ON TJ.NodeClassID = [C].ClassID
JOIN dbo.CMS_Category CC on CDC.CategoryID = CC.CategoryID AND CC.CategoryUserID IS NULL
GROUP BY C.ClassName, C.ClassGuid, C.ClassID
GROUP BY C.ClassName, C.ClassGuid, C.ClassID, CC.CategoryID
""";

var classesWithCategories = modelFacade.Select(query, (reader, version) => new { ClassName = reader.Unbox<string>("ClassName"), ClassGuid = reader.Unbox<Guid>("ClassGuid"), ClassID = reader.Unbox<int>("ClassID") });
var classesWithCategories = modelFacade.Select(query, (reader, version) => new { ClassName = reader.Unbox<string>("ClassName"), ClassGuid = reader.Unbox<Guid>("ClassGuid"), ClassID = reader.Unbox<int>("ClassID"), CategoryID = reader.Unbox<int>("CategoryID") })
.GroupBy(x => x.ClassGuid)
.Select(x => new { ClassGuid = x.Key, x.First().ClassName, x.First().ClassID, Categories = x.Select(row => row.CategoryID) });

// For each source instance class whose documents have some categories assigned, include taxonomy-storage reusable schema in the target class
#region Ensure reusable schema
var skippedClasses = new List<int>();
var schemaGuid = Guid.Empty;
string categoryFieldName = "Category_Legacy";
foreach (var classWithCategoryUsage in classesWithCategories)
{
var targetDataClass = DataClassInfoProvider.ProviderObject.Get(classWithCategoryUsage.ClassGuid);
if (targetDataClass == null)
if (targetDataClass is null)
{
skippedClasses.Add(classWithCategoryUsage.ClassID);
logger.LogWarning("Data class not found by ClassGuid {Guid}", classWithCategoryUsage.ClassGuid);
// No direct-mapped target class found. Try to identify custom-mapped target class
var classMapping = classMappingProvider.GetMapping(classWithCategoryUsage.ClassName);
if (classMapping is not null)
{
if (classWithCategoryUsage.Categories.Any(cat => classMapping.IsCategoryMapped(classWithCategoryUsage.ClassName, cat)))
{
targetDataClass = kxpClassFacade.GetClass(classMapping.TargetClassName);
}
}
}

if (targetDataClass is null)
{
logger.LogWarning($"Class(ClassGuid {{Guid}}) has documents with categories, but no directly-mapped data class nor custom-mapped class that would receive the categories (declared via {nameof(IClassMapping.IsCategoryMapped)}) was found", classWithCategoryUsage.ClassGuid);
continue;
}

Expand All @@ -82,6 +102,8 @@ FROM View_CMS_Tree_Joined [TJ]
DataClassInfoProvider.SetDataClassInfo(targetDataClass);
}
}
#endregion


var categories = modelFacade.Select<ICmsCategory>(
"CategoryEnabled = 1 AND CategoryUserID IS NULL",
Expand All @@ -95,23 +117,24 @@ FROM View_CMS_Tree_Joined [TJ]
categoryId2Guid.Add(cmsCategory.CategoryID, cmsCategory.CategoryGUID);
// CategorySiteID - not migrated, Taxonomies are global!

var mapped = tagModelMapper.Map(new TagModelSource(
var tagUMTModels = tagModelMapper.Map(new TagModelSource(
taxonomy.TaxonomyGUID,
cmsCategory,
categoryId2Guid
));

foreach (var umtModel in mapped)
foreach (var tagUMTModel in tagUMTModels)
{
if (await importer
.ImportAsync(umtModel)
.ImportAsync(tagUMTModel)
.AssertSuccess<TagInfo>(logger) is { Success: true, Info: { } tag })
{
query = """
SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID
SELECT TJ.DocumentGUID, TJ.NodeSiteID, TJ.NodeID, TJ.DocumentID, CDC.CategoryID, TJ.DocumentCheckedOutVersionHistoryID, TJ.NodeClassID, C.ClassName
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
JOIN CMS_Class [C] ON TJ.NodeClassID = [C].ClassID
WHERE CDC.CategoryID = @categoryId
""";

Expand All @@ -120,6 +143,7 @@ FROM View_CMS_Tree_Joined [TJ]
CategoryID = reader.Unbox<int?>("CategoryID"),
DocumentCheckedOutVersionHistoryID = reader.Unbox<int?>("DocumentCheckedOutVersionHistoryID"),
NodeClassID = reader.Unbox<int>("NodeClassID"),
NodeClassName = reader.Unbox<string>("ClassName"),
NodeSiteID = reader.Unbox<int>("NodeSiteID"),
DocumentGUID = spoiledGuidContext.EnsureDocumentGuid(
reader.Unbox<Guid>("DocumentGUID"),
Expand All @@ -137,13 +161,23 @@ FROM View_CMS_Tree_Joined [TJ]
continue;
}

var classMapping = classMappingProvider.GetMapping(dwc.NodeClassName);
if (classMapping is not null)
{
Debug.Assert(dwc.CategoryID.HasValue, "dwc.CategoryID should have value, otherwise the row would not be included in the query due to inner join");
if (!classMapping.IsCategoryMapped(dwc.NodeClassName, dwc.CategoryID.Value))
{
continue;
}
}

var commonData = ContentItemCommonDataInfo.Provider.Get()
.WhereEquals(nameof(ContentItemCommonDataInfo.ContentItemCommonDataGUID), dwc.DocumentGUID)
.FirstOrDefault();

if (commonData is null)
{
logger.LogWarning("ContentItemCommonDataInfo not found by guid {Guid}, taxonomy cannot be migrated", dwc.DocumentGUID);
logger.LogWarning("ContentItemCommonDataInfo not found by Guid {Guid}. Taxonomy cannot be migrated", dwc.DocumentGUID);
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ private async Task MigrateClasses(EntityConfiguration entityConfiguration, Cance
}
}

// special case - member migration (CMS_User splits into CMS_User and CMS_Member in XbK)
// special case - member migration (CMS_User splits into CMS_User and CMS_Member in XbyK)
await MigrateMemberClass(cancellationToken);
}

Expand Down
Loading
Loading