@itxch/contentful-import
Version:
This tool allows you to import JSON dump exported by contentful-export
315 lines (314 loc) • 11.2 kB
JavaScript
import Listr from "listr";
import verboseRenderer from "listr-verbose-renderer";
import { logEmitter } from "contentful-batch-libs/dist/logging.js";
import { wrapTask } from "contentful-batch-libs/dist/listr.js";
import { getAssetStreamForURL, processAssets } from "./assets.js";
import { createLocales, createEntities, createEntries } from "./creation.js";
import { publishEntities as publishEntities$1, archiveEntities as archiveEntities$1 } from "./publishing.js";
import { ContentfulEntityError } from "../../utils/errors.js";
const DEFAULT_CONTENT_STRUCTURE = {
entries: [],
assets: [],
contentTypes: [],
tags: [],
locales: [],
webhooks: [],
editorInterfaces: []
};
function pushToSpace({
sourceData,
destinationData = {},
client,
spaceId,
environmentId,
contentModelOnly,
skipContentModel,
skipContentUpdates,
skipLocales,
skipContentPublishing,
timeout,
retryLimit,
listrOptions,
uploadAssets,
skipAssetUpdates,
assetsDirectory,
requestQueue
}) {
sourceData = {
...DEFAULT_CONTENT_STRUCTURE,
...sourceData
};
destinationData = {
...DEFAULT_CONTENT_STRUCTURE,
...destinationData
};
listrOptions = listrOptions || {
renderer: verboseRenderer
};
const destinationDataById = {};
for (const [entityType, entities] of Object.entries(destinationData)) {
const entitiesById = /* @__PURE__ */ new Map();
for (const entity of entities) {
entitiesById.set(entity.sys.id, entity);
}
destinationDataById[entityType] = entitiesById;
}
return new Listr([
{
title: "Connecting to space",
task: wrapTask(async (ctx) => {
const space = await client.getSpace(spaceId);
const environment = await space.getEnvironment(environmentId);
ctx.space = space;
ctx.environment = environment;
})
},
{
title: "Importing Locales",
task: wrapTask(async (ctx) => {
if (!destinationDataById.locales) {
return;
}
const locales = await createLocales({
context: { target: ctx.environment, type: "Locale" },
entities: sourceData.locales,
destinationEntitiesById: destinationDataById.locales,
requestQueue
});
ctx.data.locales = locales;
}),
skip: () => skipContentModel || skipLocales
},
{
title: "Importing Content Types",
task: wrapTask(async (ctx) => {
if (!destinationDataById.contentTypes) {
return;
}
const contentTypes = await createEntities({
context: { target: ctx.environment, type: "ContentType" },
entities: sourceData.contentTypes,
destinationEntitiesById: destinationDataById.contentTypes,
skipUpdates: false,
requestQueue
});
ctx.data.contentTypes = contentTypes;
}),
skip: () => skipContentModel
},
{
title: "Publishing Content Types",
task: wrapTask(async (ctx) => {
const publishedContentTypes = await publishEntities({
entities: ctx.data.contentTypes,
sourceEntities: sourceData.contentTypes,
requestQueue
});
ctx.data.contentTypes = publishedContentTypes;
}),
skip: () => skipContentModel
},
{
title: "Importing Tags",
task: wrapTask(async (ctx) => {
if (sourceData.tags && destinationDataById.tags) {
const tags = await createEntities({
context: { target: ctx.environment, type: "Tag" },
entities: sourceData.tags,
destinationEntitiesById: destinationDataById.tags,
skipUpdates: false,
requestQueue
});
ctx.data.tags = tags;
}
}),
// we remove `tags` from destination data if an error was thrown trying to access them
// this means the user doesn't have access to this feature, skip importing tags
skip: () => !destinationDataById.tags
},
{
title: "Importing Editor Interfaces",
task: wrapTask(async (ctx) => {
const allEditorInterfacesBeingFetched = ctx.data.contentTypes.map(async (contentType) => {
if (!sourceData.editorInterfaces) {
return;
}
const editorInterface = sourceData.editorInterfaces.find((editorInterface2) => {
return editorInterface2.sys.contentType.sys.id === contentType.sys.id;
});
if (!editorInterface) {
return;
}
try {
const ctEditorInterface = await requestQueue.add(() => ctx.environment.getEditorInterfaceForContentType(contentType.sys.id));
logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
ctEditorInterface.controls = editorInterface.controls;
ctEditorInterface.groupControls = editorInterface.groupControls;
ctEditorInterface.editorLayout = editorInterface.editorLayout;
ctEditorInterface.sidebar = editorInterface.sidebar;
ctEditorInterface.editors = editorInterface.editors;
const updatedEditorInterface = await requestQueue.add(() => ctEditorInterface.update());
return updatedEditorInterface;
} catch (err) {
if (err instanceof ContentfulEntityError) {
err.entity = editorInterface;
}
throw err;
}
});
const allEditorInterfaces = await Promise.all(allEditorInterfacesBeingFetched);
const editorInterfaces = allEditorInterfaces.filter((editorInterface) => editorInterface);
ctx.data.editorInterfaces = editorInterfaces;
}),
skip: (ctx) => skipContentModel || ctx.data.contentTypes.length === 0
},
{
title: "Uploading Assets",
task: wrapTask(async (ctx) => {
const allPendingUploads = [];
for (const asset of sourceData.assets) {
for (const file of Object.values(asset.transformed.fields.file)) {
allPendingUploads.push(requestQueue.add(async () => {
try {
logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
const upload = await ctx.environment.createUpload({
fileName: asset.transformed.sys.id,
file: assetStream
});
delete file.upload;
file.uploadFrom = {
sys: {
type: "Link",
linkType: "Upload",
id: upload.sys.id
}
};
return upload;
} catch (err) {
logEmitter.emit("error", err);
}
}));
}
}
const uploads = await Promise.all(allPendingUploads);
ctx.data.uploadedAssetFiles = uploads;
}),
skip: () => !uploadAssets || !sourceData.assets.length
},
{
title: "Importing Assets",
task: wrapTask(async (ctx) => {
if (!destinationDataById.assets) {
return;
}
const assetsToProcess = await createEntities({
context: { target: ctx.environment, type: "Asset" },
entities: sourceData.assets,
destinationEntitiesById: destinationDataById.assets,
skipUpdates: skipAssetUpdates,
requestQueue
});
const processedAssets = await processAssets({
assets: assetsToProcess,
timeout,
retryLimit,
requestQueue
});
ctx.data.assets = processedAssets;
}),
skip: () => contentModelOnly
},
{
title: "Publishing Assets",
task: wrapTask(async (ctx) => {
const publishedAssets = await publishEntities({
entities: ctx.data.assets,
sourceEntities: sourceData.assets,
requestQueue
});
ctx.data.publishedAssets = publishedAssets;
}),
skip: () => contentModelOnly || skipContentPublishing
},
{
title: "Archiving Assets",
task: wrapTask(async (ctx) => {
const archivedAssets = await archiveEntities({
entities: ctx.data.assets,
sourceEntities: sourceData.assets,
requestQueue
});
ctx.data.archivedAssets = archivedAssets;
}),
skip: () => contentModelOnly || skipContentPublishing
},
{
title: "Importing Content Entries",
task: wrapTask(async (ctx) => {
const entries = await createEntries({
context: { target: ctx.environment, skipContentModel },
entities: sourceData.entries,
destinationEntitiesById: destinationDataById.entries,
skipUpdates: skipContentUpdates,
requestQueue
});
ctx.data.entries = entries;
}),
skip: () => contentModelOnly
},
{
title: "Publishing Content Entries",
task: wrapTask(async (ctx) => {
const publishedEntries = await publishEntities({
entities: ctx.data.entries,
sourceEntities: sourceData.entries,
requestQueue
});
ctx.data.publishedEntries = publishedEntries;
}),
skip: () => contentModelOnly || skipContentPublishing
},
{
title: "Archiving Entries",
task: wrapTask(async (ctx) => {
const archivedEntries = await archiveEntities({
entities: ctx.data.entries,
sourceEntities: sourceData.entries,
requestQueue
});
ctx.data.archivedEntries = archivedEntries;
}),
skip: () => contentModelOnly || skipContentPublishing
},
{
title: "Creating Web Hooks",
task: wrapTask(async (ctx) => {
if (!sourceData.webhooks || !destinationDataById.webhooks) {
return;
}
const webhooks = await createEntities({
context: { target: ctx.space, type: "Webhook" },
entities: sourceData.webhooks,
destinationEntitiesById: destinationDataById.webhooks,
requestQueue
});
ctx.data.webhooks = webhooks;
}),
skip: () => contentModelOnly || environmentId !== "master" && "Webhooks can only be imported in master environment"
}
], listrOptions);
}
function archiveEntities({ entities, sourceEntities, requestQueue }) {
const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
return archiveEntities$1({ entities: entitiesToArchive, requestQueue });
}
function publishEntities({ entities, sourceEntities, requestQueue }) {
const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
const entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
return publishEntities$1({ entities: entitiesToPublish, requestQueue });
}
export {
pushToSpace as default
};