@kontent-ai/migration-toolkit 2.2.2 → 2.3.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/README.md +10 -9
- package/dist/es2022/core/logs/loggers.js +4 -1
- package/dist/es2022/core/logs/loggers.js.map +1 -1
- package/dist/es2022/core/models/core.models.d.ts +12 -0
- package/dist/es2022/core/models/log.models.d.ts +1 -1
- package/dist/es2022/core/models/migration.schema.d.ts +12 -12
- package/dist/es2022/core/models/migration.schema.js +2 -2
- package/dist/es2022/core/models/migration.schema.js.map +1 -1
- package/dist/es2022/core/utils/binary-data.utils.d.ts +4 -4
- package/dist/es2022/core/utils/binary-data.utils.js +2 -2
- package/dist/es2022/core/utils/binary-data.utils.js.map +1 -1
- package/dist/es2022/core/utils/global.utils.d.ts +3 -3
- package/dist/es2022/core/utils/global.utils.js +1 -1
- package/dist/es2022/core/utils/global.utils.js.map +1 -1
- package/dist/es2022/core/utils/http.utils.js +0 -3
- package/dist/es2022/core/utils/http.utils.js.map +1 -1
- package/dist/es2022/core/utils/processing-utils.d.ts +3 -3
- package/dist/es2022/core/utils/processing-utils.js +31 -4
- package/dist/es2022/core/utils/processing-utils.js.map +1 -1
- package/dist/es2022/export/context/export-context-fetcher.js +13 -7
- package/dist/es2022/export/context/export-context-fetcher.js.map +1 -1
- package/dist/es2022/export/export-manager.js +5 -4
- package/dist/es2022/export/export-manager.js.map +1 -1
- package/dist/es2022/file/file-manager.d.ts +3 -2
- package/dist/es2022/file/file-manager.js.map +1 -1
- package/dist/es2022/import/context/import-context-fetcher.js +14 -8
- package/dist/es2022/import/context/import-context-fetcher.js.map +1 -1
- package/dist/es2022/import/import-manager.d.ts +4 -0
- package/dist/es2022/import/import-manager.js +133 -4
- package/dist/es2022/import/import-manager.js.map +1 -1
- package/dist/es2022/import/import.models.d.ts +15 -5
- package/dist/es2022/import/importers/assets-importer.js.map +1 -1
- package/dist/es2022/import/importers/content-items-importer.d.ts +3 -3
- package/dist/es2022/import/importers/content-items-importer.js +11 -5
- package/dist/es2022/import/importers/content-items-importer.js.map +1 -1
- package/dist/es2022/import/importers/language-variant-importer.d.ts +4 -4
- package/dist/es2022/import/importers/language-variant-importer.js +4 -4
- package/dist/es2022/import/importers/language-variant-importer.js.map +1 -1
- package/dist/es2022/metadata.js +2 -2
- package/dist/es2022/node/cli/actions/import-action.js +3 -1
- package/dist/es2022/node/cli/actions/import-action.js.map +1 -1
- package/dist/es2022/toolkit/file.js +2 -2
- package/dist/es2022/toolkit/file.js.map +1 -1
- package/dist/es2022/toolkit/import.js +14 -2
- package/dist/es2022/toolkit/import.js.map +1 -1
- package/dist/es2022/translation/extraction/items-extraction.processor.d.ts +1 -1
- package/dist/es2022/translation/extraction/items-extraction.processor.js.map +1 -1
- package/dist/es2022/zip/zip-manager.d.ts +2 -2
- package/dist/es2022/zip/zip-manager.js.map +1 -1
- package/dist/es2022/zip/zip-packager.js +3 -4
- package/dist/es2022/zip/zip-packager.js.map +1 -1
- package/dist/es2022/zip/zip-transformer.js.map +1 -1
- package/dist/es2022/zip/zip.models.d.ts +4 -4
- package/lib/core/logs/loggers.ts +5 -1
- package/lib/core/models/core.models.ts +16 -0
- package/lib/core/models/log.models.ts +1 -1
- package/lib/core/models/migration.schema.ts +2 -2
- package/lib/core/utils/binary-data.utils.ts +6 -6
- package/lib/core/utils/global.utils.ts +8 -5
- package/lib/core/utils/http.utils.ts +1 -4
- package/lib/core/utils/processing-utils.ts +48 -18
- package/lib/export/context/export-context-fetcher.ts +67 -60
- package/lib/export/export-manager.ts +58 -56
- package/lib/file/file-manager.ts +4 -3
- package/lib/import/context/import-context-fetcher.ts +18 -13
- package/lib/import/import-manager.ts +174 -10
- package/lib/import/import.models.ts +20 -4
- package/lib/import/importers/assets-importer.ts +3 -9
- package/lib/import/importers/content-items-importer.ts +18 -17
- package/lib/import/importers/language-variant-importer.ts +32 -34
- package/lib/metadata.ts +2 -2
- package/lib/node/cli/actions/import-action.ts +3 -1
- package/lib/toolkit/file.ts +2 -2
- package/lib/toolkit/import.ts +16 -2
- package/lib/translation/extraction/items-extraction.processor.ts +3 -3
- package/lib/zip/zip-manager.ts +2 -2
- package/lib/zip/zip-packager.ts +3 -4
- package/lib/zip/zip-transformer.ts +1 -1
- package/lib/zip/zip.models.ts +3 -3
- package/package.json +13 -13
|
@@ -45,8 +45,8 @@ export function exportManager(config: ExportConfig) {
|
|
|
45
45
|
codename: exportItem.workflow.codename
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
|
-
versions: exportItem.versions.map((version) => {
|
|
49
|
-
return
|
|
48
|
+
versions: exportItem.versions.map<MigrationItemVersion>((version) => {
|
|
49
|
+
return {
|
|
50
50
|
elements: getMigrationElements(context, exportItem.contentType, version.languageVariant.elements),
|
|
51
51
|
schedule: {
|
|
52
52
|
publish_time: version.languageVariant.schedule.publishTime ?? undefined,
|
|
@@ -124,10 +124,10 @@ export function exportManager(config: ExportConfig) {
|
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
const getMigrationElementToStore = (data: {
|
|
127
|
-
context: ExportContext;
|
|
128
|
-
contentType: FlattenedContentType;
|
|
129
|
-
typeElement: FlattenedContentTypeElement;
|
|
130
|
-
exportElement: ElementModels.ContentItemElement;
|
|
127
|
+
readonly context: ExportContext;
|
|
128
|
+
readonly contentType: FlattenedContentType;
|
|
129
|
+
readonly typeElement: FlattenedContentTypeElement;
|
|
130
|
+
readonly exportElement: ElementModels.ContentItemElement;
|
|
131
131
|
}): MigrationElementTransformData => {
|
|
132
132
|
try {
|
|
133
133
|
return exportTransforms[data.typeElement.type]({
|
|
@@ -175,60 +175,62 @@ export function exportManager(config: ExportConfig) {
|
|
|
175
175
|
message: `Preparing to download '${chalk.yellow(assets.length.toString())}' assets`
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
178
|
+
return (
|
|
179
|
+
await processItemsAsync<Readonly<AssetModels.Asset>, MigrationAsset>({
|
|
180
|
+
action: 'Downloading assets',
|
|
181
|
+
logger: logger,
|
|
182
|
+
parallelLimit: 5,
|
|
183
|
+
itemInfo: (input) => {
|
|
184
|
+
return {
|
|
185
|
+
title: input.codename,
|
|
186
|
+
itemType: 'asset'
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
items: assets,
|
|
190
|
+
processAsync: async (asset, logSpinner) => {
|
|
191
|
+
const assetCollection: Readonly<CollectionModels.Collection> | undefined = context.environmentData.collections.find(
|
|
192
|
+
(m) => m.id === asset.collection?.reference?.id
|
|
193
|
+
);
|
|
194
|
+
const assetFolder: Readonly<AssetFolderModels.AssetFolder> | undefined = context.environmentData.assetFolders.find(
|
|
195
|
+
(m) => m.id === asset.folder?.id
|
|
196
|
+
);
|
|
196
197
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
logSpinner({
|
|
199
|
+
type: 'download',
|
|
200
|
+
message: `${asset.url}`
|
|
201
|
+
});
|
|
201
202
|
|
|
202
|
-
|
|
203
|
+
const migrationAsset: MigrationAsset = {
|
|
204
|
+
filename: asset.fileName,
|
|
205
|
+
title: asset.title ?? '',
|
|
206
|
+
codename: asset.codename,
|
|
207
|
+
binary_data: (await getBinaryDataFromUrlAsync(asset.url)).data,
|
|
208
|
+
collection: assetCollection ? { codename: assetCollection.codename } : undefined,
|
|
209
|
+
folder: assetFolder ? { codename: assetFolder.codename } : undefined,
|
|
210
|
+
descriptions: asset.descriptions.map((description) => {
|
|
211
|
+
const language = findRequired(
|
|
212
|
+
context.environmentData.languages,
|
|
213
|
+
(language) => language.id === description.language.id,
|
|
214
|
+
`Could not find language with id '${chalk.red(description.language.id)}' requested by asset '${chalk.red(
|
|
215
|
+
asset.codename
|
|
216
|
+
)}'`
|
|
217
|
+
);
|
|
203
218
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const language = findRequired(
|
|
213
|
-
context.environmentData.languages,
|
|
214
|
-
(language) => language.id === description.language.id,
|
|
215
|
-
`Could not find language with id '${chalk.red(description.language.id)}' requested by asset '${chalk.red(
|
|
216
|
-
asset.codename
|
|
217
|
-
)}'`
|
|
218
|
-
);
|
|
219
|
+
return {
|
|
220
|
+
description: description.description ?? undefined,
|
|
221
|
+
language: {
|
|
222
|
+
codename: language.codename
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
})
|
|
226
|
+
};
|
|
219
227
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
})
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
return migrationAsset;
|
|
230
|
-
}
|
|
231
|
-
});
|
|
228
|
+
return migrationAsset;
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
)
|
|
232
|
+
.filter((m) => m.state === 'valid')
|
|
233
|
+
.map((m) => m.outputItem);
|
|
232
234
|
};
|
|
233
235
|
|
|
234
236
|
return {
|
package/lib/file/file-manager.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { Buffer as BufferProxy } from 'buffer';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
|
-
import { Logger } from '../core/index.js';
|
|
3
3
|
import { promises } from 'fs';
|
|
4
|
+
import { Logger } from '../core/index.js';
|
|
4
5
|
|
|
5
6
|
export function fileManager(logger: Logger) {
|
|
6
7
|
const getFilePath = (filename: string): string => {
|
|
7
8
|
return `./${filename}`;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
|
-
const loadFileAsync = async (filename: string): Promise<
|
|
11
|
+
const loadFileAsync = async (filename: string): Promise<BufferProxy> => {
|
|
11
12
|
const filePath = getFilePath(filename);
|
|
12
13
|
|
|
13
14
|
logger.log({
|
|
@@ -20,7 +21,7 @@ export function fileManager(logger: Logger) {
|
|
|
20
21
|
return file;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
const writeFileAsync = async (fileNameWithoutExtension: string, content: string |
|
|
24
|
+
const writeFileAsync = async (fileNameWithoutExtension: string, content: string | BufferProxy): Promise<void> => {
|
|
24
25
|
const filePath = getFilePath(fileNameWithoutExtension);
|
|
25
26
|
|
|
26
27
|
logger.log({
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AssetModels, ContentItemModels, LanguageVariantModels } from '@kontent-ai/management-sdk';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { match } from 'ts-pattern';
|
|
2
4
|
import {
|
|
3
5
|
AssetStateInTargetEnvironmentByCodename,
|
|
4
6
|
ItemStateInTargetEnvironmentByCodename,
|
|
@@ -12,16 +14,13 @@ import {
|
|
|
12
14
|
WorkflowStep,
|
|
13
15
|
findRequired,
|
|
14
16
|
is404Error,
|
|
15
|
-
isNotUndefined,
|
|
16
17
|
managementClientUtils,
|
|
17
18
|
processItemsAsync,
|
|
18
19
|
runMapiRequestAsync,
|
|
19
20
|
workflowHelper as workflowHelperInit
|
|
20
21
|
} from '../../core/index.js';
|
|
21
|
-
import { GetFlattenedElementByCodenames, ImportContext, ImportContextConfig, ImportContextEnvironmentData } from '../import.models.js';
|
|
22
22
|
import { ExtractItemByCodename, itemsExtractionProcessor } from '../../translation/index.js';
|
|
23
|
-
import
|
|
24
|
-
import { match } from 'ts-pattern';
|
|
23
|
+
import { GetFlattenedElementByCodenames, ImportContext, ImportContextConfig, ImportContextEnvironmentData } from '../import.models.js';
|
|
25
24
|
|
|
26
25
|
interface LanguageVariantWrapper {
|
|
27
26
|
readonly draftLanguageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined;
|
|
@@ -150,7 +149,7 @@ export async function importContextFetcherAsync(config: ImportContextConfig) {
|
|
|
150
149
|
migrationItems: readonly MigrationItem[]
|
|
151
150
|
): Promise<readonly LanguageVariantWrapper[]> => {
|
|
152
151
|
return (
|
|
153
|
-
await processItemsAsync<MigrationItem, LanguageVariantWrapper
|
|
152
|
+
await processItemsAsync<MigrationItem, LanguageVariantWrapper>({
|
|
154
153
|
action: 'Fetching language variants',
|
|
155
154
|
logger: config.logger,
|
|
156
155
|
parallelLimit: 1,
|
|
@@ -166,7 +165,7 @@ export async function importContextFetcherAsync(config: ImportContextConfig) {
|
|
|
166
165
|
|
|
167
166
|
if (!latestLanguageVariant) {
|
|
168
167
|
// there is neither published or draft version as latest version does not exist at all
|
|
169
|
-
return
|
|
168
|
+
return '404';
|
|
170
169
|
}
|
|
171
170
|
|
|
172
171
|
if (workflowHelper.isPublishedStepById(latestLanguageVariant.workflow.stepIdentifier.id ?? '')) {
|
|
@@ -186,14 +185,16 @@ export async function importContextFetcherAsync(config: ImportContextConfig) {
|
|
|
186
185
|
};
|
|
187
186
|
}
|
|
188
187
|
})
|
|
189
|
-
)
|
|
188
|
+
)
|
|
189
|
+
.filter((m) => m.state === 'valid')
|
|
190
|
+
.map((m) => m.outputItem);
|
|
190
191
|
};
|
|
191
192
|
|
|
192
193
|
const getContentItemsByCodenamesAsync = async (
|
|
193
194
|
itemCodenames: ReadonlySet<string>
|
|
194
195
|
): Promise<readonly ContentItemModels.ContentItem[]> => {
|
|
195
196
|
return (
|
|
196
|
-
await processItemsAsync<string, Readonly<ContentItemModels.ContentItem
|
|
197
|
+
await processItemsAsync<string, Readonly<ContentItemModels.ContentItem>>({
|
|
197
198
|
action: 'Fetching content items',
|
|
198
199
|
logger: config.logger,
|
|
199
200
|
parallelLimit: 1,
|
|
@@ -219,16 +220,18 @@ export async function importContextFetcherAsync(config: ImportContextConfig) {
|
|
|
219
220
|
throw error;
|
|
220
221
|
}
|
|
221
222
|
|
|
222
|
-
return
|
|
223
|
+
return '404';
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
226
|
})
|
|
226
|
-
)
|
|
227
|
+
)
|
|
228
|
+
.filter((m) => m.state === 'valid')
|
|
229
|
+
.map((m) => m.outputItem);
|
|
227
230
|
};
|
|
228
231
|
|
|
229
232
|
const getAssetsByCodenamesAsync = async (assetCodenames: ReadonlySet<string>): Promise<readonly AssetModels.Asset[]> => {
|
|
230
233
|
return (
|
|
231
|
-
await processItemsAsync<string, Readonly<AssetModels.Asset
|
|
234
|
+
await processItemsAsync<string, Readonly<AssetModels.Asset>>({
|
|
232
235
|
action: 'Fetching assets',
|
|
233
236
|
logger: config.logger,
|
|
234
237
|
parallelLimit: 1,
|
|
@@ -254,11 +257,13 @@ export async function importContextFetcherAsync(config: ImportContextConfig) {
|
|
|
254
257
|
throw error;
|
|
255
258
|
}
|
|
256
259
|
|
|
257
|
-
return
|
|
260
|
+
return '404';
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
})
|
|
261
|
-
)
|
|
264
|
+
)
|
|
265
|
+
.filter((m) => m.state === 'valid')
|
|
266
|
+
.map((m) => m.outputItem);
|
|
262
267
|
};
|
|
263
268
|
|
|
264
269
|
const getVariantState = (languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>): LanguageVariantStateData => {
|
|
@@ -1,12 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { defaultExternalIdGenerator, getDefaultLogger, getMigrationManagementClient, Logger } from '../core/index.js';
|
|
1
|
+
import { ManagementClient } from '@kontent-ai/management-sdk';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { defaultExternalIdGenerator, extractErrorData, getDefaultLogger, getMigrationManagementClient, Logger } from '../core/index.js';
|
|
4
4
|
import { importContextFetcherAsync } from './context/import-context-fetcher.js';
|
|
5
|
-
import { ImportConfig, ImportContext, ImportResult } from './import.models.js';
|
|
5
|
+
import { ImportConfig, ImportContext, ImportedItem, ImportedLanguageVariant, ImportResult } from './import.models.js';
|
|
6
6
|
import { assetsImporter } from './importers/assets-importer.js';
|
|
7
7
|
import { contentItemsImporter } from './importers/content-items-importer.js';
|
|
8
8
|
import { languageVariantImporter } from './importers/language-variant-importer.js';
|
|
9
9
|
|
|
10
|
+
const reportFilename: string = `import-report.json`;
|
|
11
|
+
|
|
12
|
+
type ReportResult = {
|
|
13
|
+
readonly errorsCount: number;
|
|
14
|
+
readonly assets: {
|
|
15
|
+
readonly count: number;
|
|
16
|
+
readonly successful: Array<{ readonly codename: string }>;
|
|
17
|
+
readonly failed: Array<{ readonly codename: string; readonly error: string }>;
|
|
18
|
+
};
|
|
19
|
+
readonly languageVariants: {
|
|
20
|
+
readonly count: number;
|
|
21
|
+
readonly successful: Array<{
|
|
22
|
+
readonly codename: string;
|
|
23
|
+
readonly language: { readonly codename: string };
|
|
24
|
+
readonly type: { readonly codename: string };
|
|
25
|
+
}>;
|
|
26
|
+
readonly failed: Array<{
|
|
27
|
+
readonly codename: string;
|
|
28
|
+
readonly language: { readonly codename: string };
|
|
29
|
+
readonly type: { readonly codename: string };
|
|
30
|
+
readonly error: string;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
readonly contentItems: {
|
|
34
|
+
readonly count: number;
|
|
35
|
+
readonly successful: Array<{ readonly codename: string }>;
|
|
36
|
+
readonly failed: Array<{ readonly codename: string; readonly type: { readonly codename: string }; readonly error: string }>;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
10
40
|
export function importManager(config: ImportConfig) {
|
|
11
41
|
const logger: Logger = config.logger ?? getDefaultLogger();
|
|
12
42
|
const targetEnvironmentClient: Readonly<ManagementClient> = getMigrationManagementClient(config);
|
|
@@ -29,7 +59,8 @@ export function importManager(config: ImportConfig) {
|
|
|
29
59
|
logger: logger
|
|
30
60
|
}).importAsync();
|
|
31
61
|
};
|
|
32
|
-
|
|
62
|
+
|
|
63
|
+
const importContentItemsAsync = async (importContext: ImportContext): Promise<readonly ImportedItem[]> => {
|
|
33
64
|
if (!importContext.categorizedImportData.contentItems.length) {
|
|
34
65
|
logger.log({
|
|
35
66
|
type: 'info',
|
|
@@ -46,8 +77,8 @@ export function importManager(config: ImportConfig) {
|
|
|
46
77
|
|
|
47
78
|
const importLanguageVariantsAsync = async (
|
|
48
79
|
importContext: ImportContext,
|
|
49
|
-
contentItems: readonly
|
|
50
|
-
): Promise<readonly
|
|
80
|
+
contentItems: readonly ImportedItem[]
|
|
81
|
+
): Promise<readonly ImportedLanguageVariant[]> => {
|
|
51
82
|
if (!importContext.categorizedImportData.contentItems.length) {
|
|
52
83
|
logger.log({
|
|
53
84
|
type: 'info',
|
|
@@ -63,6 +94,116 @@ export function importManager(config: ImportConfig) {
|
|
|
63
94
|
}).importAsync();
|
|
64
95
|
};
|
|
65
96
|
|
|
97
|
+
const getReportResult = (importResult: ImportResult): ReportResult => {
|
|
98
|
+
return {
|
|
99
|
+
errorsCount:
|
|
100
|
+
importResult.editedAssets.filter((m) => m.state === 'error').length +
|
|
101
|
+
importResult.uploadedAssets.filter((m) => m.state === 'error').length +
|
|
102
|
+
importResult.contentItems.filter((m) => m.state === 'error').length +
|
|
103
|
+
importResult.languageVariants.filter((m) => m.state === 'error').length,
|
|
104
|
+
assets: {
|
|
105
|
+
count: importResult.uploadedAssets.length + importResult.editedAssets.length,
|
|
106
|
+
successful: [
|
|
107
|
+
...importResult.uploadedAssets.filter((m) => m.state === 'valid').map((m) => m.inputItem.codename),
|
|
108
|
+
...importResult.editedAssets.filter((m) => m.state === 'valid').map((m) => m.inputItem.migrationAsset.codename)
|
|
109
|
+
].map((m) => {
|
|
110
|
+
return {
|
|
111
|
+
codename: m
|
|
112
|
+
};
|
|
113
|
+
}),
|
|
114
|
+
failed: [
|
|
115
|
+
...importResult.uploadedAssets
|
|
116
|
+
.filter((m) => m.state === 'error')
|
|
117
|
+
.map((m) => {
|
|
118
|
+
return {
|
|
119
|
+
codename: m.inputItem.codename,
|
|
120
|
+
error: extractErrorData(m.state === 'error').message
|
|
121
|
+
};
|
|
122
|
+
}),
|
|
123
|
+
...importResult.editedAssets
|
|
124
|
+
.filter((m) => m.state === 'error')
|
|
125
|
+
.map((m) => {
|
|
126
|
+
return {
|
|
127
|
+
codename: m.inputItem.migrationAsset.codename,
|
|
128
|
+
error: extractErrorData(m.state === 'error').message
|
|
129
|
+
};
|
|
130
|
+
})
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
contentItems: {
|
|
134
|
+
count: importResult.contentItems.length,
|
|
135
|
+
successful: importResult.contentItems
|
|
136
|
+
.filter((m) => m.state === 'valid')
|
|
137
|
+
.map((m) => {
|
|
138
|
+
return {
|
|
139
|
+
codename: m.inputItem.system.codename
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
failed: importResult.contentItems
|
|
143
|
+
.filter((m) => m.state === 'error')
|
|
144
|
+
.map((m) => {
|
|
145
|
+
return {
|
|
146
|
+
codename: m.inputItem.system.codename,
|
|
147
|
+
type: m.inputItem.system.type,
|
|
148
|
+
error: extractErrorData(m.state === 'error').message
|
|
149
|
+
};
|
|
150
|
+
})
|
|
151
|
+
},
|
|
152
|
+
languageVariants: {
|
|
153
|
+
count: importResult.languageVariants.length,
|
|
154
|
+
successful: importResult.languageVariants
|
|
155
|
+
.filter((m) => m.state === 'valid')
|
|
156
|
+
.map((m) => {
|
|
157
|
+
return {
|
|
158
|
+
codename: m.inputItem.system.codename,
|
|
159
|
+
language: m.inputItem.system.language,
|
|
160
|
+
type: m.inputItem.system.type
|
|
161
|
+
};
|
|
162
|
+
}),
|
|
163
|
+
failed: importResult.languageVariants
|
|
164
|
+
.filter((m) => m.state === 'error')
|
|
165
|
+
.map((m) => {
|
|
166
|
+
return {
|
|
167
|
+
codename: m.inputItem.system.codename,
|
|
168
|
+
language: m.inputItem.system.language,
|
|
169
|
+
type: m.inputItem.system.type,
|
|
170
|
+
error: extractErrorData(m.state === 'error').message
|
|
171
|
+
};
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const printReportToConsole = (reportResult: ReportResult, logger: Logger): void => {
|
|
178
|
+
const errors = [
|
|
179
|
+
...reportResult.assets.failed.map((m) => [
|
|
180
|
+
`Object type: ${chalk.yellow('Asset')}`,
|
|
181
|
+
`Codename: ${chalk.yellow(m.codename)}`,
|
|
182
|
+
`${chalk.red(m.error)}`
|
|
183
|
+
]),
|
|
184
|
+
...reportResult.contentItems.failed.map((m) => [
|
|
185
|
+
`Object type: ${chalk.yellow('Content item')}`,
|
|
186
|
+
`Codename:${chalk.yellow(m.codename)}`,
|
|
187
|
+
`Content Type: ${chalk.yellow(m.type.codename)}`,
|
|
188
|
+
`${chalk.red(m.error)}`
|
|
189
|
+
]),
|
|
190
|
+
...reportResult.languageVariants.failed.map((m) => [
|
|
191
|
+
`Object type: ${chalk.yellow('Language variant')}`,
|
|
192
|
+
`Codename: ${chalk.yellow(m.codename)}`,
|
|
193
|
+
`Language: ${chalk.yellow(m.language.codename)}`,
|
|
194
|
+
`Content Type: ${chalk.yellow(m.type.codename)}`,
|
|
195
|
+
`${chalk.red(m.error)}`
|
|
196
|
+
])
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
errors.forEach((error, index) => {
|
|
200
|
+
logger.log({ message: `${chalk.red(`\nError #${index + 1}`)}` });
|
|
201
|
+
error.forEach((m) => {
|
|
202
|
+
logger.log({ message: m });
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
|
|
66
207
|
return {
|
|
67
208
|
async importAsync(): Promise<ImportResult> {
|
|
68
209
|
const importContext = await (
|
|
@@ -83,17 +224,40 @@ export function importManager(config: ImportConfig) {
|
|
|
83
224
|
// #3 Language variants
|
|
84
225
|
const languageVariants = await importLanguageVariantsAsync(importContext, contentItems);
|
|
85
226
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
227
|
+
const reportResult = getReportResult({
|
|
228
|
+
contentItems,
|
|
229
|
+
editedAssets,
|
|
230
|
+
languageVariants,
|
|
231
|
+
uploadedAssets
|
|
89
232
|
});
|
|
90
233
|
|
|
234
|
+
if (reportResult.errorsCount) {
|
|
235
|
+
printReportToConsole(reportResult, logger);
|
|
236
|
+
logger.log({
|
|
237
|
+
type: 'completed',
|
|
238
|
+
message: `Finished import with '${chalk.red(reportResult.errorsCount)}' ${
|
|
239
|
+
reportResult.errorsCount === 1 ? 'error' : 'errors'
|
|
240
|
+
}`
|
|
241
|
+
});
|
|
242
|
+
} else {
|
|
243
|
+
logger.log({
|
|
244
|
+
type: 'completed',
|
|
245
|
+
message: `Finished import`
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
91
249
|
return {
|
|
92
250
|
editedAssets,
|
|
93
251
|
uploadedAssets,
|
|
94
252
|
contentItems,
|
|
95
253
|
languageVariants
|
|
96
254
|
};
|
|
255
|
+
},
|
|
256
|
+
getReportFile(importResult: ImportResult): { readonly filename: string; readonly content: string } {
|
|
257
|
+
return {
|
|
258
|
+
filename: reportFilename,
|
|
259
|
+
content: JSON.stringify(getReportResult(importResult))
|
|
260
|
+
};
|
|
97
261
|
}
|
|
98
262
|
};
|
|
99
263
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
ExternalIdGenerator,
|
|
15
15
|
FlattenedContentType,
|
|
16
16
|
FlattenedContentTypeElement,
|
|
17
|
+
ItemProcessingResult,
|
|
17
18
|
ItemStateInTargetEnvironmentByCodename,
|
|
18
19
|
LanguageVariantStateInTargetEnvironmentByCodename,
|
|
19
20
|
Logger,
|
|
@@ -75,12 +76,27 @@ export type ImportTransformFunc = (data: {
|
|
|
75
76
|
export interface ImportConfig extends ManagementClientConfig {
|
|
76
77
|
readonly data: MigrationData;
|
|
77
78
|
readonly externalIdGenerator?: ExternalIdGenerator;
|
|
79
|
+
readonly createReportFile?: boolean;
|
|
78
80
|
readonly logger?: Logger;
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
export interface AssetToEdit {
|
|
84
|
+
readonly migrationAsset: MigrationAsset;
|
|
85
|
+
readonly targetAsset: Readonly<AssetModels.Asset>;
|
|
86
|
+
readonly replaceBinaryFile: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type ImportedItem = ItemProcessingResult<MigrationItem, Readonly<ContentItemModels.ContentItem>>;
|
|
90
|
+
export type ImportedLanguageVariant = ItemProcessingResult<
|
|
91
|
+
MigrationItem,
|
|
92
|
+
readonly Readonly<LanguageVariantModels.ContentItemLanguageVariant>[]
|
|
93
|
+
>;
|
|
94
|
+
export type EditedAsset = ItemProcessingResult<AssetToEdit, Readonly<AssetModels.Asset>>;
|
|
95
|
+
export type ImportedAsset = ItemProcessingResult<MigrationAsset, Readonly<AssetModels.Asset>>;
|
|
96
|
+
|
|
81
97
|
export interface ImportResult {
|
|
82
|
-
readonly uploadedAssets: readonly
|
|
83
|
-
readonly editedAssets: readonly
|
|
84
|
-
readonly contentItems: readonly
|
|
85
|
-
readonly languageVariants: readonly
|
|
98
|
+
readonly uploadedAssets: readonly ImportedAsset[];
|
|
99
|
+
readonly editedAssets: readonly EditedAsset[];
|
|
100
|
+
readonly contentItems: readonly ImportedItem[];
|
|
101
|
+
readonly languageVariants: readonly ImportedLanguageVariant[];
|
|
86
102
|
}
|
|
@@ -13,13 +13,7 @@ import {
|
|
|
13
13
|
runMapiRequestAsync
|
|
14
14
|
} from '../../core/index.js';
|
|
15
15
|
import { shouldReplaceBinaryFile, shouldUpdateAsset } from '../comparers/asset-comparer.js';
|
|
16
|
-
import { ImportContext, ImportResult } from '../import.models.js';
|
|
17
|
-
|
|
18
|
-
interface AssetToEdit {
|
|
19
|
-
migrationAsset: MigrationAsset;
|
|
20
|
-
targetAsset: Readonly<AssetModels.Asset>;
|
|
21
|
-
replaceBinaryFile: boolean;
|
|
22
|
-
}
|
|
16
|
+
import { AssetToEdit, EditedAsset, ImportContext, ImportedAsset, ImportResult } from '../import.models.js';
|
|
23
17
|
|
|
24
18
|
export function assetsImporter(data: {
|
|
25
19
|
readonly logger: Logger;
|
|
@@ -70,7 +64,7 @@ export function assetsImporter(data: {
|
|
|
70
64
|
.filter(isNotUndefined);
|
|
71
65
|
};
|
|
72
66
|
|
|
73
|
-
const editAssets = async (assetsToEdit: readonly AssetToEdit[]): Promise<readonly
|
|
67
|
+
const editAssets = async (assetsToEdit: readonly AssetToEdit[]): Promise<readonly EditedAsset[]> => {
|
|
74
68
|
data.logger.log({
|
|
75
69
|
type: 'upsert',
|
|
76
70
|
message: `Upserting '${chalk.yellow(assetsToEdit.length.toString())}' assets`
|
|
@@ -189,7 +183,7 @@ export function assetsImporter(data: {
|
|
|
189
183
|
});
|
|
190
184
|
};
|
|
191
185
|
|
|
192
|
-
const uploadAssetsAsync = async (assetsToUpload: readonly MigrationAsset[]): Promise<readonly
|
|
186
|
+
const uploadAssetsAsync = async (assetsToUpload: readonly MigrationAsset[]): Promise<readonly ImportedAsset[]> => {
|
|
193
187
|
data.logger.log({
|
|
194
188
|
type: 'upload',
|
|
195
189
|
message: `Uploading '${chalk.yellow(assetsToUpload.length.toString())}' assets`
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { ContentItemModels, ManagementClient } from '@kontent-ai/management-sdk';
|
|
2
|
-
import {
|
|
3
|
-
Logger,
|
|
4
|
-
processItemsAsync,
|
|
5
|
-
runMapiRequestAsync,
|
|
6
|
-
MigrationItem,
|
|
7
|
-
LogSpinnerData,
|
|
8
|
-
findRequired
|
|
9
|
-
} from '../../core/index.js';
|
|
10
2
|
import chalk from 'chalk';
|
|
11
|
-
import {
|
|
3
|
+
import { LogSpinnerData, Logger, MigrationItem, findRequired, processItemsAsync, runMapiRequestAsync } from '../../core/index.js';
|
|
4
|
+
import { ImportContext, ImportedItem } from '../import.models.js';
|
|
12
5
|
|
|
13
6
|
export function contentItemsImporter(data: {
|
|
14
7
|
readonly logger: Logger;
|
|
@@ -26,8 +19,7 @@ export function contentItemsImporter(data: {
|
|
|
26
19
|
);
|
|
27
20
|
|
|
28
21
|
return (
|
|
29
|
-
migrationContentItem.system.name !== contentItem.name ||
|
|
30
|
-
migrationContentItem.system.collection.codename !== collection.codename
|
|
22
|
+
migrationContentItem.system.name !== contentItem.name || migrationContentItem.system.collection.codename !== collection.codename
|
|
31
23
|
);
|
|
32
24
|
};
|
|
33
25
|
|
|
@@ -35,9 +27,7 @@ export function contentItemsImporter(data: {
|
|
|
35
27
|
logSpinner: LogSpinnerData,
|
|
36
28
|
migrationContentItem: MigrationItem
|
|
37
29
|
): Promise<{ contentItem: Readonly<ContentItemModels.ContentItem>; status: 'created' | 'itemAlreadyExists' }> => {
|
|
38
|
-
const itemStateInTargetEnv = data.importContext.getItemStateInTargetEnvironment(
|
|
39
|
-
migrationContentItem.system.codename
|
|
40
|
-
);
|
|
30
|
+
const itemStateInTargetEnv = data.importContext.getItemStateInTargetEnvironment(migrationContentItem.system.codename);
|
|
41
31
|
|
|
42
32
|
if (itemStateInTargetEnv.state === 'exists' && itemStateInTargetEnv.item) {
|
|
43
33
|
return {
|
|
@@ -111,8 +101,19 @@ export function contentItemsImporter(data: {
|
|
|
111
101
|
return preparedContentItemResult.contentItem;
|
|
112
102
|
};
|
|
113
103
|
|
|
114
|
-
const importAsync = async (): Promise<readonly
|
|
115
|
-
|
|
104
|
+
const importAsync = async (): Promise<readonly ImportedItem[]> => {
|
|
105
|
+
// Only import unique content items based on their codename. The input may contain items in various language variants which share
|
|
106
|
+
// the same underlying content item.
|
|
107
|
+
const contentItemsToImport = data.importContext.categorizedImportData.contentItems.reduce<Readonly<MigrationItem[]>>(
|
|
108
|
+
(filteredItems, item) => {
|
|
109
|
+
if (filteredItems.some((m) => m.system.codename === item.system.codename)) {
|
|
110
|
+
return filteredItems;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return [...filteredItems, item];
|
|
114
|
+
},
|
|
115
|
+
[]
|
|
116
|
+
);
|
|
116
117
|
|
|
117
118
|
data.logger.log({
|
|
118
119
|
type: 'info',
|
|
@@ -127,7 +128,7 @@ export function contentItemsImporter(data: {
|
|
|
127
128
|
itemInfo: (item) => {
|
|
128
129
|
return {
|
|
129
130
|
itemType: 'contentItem',
|
|
130
|
-
title: `${item.system.codename} -> ${item.system.
|
|
131
|
+
title: `${item.system.codename} -> ${item.system.type.codename}`
|
|
131
132
|
};
|
|
132
133
|
},
|
|
133
134
|
processAsync: async (item, logSpinner) => {
|