Comparing version 0.0.4 to 0.0.5
export interface ICliFileConfig { | ||
projectId: string; | ||
apiKey?: string; | ||
secureApiKey?: string; | ||
previewApiKey?: string; | ||
skipFailedItems: boolean; | ||
@@ -10,6 +12,7 @@ action: CliAction; | ||
exportAssets: boolean; | ||
fetchAssetDetails?: boolean; | ||
} | ||
export declare type CliAction = 'backup' | 'restore'; | ||
export declare type ItemType = 'component' | 'contentItem' | 'languageVariant' | 'asset' | 'binaryFile'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'update'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'unArchive' | 'update'; | ||
export interface IProcessedItem { | ||
@@ -16,0 +19,0 @@ title: string; |
@@ -12,2 +12,5 @@ import { IRetryStrategyOptions } from '@kontent-ai/core-sdk'; | ||
projectId: string; | ||
secureApiKey?: string; | ||
apiKey?: string; | ||
previewApiKey?: string; | ||
baseUrl?: string; | ||
@@ -18,2 +21,3 @@ onProcess?: (item: IProcessedItem) => void; | ||
retryStrategy?: IRetryStrategyOptions; | ||
fetchAssetDetails?: boolean; | ||
} | ||
@@ -20,0 +24,0 @@ export interface IExportData { |
@@ -14,3 +14,3 @@ import { IExportAllResult, IExportConfig } from './export.models'; | ||
private processItem; | ||
private extractAssets; | ||
private extractAssetsAsync; | ||
} |
@@ -81,2 +81,3 @@ "use strict"; | ||
var colors_1 = require("colors"); | ||
var management_sdk_1 = require("@kontent-ai/management-sdk"); | ||
var ExportService = /** @class */ (function () { | ||
@@ -95,2 +96,8 @@ function ExportService(config) { | ||
httpService: this.httpService, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
defaultQueryConfig: { | ||
usePreviewMode: config.previewApiKey ? true : false, | ||
useSecuredMode: config.secureApiKey ? true : false | ||
}, | ||
proxy: { | ||
@@ -119,9 +126,12 @@ baseUrl: config.baseUrl | ||
console.log(''); | ||
if (this.config.exportAssets) { | ||
console.log("Extracting assets referenced by content items"); | ||
assets = this.extractAssets(contentItems, types); | ||
} | ||
else { | ||
console.log("Assets export is disabled"); | ||
} | ||
if (!this.config.exportAssets) return [3 /*break*/, 5]; | ||
console.log("Extracting assets referenced by content items"); | ||
return [4 /*yield*/, this.extractAssetsAsync(contentItems, types)]; | ||
case 4: | ||
assets = _a.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
console.log("Assets export is disabled"); | ||
_a.label = 6; | ||
case 6: | ||
data = { | ||
@@ -219,3 +229,3 @@ contentItems: contentItems, | ||
var contentItem = _d.value; | ||
_this.processItem("".concat(contentItem.system.name, " ").concat((0, colors_1.magenta)(contentItem.system.language)), 'contentItem', 'fetch', contentItem); | ||
_this.processItem("'".concat((0, colors_1.yellow)(contentItem.system.name), "' | ").concat((0, colors_1.magenta)(contentItem.system.language)), 'contentItem', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -233,3 +243,3 @@ } | ||
if (!contentItems.find(function (m) { return m.system.codename === codename; })) { | ||
_this.processItem("".concat(contentItem.system.name, " ").concat((0, colors_1.magenta)(contentItem.system.language)), 'component', 'fetch', contentItem); | ||
_this.processItem("'".concat((0, colors_1.yellow)(contentItem.system.name), "' | ").concat((0, colors_1.magenta)(contentItem.system.language)), 'component', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -327,96 +337,143 @@ } | ||
}; | ||
ExportService.prototype.extractAssets = function (items, types) { | ||
var e_6, _a; | ||
var assets = []; | ||
var _loop_3 = function (type) { | ||
var e_7, _b, e_8, _c, e_9, _d; | ||
var itemsOfType = items.filter(function (m) { return m.system.type === type.system.codename; }); | ||
try { | ||
for (var _e = (e_7 = void 0, __values(type.elements)), _f = _e.next(); !_f.done; _f = _e.next()) { | ||
var element = _f.value; | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === delivery_sdk_1.ElementType.Asset) { | ||
try { | ||
for (var itemsOfType_1 = (e_8 = void 0, __values(itemsOfType)), itemsOfType_1_1 = itemsOfType_1.next(); !itemsOfType_1_1.done; itemsOfType_1_1 = itemsOfType_1.next()) { | ||
var item = itemsOfType_1_1.value; | ||
var assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(assetElement.value.map(function (m) { | ||
var _a; | ||
var assetId = (0, core_1.extractAssetIdFromUrl)(m.url); | ||
var extension = (_a = (0, core_1.getExtension)(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
ExportService.prototype.extractAssetsAsync = function (items, types) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var assets, _loop_3, types_2, types_2_1, type, uniqueAssets, managementClient, uniqueAssets_1, uniqueAssets_1_1, asset, assetResponse, e_6_1; | ||
var e_7, _b, e_6, _c; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
assets = []; | ||
_loop_3 = function (type) { | ||
var e_8, _e, e_9, _f, e_10, _g; | ||
var itemsOfType = items.filter(function (m) { return m.system.type === type.system.codename; }); | ||
try { | ||
for (var _h = (e_8 = void 0, __values(type.elements)), _j = _h.next(); !_j.done; _j = _h.next()) { | ||
var element = _j.value; | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === delivery_sdk_1.ElementType.Asset) { | ||
try { | ||
for (var itemsOfType_1 = (e_9 = void 0, __values(itemsOfType)), itemsOfType_1_1 = itemsOfType_1.next(); !itemsOfType_1_1.done; itemsOfType_1_1 = itemsOfType_1.next()) { | ||
var item = itemsOfType_1_1.value; | ||
var assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(assetElement.value.map(function (m) { | ||
var _a; | ||
var assetId = (0, core_1.extractAssetIdFromUrl)(m.url); | ||
var extension = (_a = (0, core_1.getExtension)(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_1_1 && !itemsOfType_1_1.done && (_f = itemsOfType_1.return)) _f.call(itemsOfType_1); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
} | ||
else if (element.type === delivery_sdk_1.ElementType.RichText) { | ||
try { | ||
for (var itemsOfType_2 = (e_10 = void 0, __values(itemsOfType)), itemsOfType_2_1 = itemsOfType_2.next(); !itemsOfType_2_1.done; itemsOfType_2_1 = itemsOfType_2.next()) { | ||
var item = itemsOfType_2_1.value; | ||
var richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(richTextElement.images.map(function (m) { | ||
var _a; | ||
var assetId = (0, core_1.extractAssetIdFromUrl)(m.url); | ||
var extension = (_a = (0, core_1.getExtension)(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
} | ||
} | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_2_1 && !itemsOfType_2_1.done && (_g = itemsOfType_2.return)) _g.call(itemsOfType_2); | ||
} | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_1_1 && !itemsOfType_1_1.done && (_c = itemsOfType_1.return)) _c.call(itemsOfType_1); | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (_j && !_j.done && (_e = _h.return)) _e.call(_h); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
} | ||
else if (element.type === delivery_sdk_1.ElementType.RichText) { | ||
}; | ||
try { | ||
for (var itemsOfType_2 = (e_9 = void 0, __values(itemsOfType)), itemsOfType_2_1 = itemsOfType_2.next(); !itemsOfType_2_1.done; itemsOfType_2_1 = itemsOfType_2.next()) { | ||
var item = itemsOfType_2_1.value; | ||
var richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(richTextElement.images.map(function (m) { | ||
var _a; | ||
var assetId = (0, core_1.extractAssetIdFromUrl)(m.url); | ||
var extension = (_a = (0, core_1.getExtension)(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
for (types_2 = __values(types), types_2_1 = types_2.next(); !types_2_1.done; types_2_1 = types_2.next()) { | ||
type = types_2_1.value; | ||
_loop_3(type); | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_2_1 && !itemsOfType_2_1.done && (_d = itemsOfType_2.return)) _d.call(itemsOfType_2); | ||
if (types_2_1 && !types_2_1.done && (_b = types_2.return)) _b.call(types_2); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
} | ||
uniqueAssets = __spreadArray([], __read(new Map(assets.map(function (item) { return [item.url, item]; })).values()), false); | ||
if (!(this.config.fetchAssetDetails === true)) return [3 /*break*/, 8]; | ||
if (!this.config.apiKey) { | ||
throw Error("Management API key is required to fetch asset details"); | ||
} | ||
managementClient = (0, management_sdk_1.createManagementClient)({ | ||
apiKey: this.config.apiKey, | ||
projectId: this.config.projectId, | ||
retryStrategy: (_a = this.config.retryStrategy) !== null && _a !== void 0 ? _a : core_1.defaultRetryStrategy | ||
}); | ||
_d.label = 1; | ||
case 1: | ||
_d.trys.push([1, 6, 7, 8]); | ||
uniqueAssets_1 = __values(uniqueAssets), uniqueAssets_1_1 = uniqueAssets_1.next(); | ||
_d.label = 2; | ||
case 2: | ||
if (!!uniqueAssets_1_1.done) return [3 /*break*/, 5]; | ||
asset = uniqueAssets_1_1.value; | ||
return [4 /*yield*/, managementClient.viewAsset().byAssetId(asset.assetId).toPromise()]; | ||
case 3: | ||
assetResponse = _d.sent(); | ||
console.log("Fetched asset details '".concat((0, colors_1.yellow)(asset.assetId), "' -> '").concat((0, colors_1.green)(assetResponse.data.fileName), "'")); | ||
asset.filename = assetResponse.data.fileName; | ||
_d.label = 4; | ||
case 4: | ||
uniqueAssets_1_1 = uniqueAssets_1.next(); | ||
return [3 /*break*/, 2]; | ||
case 5: return [3 /*break*/, 8]; | ||
case 6: | ||
e_6_1 = _d.sent(); | ||
e_6 = { error: e_6_1 }; | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
try { | ||
if (uniqueAssets_1_1 && !uniqueAssets_1_1.done && (_c = uniqueAssets_1.return)) _c.call(uniqueAssets_1); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
return [7 /*endfinally*/]; | ||
case 8: return [2 /*return*/, uniqueAssets]; | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_f && !_f.done && (_b = _e.return)) _b.call(_e); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
}; | ||
try { | ||
for (var types_2 = __values(types), types_2_1 = types_2.next(); !types_2_1.done; types_2_1 = types_2.next()) { | ||
var type = types_2_1.value; | ||
_loop_3(type); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (types_2_1 && !types_2_1.done && (_a = types_2.return)) _a.call(types_2); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
return __spreadArray([], __read(new Map(assets.map(function (item) { return [item.url, item]; })).values()), false); // filters unique values | ||
}); | ||
}); | ||
}; | ||
@@ -423,0 +480,0 @@ return ExportService; |
@@ -22,1 +22,5 @@ import { IContentType } from '@kontent-ai/delivery-sdk'; | ||
} | ||
export interface IAssetDetailModel { | ||
assetId: string; | ||
filename: string; | ||
} |
@@ -8,2 +8,3 @@ import { IExportAllResult } from '../export'; | ||
private readonly metadataName; | ||
private readonly assetDetailsName; | ||
private readonly assetsFolderName; | ||
@@ -18,2 +19,3 @@ private readonly contentItemsFolderName; | ||
private mapLanguageVariantsToCsvAsync; | ||
private getAssetDetailModels; | ||
private getBaseContentItemFields; | ||
@@ -20,0 +22,0 @@ private getLanguageVariantFields; |
@@ -96,2 +96,3 @@ "use strict"; | ||
this.metadataName = 'metadata.json'; | ||
this.assetDetailsName = '_details.json'; | ||
this.assetsFolderName = 'assets'; | ||
@@ -196,2 +197,3 @@ this.contentItemsFolderName = 'items'; | ||
if (!exportData.data.assets.length) return [3 /*break*/, 11]; | ||
assetsFolder.file(this.assetDetailsName, JSON.stringify(this.getAssetDetailModels(exportData.data.assets))); | ||
console.log("Preparing to download '".concat((0, colors_1.yellow)(exportData.data.assets.length.toString()), "' assets")); | ||
@@ -206,3 +208,3 @@ _h.label = 2; | ||
asset = _b.value; | ||
assetFilename = asset.filename; | ||
assetFilename = "".concat(asset.assetId, ".").concat(asset.extension); | ||
_d = (_c = assetsFolder).file; | ||
@@ -322,2 +324,11 @@ _e = [assetFilename]; | ||
}; | ||
FileProcessorService.prototype.getAssetDetailModels = function (extractedAssets) { | ||
return extractedAssets.map(function (m) { | ||
var item = { | ||
assetId: m.assetId, | ||
filename: m.filename | ||
}; | ||
return item; | ||
}); | ||
}; | ||
FileProcessorService.prototype.getBaseContentItemFields = function () { | ||
@@ -393,54 +404,81 @@ return ['codename', 'name', 'language', 'type', 'collection', 'last_modified', 'workflow_step']; | ||
FileProcessorService.prototype.extractAssetsAsync = function (zip) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var assets, files, _d, _e, _f, file, binaryData, filename, extension, e_5_1; | ||
var e_5, _g; | ||
return __generator(this, function (_h) { | ||
switch (_h.label) { | ||
var assets, files, assetDetailsFilePath, assetDetailsFile, assetDetailModels, _e, _f, _loop_2, this_2, _g, _h, _j, file, e_5_1; | ||
var e_5, _k; | ||
return __generator(this, function (_l) { | ||
switch (_l.label) { | ||
case 0: | ||
assets = []; | ||
files = zip.files; | ||
_h.label = 1; | ||
assetDetailsFilePath = "".concat(this.assetsFolderName, "/").concat(this.assetDetailsName); | ||
assetDetailsFile = files[assetDetailsFilePath]; | ||
if (!assetDetailsFile) { | ||
throw Error("Invalid file path '".concat(assetDetailsFilePath, "'")); | ||
} | ||
_f = (_e = JSON).parse; | ||
return [4 /*yield*/, assetDetailsFile.async('string')]; | ||
case 1: | ||
_h.trys.push([1, 6, 7, 8]); | ||
_d = __values(Object.entries(files)), _e = _d.next(); | ||
_h.label = 2; | ||
assetDetailModels = _f.apply(_e, [_l.sent()]); | ||
_loop_2 = function (file) { | ||
var binaryData, assetId, assetDetailModel, extension, filename, mimeType; | ||
return __generator(this, function (_m) { | ||
switch (_m.label) { | ||
case 0: | ||
if (!((_a = file === null || file === void 0 ? void 0 : file.name) === null || _a === void 0 ? void 0 : _a.startsWith("".concat(this_2.assetsFolderName, "/")))) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
if ((_b = file === null || file === void 0 ? void 0 : file.name) === null || _b === void 0 ? void 0 : _b.endsWith('/')) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
if ((file === null || file === void 0 ? void 0 : file.name) === this_2.assetDetailsName) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
return [4 /*yield*/, file.async(this_2.getZipOutputType())]; | ||
case 1: | ||
binaryData = _m.sent(); | ||
assetId = this_2.getAssetIdFromFilename(file.name); | ||
assetDetailModel = assetDetailModels.find(function (m) { return m.assetId === assetId; }); | ||
extension = (0, core_1.getExtension)(file.name); | ||
filename = (_c = assetDetailModel === null || assetDetailModel === void 0 ? void 0 : assetDetailModel.filename) !== null && _c !== void 0 ? _c : "".concat(assetId, ".").concat(extension); | ||
mimeType = (_d = (0, mime_1.getType)(file.name)) !== null && _d !== void 0 ? _d : undefined; | ||
assets.push({ | ||
assetId: assetId, | ||
binaryData: binaryData, | ||
filename: filename, | ||
mimeType: mimeType, | ||
extension: extension | ||
}); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}; | ||
this_2 = this; | ||
_l.label = 2; | ||
case 2: | ||
if (!!_e.done) return [3 /*break*/, 5]; | ||
_f = __read(_e.value, 2), file = _f[1]; | ||
if (!((_a = file === null || file === void 0 ? void 0 : file.name) === null || _a === void 0 ? void 0 : _a.startsWith("".concat(this.assetsFolderName, "/")))) { | ||
// iterate through assets only | ||
return [3 /*break*/, 4]; | ||
} | ||
if ((_b = file === null || file === void 0 ? void 0 : file.name) === null || _b === void 0 ? void 0 : _b.endsWith('/')) { | ||
return [3 /*break*/, 4]; | ||
} | ||
return [4 /*yield*/, file.async(this.getZipOutputType())]; | ||
_l.trys.push([2, 7, 8, 9]); | ||
_g = __values(Object.entries(files)), _h = _g.next(); | ||
_l.label = 3; | ||
case 3: | ||
binaryData = _h.sent(); | ||
filename = file.name; | ||
extension = (0, core_1.getExtension)(filename); | ||
assets.push({ | ||
assetId: this.getAssetIdFromFilename(filename), | ||
binaryData: binaryData, | ||
filename: filename.split('/')[1], | ||
mimeType: (_c = (0, mime_1.getType)(filename)) !== null && _c !== void 0 ? _c : undefined, | ||
extension: extension | ||
}); | ||
_h.label = 4; | ||
if (!!_h.done) return [3 /*break*/, 6]; | ||
_j = __read(_h.value, 2), file = _j[1]; | ||
return [5 /*yield**/, _loop_2(file)]; | ||
case 4: | ||
_e = _d.next(); | ||
return [3 /*break*/, 2]; | ||
case 5: return [3 /*break*/, 8]; | ||
case 6: | ||
e_5_1 = _h.sent(); | ||
_l.sent(); | ||
_l.label = 5; | ||
case 5: | ||
_h = _g.next(); | ||
return [3 /*break*/, 3]; | ||
case 6: return [3 /*break*/, 9]; | ||
case 7: | ||
e_5_1 = _l.sent(); | ||
e_5 = { error: e_5_1 }; | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
try { | ||
if (_e && !_e.done && (_g = _d.return)) _g.call(_d); | ||
if (_h && !_h.done && (_k = _g.return)) _k.call(_g); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
return [7 /*endfinally*/]; | ||
case 8: return [2 /*return*/, assets]; | ||
case 9: return [2 /*return*/, assets]; | ||
} | ||
@@ -447,0 +485,0 @@ }); |
@@ -16,3 +16,4 @@ import { IImportItemResult } from '../core'; | ||
private importContentItemsAsync; | ||
private getWorkflowForGivenStep; | ||
private getWorkflowForGivenStepByCodename; | ||
private getWorkflowForGivenStepById; | ||
private prepareContentItemForImportAsync; | ||
@@ -23,2 +24,3 @@ private prepareLanguageVariantForImportAsync; | ||
private isLanguageVariantPublished; | ||
private isLanguageVariantArchived; | ||
private doesWorkflowStepCodenameRepresentPublishedStep; | ||
@@ -25,0 +27,0 @@ private doesWorkflowStepCodenameRepresentArchivedStep; |
@@ -527,3 +527,3 @@ "use strict"; | ||
if (!this_4.doesWorkflowStepCodenameRepresentArchivedStep(importContentItem.workflow_step, workflows)) return [3 /*break*/, 6]; | ||
workflow = this_4.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
workflow = this_4.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
return [4 /*yield*/, this_4.client | ||
@@ -554,3 +554,3 @@ .changeWorkflowOfLanguageVariant() | ||
case 6: | ||
workflow = this_4.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
workflow = this_4.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
return [4 /*yield*/, this_4.client | ||
@@ -639,3 +639,3 @@ .changeWorkflowOfLanguageVariant() | ||
}; | ||
ImportService.prototype.getWorkflowForGivenStep = function (itemWorkflowCodename, workflows) { | ||
ImportService.prototype.getWorkflowForGivenStepByCodename = function (itemWorkflowCodename, workflows) { | ||
var e_6, _a; | ||
@@ -670,2 +670,32 @@ try { | ||
}; | ||
ImportService.prototype.getWorkflowForGivenStepById = function (workflowId, workflows) { | ||
var e_7, _a; | ||
try { | ||
for (var workflows_2 = __values(workflows), workflows_2_1 = workflows_2.next(); !workflows_2_1.done; workflows_2_1 = workflows_2.next()) { | ||
var workflow = workflows_2_1.value; | ||
if (workflow.archivedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
if (workflow.publishedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
var step = workflow.steps.find(function (m) { return m.id === workflowId; }); | ||
if (step) { | ||
return workflow; | ||
} | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (workflows_2_1 && !workflows_2_1.done && (_a = workflows_2.return)) _a.call(workflows_2); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
var defaultWorkflow = workflows.find(function (m) { return m.codename.toLowerCase() === core_1.defaultWorkflowCodename.toLowerCase(); }); | ||
if (!defaultWorkflow) { | ||
throw Error("Missing default workflow"); | ||
} | ||
return defaultWorkflow; | ||
}; | ||
ImportService.prototype.prepareContentItemForImportAsync = function (importContentItem, importedItems) { | ||
@@ -728,3 +758,3 @@ var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var languageVariantOfContentItem, error_7, throwError; | ||
var languageVariantOfContentItem, error_7, throwError, workflow, newWorkflowStep; | ||
return __generator(this, function (_c) { | ||
@@ -760,3 +790,3 @@ switch (_c.label) { | ||
case 3: | ||
if (!languageVariantOfContentItem) return [3 /*break*/, 5]; | ||
if (!languageVariantOfContentItem) return [3 /*break*/, 7]; | ||
if (!this.isLanguageVariantPublished(languageVariantOfContentItem, workflows)) return [3 /*break*/, 5]; | ||
@@ -773,8 +803,27 @@ // create new version | ||
this.processItem(importItems, 'createNewVersion', 'languageVariant', { | ||
title: "".concat(importContentItem.name, " (").concat((0, colors_1.magenta)(importContentItem.language), ")"), | ||
title: "".concat((0, colors_1.yellow)(importContentItem.name), " (").concat((0, colors_1.magenta)(importContentItem.language), ")"), | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
_c.label = 5; | ||
case 5: return [2 /*return*/]; | ||
return [3 /*break*/, 7]; | ||
case 5: | ||
if (!this.isLanguageVariantArchived(languageVariantOfContentItem, workflows)) return [3 /*break*/, 7]; | ||
if (!languageVariantOfContentItem.workflowStep.id) return [3 /*break*/, 7]; | ||
workflow = this.getWorkflowForGivenStepById(languageVariantOfContentItem.workflowStep.id, workflows); | ||
newWorkflowStep = workflow.steps[0]; | ||
return [4 /*yield*/, this.client | ||
.changeWorkflowStepOfLanguageVariant() | ||
.byItemCodename(importContentItem.codename) | ||
.byLanguageCodename(importContentItem.language) | ||
.byWorkflowStepCodename(newWorkflowStep.codename) | ||
.toPromise()]; | ||
case 6: | ||
_c.sent(); | ||
this.processItem(importItems, 'unArchive', 'languageVariant', { | ||
title: "".concat((0, colors_1.yellow)(importContentItem.name), " | (").concat(newWorkflowStep.codename, ") (").concat((0, colors_1.magenta)(importContentItem.language), ")"), | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
_c.label = 7; | ||
case 7: return [2 /*return*/]; | ||
} | ||
@@ -805,6 +854,6 @@ }); | ||
ImportService.prototype.isLanguageVariantPublished = function (languageVariant, workflows) { | ||
var e_7, _a; | ||
var e_8, _a; | ||
try { | ||
for (var workflows_2 = __values(workflows), workflows_2_1 = workflows_2.next(); !workflows_2_1.done; workflows_2_1 = workflows_2.next()) { | ||
var workflow = workflows_2_1.value; | ||
for (var workflows_3 = __values(workflows), workflows_3_1 = workflows_3.next(); !workflows_3_1.done; workflows_3_1 = workflows_3.next()) { | ||
var workflow = workflows_3_1.value; | ||
if (workflow.publishedStep.id === languageVariant.workflowStep.id) { | ||
@@ -815,16 +864,35 @@ return true; | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (workflows_2_1 && !workflows_2_1.done && (_a = workflows_2.return)) _a.call(workflows_2); | ||
if (workflows_3_1 && !workflows_3_1.done && (_a = workflows_3.return)) _a.call(workflows_3); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
return false; | ||
}; | ||
ImportService.prototype.isLanguageVariantArchived = function (languageVariant, workflows) { | ||
var e_9, _a; | ||
try { | ||
for (var workflows_4 = __values(workflows), workflows_4_1 = workflows_4.next(); !workflows_4_1.done; workflows_4_1 = workflows_4.next()) { | ||
var workflow = workflows_4_1.value; | ||
if (workflow.archivedStep.id === languageVariant.workflowStep.id) { | ||
return true; | ||
} | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (workflows_4_1 && !workflows_4_1.done && (_a = workflows_4.return)) _a.call(workflows_4); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
return false; | ||
}; | ||
ImportService.prototype.doesWorkflowStepCodenameRepresentPublishedStep = function (workflowStepCodename, workflows) { | ||
var e_8, _a; | ||
var e_10, _a; | ||
try { | ||
for (var workflows_3 = __values(workflows), workflows_3_1 = workflows_3.next(); !workflows_3_1.done; workflows_3_1 = workflows_3.next()) { | ||
var workflow = workflows_3_1.value; | ||
for (var workflows_5 = __values(workflows), workflows_5_1 = workflows_5.next(); !workflows_5_1.done; workflows_5_1 = workflows_5.next()) { | ||
var workflow = workflows_5_1.value; | ||
if (workflow.publishedStep.codename === workflowStepCodename) { | ||
@@ -835,8 +903,8 @@ return true; | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
try { | ||
if (workflows_3_1 && !workflows_3_1.done && (_a = workflows_3.return)) _a.call(workflows_3); | ||
if (workflows_5_1 && !workflows_5_1.done && (_a = workflows_5.return)) _a.call(workflows_5); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
@@ -846,6 +914,6 @@ return false; | ||
ImportService.prototype.doesWorkflowStepCodenameRepresentArchivedStep = function (workflowStepCodename, workflows) { | ||
var e_9, _a; | ||
var e_11, _a; | ||
try { | ||
for (var workflows_4 = __values(workflows), workflows_4_1 = workflows_4.next(); !workflows_4_1.done; workflows_4_1 = workflows_4.next()) { | ||
var workflow = workflows_4_1.value; | ||
for (var workflows_6 = __values(workflows), workflows_6_1 = workflows_6.next(); !workflows_6_1.done; workflows_6_1 = workflows_6.next()) { | ||
var workflow = workflows_6_1.value; | ||
if (workflow.archivedStep.codename === workflowStepCodename) { | ||
@@ -856,8 +924,8 @@ return true; | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
catch (e_11_1) { e_11 = { error: e_11_1 }; } | ||
finally { | ||
try { | ||
if (workflows_4_1 && !workflows_4_1.done && (_a = workflows_4.return)) _a.call(workflows_4); | ||
if (workflows_6_1 && !workflows_6_1.done && (_a = workflows_6.return)) _a.call(workflows_6); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
finally { if (e_11) throw e_11.error; } | ||
} | ||
@@ -864,0 +932,0 @@ return false; |
@@ -65,4 +65,8 @@ #!/usr/bin/env node | ||
.describe('p', 'ProjectId') | ||
.alias('k', 'apiKey') | ||
.describe('k', 'Management API Key') | ||
.alias('ak', 'apiKey') | ||
.describe('ak', 'Management API Key') | ||
.alias('sk', 'secureApiKey') | ||
.describe('sk', 'API Key required when Delivery API has secure access enabled') | ||
.alias('pk', 'previewApiKey') | ||
.describe('pk', 'Use if you want to export data using Preview API') | ||
.alias('a', 'action') | ||
@@ -89,7 +93,11 @@ .describe('a', 'Action to perform. One of: "backup" | "restore"') | ||
projectId: config.projectId, | ||
apiKey: config.apiKey, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
baseUrl: config.baseUrl, | ||
exportTypes: config.exportTypes, | ||
exportAssets: config.exportAssets, | ||
fetchAssetDetails: config.fetchAssetDetails, | ||
onProcess: function (item) { | ||
console.log("Exported ".concat((0, colors_1.yellow)(item.title), " | ").concat((0, colors_1.green)(item.data.system.type))); | ||
console.log("Exported ".concat((item.title), " | ").concat((0, colors_1.green)(item.data.system.type))); | ||
} | ||
@@ -210,12 +218,12 @@ }); | ||
var getConfig = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var resolvedArgs, configFilename, configFile, action, apiKey, projectId, baseUrl, filename, exportTypes, exportAssets, skipFailedItems, typesMapped, config; | ||
var _a, _b, _c, _d, _e; | ||
return __generator(this, function (_f) { | ||
switch (_f.label) { | ||
var resolvedArgs, configFilename, configFile, action, apiKey, secureApiKey, previewApiKey, projectId, baseUrl, filename, exportTypes, exportAssets, skipFailedItems, fetchAssetDetails, typesMapped, config; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
return __generator(this, function (_h) { | ||
switch (_h.label) { | ||
case 0: return [4 /*yield*/, argv]; | ||
case 1: | ||
resolvedArgs = _f.sent(); | ||
resolvedArgs = _h.sent(); | ||
return [4 /*yield*/, resolvedArgs.config]; | ||
case 2: | ||
configFilename = (_f.sent()); | ||
configFilename = (_h.sent()); | ||
if (configFilename) { | ||
@@ -227,2 +235,4 @@ configFile = (0, fs_1.readFileSync)("./".concat(configFilename)); | ||
apiKey = resolvedArgs.apiKey; | ||
secureApiKey = resolvedArgs.secureApiKey; | ||
previewApiKey = resolvedArgs.previewApiKey; | ||
projectId = resolvedArgs.projectId; | ||
@@ -234,2 +244,3 @@ baseUrl = resolvedArgs.baseUrl; | ||
skipFailedItems = (_e = ((_d = resolvedArgs.skipFailedItems) === null || _d === void 0 ? void 0 : _d.toLowerCase()) === 'true'.toLowerCase()) !== null && _e !== void 0 ? _e : true; | ||
fetchAssetDetails = (_g = ((_f = resolvedArgs.fetchAssetDetails) === null || _f === void 0 ? void 0 : _f.toLowerCase()) === 'true'.toLowerCase()) !== null && _g !== void 0 ? _g : false; | ||
typesMapped = exportTypes ? exportTypes.split(',').map(function (m) { return m.trim(); }) : []; | ||
@@ -250,3 +261,6 @@ if (!action) { | ||
exportAssets: exportAssets, | ||
skipFailedItems: skipFailedItems | ||
skipFailedItems: skipFailedItems, | ||
previewApiKey: previewApiKey, | ||
secureApiKey: secureApiKey, | ||
fetchAssetDetails: fetchAssetDetails | ||
}; | ||
@@ -259,3 +273,3 @@ return [2 /*return*/, config]; | ||
var date = new Date(); | ||
return "csvm-backup-".concat(date.getDate(), "-").concat(date.getMonth() + 1, "-").concat(date.getFullYear(), "-").concat(date.getHours(), "-").concat(date.getMinutes(), ".zip"); | ||
return "csv-backup-".concat(date.getDate(), "-").concat(date.getMonth() + 1, "-").concat(date.getFullYear(), "-").concat(date.getHours(), "-").concat(date.getMinutes(), ".zip"); | ||
}; | ||
@@ -262,0 +276,0 @@ run() |
{ | ||
"name": "xeno-test", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "This utility enables users to export & import content data from / to Kontent.ai projects", | ||
@@ -8,3 +8,3 @@ "preferGlobal": true, | ||
"bin": { | ||
"csvm": "./dist/cjs/lib/node/cli/app.js" | ||
"kcsvm": "./dist/cjs/lib/node/cli/app.js" | ||
}, | ||
@@ -11,0 +11,0 @@ "repository": { |
export interface ICliFileConfig { | ||
projectId: string; | ||
apiKey?: string; | ||
secureApiKey?: string; | ||
previewApiKey?: string; | ||
skipFailedItems: boolean; | ||
@@ -10,6 +12,7 @@ action: CliAction; | ||
exportAssets: boolean; | ||
fetchAssetDetails?: boolean; | ||
} | ||
export declare type CliAction = 'backup' | 'restore'; | ||
export declare type ItemType = 'component' | 'contentItem' | 'languageVariant' | 'asset' | 'binaryFile'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'update'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'unArchive' | 'update'; | ||
export interface IProcessedItem { | ||
@@ -16,0 +19,0 @@ title: string; |
@@ -12,2 +12,5 @@ import { IRetryStrategyOptions } from '@kontent-ai/core-sdk'; | ||
projectId: string; | ||
secureApiKey?: string; | ||
apiKey?: string; | ||
previewApiKey?: string; | ||
baseUrl?: string; | ||
@@ -18,2 +21,3 @@ onProcess?: (item: IProcessedItem) => void; | ||
retryStrategy?: IRetryStrategyOptions; | ||
fetchAssetDetails?: boolean; | ||
} | ||
@@ -20,0 +24,0 @@ export interface IExportData { |
@@ -14,3 +14,3 @@ import { IExportAllResult, IExportConfig } from './export.models'; | ||
private processItem; | ||
private extractAssets; | ||
private extractAssetsAsync; | ||
} |
@@ -6,3 +6,4 @@ import { __awaiter } from "tslib"; | ||
import { version } from '../../package.json'; | ||
import { magenta, yellow } from 'colors'; | ||
import { green, magenta, yellow } from 'colors'; | ||
import { createManagementClient } from '@kontent-ai/management-sdk'; | ||
export class ExportService { | ||
@@ -21,2 +22,8 @@ constructor(config) { | ||
httpService: this.httpService, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
defaultQueryConfig: { | ||
usePreviewMode: config.previewApiKey ? true : false, | ||
useSecuredMode: config.secureApiKey ? true : false | ||
}, | ||
proxy: { | ||
@@ -37,3 +44,3 @@ baseUrl: config.baseUrl | ||
console.log(`Extracting assets referenced by content items`); | ||
assets = this.extractAssets(contentItems, types); | ||
assets = yield this.extractAssetsAsync(contentItems, types); | ||
} | ||
@@ -95,3 +102,3 @@ else { | ||
for (const contentItem of response.data.items) { | ||
this.processItem(`${contentItem.system.name} ${magenta(contentItem.system.language)}`, 'contentItem', 'fetch', contentItem); | ||
this.processItem(`'${yellow(contentItem.system.name)}' | ${magenta(contentItem.system.language)}`, 'contentItem', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -102,3 +109,3 @@ } | ||
if (!contentItems.find((m) => m.system.codename === codename)) { | ||
this.processItem(`${contentItem.system.name} ${magenta(contentItem.system.language)}`, 'component', 'fetch', contentItem); | ||
this.processItem(`'${yellow(contentItem.system.name)}' | ${magenta(contentItem.system.language)}`, 'component', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -137,45 +144,48 @@ } | ||
} | ||
extractAssets(items, types) { | ||
const assets = []; | ||
for (const type of types) { | ||
const itemsOfType = items.filter((m) => m.system.type === type.system.codename); | ||
for (const element of type.elements) { | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === ElementType.Asset) { | ||
for (const item of itemsOfType) { | ||
const assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push(...assetElement.value.map((m) => { | ||
var _a; | ||
const assetId = extractAssetIdFromUrl(m.url); | ||
const extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
const asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: `${assetId}.${extension}`, | ||
extension: extension | ||
}; | ||
return asset; | ||
})); | ||
extractAssetsAsync(items, types) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const assets = []; | ||
for (const type of types) { | ||
const itemsOfType = items.filter((m) => m.system.type === type.system.codename); | ||
for (const element of type.elements) { | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === ElementType.Asset) { | ||
for (const item of itemsOfType) { | ||
const assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push(...assetElement.value.map((m) => { | ||
var _a; | ||
const assetId = extractAssetIdFromUrl(m.url); | ||
const extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
const asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: `${assetId}.${extension}`, | ||
extension: extension | ||
}; | ||
return asset; | ||
})); | ||
} | ||
} | ||
} | ||
} | ||
else if (element.type === ElementType.RichText) { | ||
for (const item of itemsOfType) { | ||
const richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push(...richTextElement.images.map((m) => { | ||
var _a; | ||
const assetId = extractAssetIdFromUrl(m.url); | ||
const extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
const asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: `${assetId}.${extension}`, | ||
extension: extension | ||
}; | ||
return asset; | ||
})); | ||
else if (element.type === ElementType.RichText) { | ||
for (const item of itemsOfType) { | ||
const richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push(...richTextElement.images.map((m) => { | ||
var _a; | ||
const assetId = extractAssetIdFromUrl(m.url); | ||
const extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
const asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: `${assetId}.${extension}`, | ||
extension: extension | ||
}; | ||
return asset; | ||
})); | ||
} | ||
} | ||
@@ -185,6 +195,22 @@ } | ||
} | ||
} | ||
return [...new Map(assets.map((item) => [item.url, item])).values()]; // filters unique values | ||
const uniqueAssets = [...new Map(assets.map((item) => [item.url, item])).values()]; // filters unique values | ||
if (this.config.fetchAssetDetails === true) { | ||
if (!this.config.apiKey) { | ||
throw Error(`Management API key is required to fetch asset details`); | ||
} | ||
const managementClient = createManagementClient({ | ||
apiKey: this.config.apiKey, | ||
projectId: this.config.projectId, | ||
retryStrategy: (_a = this.config.retryStrategy) !== null && _a !== void 0 ? _a : defaultRetryStrategy | ||
}); | ||
for (const asset of uniqueAssets) { | ||
const assetResponse = yield managementClient.viewAsset().byAssetId(asset.assetId).toPromise(); | ||
console.log(`Fetched asset details '${yellow(asset.assetId)}' -> '${green(assetResponse.data.fileName)}'`); | ||
asset.filename = assetResponse.data.fileName; | ||
} | ||
} | ||
return uniqueAssets; | ||
}); | ||
} | ||
} | ||
//# sourceMappingURL=export.service.js.map |
@@ -22,1 +22,5 @@ import { IContentType } from '@kontent-ai/delivery-sdk'; | ||
} | ||
export interface IAssetDetailModel { | ||
assetId: string; | ||
filename: string; | ||
} |
@@ -8,2 +8,3 @@ import { IExportAllResult } from '../export'; | ||
private readonly metadataName; | ||
private readonly assetDetailsName; | ||
private readonly assetsFolderName; | ||
@@ -18,2 +19,3 @@ private readonly contentItemsFolderName; | ||
private mapLanguageVariantsToCsvAsync; | ||
private getAssetDetailModels; | ||
private getBaseContentItemFields; | ||
@@ -20,0 +22,0 @@ private getLanguageVariantFields; |
@@ -15,2 +15,3 @@ import { __asyncValues, __awaiter } from "tslib"; | ||
this.metadataName = 'metadata.json'; | ||
this.assetDetailsName = '_details.json'; | ||
this.assetsFolderName = 'assets'; | ||
@@ -74,5 +75,6 @@ this.contentItemsFolderName = 'items'; | ||
if (exportData.data.assets.length) { | ||
assetsFolder.file(this.assetDetailsName, JSON.stringify(this.getAssetDetailModels(exportData.data.assets))); | ||
console.log(`Preparing to download '${yellow(exportData.data.assets.length.toString())}' assets`); | ||
for (const asset of exportData.data.assets) { | ||
const assetFilename = asset.filename; | ||
const assetFilename = `${asset.assetId}.${asset.extension}`; // use id as filename to prevent name conflicts | ||
assetsFolder.file(assetFilename, yield this.getBinaryDataFromUrlAsync(asset.url), { | ||
@@ -119,2 +121,11 @@ binary: true | ||
} | ||
getAssetDetailModels(extractedAssets) { | ||
return extractedAssets.map((m) => { | ||
const item = { | ||
assetId: m.assetId, | ||
filename: m.filename | ||
}; | ||
return item; | ||
}); | ||
} | ||
getBaseContentItemFields() { | ||
@@ -181,6 +192,12 @@ return ['codename', 'name', 'language', 'type', 'collection', 'last_modified', 'workflow_step']; | ||
extractAssetsAsync(zip) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const assets = []; | ||
const files = zip.files; | ||
const assetDetailsFilePath = `${this.assetsFolderName}/${this.assetDetailsName}`; | ||
const assetDetailsFile = files[assetDetailsFilePath]; | ||
if (!assetDetailsFile) { | ||
throw Error(`Invalid file path '${assetDetailsFilePath}'`); | ||
} | ||
const assetDetailModels = JSON.parse(yield assetDetailsFile.async('string')); | ||
for (const [, file] of Object.entries(files)) { | ||
@@ -194,10 +211,16 @@ if (!((_a = file === null || file === void 0 ? void 0 : file.name) === null || _a === void 0 ? void 0 : _a.startsWith(`${this.assetsFolderName}/`))) { | ||
} | ||
if ((file === null || file === void 0 ? void 0 : file.name) === this.assetDetailsName) { | ||
continue; | ||
} | ||
const binaryData = yield file.async(this.getZipOutputType()); | ||
const filename = file.name; | ||
const extension = getExtension(filename); | ||
const assetId = this.getAssetIdFromFilename(file.name); | ||
const assetDetailModel = assetDetailModels.find((m) => m.assetId === assetId); | ||
const extension = getExtension(file.name); | ||
const filename = (_c = assetDetailModel === null || assetDetailModel === void 0 ? void 0 : assetDetailModel.filename) !== null && _c !== void 0 ? _c : `${assetId}.${extension}`; | ||
const mimeType = (_d = getType(file.name)) !== null && _d !== void 0 ? _d : undefined; | ||
assets.push({ | ||
assetId: this.getAssetIdFromFilename(filename), | ||
assetId: assetId, | ||
binaryData: binaryData, | ||
filename: filename.split('/')[1], | ||
mimeType: (_c = getType(filename)) !== null && _c !== void 0 ? _c : undefined, | ||
filename: filename, | ||
mimeType: mimeType, | ||
extension: extension | ||
@@ -204,0 +227,0 @@ }); |
@@ -16,3 +16,4 @@ import { IImportItemResult } from '../core'; | ||
private importContentItemsAsync; | ||
private getWorkflowForGivenStep; | ||
private getWorkflowForGivenStepByCodename; | ||
private getWorkflowForGivenStepById; | ||
private prepareContentItemForImportAsync; | ||
@@ -23,2 +24,3 @@ private prepareLanguageVariantForImportAsync; | ||
private isLanguageVariantPublished; | ||
private isLanguageVariantArchived; | ||
private doesWorkflowStepCodenameRepresentPublishedStep; | ||
@@ -25,0 +27,0 @@ private doesWorkflowStepCodenameRepresentArchivedStep; |
@@ -306,3 +306,3 @@ import { __awaiter } from "tslib"; | ||
else if (this.doesWorkflowStepCodenameRepresentArchivedStep(importContentItem.workflow_step, workflows)) { | ||
const workflow = this.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
const workflow = this.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
yield this.client | ||
@@ -331,3 +331,3 @@ .changeWorkflowOfLanguageVariant() | ||
else { | ||
const workflow = this.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
const workflow = this.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
yield this.client | ||
@@ -382,3 +382,3 @@ .changeWorkflowOfLanguageVariant() | ||
} | ||
getWorkflowForGivenStep(itemWorkflowCodename, workflows) { | ||
getWorkflowForGivenStepByCodename(itemWorkflowCodename, workflows) { | ||
for (const workflow of workflows) { | ||
@@ -402,2 +402,21 @@ if (workflow.archivedStep.codename === itemWorkflowCodename) { | ||
} | ||
getWorkflowForGivenStepById(workflowId, workflows) { | ||
for (const workflow of workflows) { | ||
if (workflow.archivedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
if (workflow.publishedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
const step = workflow.steps.find((m) => m.id === workflowId); | ||
if (step) { | ||
return workflow; | ||
} | ||
} | ||
const defaultWorkflow = workflows.find((m) => m.codename.toLowerCase() === defaultWorkflowCodename.toLowerCase()); | ||
if (!defaultWorkflow) { | ||
throw Error(`Missing default workflow`); | ||
} | ||
return defaultWorkflow; | ||
} | ||
prepareContentItemForImportAsync(importContentItem, importedItems) { | ||
@@ -478,3 +497,3 @@ var _a, _b; | ||
// language variant exists | ||
// check if variant is published or not | ||
// check if variant is published or archived | ||
if (this.isLanguageVariantPublished(languageVariantOfContentItem, workflows)) { | ||
@@ -488,3 +507,3 @@ // create new version | ||
this.processItem(importItems, 'createNewVersion', 'languageVariant', { | ||
title: `${importContentItem.name} (${magenta(importContentItem.language)})`, | ||
title: `${yellow(importContentItem.name)} (${magenta(importContentItem.language)})`, | ||
imported: languageVariantOfContentItem, | ||
@@ -494,2 +513,20 @@ original: importContentItem | ||
} | ||
else if (this.isLanguageVariantArchived(languageVariantOfContentItem, workflows)) { | ||
// change workflow step to draft | ||
if (languageVariantOfContentItem.workflowStep.id) { | ||
const workflow = this.getWorkflowForGivenStepById(languageVariantOfContentItem.workflowStep.id, workflows); | ||
const newWorkflowStep = workflow.steps[0]; | ||
yield this.client | ||
.changeWorkflowStepOfLanguageVariant() | ||
.byItemCodename(importContentItem.codename) | ||
.byLanguageCodename(importContentItem.language) | ||
.byWorkflowStepCodename(newWorkflowStep.codename) | ||
.toPromise(); | ||
this.processItem(importItems, 'unArchive', 'languageVariant', { | ||
title: `${yellow(importContentItem.name)} | (${newWorkflowStep.codename}) (${magenta(importContentItem.language)})`, | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
} | ||
} | ||
} | ||
@@ -516,2 +553,10 @@ }); | ||
} | ||
isLanguageVariantArchived(languageVariant, workflows) { | ||
for (const workflow of workflows) { | ||
if (workflow.archivedStep.id === languageVariant.workflowStep.id) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
doesWorkflowStepCodenameRepresentPublishedStep(workflowStepCodename, workflows) { | ||
@@ -518,0 +563,0 @@ for (const workflow of workflows) { |
@@ -17,4 +17,8 @@ #!/usr/bin/env node | ||
.describe('p', 'ProjectId') | ||
.alias('k', 'apiKey') | ||
.describe('k', 'Management API Key') | ||
.alias('ak', 'apiKey') | ||
.describe('ak', 'Management API Key') | ||
.alias('sk', 'secureApiKey') | ||
.describe('sk', 'API Key required when Delivery API has secure access enabled') | ||
.alias('pk', 'previewApiKey') | ||
.describe('pk', 'Use if you want to export data using Preview API') | ||
.alias('a', 'action') | ||
@@ -37,7 +41,11 @@ .describe('a', 'Action to perform. One of: "backup" | "restore"') | ||
projectId: config.projectId, | ||
apiKey: config.apiKey, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
baseUrl: config.baseUrl, | ||
exportTypes: config.exportTypes, | ||
exportAssets: config.exportAssets, | ||
fetchAssetDetails: config.fetchAssetDetails, | ||
onProcess: (item) => { | ||
console.log(`Exported ${yellow(item.title)} | ${green(item.data.system.type)}`); | ||
console.log(`Exported ${(item.title)} | ${green(item.data.system.type)}`); | ||
} | ||
@@ -121,3 +129,3 @@ }); | ||
const getConfig = () => __awaiter(void 0, void 0, void 0, function* () { | ||
var _a, _b, _c, _d, _e; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
const resolvedArgs = yield argv; | ||
@@ -132,2 +140,4 @@ const configFilename = (yield resolvedArgs.config); | ||
const apiKey = resolvedArgs.apiKey; | ||
const secureApiKey = resolvedArgs.secureApiKey; | ||
const previewApiKey = resolvedArgs.previewApiKey; | ||
const projectId = resolvedArgs.projectId; | ||
@@ -139,2 +149,3 @@ const baseUrl = resolvedArgs.baseUrl; | ||
const skipFailedItems = (_e = ((_d = resolvedArgs.skipFailedItems) === null || _d === void 0 ? void 0 : _d.toLowerCase()) === 'true'.toLowerCase()) !== null && _e !== void 0 ? _e : true; | ||
const fetchAssetDetails = (_g = ((_f = resolvedArgs.fetchAssetDetails) === null || _f === void 0 ? void 0 : _f.toLowerCase()) === 'true'.toLowerCase()) !== null && _g !== void 0 ? _g : false; | ||
const typesMapped = exportTypes ? exportTypes.split(',').map((m) => m.trim()) : []; | ||
@@ -156,3 +167,6 @@ if (!action) { | ||
exportAssets: exportAssets, | ||
skipFailedItems: skipFailedItems | ||
skipFailedItems: skipFailedItems, | ||
previewApiKey: previewApiKey, | ||
secureApiKey: secureApiKey, | ||
fetchAssetDetails: fetchAssetDetails | ||
}; | ||
@@ -163,3 +177,3 @@ return config; | ||
const date = new Date(); | ||
return `csvm-backup-${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()}-${date.getHours()}-${date.getMinutes()}.zip`; | ||
return `csv-backup-${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()}-${date.getHours()}-${date.getMinutes()}.zip`; | ||
}; | ||
@@ -166,0 +180,0 @@ run() |
{ | ||
"name": "xeno-test", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "This utility enables users to export & import content data from / to Kontent.ai projects", | ||
@@ -8,3 +8,3 @@ "preferGlobal": true, | ||
"bin": { | ||
"csvm": "./dist/cjs/lib/node/cli/app.js" | ||
"kcsvm": "./dist/cjs/lib/node/cli/app.js" | ||
}, | ||
@@ -11,0 +11,0 @@ "repository": { |
export interface ICliFileConfig { | ||
projectId: string; | ||
apiKey?: string; | ||
secureApiKey?: string; | ||
previewApiKey?: string; | ||
skipFailedItems: boolean; | ||
@@ -10,6 +12,7 @@ action: CliAction; | ||
exportAssets: boolean; | ||
fetchAssetDetails?: boolean; | ||
} | ||
export declare type CliAction = 'backup' | 'restore'; | ||
export declare type ItemType = 'component' | 'contentItem' | 'languageVariant' | 'asset' | 'binaryFile'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'update'; | ||
export declare type ActionType = 'skipUpdate' | 'archive' | 'upsert' | 'upload' | 'publish' | 'changeWorkflowStep' | 'createNewVersion' | 'fetch' | 'create' | 'publish' | 'unArchive' | 'update'; | ||
export interface IProcessedItem { | ||
@@ -16,0 +19,0 @@ title: string; |
@@ -12,2 +12,5 @@ import { IRetryStrategyOptions } from '@kontent-ai/core-sdk'; | ||
projectId: string; | ||
secureApiKey?: string; | ||
apiKey?: string; | ||
previewApiKey?: string; | ||
baseUrl?: string; | ||
@@ -18,2 +21,3 @@ onProcess?: (item: IProcessedItem) => void; | ||
retryStrategy?: IRetryStrategyOptions; | ||
fetchAssetDetails?: boolean; | ||
} | ||
@@ -20,0 +24,0 @@ export interface IExportData { |
@@ -14,3 +14,3 @@ import { IExportAllResult, IExportConfig } from './export.models'; | ||
private processItem; | ||
private extractAssets; | ||
private extractAssetsAsync; | ||
} |
@@ -6,3 +6,4 @@ import { __awaiter, __generator, __read, __spreadArray, __values } from "tslib"; | ||
import { version } from '../../package.json'; | ||
import { magenta, yellow } from 'colors'; | ||
import { green, magenta, yellow } from 'colors'; | ||
import { createManagementClient } from '@kontent-ai/management-sdk'; | ||
var ExportService = /** @class */ (function () { | ||
@@ -21,2 +22,8 @@ function ExportService(config) { | ||
httpService: this.httpService, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
defaultQueryConfig: { | ||
usePreviewMode: config.previewApiKey ? true : false, | ||
useSecuredMode: config.secureApiKey ? true : false | ||
}, | ||
proxy: { | ||
@@ -45,9 +52,12 @@ baseUrl: config.baseUrl | ||
console.log(''); | ||
if (this.config.exportAssets) { | ||
console.log("Extracting assets referenced by content items"); | ||
assets = this.extractAssets(contentItems, types); | ||
} | ||
else { | ||
console.log("Assets export is disabled"); | ||
} | ||
if (!this.config.exportAssets) return [3 /*break*/, 5]; | ||
console.log("Extracting assets referenced by content items"); | ||
return [4 /*yield*/, this.extractAssetsAsync(contentItems, types)]; | ||
case 4: | ||
assets = _a.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
console.log("Assets export is disabled"); | ||
_a.label = 6; | ||
case 6: | ||
data = { | ||
@@ -145,3 +155,3 @@ contentItems: contentItems, | ||
var contentItem = _d.value; | ||
_this.processItem("".concat(contentItem.system.name, " ").concat(magenta(contentItem.system.language)), 'contentItem', 'fetch', contentItem); | ||
_this.processItem("'".concat(yellow(contentItem.system.name), "' | ").concat(magenta(contentItem.system.language)), 'contentItem', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -159,3 +169,3 @@ } | ||
if (!contentItems.find(function (m) { return m.system.codename === codename; })) { | ||
_this.processItem("".concat(contentItem.system.name, " ").concat(magenta(contentItem.system.language)), 'component', 'fetch', contentItem); | ||
_this.processItem("'".concat(yellow(contentItem.system.name), "' | ").concat(magenta(contentItem.system.language)), 'component', 'fetch', contentItem); | ||
contentItems.push(contentItem); | ||
@@ -253,96 +263,143 @@ } | ||
}; | ||
ExportService.prototype.extractAssets = function (items, types) { | ||
var e_6, _a; | ||
var assets = []; | ||
var _loop_3 = function (type) { | ||
var e_7, _b, e_8, _c, e_9, _d; | ||
var itemsOfType = items.filter(function (m) { return m.system.type === type.system.codename; }); | ||
try { | ||
for (var _e = (e_7 = void 0, __values(type.elements)), _f = _e.next(); !_f.done; _f = _e.next()) { | ||
var element = _f.value; | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === ElementType.Asset) { | ||
try { | ||
for (var itemsOfType_1 = (e_8 = void 0, __values(itemsOfType)), itemsOfType_1_1 = itemsOfType_1.next(); !itemsOfType_1_1.done; itemsOfType_1_1 = itemsOfType_1.next()) { | ||
var item = itemsOfType_1_1.value; | ||
var assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(assetElement.value.map(function (m) { | ||
var _a; | ||
var assetId = extractAssetIdFromUrl(m.url); | ||
var extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
ExportService.prototype.extractAssetsAsync = function (items, types) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var assets, _loop_3, types_2, types_2_1, type, uniqueAssets, managementClient, uniqueAssets_1, uniqueAssets_1_1, asset, assetResponse, e_6_1; | ||
var e_7, _b, e_6, _c; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
assets = []; | ||
_loop_3 = function (type) { | ||
var e_8, _e, e_9, _f, e_10, _g; | ||
var itemsOfType = items.filter(function (m) { return m.system.type === type.system.codename; }); | ||
try { | ||
for (var _h = (e_8 = void 0, __values(type.elements)), _j = _h.next(); !_j.done; _j = _h.next()) { | ||
var element = _j.value; | ||
if (!element.codename) { | ||
continue; | ||
} | ||
if (element.type === ElementType.Asset) { | ||
try { | ||
for (var itemsOfType_1 = (e_9 = void 0, __values(itemsOfType)), itemsOfType_1_1 = itemsOfType_1.next(); !itemsOfType_1_1.done; itemsOfType_1_1 = itemsOfType_1.next()) { | ||
var item = itemsOfType_1_1.value; | ||
var assetElement = item.elements[element.codename]; | ||
if (assetElement.value.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(assetElement.value.map(function (m) { | ||
var _a; | ||
var assetId = extractAssetIdFromUrl(m.url); | ||
var extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_1_1 && !itemsOfType_1_1.done && (_f = itemsOfType_1.return)) _f.call(itemsOfType_1); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
} | ||
else if (element.type === ElementType.RichText) { | ||
try { | ||
for (var itemsOfType_2 = (e_10 = void 0, __values(itemsOfType)), itemsOfType_2_1 = itemsOfType_2.next(); !itemsOfType_2_1.done; itemsOfType_2_1 = itemsOfType_2.next()) { | ||
var item = itemsOfType_2_1.value; | ||
var richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(richTextElement.images.map(function (m) { | ||
var _a; | ||
var assetId = extractAssetIdFromUrl(m.url); | ||
var extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
} | ||
} | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_2_1 && !itemsOfType_2_1.done && (_g = itemsOfType_2.return)) _g.call(itemsOfType_2); | ||
} | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_1_1 && !itemsOfType_1_1.done && (_c = itemsOfType_1.return)) _c.call(itemsOfType_1); | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (_j && !_j.done && (_e = _h.return)) _e.call(_h); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
} | ||
else if (element.type === ElementType.RichText) { | ||
}; | ||
try { | ||
for (var itemsOfType_2 = (e_9 = void 0, __values(itemsOfType)), itemsOfType_2_1 = itemsOfType_2.next(); !itemsOfType_2_1.done; itemsOfType_2_1 = itemsOfType_2.next()) { | ||
var item = itemsOfType_2_1.value; | ||
var richTextElement = item.elements[element.codename]; | ||
if (richTextElement.images.length) { | ||
assets.push.apply(assets, __spreadArray([], __read(richTextElement.images.map(function (m) { | ||
var _a; | ||
var assetId = extractAssetIdFromUrl(m.url); | ||
var extension = (_a = getExtension(m.url)) !== null && _a !== void 0 ? _a : ''; | ||
var asset = { | ||
url: m.url, | ||
assetId: assetId, | ||
filename: "".concat(assetId, ".").concat(extension), | ||
extension: extension | ||
}; | ||
return asset; | ||
})), false)); | ||
} | ||
for (types_2 = __values(types), types_2_1 = types_2.next(); !types_2_1.done; types_2_1 = types_2.next()) { | ||
type = types_2_1.value; | ||
_loop_3(type); | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (itemsOfType_2_1 && !itemsOfType_2_1.done && (_d = itemsOfType_2.return)) _d.call(itemsOfType_2); | ||
if (types_2_1 && !types_2_1.done && (_b = types_2.return)) _b.call(types_2); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
} | ||
uniqueAssets = __spreadArray([], __read(new Map(assets.map(function (item) { return [item.url, item]; })).values()), false); | ||
if (!(this.config.fetchAssetDetails === true)) return [3 /*break*/, 8]; | ||
if (!this.config.apiKey) { | ||
throw Error("Management API key is required to fetch asset details"); | ||
} | ||
managementClient = createManagementClient({ | ||
apiKey: this.config.apiKey, | ||
projectId: this.config.projectId, | ||
retryStrategy: (_a = this.config.retryStrategy) !== null && _a !== void 0 ? _a : defaultRetryStrategy | ||
}); | ||
_d.label = 1; | ||
case 1: | ||
_d.trys.push([1, 6, 7, 8]); | ||
uniqueAssets_1 = __values(uniqueAssets), uniqueAssets_1_1 = uniqueAssets_1.next(); | ||
_d.label = 2; | ||
case 2: | ||
if (!!uniqueAssets_1_1.done) return [3 /*break*/, 5]; | ||
asset = uniqueAssets_1_1.value; | ||
return [4 /*yield*/, managementClient.viewAsset().byAssetId(asset.assetId).toPromise()]; | ||
case 3: | ||
assetResponse = _d.sent(); | ||
console.log("Fetched asset details '".concat(yellow(asset.assetId), "' -> '").concat(green(assetResponse.data.fileName), "'")); | ||
asset.filename = assetResponse.data.fileName; | ||
_d.label = 4; | ||
case 4: | ||
uniqueAssets_1_1 = uniqueAssets_1.next(); | ||
return [3 /*break*/, 2]; | ||
case 5: return [3 /*break*/, 8]; | ||
case 6: | ||
e_6_1 = _d.sent(); | ||
e_6 = { error: e_6_1 }; | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
try { | ||
if (uniqueAssets_1_1 && !uniqueAssets_1_1.done && (_c = uniqueAssets_1.return)) _c.call(uniqueAssets_1); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
return [7 /*endfinally*/]; | ||
case 8: return [2 /*return*/, uniqueAssets]; | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_f && !_f.done && (_b = _e.return)) _b.call(_e); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
}; | ||
try { | ||
for (var types_2 = __values(types), types_2_1 = types_2.next(); !types_2_1.done; types_2_1 = types_2.next()) { | ||
var type = types_2_1.value; | ||
_loop_3(type); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (types_2_1 && !types_2_1.done && (_a = types_2.return)) _a.call(types_2); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
return __spreadArray([], __read(new Map(assets.map(function (item) { return [item.url, item]; })).values()), false); // filters unique values | ||
}); | ||
}); | ||
}; | ||
@@ -349,0 +406,0 @@ return ExportService; |
@@ -22,1 +22,5 @@ import { IContentType } from '@kontent-ai/delivery-sdk'; | ||
} | ||
export interface IAssetDetailModel { | ||
assetId: string; | ||
filename: string; | ||
} |
@@ -8,2 +8,3 @@ import { IExportAllResult } from '../export'; | ||
private readonly metadataName; | ||
private readonly assetDetailsName; | ||
private readonly assetsFolderName; | ||
@@ -18,2 +19,3 @@ private readonly contentItemsFolderName; | ||
private mapLanguageVariantsToCsvAsync; | ||
private getAssetDetailModels; | ||
private getBaseContentItemFields; | ||
@@ -20,0 +22,0 @@ private getLanguageVariantFields; |
@@ -15,2 +15,3 @@ import { __asyncValues, __awaiter, __generator, __read, __spreadArray, __values } from "tslib"; | ||
this.metadataName = 'metadata.json'; | ||
this.assetDetailsName = '_details.json'; | ||
this.assetsFolderName = 'assets'; | ||
@@ -115,2 +116,3 @@ this.contentItemsFolderName = 'items'; | ||
if (!exportData.data.assets.length) return [3 /*break*/, 11]; | ||
assetsFolder.file(this.assetDetailsName, JSON.stringify(this.getAssetDetailModels(exportData.data.assets))); | ||
console.log("Preparing to download '".concat(yellow(exportData.data.assets.length.toString()), "' assets")); | ||
@@ -125,3 +127,3 @@ _h.label = 2; | ||
asset = _b.value; | ||
assetFilename = asset.filename; | ||
assetFilename = "".concat(asset.assetId, ".").concat(asset.extension); | ||
_d = (_c = assetsFolder).file; | ||
@@ -241,2 +243,11 @@ _e = [assetFilename]; | ||
}; | ||
FileProcessorService.prototype.getAssetDetailModels = function (extractedAssets) { | ||
return extractedAssets.map(function (m) { | ||
var item = { | ||
assetId: m.assetId, | ||
filename: m.filename | ||
}; | ||
return item; | ||
}); | ||
}; | ||
FileProcessorService.prototype.getBaseContentItemFields = function () { | ||
@@ -312,54 +323,81 @@ return ['codename', 'name', 'language', 'type', 'collection', 'last_modified', 'workflow_step']; | ||
FileProcessorService.prototype.extractAssetsAsync = function (zip) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var assets, files, _d, _e, _f, file, binaryData, filename, extension, e_5_1; | ||
var e_5, _g; | ||
return __generator(this, function (_h) { | ||
switch (_h.label) { | ||
var assets, files, assetDetailsFilePath, assetDetailsFile, assetDetailModels, _e, _f, _loop_2, this_2, _g, _h, _j, file, e_5_1; | ||
var e_5, _k; | ||
return __generator(this, function (_l) { | ||
switch (_l.label) { | ||
case 0: | ||
assets = []; | ||
files = zip.files; | ||
_h.label = 1; | ||
assetDetailsFilePath = "".concat(this.assetsFolderName, "/").concat(this.assetDetailsName); | ||
assetDetailsFile = files[assetDetailsFilePath]; | ||
if (!assetDetailsFile) { | ||
throw Error("Invalid file path '".concat(assetDetailsFilePath, "'")); | ||
} | ||
_f = (_e = JSON).parse; | ||
return [4 /*yield*/, assetDetailsFile.async('string')]; | ||
case 1: | ||
_h.trys.push([1, 6, 7, 8]); | ||
_d = __values(Object.entries(files)), _e = _d.next(); | ||
_h.label = 2; | ||
assetDetailModels = _f.apply(_e, [_l.sent()]); | ||
_loop_2 = function (file) { | ||
var binaryData, assetId, assetDetailModel, extension, filename, mimeType; | ||
return __generator(this, function (_m) { | ||
switch (_m.label) { | ||
case 0: | ||
if (!((_a = file === null || file === void 0 ? void 0 : file.name) === null || _a === void 0 ? void 0 : _a.startsWith("".concat(this_2.assetsFolderName, "/")))) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
if ((_b = file === null || file === void 0 ? void 0 : file.name) === null || _b === void 0 ? void 0 : _b.endsWith('/')) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
if ((file === null || file === void 0 ? void 0 : file.name) === this_2.assetDetailsName) { | ||
return [2 /*return*/, "continue"]; | ||
} | ||
return [4 /*yield*/, file.async(this_2.getZipOutputType())]; | ||
case 1: | ||
binaryData = _m.sent(); | ||
assetId = this_2.getAssetIdFromFilename(file.name); | ||
assetDetailModel = assetDetailModels.find(function (m) { return m.assetId === assetId; }); | ||
extension = getExtension(file.name); | ||
filename = (_c = assetDetailModel === null || assetDetailModel === void 0 ? void 0 : assetDetailModel.filename) !== null && _c !== void 0 ? _c : "".concat(assetId, ".").concat(extension); | ||
mimeType = (_d = getType(file.name)) !== null && _d !== void 0 ? _d : undefined; | ||
assets.push({ | ||
assetId: assetId, | ||
binaryData: binaryData, | ||
filename: filename, | ||
mimeType: mimeType, | ||
extension: extension | ||
}); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}; | ||
this_2 = this; | ||
_l.label = 2; | ||
case 2: | ||
if (!!_e.done) return [3 /*break*/, 5]; | ||
_f = __read(_e.value, 2), file = _f[1]; | ||
if (!((_a = file === null || file === void 0 ? void 0 : file.name) === null || _a === void 0 ? void 0 : _a.startsWith("".concat(this.assetsFolderName, "/")))) { | ||
// iterate through assets only | ||
return [3 /*break*/, 4]; | ||
} | ||
if ((_b = file === null || file === void 0 ? void 0 : file.name) === null || _b === void 0 ? void 0 : _b.endsWith('/')) { | ||
return [3 /*break*/, 4]; | ||
} | ||
return [4 /*yield*/, file.async(this.getZipOutputType())]; | ||
_l.trys.push([2, 7, 8, 9]); | ||
_g = __values(Object.entries(files)), _h = _g.next(); | ||
_l.label = 3; | ||
case 3: | ||
binaryData = _h.sent(); | ||
filename = file.name; | ||
extension = getExtension(filename); | ||
assets.push({ | ||
assetId: this.getAssetIdFromFilename(filename), | ||
binaryData: binaryData, | ||
filename: filename.split('/')[1], | ||
mimeType: (_c = getType(filename)) !== null && _c !== void 0 ? _c : undefined, | ||
extension: extension | ||
}); | ||
_h.label = 4; | ||
if (!!_h.done) return [3 /*break*/, 6]; | ||
_j = __read(_h.value, 2), file = _j[1]; | ||
return [5 /*yield**/, _loop_2(file)]; | ||
case 4: | ||
_e = _d.next(); | ||
return [3 /*break*/, 2]; | ||
case 5: return [3 /*break*/, 8]; | ||
case 6: | ||
e_5_1 = _h.sent(); | ||
_l.sent(); | ||
_l.label = 5; | ||
case 5: | ||
_h = _g.next(); | ||
return [3 /*break*/, 3]; | ||
case 6: return [3 /*break*/, 9]; | ||
case 7: | ||
e_5_1 = _l.sent(); | ||
e_5 = { error: e_5_1 }; | ||
return [3 /*break*/, 8]; | ||
case 7: | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
try { | ||
if (_e && !_e.done && (_g = _d.return)) _g.call(_d); | ||
if (_h && !_h.done && (_k = _g.return)) _k.call(_g); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
return [7 /*endfinally*/]; | ||
case 8: return [2 /*return*/, assets]; | ||
case 9: return [2 /*return*/, assets]; | ||
} | ||
@@ -366,0 +404,0 @@ }); |
@@ -16,3 +16,4 @@ import { IImportItemResult } from '../core'; | ||
private importContentItemsAsync; | ||
private getWorkflowForGivenStep; | ||
private getWorkflowForGivenStepByCodename; | ||
private getWorkflowForGivenStepById; | ||
private prepareContentItemForImportAsync; | ||
@@ -23,2 +24,3 @@ private prepareLanguageVariantForImportAsync; | ||
private isLanguageVariantPublished; | ||
private isLanguageVariantArchived; | ||
private doesWorkflowStepCodenameRepresentPublishedStep; | ||
@@ -25,0 +27,0 @@ private doesWorkflowStepCodenameRepresentArchivedStep; |
@@ -453,3 +453,3 @@ import { __awaiter, __generator, __read, __spreadArray, __values } from "tslib"; | ||
if (!this_4.doesWorkflowStepCodenameRepresentArchivedStep(importContentItem.workflow_step, workflows)) return [3 /*break*/, 6]; | ||
workflow = this_4.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
workflow = this_4.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
return [4 /*yield*/, this_4.client | ||
@@ -480,3 +480,3 @@ .changeWorkflowOfLanguageVariant() | ||
case 6: | ||
workflow = this_4.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
workflow = this_4.getWorkflowForGivenStepByCodename(importContentItem.workflow_step, workflows); | ||
return [4 /*yield*/, this_4.client | ||
@@ -565,3 +565,3 @@ .changeWorkflowOfLanguageVariant() | ||
}; | ||
ImportService.prototype.getWorkflowForGivenStep = function (itemWorkflowCodename, workflows) { | ||
ImportService.prototype.getWorkflowForGivenStepByCodename = function (itemWorkflowCodename, workflows) { | ||
var e_6, _a; | ||
@@ -596,2 +596,32 @@ try { | ||
}; | ||
ImportService.prototype.getWorkflowForGivenStepById = function (workflowId, workflows) { | ||
var e_7, _a; | ||
try { | ||
for (var workflows_2 = __values(workflows), workflows_2_1 = workflows_2.next(); !workflows_2_1.done; workflows_2_1 = workflows_2.next()) { | ||
var workflow = workflows_2_1.value; | ||
if (workflow.archivedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
if (workflow.publishedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
var step = workflow.steps.find(function (m) { return m.id === workflowId; }); | ||
if (step) { | ||
return workflow; | ||
} | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (workflows_2_1 && !workflows_2_1.done && (_a = workflows_2.return)) _a.call(workflows_2); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
var defaultWorkflow = workflows.find(function (m) { return m.codename.toLowerCase() === defaultWorkflowCodename.toLowerCase(); }); | ||
if (!defaultWorkflow) { | ||
throw Error("Missing default workflow"); | ||
} | ||
return defaultWorkflow; | ||
}; | ||
ImportService.prototype.prepareContentItemForImportAsync = function (importContentItem, importedItems) { | ||
@@ -654,3 +684,3 @@ var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var languageVariantOfContentItem, error_7, throwError; | ||
var languageVariantOfContentItem, error_7, throwError, workflow, newWorkflowStep; | ||
return __generator(this, function (_c) { | ||
@@ -686,3 +716,3 @@ switch (_c.label) { | ||
case 3: | ||
if (!languageVariantOfContentItem) return [3 /*break*/, 5]; | ||
if (!languageVariantOfContentItem) return [3 /*break*/, 7]; | ||
if (!this.isLanguageVariantPublished(languageVariantOfContentItem, workflows)) return [3 /*break*/, 5]; | ||
@@ -699,8 +729,27 @@ // create new version | ||
this.processItem(importItems, 'createNewVersion', 'languageVariant', { | ||
title: "".concat(importContentItem.name, " (").concat(magenta(importContentItem.language), ")"), | ||
title: "".concat(yellow(importContentItem.name), " (").concat(magenta(importContentItem.language), ")"), | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
_c.label = 5; | ||
case 5: return [2 /*return*/]; | ||
return [3 /*break*/, 7]; | ||
case 5: | ||
if (!this.isLanguageVariantArchived(languageVariantOfContentItem, workflows)) return [3 /*break*/, 7]; | ||
if (!languageVariantOfContentItem.workflowStep.id) return [3 /*break*/, 7]; | ||
workflow = this.getWorkflowForGivenStepById(languageVariantOfContentItem.workflowStep.id, workflows); | ||
newWorkflowStep = workflow.steps[0]; | ||
return [4 /*yield*/, this.client | ||
.changeWorkflowStepOfLanguageVariant() | ||
.byItemCodename(importContentItem.codename) | ||
.byLanguageCodename(importContentItem.language) | ||
.byWorkflowStepCodename(newWorkflowStep.codename) | ||
.toPromise()]; | ||
case 6: | ||
_c.sent(); | ||
this.processItem(importItems, 'unArchive', 'languageVariant', { | ||
title: "".concat(yellow(importContentItem.name), " | (").concat(newWorkflowStep.codename, ") (").concat(magenta(importContentItem.language), ")"), | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
_c.label = 7; | ||
case 7: return [2 /*return*/]; | ||
} | ||
@@ -731,6 +780,6 @@ }); | ||
ImportService.prototype.isLanguageVariantPublished = function (languageVariant, workflows) { | ||
var e_7, _a; | ||
var e_8, _a; | ||
try { | ||
for (var workflows_2 = __values(workflows), workflows_2_1 = workflows_2.next(); !workflows_2_1.done; workflows_2_1 = workflows_2.next()) { | ||
var workflow = workflows_2_1.value; | ||
for (var workflows_3 = __values(workflows), workflows_3_1 = workflows_3.next(); !workflows_3_1.done; workflows_3_1 = workflows_3.next()) { | ||
var workflow = workflows_3_1.value; | ||
if (workflow.publishedStep.id === languageVariant.workflowStep.id) { | ||
@@ -741,16 +790,35 @@ return true; | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (workflows_2_1 && !workflows_2_1.done && (_a = workflows_2.return)) _a.call(workflows_2); | ||
if (workflows_3_1 && !workflows_3_1.done && (_a = workflows_3.return)) _a.call(workflows_3); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
return false; | ||
}; | ||
ImportService.prototype.isLanguageVariantArchived = function (languageVariant, workflows) { | ||
var e_9, _a; | ||
try { | ||
for (var workflows_4 = __values(workflows), workflows_4_1 = workflows_4.next(); !workflows_4_1.done; workflows_4_1 = workflows_4.next()) { | ||
var workflow = workflows_4_1.value; | ||
if (workflow.archivedStep.id === languageVariant.workflowStep.id) { | ||
return true; | ||
} | ||
} | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (workflows_4_1 && !workflows_4_1.done && (_a = workflows_4.return)) _a.call(workflows_4); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
return false; | ||
}; | ||
ImportService.prototype.doesWorkflowStepCodenameRepresentPublishedStep = function (workflowStepCodename, workflows) { | ||
var e_8, _a; | ||
var e_10, _a; | ||
try { | ||
for (var workflows_3 = __values(workflows), workflows_3_1 = workflows_3.next(); !workflows_3_1.done; workflows_3_1 = workflows_3.next()) { | ||
var workflow = workflows_3_1.value; | ||
for (var workflows_5 = __values(workflows), workflows_5_1 = workflows_5.next(); !workflows_5_1.done; workflows_5_1 = workflows_5.next()) { | ||
var workflow = workflows_5_1.value; | ||
if (workflow.publishedStep.codename === workflowStepCodename) { | ||
@@ -761,8 +829,8 @@ return true; | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
try { | ||
if (workflows_3_1 && !workflows_3_1.done && (_a = workflows_3.return)) _a.call(workflows_3); | ||
if (workflows_5_1 && !workflows_5_1.done && (_a = workflows_5.return)) _a.call(workflows_5); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
@@ -772,6 +840,6 @@ return false; | ||
ImportService.prototype.doesWorkflowStepCodenameRepresentArchivedStep = function (workflowStepCodename, workflows) { | ||
var e_9, _a; | ||
var e_11, _a; | ||
try { | ||
for (var workflows_4 = __values(workflows), workflows_4_1 = workflows_4.next(); !workflows_4_1.done; workflows_4_1 = workflows_4.next()) { | ||
var workflow = workflows_4_1.value; | ||
for (var workflows_6 = __values(workflows), workflows_6_1 = workflows_6.next(); !workflows_6_1.done; workflows_6_1 = workflows_6.next()) { | ||
var workflow = workflows_6_1.value; | ||
if (workflow.archivedStep.codename === workflowStepCodename) { | ||
@@ -782,8 +850,8 @@ return true; | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
catch (e_11_1) { e_11 = { error: e_11_1 }; } | ||
finally { | ||
try { | ||
if (workflows_4_1 && !workflows_4_1.done && (_a = workflows_4.return)) _a.call(workflows_4); | ||
if (workflows_6_1 && !workflows_6_1.done && (_a = workflows_6.return)) _a.call(workflows_6); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
finally { if (e_11) throw e_11.error; } | ||
} | ||
@@ -790,0 +858,0 @@ return false; |
@@ -17,4 +17,8 @@ #!/usr/bin/env node | ||
.describe('p', 'ProjectId') | ||
.alias('k', 'apiKey') | ||
.describe('k', 'Management API Key') | ||
.alias('ak', 'apiKey') | ||
.describe('ak', 'Management API Key') | ||
.alias('sk', 'secureApiKey') | ||
.describe('sk', 'API Key required when Delivery API has secure access enabled') | ||
.alias('pk', 'previewApiKey') | ||
.describe('pk', 'Use if you want to export data using Preview API') | ||
.alias('a', 'action') | ||
@@ -41,7 +45,11 @@ .describe('a', 'Action to perform. One of: "backup" | "restore"') | ||
projectId: config.projectId, | ||
apiKey: config.apiKey, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
baseUrl: config.baseUrl, | ||
exportTypes: config.exportTypes, | ||
exportAssets: config.exportAssets, | ||
fetchAssetDetails: config.fetchAssetDetails, | ||
onProcess: function (item) { | ||
console.log("Exported ".concat(yellow(item.title), " | ").concat(green(item.data.system.type))); | ||
console.log("Exported ".concat((item.title), " | ").concat(green(item.data.system.type))); | ||
} | ||
@@ -162,12 +170,12 @@ }); | ||
var getConfig = function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var resolvedArgs, configFilename, configFile, action, apiKey, projectId, baseUrl, filename, exportTypes, exportAssets, skipFailedItems, typesMapped, config; | ||
var _a, _b, _c, _d, _e; | ||
return __generator(this, function (_f) { | ||
switch (_f.label) { | ||
var resolvedArgs, configFilename, configFile, action, apiKey, secureApiKey, previewApiKey, projectId, baseUrl, filename, exportTypes, exportAssets, skipFailedItems, fetchAssetDetails, typesMapped, config; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
return __generator(this, function (_h) { | ||
switch (_h.label) { | ||
case 0: return [4 /*yield*/, argv]; | ||
case 1: | ||
resolvedArgs = _f.sent(); | ||
resolvedArgs = _h.sent(); | ||
return [4 /*yield*/, resolvedArgs.config]; | ||
case 2: | ||
configFilename = (_f.sent()); | ||
configFilename = (_h.sent()); | ||
if (configFilename) { | ||
@@ -179,2 +187,4 @@ configFile = readFileSync("./".concat(configFilename)); | ||
apiKey = resolvedArgs.apiKey; | ||
secureApiKey = resolvedArgs.secureApiKey; | ||
previewApiKey = resolvedArgs.previewApiKey; | ||
projectId = resolvedArgs.projectId; | ||
@@ -186,2 +196,3 @@ baseUrl = resolvedArgs.baseUrl; | ||
skipFailedItems = (_e = ((_d = resolvedArgs.skipFailedItems) === null || _d === void 0 ? void 0 : _d.toLowerCase()) === 'true'.toLowerCase()) !== null && _e !== void 0 ? _e : true; | ||
fetchAssetDetails = (_g = ((_f = resolvedArgs.fetchAssetDetails) === null || _f === void 0 ? void 0 : _f.toLowerCase()) === 'true'.toLowerCase()) !== null && _g !== void 0 ? _g : false; | ||
typesMapped = exportTypes ? exportTypes.split(',').map(function (m) { return m.trim(); }) : []; | ||
@@ -202,3 +213,6 @@ if (!action) { | ||
exportAssets: exportAssets, | ||
skipFailedItems: skipFailedItems | ||
skipFailedItems: skipFailedItems, | ||
previewApiKey: previewApiKey, | ||
secureApiKey: secureApiKey, | ||
fetchAssetDetails: fetchAssetDetails | ||
}; | ||
@@ -211,3 +225,3 @@ return [2 /*return*/, config]; | ||
var date = new Date(); | ||
return "csvm-backup-".concat(date.getDate(), "-").concat(date.getMonth() + 1, "-").concat(date.getFullYear(), "-").concat(date.getHours(), "-").concat(date.getMinutes(), ".zip"); | ||
return "csv-backup-".concat(date.getDate(), "-").concat(date.getMonth() + 1, "-").concat(date.getFullYear(), "-").concat(date.getHours(), "-").concat(date.getMinutes(), ".zip"); | ||
}; | ||
@@ -214,0 +228,0 @@ run() |
{ | ||
"name": "xeno-test", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "This utility enables users to export & import content data from / to Kontent.ai projects", | ||
@@ -8,3 +8,3 @@ "preferGlobal": true, | ||
"bin": { | ||
"csvm": "./dist/cjs/lib/node/cli/app.js" | ||
"kcsvm": "./dist/cjs/lib/node/cli/app.js" | ||
}, | ||
@@ -11,0 +11,0 @@ "repository": { |
export interface ICliFileConfig { | ||
projectId: string; | ||
apiKey?: string; | ||
secureApiKey?: string; | ||
previewApiKey?: string; | ||
skipFailedItems: boolean; | ||
@@ -10,2 +12,3 @@ action: CliAction; | ||
exportAssets: boolean; | ||
fetchAssetDetails?: boolean; | ||
} | ||
@@ -27,2 +30,3 @@ | ||
| 'publish' | ||
| 'unArchive' | ||
| 'update'; | ||
@@ -29,0 +33,0 @@ |
@@ -15,2 +15,5 @@ import { IRetryStrategyOptions } from '@kontent-ai/core-sdk'; | ||
projectId: string; | ||
secureApiKey?: string; | ||
apiKey?: string; | ||
previewApiKey?: string; | ||
baseUrl?: string; | ||
@@ -21,2 +24,3 @@ onProcess?: (item: IProcessedItem) => void; | ||
retryStrategy?: IRetryStrategyOptions; | ||
fetchAssetDetails?: boolean; | ||
} | ||
@@ -23,0 +27,0 @@ |
@@ -15,3 +15,4 @@ import { HttpService } from '@kontent-ai/core-sdk'; | ||
import { version } from '../../package.json'; | ||
import { magenta, yellow } from 'colors'; | ||
import { green, magenta, yellow } from 'colors'; | ||
import { createManagementClient } from '@kontent-ai/management-sdk'; | ||
@@ -34,2 +35,8 @@ export class ExportService { | ||
httpService: this.httpService, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
defaultQueryConfig: { | ||
usePreviewMode: config.previewApiKey ? true : false, | ||
useSecuredMode: config.secureApiKey ? true : false | ||
}, | ||
proxy: { | ||
@@ -53,3 +60,3 @@ baseUrl: config.baseUrl | ||
console.log(`Extracting assets referenced by content items`); | ||
assets = this.extractAssets(contentItems, types); | ||
assets = await this.extractAssetsAsync(contentItems, types); | ||
} else { | ||
@@ -119,3 +126,3 @@ console.log(`Assets export is disabled`); | ||
this.processItem( | ||
`${contentItem.system.name} ${magenta(contentItem.system.language)}`, | ||
`'${yellow(contentItem.system.name)}' | ${magenta(contentItem.system.language)}`, | ||
'contentItem', | ||
@@ -132,3 +139,3 @@ 'fetch', | ||
this.processItem( | ||
`${contentItem.system.name} ${magenta(contentItem.system.language)}`, | ||
`'${yellow(contentItem.system.name)}' | ${magenta(contentItem.system.language)}`, | ||
'component', | ||
@@ -172,3 +179,3 @@ 'fetch', | ||
private extractAssets(items: IContentItem[], types: IContentType[]): IExportedAsset[] { | ||
private async extractAssetsAsync(items: IContentItem[], types: IContentType[]): Promise<IExportedAsset[]> { | ||
const assets: IExportedAsset[] = []; | ||
@@ -229,4 +236,24 @@ | ||
return [...new Map(assets.map((item) => [item.url, item])).values()]; // filters unique values | ||
const uniqueAssets = [...new Map(assets.map((item) => [item.url, item])).values()]; // filters unique values | ||
if (this.config.fetchAssetDetails === true) { | ||
if (!this.config.apiKey) { | ||
throw Error(`Management API key is required to fetch asset details`); | ||
} | ||
const managementClient = createManagementClient({ | ||
apiKey: this.config.apiKey, | ||
projectId: this.config.projectId, | ||
retryStrategy: this.config.retryStrategy ?? defaultRetryStrategy | ||
}); | ||
for (const asset of uniqueAssets) { | ||
const assetResponse = await managementClient.viewAsset().byAssetId(asset.assetId).toPromise(); | ||
console.log(`Fetched asset details '${yellow(asset.assetId)}' -> '${green(assetResponse.data.fileName)}'`); | ||
asset.filename = assetResponse.data.fileName; | ||
} | ||
} | ||
return uniqueAssets; | ||
} | ||
} |
@@ -27,1 +27,6 @@ import { IContentType } from '@kontent-ai/delivery-sdk'; | ||
} | ||
export interface IAssetDetailModel { | ||
assetId: string; | ||
filename: string; | ||
} |
@@ -5,3 +5,3 @@ import { HttpService } from '@kontent-ai/core-sdk'; | ||
import { IExportAllResult } from '../export'; | ||
import { IExportAllResult, IExportedAsset } from '../export'; | ||
import { IImportAsset, IImportContentItem, IImportSource } from '../import'; | ||
@@ -11,3 +11,4 @@ import { | ||
ILanguageVariantsTypeCsvWrapper, | ||
IFileProcessorConfig | ||
IFileProcessorConfig, | ||
IAssetDetailModel | ||
} from './file-processor.models'; | ||
@@ -25,2 +26,3 @@ import { yellow } from 'colors'; | ||
private readonly metadataName: string = 'metadata.json'; | ||
private readonly assetDetailsName: string = '_details.json'; | ||
private readonly assetsFolderName: string = 'assets'; | ||
@@ -108,6 +110,8 @@ private readonly contentItemsFolderName: string = 'items'; | ||
if (exportData.data.assets.length) { | ||
assetsFolder.file(this.assetDetailsName, JSON.stringify(this.getAssetDetailModels(exportData.data.assets))); | ||
console.log(`Preparing to download '${yellow(exportData.data.assets.length.toString())}' assets`); | ||
for (const asset of exportData.data.assets) { | ||
const assetFilename = asset.filename; | ||
const assetFilename = `${asset.assetId}.${asset.extension}`; // use id as filename to prevent name conflicts | ||
assetsFolder.file(assetFilename, await this.getBinaryDataFromUrlAsync(asset.url), { | ||
@@ -168,2 +172,13 @@ binary: true | ||
private getAssetDetailModels(extractedAssets: IExportedAsset[]): IAssetDetailModel[] { | ||
return extractedAssets.map((m) => { | ||
const item: IAssetDetailModel = { | ||
assetId: m.assetId, | ||
filename: m.filename | ||
}; | ||
return item; | ||
}); | ||
} | ||
private getBaseContentItemFields(): string[] { | ||
@@ -246,2 +261,11 @@ return ['codename', 'name', 'language', 'type', 'collection', 'last_modified', 'workflow_step']; | ||
const assetDetailsFilePath = `${this.assetsFolderName}/${this.assetDetailsName}`; | ||
const assetDetailsFile = files[assetDetailsFilePath]; | ||
if (!assetDetailsFile) { | ||
throw Error(`Invalid file path '${assetDetailsFilePath}'`); | ||
} | ||
const assetDetailModels = JSON.parse(await assetDetailsFile.async('string')) as IAssetDetailModel[]; | ||
for (const [, file] of Object.entries(files)) { | ||
@@ -257,11 +281,19 @@ if (!file?.name?.startsWith(`${this.assetsFolderName}/`)) { | ||
if (file?.name === this.assetDetailsName) { | ||
continue; | ||
} | ||
const binaryData = await file.async(this.getZipOutputType()); | ||
const filename = file.name; | ||
const extension = getExtension(filename); | ||
const assetId = this.getAssetIdFromFilename(file.name); | ||
const assetDetailModel = assetDetailModels.find((m) => m.assetId === assetId); | ||
const extension = getExtension(file.name); | ||
const filename = assetDetailModel?.filename ?? `${assetId}.${extension}`; | ||
const mimeType = getType(file.name) ?? undefined; | ||
assets.push({ | ||
assetId: this.getAssetIdFromFilename(filename), | ||
assetId: assetId, | ||
binaryData: binaryData, | ||
filename: filename.split('/')[1], | ||
mimeType: getType(filename) ?? undefined, | ||
filename: filename, | ||
mimeType: mimeType, | ||
extension: extension | ||
@@ -268,0 +300,0 @@ }); |
@@ -383,3 +383,6 @@ import { | ||
) { | ||
const workflow = this.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
const workflow = this.getWorkflowForGivenStepByCodename( | ||
importContentItem.workflow_step, | ||
workflows | ||
); | ||
@@ -411,3 +414,6 @@ await this.client | ||
} else { | ||
const workflow = this.getWorkflowForGivenStep(importContentItem.workflow_step, workflows); | ||
const workflow = this.getWorkflowForGivenStepByCodename( | ||
importContentItem.workflow_step, | ||
workflows | ||
); | ||
@@ -470,3 +476,3 @@ await this.client | ||
private getWorkflowForGivenStep( | ||
private getWorkflowForGivenStepByCodename( | ||
itemWorkflowCodename: string, | ||
@@ -500,2 +506,31 @@ workflows: WorkflowModels.Workflow[] | ||
private getWorkflowForGivenStepById( | ||
workflowId: string, | ||
workflows: WorkflowModels.Workflow[] | ||
): WorkflowModels.Workflow { | ||
for (const workflow of workflows) { | ||
if (workflow.archivedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
if (workflow.publishedStep.id === workflowId) { | ||
return workflow; | ||
} | ||
const step = workflow.steps.find((m) => m.id === workflowId); | ||
if (step) { | ||
return workflow; | ||
} | ||
} | ||
const defaultWorkflow = workflows.find( | ||
(m) => m.codename.toLowerCase() === defaultWorkflowCodename.toLowerCase() | ||
); | ||
if (!defaultWorkflow) { | ||
throw Error(`Missing default workflow`); | ||
} | ||
return defaultWorkflow; | ||
} | ||
private async prepareContentItemForImportAsync( | ||
@@ -593,3 +628,3 @@ importContentItem: IImportContentItem, | ||
// language variant exists | ||
// check if variant is published or not | ||
// check if variant is published or archived | ||
if (this.isLanguageVariantPublished(languageVariantOfContentItem, workflows)) { | ||
@@ -604,6 +639,30 @@ // create new version | ||
this.processItem(importItems, 'createNewVersion', 'languageVariant', { | ||
title: `${importContentItem.name} (${magenta(importContentItem.language)})`, | ||
title: `${yellow(importContentItem.name)} (${magenta(importContentItem.language)})`, | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
} else if (this.isLanguageVariantArchived(languageVariantOfContentItem, workflows)) { | ||
// change workflow step to draft | ||
if (languageVariantOfContentItem.workflowStep.id) { | ||
const workflow = this.getWorkflowForGivenStepById( | ||
languageVariantOfContentItem.workflowStep.id, | ||
workflows | ||
); | ||
const newWorkflowStep = workflow.steps[0]; | ||
await this.client | ||
.changeWorkflowStepOfLanguageVariant() | ||
.byItemCodename(importContentItem.codename) | ||
.byLanguageCodename(importContentItem.language) | ||
.byWorkflowStepCodename(newWorkflowStep.codename) | ||
.toPromise(); | ||
this.processItem(importItems, 'unArchive', 'languageVariant', { | ||
title: `${yellow(importContentItem.name)} | (${newWorkflowStep.codename}) (${magenta( | ||
importContentItem.language | ||
)})`, | ||
imported: languageVariantOfContentItem, | ||
original: importContentItem | ||
}); | ||
} | ||
} | ||
@@ -634,2 +693,15 @@ } | ||
private isLanguageVariantArchived( | ||
languageVariant: LanguageVariantModels.ContentItemLanguageVariant, | ||
workflows: WorkflowModels.Workflow[] | ||
): boolean { | ||
for (const workflow of workflows) { | ||
if (workflow.archivedStep.id === languageVariant.workflowStep.id) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
private doesWorkflowStepCodenameRepresentPublishedStep( | ||
@@ -636,0 +708,0 @@ workflowStepCodename: string, |
@@ -21,4 +21,8 @@ #!/usr/bin/env node | ||
.describe('p', 'ProjectId') | ||
.alias('k', 'apiKey') | ||
.describe('k', 'Management API Key') | ||
.alias('ak', 'apiKey') | ||
.describe('ak', 'Management API Key') | ||
.alias('sk', 'secureApiKey') | ||
.describe('sk', 'API Key required when Delivery API has secure access enabled') | ||
.alias('pk', 'previewApiKey') | ||
.describe('pk', 'Use if you want to export data using Preview API') | ||
.alias('a', 'action') | ||
@@ -45,7 +49,11 @@ .describe('a', 'Action to perform. One of: "backup" | "restore"') | ||
projectId: config.projectId, | ||
apiKey: config.apiKey, | ||
previewApiKey: config.previewApiKey, | ||
secureApiKey: config.secureApiKey, | ||
baseUrl: config.baseUrl, | ||
exportTypes: config.exportTypes, | ||
exportAssets: config.exportAssets, | ||
fetchAssetDetails: config.fetchAssetDetails, | ||
onProcess: (item) => { | ||
console.log(`Exported ${yellow(item.title)} | ${green(item.data.system.type)}`); | ||
console.log(`Exported ${(item.title)} | ${green(item.data.system.type)}`); | ||
} | ||
@@ -157,2 +165,4 @@ }); | ||
const apiKey: string | undefined = resolvedArgs.apiKey as string | undefined; | ||
const secureApiKey: string | undefined = resolvedArgs.secureApiKey as string | undefined; | ||
const previewApiKey: string | undefined = resolvedArgs.previewApiKey as string | undefined; | ||
const projectId: string | undefined = resolvedArgs.projectId as string | undefined; | ||
@@ -166,2 +176,4 @@ const baseUrl: string | undefined = resolvedArgs.baseUrl as string | undefined; | ||
(resolvedArgs.skipFailedItems as string | undefined)?.toLowerCase() === 'true'.toLowerCase() ?? true; | ||
const fetchAssetDetails: boolean = | ||
(resolvedArgs.fetchAssetDetails as string | undefined)?.toLowerCase() === 'true'.toLowerCase() ?? false; | ||
@@ -187,3 +199,6 @@ const typesMapped: string[] = exportTypes ? exportTypes.split(',').map((m) => m.trim()) : []; | ||
exportAssets: exportAssets, | ||
skipFailedItems: skipFailedItems | ||
skipFailedItems: skipFailedItems, | ||
previewApiKey: previewApiKey, | ||
secureApiKey: secureApiKey, | ||
fetchAssetDetails: fetchAssetDetails | ||
}; | ||
@@ -196,3 +211,3 @@ | ||
const date = new Date(); | ||
return `csvm-backup-${date.getDate()}-${ | ||
return `csv-backup-${date.getDate()}-${ | ||
date.getMonth() + 1 | ||
@@ -199,0 +214,0 @@ }-${date.getFullYear()}-${date.getHours()}-${date.getMinutes()}.zip`; |
{ | ||
"name": "xeno-test", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "This utility enables users to export & import content data from / to Kontent.ai projects", | ||
@@ -8,3 +8,3 @@ "preferGlobal": true, | ||
"bin": { | ||
"csvm": "./dist/cjs/lib/node/cli/app.js" | ||
"kcsvm": "./dist/cjs/lib/node/cli/app.js" | ||
}, | ||
@@ -11,0 +11,0 @@ "repository": { |
@@ -22,3 +22,3 @@ # Kontent.ai CSV Manager | ||
will be used as a filename. The CSV Manager will also set `external_id` of newly uploaded assets to equal their original | ||
id. | ||
id. If you enable `fetchAssetDetails` option the original filename of the asset will be preserved. | ||
@@ -29,3 +29,3 @@ ## Installation | ||
`npm i todo -g` | ||
`npm i xeno-test -g` | ||
@@ -41,2 +41,4 @@ ## Use via CLI | ||
| **action** | Action. Possible values are: `restore` & `backup` **(required)** | | ||
| previewApiKey | When set, Preview API will be used to make data export | | ||
| secureApiKey | When set, Secure API will be used to make data export | | ||
| filename | Name of zip used for export / restoring data. (e.g. 'kontent-backup.zip'). When restoring data you may also use individual `*.csv` file. | | ||
@@ -47,2 +49,3 @@ | baseUrl | Custom base URL for Management API calls. | | ||
| skipFailedItems | Indicates if failed content items & language variants should be skipped if their import fails. Supported are `true` & `false` | | ||
| fetchAssetDetails | Indicates if asset details should be fetched when making data export. If you enable this option, you also must use provide `apiKey` because fetching asset data relies on Management API. Supported are `true` & `false` | | ||
@@ -59,11 +62,11 @@ ### Execution | ||
`csvm --action=backup --projectId=xxx` | ||
`kcsvm --action=backup --projectId=xxx` | ||
To restore data use: | ||
`csvm --action=restore --apiKey=xxx --projectId=xxx --filename=backup.zip|data.csv` | ||
`kcsvm --action=restore --apiKey=xxx --projectId=xxx --filename=backup.zip|data.csv` | ||
To get some help you can use: | ||
`csvm --help` | ||
`kcsvm --help` | ||
@@ -87,3 +90,3 @@ ### Use with config file | ||
`csvm --config=backup-config.json` | ||
`kcsvm --config=backup-config.json` | ||
@@ -182,3 +185,4 @@ ## Use via code | ||
`Asset Id` is used as a filename. However, if you import back to the same project, the asset will not be imported if | ||
it is already there. | ||
it is already there. You may enable `fetchAssetDetails` option to fetch asset details including filenames using the Magement API. If you enable this option you also need | ||
to provide `apiKey` | ||
@@ -185,0 +189,0 @@ ### FAQ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
791899
11807
195