UNPKG

@itxch/contentful-import

Version:

This tool allows you to import JSON dump exported by contentful-export

154 lines (153 loc) 6.89 kB
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 };