sb-mig
Version:
CLI to rule the world. (and handle stuff related to Storyblok CMS)
131 lines (130 loc) • 4.84 kB
JavaScript
import { generateDatestamp } from "../../utils/date-utils.js";
import { createDir, createJsonFile } from "../../utils/files.js";
import Logger from "../../utils/logger.js";
const serializeError = (error) => {
if (!error) {
return null;
}
if (error instanceof Error) {
return {
name: error.name,
message: error.message,
stack: error.stack,
};
}
try {
return JSON.parse(JSON.stringify(error));
}
catch {
return String(error);
}
};
const resolveChangedItemPayload = (item) => item?.story || item;
const resolveWriteResultValue = (result) => {
if (result.status === "fulfilled") {
return (result.value || {
ok: false,
});
}
return {
ok: false,
error: result.reason,
};
};
export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publish, publishLanguages, resolvedPublishLanguages, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }) => {
const timestamp = new Date().toISOString();
const runId = `${itemType}-${timestamp}`;
const baseRecord = {
timestamp,
runId,
itemType,
source: {
migrateFrom,
from,
fromFilePath: fromFilePath || null,
},
target: {
to,
},
writeMode: itemType === "story" && publish ? "publish" : "save",
...(publish
? {
publishLanguages: {
requested: publishLanguages,
resolved: resolvedPublishLanguages,
},
}
: {}),
dryRun: Boolean(dryRun),
migrationConfigs: pipelineResult.stepReports.map((step) => step.migrationConfig),
totalItems: pipelineResult.totalItems,
totalChangedItems: pipelineResult.changedItems.length,
};
const updateRecords = writeResults.map((result, index) => {
const value = resolveWriteResultValue(result);
const changedItem = resolveChangedItemPayload(pipelineResult.changedItems[index]);
const stage = value.stage || "update";
const event = value.publishSkippedReason
? "publish_skipped"
: value.ok
? stage === "publish"
? "publish_success"
: "update_success"
: stage === "publish"
? "publish_failed"
: "update_failed";
return {
...baseRecord,
event,
item: {
index,
id: value.id || changedItem?.id,
name: value.name || changedItem?.name,
slug: value.slug ||
changedItem?.full_slug ||
changedItem?.slug,
spaceId: value.spaceId || to,
},
status: value.status || null,
response: value.response || null,
stage,
sourcePublishState: value.sourcePublishState,
publishSkippedReason: value.publishSkippedReason,
...(value.ok ? {} : { error: serializeError(value.error) }),
};
});
const summaryRecord = {
...baseRecord,
event: "migration_write_summary",
writeSummary: {
total: writeSummary.total,
successful: writeSummary.successful,
failed: writeSummary.failed,
failedItems: writeSummary.failedItems.map((item) => ({
id: item.id,
name: item.name,
slug: item.slug,
spaceId: item.spaceId,
status: item.status,
response: item.response || null,
stage: item.stage,
sourcePublishState: item.sourcePublishState,
publishSkippedReason: item.publishSkippedReason,
})),
},
};
return [...updateRecords, summaryRecord];
};
export const recordsToJsonl = (records) => `${records.map((record) => JSON.stringify(record)).join("\n")}\n`;
export const saveMigrationRunLog = async (args, config) => {
const { sbmigWorkingDirectory } = config;
const { artifactBaseName, useDatestamp, itemType, dryRun } = args;
const timestamp = generateDatestamp(new Date());
const finalFilename = `${dryRun ? "dry-run--" : ""}${artifactBaseName}---${itemType}-migration-run-log${useDatestamp ? `__${timestamp}` : ""}.jsonl`;
const folderPath = `${sbmigWorkingDirectory}/migrations/`;
const fullPath = `${folderPath}${finalFilename}`;
const records = buildMigrationRunLogRecords(args);
await createDir(folderPath);
await createJsonFile(recordsToJsonl(records), fullPath);
Logger.success(`Migration run log written to a file: ${fullPath}`);
};