@itxch/contentful-import
Version:
This tool allows you to import JSON dump exported by contentful-export
154 lines (153 loc) • 6.89 kB
JavaScript
import { find } from "lodash-es/collection.js";
import { get, assign, omitBy, omit } from "lodash-es/object.js";
import { getEntityName } from "contentful-batch-libs/dist/get-entity-name.js";
import { logEmitter } from "contentful-batch-libs/dist/logging.js";
import { ContentfulEntityError } from "../../utils/errors.js";
function createEntities({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
return createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue });
}
function createLocales({ context, entities, destinationEntitiesById, requestQueue }) {
return createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue });
}
async function createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
const pendingCreatedEntities = entities.map((entity) => {
const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
const updateOperation = skipUpdates ? "skip" : "update";
const operation = destinationEntity ? updateOperation : "create";
if (destinationEntity && skipUpdates) {
creationSuccessNotifier(operation, entity.transformed);
return void 0;
}
return requestQueue.add(async () => {
try {
const createdEntity = await (destinationEntity ? updateDestinationWithSourceData(destinationEntity, entity.transformed) : createInDestination(context, entity.transformed));
creationSuccessNotifier(operation, createdEntity);
return createdEntity;
} catch (err) {
return handleCreationErrors(entity, err);
}
});
});
const createdEntities = await Promise.all(pendingCreatedEntities);
return createdEntities.filter((entity) => entity);
}
async function createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue }) {
const createdEntities = [];
for (const entity of entities) {
const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
const operation = destinationEntity ? "update" : "create";
try {
const createdEntity = await requestQueue.add(async () => {
const createdOrUpdatedEntity = await (destinationEntity ? updateDestinationWithSourceData(destinationEntity, entity.transformed) : createInDestination(context, entity.transformed));
return createdOrUpdatedEntity;
});
creationSuccessNotifier(operation, createdEntity);
createdEntities.push(createdEntity);
} catch (err) {
const maybeSubstituteEntity = handleCreationErrors(entity, err);
if (maybeSubstituteEntity) {
createdEntities.push(maybeSubstituteEntity);
}
}
}
return createdEntities;
}
async function createEntries({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
const createdEntries = await Promise.all(entities.map((entry) => {
return createEntry({ entry, target: context.target, skipContentModel: context.skipContentModel, destinationEntitiesById, skipUpdates, requestQueue });
}));
return createdEntries.filter((entry) => entry);
}
async function createEntry({ entry, target, skipContentModel, destinationEntitiesById, skipUpdates, requestQueue }) {
const contentTypeId = entry.original.sys.contentType.sys.id;
const destinationEntry = getDestinationEntityForSourceEntity(
destinationEntitiesById,
entry.transformed
);
const updateOperation = skipUpdates ? "skip" : "update";
const operation = destinationEntry ? updateOperation : "create";
if (destinationEntry && skipUpdates) {
creationSuccessNotifier(operation, entry.transformed);
return entry.transformed;
}
try {
const createdOrUpdatedEntry = await requestQueue.add(() => {
return destinationEntry ? updateDestinationWithSourceData(destinationEntry, entry.transformed) : createEntryInDestination(target, contentTypeId, entry.transformed);
});
creationSuccessNotifier(operation, createdOrUpdatedEntry);
return createdOrUpdatedEntry;
} catch (err) {
if (err instanceof Error) {
if (skipContentModel && err.name === "UnknownField") {
const errors = get(JSON.parse(err.message), "details.errors");
entry.transformed.fields = cleanupUnknownFields(entry.transformed.fields, errors);
return createEntry({ entry, target, skipContentModel, destinationEntitiesById, skipUpdates, requestQueue });
}
}
if (err instanceof ContentfulEntityError) {
err.entity = entry;
}
logEmitter.emit("error", err);
return null;
}
}
function updateDestinationWithSourceData(destinationEntity, sourceEntity) {
const plainData = getPlainData(sourceEntity);
assign(destinationEntity, plainData);
return destinationEntity.update();
}
function createInDestination(context, sourceEntity) {
const { type, target } = context;
if (type === "Tag") {
return createTagInDestination(context, sourceEntity);
}
const id = get(sourceEntity, "sys.id");
const plainData = getPlainData(sourceEntity);
return id ? target[`create${type}WithId`](id, plainData) : target[`create${type}`](plainData);
}
function createEntryInDestination(space, contentTypeId, sourceEntity) {
const id = sourceEntity.sys.id;
const plainData = getPlainData(sourceEntity);
return id ? space.createEntryWithId(contentTypeId, id, plainData) : space.createEntry(contentTypeId, plainData);
}
function createTagInDestination(context, sourceEntity) {
const id = sourceEntity.sys.id;
const visibility = sourceEntity.sys.visibility || "private";
const name = sourceEntity.name;
return context.target.createTag(id, name, visibility);
}
function handleCreationErrors(entity, err) {
if (get(err, "error.sys.id") === "ValidationFailed") {
const errors = get(err, "error.details.errors");
if (errors && errors.length > 0 && errors[0].name === "taken") {
return entity;
}
}
err.entity = entity.original;
logEmitter.emit("error", err);
return null;
}
function cleanupUnknownFields(fields, errors) {
return omitBy(fields, (field, fieldId) => {
return find(errors, (error) => {
const [, errorFieldId] = error.path;
return error.name === "unknown" && errorFieldId === fieldId;
});
});
}
function getDestinationEntityForSourceEntity(destinationEntitiesById, sourceEntity) {
return destinationEntitiesById.get(get(sourceEntity, "sys.id")) || null;
}
function creationSuccessNotifier(method, createdEntity) {
logEmitter.emit("info", `${method.toUpperCase()} ${createdEntity.sys.type} ${getEntityName(createdEntity)}`);
return createdEntity;
}
function getPlainData(entity) {
const data = entity.toPlainObject ? entity.toPlainObject() : entity;
return omit(data, "sys");
}
export {
createEntities,
createEntries,
createLocales
};