@kontent-ai/migration-toolkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +13 -0
- package/LICENSE.md +9 -0
- package/README.md +272 -0
- package/dist/es2022/core/helpers/workflow-helper.d.ts +37 -0
- package/dist/es2022/core/helpers/workflow-helper.js +67 -0
- package/dist/es2022/core/helpers/workflow-helper.js.map +1 -0
- package/dist/es2022/core/index.d.ts +15 -0
- package/dist/es2022/core/index.js +16 -0
- package/dist/es2022/core/index.js.map +1 -0
- package/dist/es2022/core/logs/loggers.d.ts +2 -0
- package/dist/es2022/core/logs/loggers.js +69 -0
- package/dist/es2022/core/logs/loggers.js.map +1 -0
- package/dist/es2022/core/models/core.models.d.ts +91 -0
- package/dist/es2022/core/models/core.models.js +2 -0
- package/dist/es2022/core/models/core.models.js.map +1 -0
- package/dist/es2022/core/models/log.models.d.ts +15 -0
- package/dist/es2022/core/models/log.models.js +2 -0
- package/dist/es2022/core/models/log.models.js.map +1 -0
- package/dist/es2022/core/models/migration.models.d.ts +136 -0
- package/dist/es2022/core/models/migration.models.js +2 -0
- package/dist/es2022/core/models/migration.models.js.map +1 -0
- package/dist/es2022/core/utils/array.utils.d.ts +5 -0
- package/dist/es2022/core/utils/array.utils.js +27 -0
- package/dist/es2022/core/utils/array.utils.js.map +1 -0
- package/dist/es2022/core/utils/binary-data.utils.d.ts +5 -0
- package/dist/es2022/core/utils/binary-data.utils.js +21 -0
- package/dist/es2022/core/utils/binary-data.utils.js.map +1 -0
- package/dist/es2022/core/utils/confirm.utils.d.ts +31 -0
- package/dist/es2022/core/utils/confirm.utils.js +73 -0
- package/dist/es2022/core/utils/confirm.utils.js.map +1 -0
- package/dist/es2022/core/utils/error.utils.d.ts +4 -0
- package/dist/es2022/core/utils/error.utils.js +50 -0
- package/dist/es2022/core/utils/error.utils.js.map +1 -0
- package/dist/es2022/core/utils/external-id.utils.d.ts +5 -0
- package/dist/es2022/core/utils/external-id.utils.js +5 -0
- package/dist/es2022/core/utils/external-id.utils.js.map +1 -0
- package/dist/es2022/core/utils/global.utils.d.ts +14 -0
- package/dist/es2022/core/utils/global.utils.js +45 -0
- package/dist/es2022/core/utils/global.utils.js.map +1 -0
- package/dist/es2022/core/utils/http.utils.d.ts +3 -0
- package/dist/es2022/core/utils/http.utils.js +29 -0
- package/dist/es2022/core/utils/http.utils.js.map +1 -0
- package/dist/es2022/core/utils/management-client-utils.d.ts +19 -0
- package/dist/es2022/core/utils/management-client-utils.js +129 -0
- package/dist/es2022/core/utils/management-client-utils.js.map +1 -0
- package/dist/es2022/core/utils/processing-utils.d.ts +12 -0
- package/dist/es2022/core/utils/processing-utils.js +39 -0
- package/dist/es2022/core/utils/processing-utils.js.map +1 -0
- package/dist/es2022/core/utils/run.utils.d.ts +10 -0
- package/dist/es2022/core/utils/run.utils.js +37 -0
- package/dist/es2022/core/utils/run.utils.js.map +1 -0
- package/dist/es2022/export/context/export-context-fetcher.d.ts +4 -0
- package/dist/es2022/export/context/export-context-fetcher.js +289 -0
- package/dist/es2022/export/context/export-context-fetcher.js.map +1 -0
- package/dist/es2022/export/export-manager.d.ts +5 -0
- package/dist/es2022/export/export-manager.js +167 -0
- package/dist/es2022/export/export-manager.js.map +1 -0
- package/dist/es2022/export/export.models.d.ts +55 -0
- package/dist/es2022/export/export.models.js +2 -0
- package/dist/es2022/export/export.models.js.map +1 -0
- package/dist/es2022/export/index.d.ts +2 -0
- package/dist/es2022/export/index.js +3 -0
- package/dist/es2022/export/index.js.map +1 -0
- package/dist/es2022/export/utils/export.utils.d.ts +2 -0
- package/dist/es2022/export/utils/export.utils.js +5 -0
- package/dist/es2022/export/utils/export.utils.js.map +1 -0
- package/dist/es2022/file/file-manager.d.ts +5 -0
- package/dist/es2022/file/file-manager.js +29 -0
- package/dist/es2022/file/file-manager.js.map +1 -0
- package/dist/es2022/file/index.d.ts +1 -0
- package/dist/es2022/file/index.js +2 -0
- package/dist/es2022/file/index.js.map +1 -0
- package/dist/es2022/import/comparers/asset-comparer.d.ts +12 -0
- package/dist/es2022/import/comparers/asset-comparer.js +65 -0
- package/dist/es2022/import/comparers/asset-comparer.js.map +1 -0
- package/dist/es2022/import/context/import-context-fetcher.d.ts +4 -0
- package/dist/es2022/import/context/import-context-fetcher.js +235 -0
- package/dist/es2022/import/context/import-context-fetcher.js.map +1 -0
- package/dist/es2022/import/import-manager.d.ts +4 -0
- package/dist/es2022/import/import-manager.js +80 -0
- package/dist/es2022/import/import-manager.js.map +1 -0
- package/dist/es2022/import/import.models.d.ts +45 -0
- package/dist/es2022/import/import.models.js +2 -0
- package/dist/es2022/import/import.models.js.map +1 -0
- package/dist/es2022/import/importers/assets-importer.d.ts +10 -0
- package/dist/es2022/import/importers/assets-importer.js +175 -0
- package/dist/es2022/import/importers/assets-importer.js.map +1 -0
- package/dist/es2022/import/importers/content-items-importer.d.ts +10 -0
- package/dist/es2022/import/importers/content-items-importer.js +97 -0
- package/dist/es2022/import/importers/content-items-importer.js.map +1 -0
- package/dist/es2022/import/importers/language-variant-importer.d.ts +11 -0
- package/dist/es2022/import/importers/language-variant-importer.js +215 -0
- package/dist/es2022/import/importers/language-variant-importer.js.map +1 -0
- package/dist/es2022/import/importers/workflow-importer.d.ts +18 -0
- package/dist/es2022/import/importers/workflow-importer.js +122 -0
- package/dist/es2022/import/importers/workflow-importer.js.map +1 -0
- package/dist/es2022/import/index.d.ts +2 -0
- package/dist/es2022/import/index.js +3 -0
- package/dist/es2022/import/index.js.map +1 -0
- package/dist/es2022/import/utils/import.utils.d.ts +2 -0
- package/dist/es2022/import/utils/import.utils.js +5 -0
- package/dist/es2022/import/utils/import.utils.js.map +1 -0
- package/dist/es2022/index.d.ts +8 -0
- package/dist/es2022/index.js +10 -0
- package/dist/es2022/index.js.map +1 -0
- package/dist/es2022/metadata.d.ts +5 -0
- package/dist/es2022/metadata.js +6 -0
- package/dist/es2022/metadata.js.map +1 -0
- package/dist/es2022/node/cli/actions/export-action.d.ts +2 -0
- package/dist/es2022/node/cli/actions/export-action.js +40 -0
- package/dist/es2022/node/cli/actions/export-action.js.map +1 -0
- package/dist/es2022/node/cli/actions/import-action.d.ts +2 -0
- package/dist/es2022/node/cli/actions/import-action.js +29 -0
- package/dist/es2022/node/cli/actions/import-action.js.map +1 -0
- package/dist/es2022/node/cli/actions/migrate-action.d.ts +2 -0
- package/dist/es2022/node/cli/actions/migrate-action.js +47 -0
- package/dist/es2022/node/cli/actions/migrate-action.js.map +1 -0
- package/dist/es2022/node/cli/app.d.ts +2 -0
- package/dist/es2022/node/cli/app.js +27 -0
- package/dist/es2022/node/cli/app.js.map +1 -0
- package/dist/es2022/node/cli/args/args-fetcher.d.ts +2 -0
- package/dist/es2022/node/cli/args/args-fetcher.js +46 -0
- package/dist/es2022/node/cli/args/args-fetcher.js.map +1 -0
- package/dist/es2022/node/cli/args/args-setter.d.ts +2 -0
- package/dist/es2022/node/cli/args/args-setter.js +31 -0
- package/dist/es2022/node/cli/args/args-setter.js.map +1 -0
- package/dist/es2022/node/cli/cli.models.d.ts +24 -0
- package/dist/es2022/node/cli/cli.models.js +2 -0
- package/dist/es2022/node/cli/cli.models.js.map +1 -0
- package/dist/es2022/node/cli/commands.d.ts +1 -0
- package/dist/es2022/node/cli/commands.js +157 -0
- package/dist/es2022/node/cli/commands.js.map +1 -0
- package/dist/es2022/toolkit/elements-builder.d.ts +14 -0
- package/dist/es2022/toolkit/elements-builder.js +69 -0
- package/dist/es2022/toolkit/elements-builder.js.map +1 -0
- package/dist/es2022/toolkit/export.d.ts +3 -0
- package/dist/es2022/toolkit/export.js +21 -0
- package/dist/es2022/toolkit/export.js.map +1 -0
- package/dist/es2022/toolkit/file.d.ts +12 -0
- package/dist/es2022/toolkit/file.js +50 -0
- package/dist/es2022/toolkit/file.js.map +1 -0
- package/dist/es2022/toolkit/import.d.ts +2 -0
- package/dist/es2022/toolkit/import.js +21 -0
- package/dist/es2022/toolkit/import.js.map +1 -0
- package/dist/es2022/toolkit/index.d.ts +5 -0
- package/dist/es2022/toolkit/index.js +6 -0
- package/dist/es2022/toolkit/index.js.map +1 -0
- package/dist/es2022/toolkit/migrate.d.ts +21 -0
- package/dist/es2022/toolkit/migrate.js +39 -0
- package/dist/es2022/toolkit/migrate.js.map +1 -0
- package/dist/es2022/translation/extraction/items-extraction.processor.d.ts +24 -0
- package/dist/es2022/translation/extraction/items-extraction.processor.js +108 -0
- package/dist/es2022/translation/extraction/items-extraction.processor.js.map +1 -0
- package/dist/es2022/translation/helpers/rich-text.processor.d.ts +22 -0
- package/dist/es2022/translation/helpers/rich-text.processor.js +209 -0
- package/dist/es2022/translation/helpers/rich-text.processor.js.map +1 -0
- package/dist/es2022/translation/index.d.ts +4 -0
- package/dist/es2022/translation/index.js +5 -0
- package/dist/es2022/translation/index.js.map +1 -0
- package/dist/es2022/translation/transforms/export-transforms.d.ts +3 -0
- package/dist/es2022/translation/transforms/export-transforms.js +196 -0
- package/dist/es2022/translation/transforms/export-transforms.js.map +1 -0
- package/dist/es2022/translation/transforms/import-transforms.d.ts +7 -0
- package/dist/es2022/translation/transforms/import-transforms.js +183 -0
- package/dist/es2022/translation/transforms/import-transforms.js.map +1 -0
- package/dist/es2022/zip/index.d.ts +4 -0
- package/dist/es2022/zip/index.js +5 -0
- package/dist/es2022/zip/index.js.map +1 -0
- package/dist/es2022/zip/zip-manager.d.ts +6 -0
- package/dist/es2022/zip/zip-manager.js +27 -0
- package/dist/es2022/zip/zip-manager.js.map +1 -0
- package/dist/es2022/zip/zip-packager.d.ts +3 -0
- package/dist/es2022/zip/zip-packager.js +69 -0
- package/dist/es2022/zip/zip-packager.js.map +1 -0
- package/dist/es2022/zip/zip-transformer.d.ts +6 -0
- package/dist/es2022/zip/zip-transformer.js +82 -0
- package/dist/es2022/zip/zip-transformer.js.map +1 -0
- package/dist/es2022/zip/zip.models.d.ts +13 -0
- package/dist/es2022/zip/zip.models.js +2 -0
- package/dist/es2022/zip/zip.models.js.map +1 -0
- package/lib/core/helpers/workflow-helper.ts +104 -0
- package/lib/core/index.ts +17 -0
- package/lib/core/logs/loggers.ts +80 -0
- package/lib/core/models/core.models.ts +134 -0
- package/lib/core/models/log.models.ts +31 -0
- package/lib/core/models/migration.models.ts +165 -0
- package/lib/core/utils/array.utils.ts +49 -0
- package/lib/core/utils/binary-data.utils.ts +29 -0
- package/lib/core/utils/confirm.utils.ts +139 -0
- package/lib/core/utils/error.utils.ts +65 -0
- package/lib/core/utils/external-id.utils.ts +9 -0
- package/lib/core/utils/global.utils.ts +59 -0
- package/lib/core/utils/http.utils.ts +35 -0
- package/lib/core/utils/management-client-utils.ts +182 -0
- package/lib/core/utils/processing-utils.ts +71 -0
- package/lib/core/utils/run.utils.ts +51 -0
- package/lib/export/context/export-context-fetcher.ts +475 -0
- package/lib/export/export-manager.ts +249 -0
- package/lib/export/export.models.ts +87 -0
- package/lib/export/index.ts +2 -0
- package/lib/export/utils/export.utils.ts +10 -0
- package/lib/file/file-manager.ts +37 -0
- package/lib/file/index.ts +1 -0
- package/lib/import/comparers/asset-comparer.ts +113 -0
- package/lib/import/context/import-context-fetcher.ts +367 -0
- package/lib/import/import-manager.ts +103 -0
- package/lib/import/import.models.ts +84 -0
- package/lib/import/importers/assets-importer.ts +239 -0
- package/lib/import/importers/content-items-importer.ts +142 -0
- package/lib/import/importers/language-variant-importer.ts +342 -0
- package/lib/import/importers/workflow-importer.ts +159 -0
- package/lib/import/index.ts +2 -0
- package/lib/import/utils/import.utils.ts +10 -0
- package/lib/index.ts +9 -0
- package/lib/metadata.ts +6 -0
- package/lib/node/cli/actions/export-action.ts +45 -0
- package/lib/node/cli/actions/import-action.ts +34 -0
- package/lib/node/cli/actions/migrate-action.ts +52 -0
- package/lib/node/cli/app.ts +29 -0
- package/lib/node/cli/args/args-fetcher.ts +61 -0
- package/lib/node/cli/args/args-setter.ts +35 -0
- package/lib/node/cli/cli.models.ts +28 -0
- package/lib/node/cli/commands.ts +161 -0
- package/lib/toolkit/elements-builder.ts +84 -0
- package/lib/toolkit/export.ts +21 -0
- package/lib/toolkit/file.ts +70 -0
- package/lib/toolkit/import.ts +21 -0
- package/lib/toolkit/index.ts +5 -0
- package/lib/toolkit/migrate.ts +71 -0
- package/lib/translation/extraction/items-extraction.processor.ts +181 -0
- package/lib/translation/helpers/rich-text.processor.ts +300 -0
- package/lib/translation/index.ts +4 -0
- package/lib/translation/transforms/export-transforms.ts +253 -0
- package/lib/translation/transforms/import-transforms.ts +215 -0
- package/lib/zip/index.ts +4 -0
- package/lib/zip/zip-manager.ts +33 -0
- package/lib/zip/zip-packager.ts +86 -0
- package/lib/zip/zip-transformer.ts +105 -0
- package/lib/zip/zip.models.ts +12 -0
- package/package.json +88 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { ExportContext, ExportConfig, ExportItem } from './export.models.js';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { AssetModels, CollectionModels, ElementModels } from '@kontent-ai/management-sdk';
|
|
4
|
+
import {
|
|
5
|
+
MigrationAsset,
|
|
6
|
+
MigrationItem,
|
|
7
|
+
FlattenedContentTypeElement,
|
|
8
|
+
extractErrorData,
|
|
9
|
+
processItemsAsync,
|
|
10
|
+
getBinaryDataFromUrlAsync,
|
|
11
|
+
MigrationElements,
|
|
12
|
+
FlattenedContentType,
|
|
13
|
+
MigrationComponent,
|
|
14
|
+
getDefaultLogger,
|
|
15
|
+
MigrationData,
|
|
16
|
+
isNotUndefined,
|
|
17
|
+
MigrationElementValue,
|
|
18
|
+
getMigrationManagementClient,
|
|
19
|
+
findRequired
|
|
20
|
+
} from '../core/index.js';
|
|
21
|
+
import { exportTransforms } from '../translation/index.js';
|
|
22
|
+
import { exportContextFetcherAsync } from './context/export-context-fetcher.js';
|
|
23
|
+
|
|
24
|
+
export function exportManager(config: ExportConfig) {
|
|
25
|
+
const logger = config.logger ?? getDefaultLogger();
|
|
26
|
+
const managementClient = getMigrationManagementClient(config);
|
|
27
|
+
|
|
28
|
+
const getMigrationItems = (context: ExportContext): readonly MigrationItem[] => {
|
|
29
|
+
return context.exportItems.map<MigrationItem>((exportItem) => mapToMigrationItem(context, exportItem));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const mapToMigrationItem = (context: ExportContext, exportItem: ExportItem): Readonly<MigrationItem> => {
|
|
33
|
+
const migrationItem: MigrationItem = {
|
|
34
|
+
system: {
|
|
35
|
+
name: exportItem.contentItem.name,
|
|
36
|
+
codename: exportItem.contentItem.codename,
|
|
37
|
+
language: { codename: exportItem.language.codename },
|
|
38
|
+
type: { codename: exportItem.contentType.contentTypeCodename },
|
|
39
|
+
collection: { codename: exportItem.collection.codename },
|
|
40
|
+
workflow: {
|
|
41
|
+
codename: exportItem.workflow.codename
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
versions: exportItem.versions.map((version) => {
|
|
45
|
+
return {
|
|
46
|
+
elements: getMigrationElements(context, exportItem.contentType, version.languageVariant.elements),
|
|
47
|
+
workflow_step: {
|
|
48
|
+
codename: version.workflowStepCodename
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return migrationItem;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const mapToMigrationComponent = (
|
|
58
|
+
context: ExportContext,
|
|
59
|
+
component: Readonly<ElementModels.ContentItemElementComponent>
|
|
60
|
+
): MigrationComponent => {
|
|
61
|
+
const componentType = context.environmentData.contentTypes.find((m) => m.contentTypeId === component.type.id);
|
|
62
|
+
|
|
63
|
+
if (!componentType) {
|
|
64
|
+
throw Error(
|
|
65
|
+
`Could not find content type with id '${chalk.red(component.type.id)}' for component '${chalk.red(
|
|
66
|
+
component.id
|
|
67
|
+
)}'`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const migrationItem: MigrationComponent = {
|
|
72
|
+
system: {
|
|
73
|
+
id: component.id,
|
|
74
|
+
type: {
|
|
75
|
+
codename: componentType.contentTypeCodename
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
elements: getMigrationElements(context, componentType, component.elements)
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return migrationItem;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const getMigrationElements = (
|
|
85
|
+
context: ExportContext,
|
|
86
|
+
contentType: FlattenedContentType,
|
|
87
|
+
elements: readonly Readonly<ElementModels.ContentItemElement>[]
|
|
88
|
+
): MigrationElements => {
|
|
89
|
+
return contentType.elements
|
|
90
|
+
.toSorted((a, b) => {
|
|
91
|
+
if (a.codename < b.codename) {
|
|
92
|
+
return -1;
|
|
93
|
+
}
|
|
94
|
+
if (a.codename > b.codename) {
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
})
|
|
99
|
+
.reduce<MigrationElements>((model, typeElement) => {
|
|
100
|
+
const itemElement = findRequired(
|
|
101
|
+
elements,
|
|
102
|
+
(m) => m.element.id === typeElement.id,
|
|
103
|
+
`Could not find element '${chalk.red(typeElement.codename)}'`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
model[typeElement.codename] = {
|
|
107
|
+
type: typeElement.type,
|
|
108
|
+
value: getValueToStoreFromElement({
|
|
109
|
+
context: context,
|
|
110
|
+
contentType: contentType,
|
|
111
|
+
exportElement: itemElement,
|
|
112
|
+
typeElement: typeElement
|
|
113
|
+
})
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return model;
|
|
117
|
+
}, {});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const getValueToStoreFromElement = (data: {
|
|
121
|
+
context: ExportContext;
|
|
122
|
+
contentType: FlattenedContentType;
|
|
123
|
+
typeElement: FlattenedContentTypeElement;
|
|
124
|
+
exportElement: ElementModels.ContentItemElement;
|
|
125
|
+
}): MigrationElementValue => {
|
|
126
|
+
try {
|
|
127
|
+
return exportTransforms[data.typeElement.type]({
|
|
128
|
+
context: data.context,
|
|
129
|
+
typeElement: data.typeElement,
|
|
130
|
+
exportElement: {
|
|
131
|
+
components: data.exportElement.components.map((component) =>
|
|
132
|
+
mapToMigrationComponent(data.context, component)
|
|
133
|
+
),
|
|
134
|
+
value: data.exportElement.value,
|
|
135
|
+
urlSlugMode: data.exportElement.mode
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const errorData = extractErrorData(error);
|
|
140
|
+
let jsonValue = 'n/a';
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
jsonValue = JSON.stringify(data.exportElement.value);
|
|
144
|
+
} catch (jsonError) {
|
|
145
|
+
console.error(`Failed to convert json value`, jsonError);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Failed to map value of element '${chalk.yellow(data.typeElement.codename)}' of type '${chalk.cyan(
|
|
150
|
+
data.typeElement.type
|
|
151
|
+
)}'. Value: ${chalk.bgMagenta(jsonValue)}. Message: ${errorData.message}`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const exportAssetsAsync = async (context: ExportContext): Promise<readonly Readonly<MigrationAsset>[]> => {
|
|
157
|
+
const assets = Array.from(context.referencedData.assetIds)
|
|
158
|
+
.map<Readonly<AssetModels.Asset> | undefined>(
|
|
159
|
+
(assetId) => context.getAssetStateInSourceEnvironment(assetId).asset
|
|
160
|
+
)
|
|
161
|
+
.filter(isNotUndefined);
|
|
162
|
+
|
|
163
|
+
return await getMigrationAssetsWithBinaryDataAsync(assets, context);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const getMigrationAssetsWithBinaryDataAsync = async (
|
|
167
|
+
assets: readonly Readonly<AssetModels.Asset>[],
|
|
168
|
+
context: ExportContext
|
|
169
|
+
): Promise<readonly MigrationAsset[]> => {
|
|
170
|
+
logger.log({
|
|
171
|
+
type: 'info',
|
|
172
|
+
message: `Preparing to download '${chalk.yellow(assets.length.toString())}' assets`
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return await processItemsAsync<Readonly<AssetModels.Asset>, MigrationAsset>({
|
|
176
|
+
action: 'Downloading assets',
|
|
177
|
+
logger: logger,
|
|
178
|
+
parallelLimit: 5,
|
|
179
|
+
itemInfo: (input) => {
|
|
180
|
+
return {
|
|
181
|
+
title: input.codename,
|
|
182
|
+
itemType: 'asset'
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
items: assets,
|
|
186
|
+
processAsync: async (asset, logSpinner) => {
|
|
187
|
+
const assetCollection: Readonly<CollectionModels.Collection> | undefined =
|
|
188
|
+
context.environmentData.collections.find((m) => m.id === asset.collection?.reference?.id);
|
|
189
|
+
|
|
190
|
+
logSpinner({
|
|
191
|
+
type: 'download',
|
|
192
|
+
message: `${asset.url}`
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const binaryData = await getBinaryDataFromUrlAsync(asset.url);
|
|
196
|
+
|
|
197
|
+
const migrationAsset: MigrationAsset = {
|
|
198
|
+
filename: asset.fileName,
|
|
199
|
+
title: asset.title ?? '',
|
|
200
|
+
codename: asset.codename,
|
|
201
|
+
binaryData: binaryData.data,
|
|
202
|
+
collection: assetCollection ? { codename: assetCollection.codename } : undefined,
|
|
203
|
+
descriptions: asset.descriptions.map((description) => {
|
|
204
|
+
const language = findRequired(
|
|
205
|
+
context.environmentData.languages,
|
|
206
|
+
(language) => language.id === description.language.id,
|
|
207
|
+
`Could not find language with id '${chalk.red(
|
|
208
|
+
description.language.id
|
|
209
|
+
)}' requested by asset '${chalk.red(asset.codename)}'`
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
description: description.description ?? undefined,
|
|
214
|
+
language: {
|
|
215
|
+
codename: language.codename
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
})
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return migrationAsset;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
async exportAsync(): Promise<MigrationData> {
|
|
228
|
+
const exportContext = await (
|
|
229
|
+
await exportContextFetcherAsync({
|
|
230
|
+
exportItems: config.exportItems,
|
|
231
|
+
logger: logger,
|
|
232
|
+
managementClient: managementClient
|
|
233
|
+
})
|
|
234
|
+
).getExportContextAsync();
|
|
235
|
+
|
|
236
|
+
const migrationData: MigrationData = {
|
|
237
|
+
items: getMigrationItems(exportContext),
|
|
238
|
+
assets: await exportAssetsAsync(exportContext)
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
logger.log({
|
|
242
|
+
type: 'completed',
|
|
243
|
+
message: `Finished export`
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return migrationData;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Logger,
|
|
3
|
+
FlattenedContentType,
|
|
4
|
+
AssetStateInSourceEnvironmentById,
|
|
5
|
+
FlattenedContentTypeElement,
|
|
6
|
+
ItemStateInSourceEnvironmentById,
|
|
7
|
+
ReferencedDataInLanguageVariants,
|
|
8
|
+
MigrationElementValue,
|
|
9
|
+
MigrationComponent,
|
|
10
|
+
MigrationUrlSlugMode,
|
|
11
|
+
ManagementClientConfig
|
|
12
|
+
} from '../core/index.js';
|
|
13
|
+
import {
|
|
14
|
+
LanguageVariantModels,
|
|
15
|
+
ContentItemModels,
|
|
16
|
+
WorkflowModels,
|
|
17
|
+
CollectionModels,
|
|
18
|
+
LanguageModels,
|
|
19
|
+
TaxonomyModels,
|
|
20
|
+
ManagementClient,
|
|
21
|
+
SharedModels
|
|
22
|
+
} from '@kontent-ai/management-sdk';
|
|
23
|
+
|
|
24
|
+
export interface ExportContextEnvironmentData {
|
|
25
|
+
readonly languages: readonly Readonly<LanguageModels.LanguageModel>[];
|
|
26
|
+
readonly contentTypes: readonly Readonly<FlattenedContentType>[];
|
|
27
|
+
readonly collections: readonly Readonly<CollectionModels.Collection>[];
|
|
28
|
+
readonly workflows: readonly Readonly<WorkflowModels.Workflow>[];
|
|
29
|
+
readonly taxonomies: readonly Readonly<TaxonomyModels.Taxonomy>[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ExportElementValue = string | number | SharedModels.ReferenceObject[] | undefined;
|
|
33
|
+
|
|
34
|
+
export interface ExportElement {
|
|
35
|
+
readonly value: ExportElementValue;
|
|
36
|
+
readonly components: readonly MigrationComponent[];
|
|
37
|
+
readonly urlSlugMode: MigrationUrlSlugMode | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type ExportTransformFunc = (data: {
|
|
41
|
+
readonly typeElement: FlattenedContentTypeElement;
|
|
42
|
+
readonly exportElement: ExportElement;
|
|
43
|
+
readonly context: ExportContext;
|
|
44
|
+
}) => MigrationElementValue;
|
|
45
|
+
|
|
46
|
+
export interface ExportContext {
|
|
47
|
+
readonly environmentData: ExportContextEnvironmentData;
|
|
48
|
+
readonly referencedData: ReferencedDataInLanguageVariants;
|
|
49
|
+
readonly getItemStateInSourceEnvironment: (id: string) => ItemStateInSourceEnvironmentById;
|
|
50
|
+
readonly getAssetStateInSourceEnvironment: (id: string) => AssetStateInSourceEnvironmentById;
|
|
51
|
+
readonly exportItems: readonly ExportItem[];
|
|
52
|
+
readonly getElement: GetFlattenedElementByIds;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface SourceExportItem {
|
|
56
|
+
readonly itemCodename: string;
|
|
57
|
+
readonly languageCodename: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ExportConfig extends ManagementClientConfig {
|
|
61
|
+
readonly exportItems: readonly SourceExportItem[];
|
|
62
|
+
readonly logger?: Logger;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface DefaultExportContextConfig {
|
|
66
|
+
readonly logger: Logger;
|
|
67
|
+
readonly exportItems: readonly SourceExportItem[];
|
|
68
|
+
readonly managementClient: Readonly<ManagementClient>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type GetFlattenedElementByIds = (contentTypeId: string, elementId: string) => FlattenedContentTypeElement;
|
|
72
|
+
|
|
73
|
+
export interface ExportItemVersion {
|
|
74
|
+
readonly workflowStepCodename: string;
|
|
75
|
+
readonly languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ExportItem {
|
|
79
|
+
readonly requestItem: SourceExportItem;
|
|
80
|
+
readonly versions: readonly ExportItemVersion[];
|
|
81
|
+
readonly contentItem: Readonly<ContentItemModels.ContentItem>;
|
|
82
|
+
|
|
83
|
+
readonly collection: Readonly<CollectionModels.Collection>;
|
|
84
|
+
readonly language: Readonly<LanguageModels.LanguageModel>;
|
|
85
|
+
readonly workflow: Readonly<WorkflowModels.Workflow>;
|
|
86
|
+
readonly contentType: Readonly<FlattenedContentType>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { SourceExportItem } from '../export.models.js';
|
|
3
|
+
|
|
4
|
+
export function throwErrorForItemRequest(itemRequest: SourceExportItem, message: string): never {
|
|
5
|
+
throw Error(
|
|
6
|
+
`Export failed for item '${chalk.yellow(itemRequest.itemCodename)}' in language '${chalk.cyan(
|
|
7
|
+
itemRequest.languageCodename
|
|
8
|
+
)}'. Reason: ${message}`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Logger } from '../core/index.js';
|
|
3
|
+
import { promises } from 'fs';
|
|
4
|
+
|
|
5
|
+
export function fileManager(logger: Logger) {
|
|
6
|
+
const getFilePath = (filename: string): string => {
|
|
7
|
+
return `./${filename}`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const loadFileAsync = async (filename: string): Promise<Buffer> => {
|
|
11
|
+
const filePath = getFilePath(filename);
|
|
12
|
+
|
|
13
|
+
logger.log({
|
|
14
|
+
type: 'readFs',
|
|
15
|
+
message: `Reading file '${chalk.yellow(filePath)}'`
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const file = await promises.readFile(filePath);
|
|
19
|
+
|
|
20
|
+
return file;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const writeFileAsync = async (fileNameWithoutExtension: string, content: string | Buffer): Promise<void> => {
|
|
24
|
+
const filePath = getFilePath(fileNameWithoutExtension);
|
|
25
|
+
|
|
26
|
+
logger.log({
|
|
27
|
+
type: 'writeFs',
|
|
28
|
+
message: `Storing file '${chalk.yellow(filePath)}'`
|
|
29
|
+
});
|
|
30
|
+
await promises.writeFile(filePath, content);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
loadFileAsync,
|
|
35
|
+
writeFileAsync
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './file-manager.js';
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { AssetModels, CollectionModels, LanguageModels } from '@kontent-ai/management-sdk';
|
|
2
|
+
import { MigrationAsset, MigrationAssetDescription, findRequired, geSizeInBytes } from '../../core/index.js';
|
|
3
|
+
import deepEqual from 'deep-equal';
|
|
4
|
+
|
|
5
|
+
export function shouldUpdateAsset(data: {
|
|
6
|
+
readonly migrationAsset: MigrationAsset;
|
|
7
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
8
|
+
readonly collections: readonly Readonly<CollectionModels.Collection>[];
|
|
9
|
+
readonly languages: readonly Readonly<LanguageModels.LanguageModel>[];
|
|
10
|
+
}): boolean {
|
|
11
|
+
if (!isInSameCollection(data)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!areDescriptionsIdentical(data)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!isTitleIdentical(data)) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!isBinaryFileIdentical(data)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function shouldReplaceBinaryFile(data: {
|
|
31
|
+
readonly migrationAsset: MigrationAsset;
|
|
32
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
33
|
+
}): boolean {
|
|
34
|
+
if (!isBinaryFileIdentical(data)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isBinaryFileIdentical(data: {
|
|
42
|
+
readonly migrationAsset: MigrationAsset;
|
|
43
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
44
|
+
}): boolean {
|
|
45
|
+
const sourceFileSize = geSizeInBytes(data.migrationAsset.binaryData);
|
|
46
|
+
const targetFileSize = data.targetAsset.size;
|
|
47
|
+
|
|
48
|
+
const sourceFilename = data.migrationAsset.filename;
|
|
49
|
+
const targetFilename = data.targetAsset.fileName;
|
|
50
|
+
|
|
51
|
+
return sourceFileSize === targetFileSize && sourceFilename === targetFilename;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isTitleIdentical(data: {
|
|
55
|
+
readonly migrationAsset: MigrationAsset;
|
|
56
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
57
|
+
}): boolean {
|
|
58
|
+
const sourceTitle = data.migrationAsset.title?.length ? data.migrationAsset.title : undefined;
|
|
59
|
+
const targetTitle = data.targetAsset.title?.length ? data.targetAsset.title : undefined;
|
|
60
|
+
|
|
61
|
+
return sourceTitle === targetTitle;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isInSameCollection(data: {
|
|
65
|
+
readonly migrationAsset: MigrationAsset;
|
|
66
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
67
|
+
readonly collections: readonly Readonly<CollectionModels.Collection>[];
|
|
68
|
+
}): boolean {
|
|
69
|
+
return (
|
|
70
|
+
data.collections.find((m) => m.id === data.targetAsset.collection?.reference?.id)?.codename ===
|
|
71
|
+
data.migrationAsset.collection?.codename
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function areDescriptionsIdentical(data: {
|
|
76
|
+
readonly migrationAsset: MigrationAsset;
|
|
77
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
78
|
+
readonly languages: readonly Readonly<LanguageModels.LanguageModel>[];
|
|
79
|
+
}): boolean {
|
|
80
|
+
const sourceMigrationDescriptions = (data.migrationAsset.descriptions ?? [])
|
|
81
|
+
.map<MigrationAssetDescription>((description) => {
|
|
82
|
+
return {
|
|
83
|
+
description: description.description?.length ? description.description : undefined,
|
|
84
|
+
language: {
|
|
85
|
+
codename: description.language.codename
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
.toSorted();
|
|
90
|
+
const targetMigrationDescriptions = mapToMigrationDescriptions(data).toSorted();
|
|
91
|
+
|
|
92
|
+
return deepEqual(sourceMigrationDescriptions, targetMigrationDescriptions);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function mapToMigrationDescriptions(data: {
|
|
96
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
97
|
+
readonly languages: readonly Readonly<LanguageModels.LanguageModel>[];
|
|
98
|
+
}): MigrationAssetDescription[] {
|
|
99
|
+
return data.targetAsset.descriptions.map((description) => {
|
|
100
|
+
const languageId = description.language.id;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
description: description.description?.length ? description.description : undefined,
|
|
104
|
+
language: {
|
|
105
|
+
codename: findRequired(
|
|
106
|
+
data.languages,
|
|
107
|
+
(language) => language.id === languageId,
|
|
108
|
+
`Could not find language with id '${languageId}'`
|
|
109
|
+
).codename
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|