UNPKG

contentful-migration

Version:
262 lines 11.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runMigration = exports.createMakeRequest = void 0; const path = __importStar(require("path")); const chalk_1 = __importDefault(require("chalk")); const inquirer_1 = __importDefault(require("inquirer")); const listr2_1 = require("listr2"); const contentful_client_1 = require("./lib/contentful-client"); const migration_parser_1 = __importDefault(require("../lib/migration-parser")); const render_migration_1 = require("./lib/render-migration"); const steps_errors_1 = __importDefault(require("./lib/steps-errors")); const write_errors_to_log_1 = __importDefault(require("./lib/write-errors-to-log")); const config_1 = require("./lib/config"); const trim_1 = __importDefault(require("lodash/trim")); const errors_1 = require("../lib/errors"); const p_throttle_1 = __importDefault(require("p-throttle")); const { version } = require('../../package.json'); class ManyError extends Error { constructor(message, errors) { super(message); this.errors = errors; } } class BatchError extends Error { constructor(message, batch, errors) { super(message); this.batch = batch; this.errors = errors; } } function hasManyErrors(error) { return error instanceof ManyError || error instanceof BatchError; } const makeTerminatingFunction = ({ shouldThrow }) => (error) => { if (shouldThrow) { throw error; } else { process.exit(1); } }; const createMakeRequest = (client, { spaceId, environmentId, limit = 10, host = 'api.contentful.com' }) => { const throttle = (0, p_throttle_1.default)({ limit, interval: 1000, strict: false }); const makeBaseUrl = (url) => { const parts = [ `https://${host}`, 'spaces', spaceId, 'environments', environmentId, (0, trim_1.default)(url, '/') ]; return parts.filter((x) => x !== '').join('/'); }; return function makeRequest(requestConfig) { const { url } = requestConfig, config = __rest(requestConfig, ["url"]); const fullUrl = makeBaseUrl(url); return throttle(() => client.raw.http(fullUrl, config))(); }; }; exports.createMakeRequest = createMakeRequest; const getMigrationFunctionFromFile = (filePath, terminate) => { try { return require(filePath); } catch (e) { const message = (0, chalk_1.default) `{red.bold The ${filePath} script could not be parsed, as it seems to contain syntax errors.}\n`; console.error(message); console.error(e); terminate(new Error(message)); } }; const createRun = ({ shouldThrow }) => async function run(argv) { const terminate = makeTerminatingFunction({ shouldThrow }); const migrationFunction = argv.migrationFunction || getMigrationFunctionFromFile(argv.filePath, terminate); const application = argv.managementApplication || `contentful.migration-cli/${version}`; const feature = argv.managementFeature || `migration-library`; const clientConfig = Object.assign({ application, feature }, (0, config_1.getConfig)(argv)); // allow users to override the default host via the contentful-cli argv.host && Object.assign(clientConfig, { host: argv.host }); const client = (0, contentful_client_1.createManagementClient)(clientConfig); const makeRequest = (0, exports.createMakeRequest)(client, { spaceId: clientConfig.spaceId, environmentId: clientConfig.environmentId, limit: argv.requestLimit, host: clientConfig.host }); const migrationParser = (0, migration_parser_1.default)(makeRequest, clientConfig); let parseResult; try { parseResult = await migrationParser(migrationFunction); } catch (e) { if (e instanceof errors_1.SpaceAccessError) { const message = [ (0, chalk_1.default) `{red.bold ${e.message}}\n`, (0, chalk_1.default) `🚨 {bold.red Migration unsuccessful}` ].join('\n'); console.error(message); terminate(new Error(message)); } console.error(e); terminate(e); } if (parseResult.hasStepsValidationErrors()) { (0, steps_errors_1.default)(parseResult.stepsValidationErrors); terminate(new ManyError('Step Validation Errors', parseResult.stepsValidationErrors)); } if (parseResult.hasPayloadValidationErrors()) { (0, steps_errors_1.default)(parseResult.payloadValidationErrors); terminate(new ManyError('Payload Validation Errors', parseResult.payloadValidationErrors)); } const migrationName = argv.migrationFunction ? argv.migrationFunction.name : path.basename(argv.filePath, '.js'); const errorsFile = path.join(process.cwd(), `errors-${migrationName}-${Date.now()}.log`); const batches = parseResult.batches; if (parseResult.hasValidationErrors()) { (0, render_migration_1.renderValidationErrors)(batches, argv.environmentId); terminate(new ManyError('Validation Errors', parseResult.getValidationErrors())); } if (parseResult.hasRuntimeErrors()) { (0, render_migration_1.renderRuntimeErrors)(batches, errorsFile); await (0, write_errors_to_log_1.default)(parseResult.getRuntimeErrors(), errorsFile); terminate(new ManyError('Runtime Errors', parseResult.getRuntimeErrors())); } await (0, render_migration_1.renderPlan)(batches, argv.environmentId, argv.quiet); const serverErrorsWritten = []; const tasks = batches.map((batch) => { return { title: batch.intent.toPlanMessage().heading, task: () => new listr2_1.Listr([ { title: 'Making requests', task: async (_ctx, task) => { // TODO: We wanted to make this an async interator // So we should not inspect the length but have a property for that const numRequests = batch.requests.length; const requestErrors = []; let requestsDone = 0; for (const request of batch.requests) { requestsDone += 1; task.title = `Making requests (${requestsDone}/${numRequests})`; task.output = `${request.method} ${request.url} at V${request.headers['X-Contentful-Version']}`; await makeRequest(request).catch((error) => { serverErrorsWritten.push((0, write_errors_to_log_1.default)(error, errorsFile)); let errorMessage; if (error instanceof TypeError) { errorMessage = { message: 'Value does not match the expected type', details: { message: error.message.toString() } }; } else { const parsed = JSON.parse(error.message); errorMessage = { status: parsed.statusText, message: parsed.message, details: parsed.details, url: parsed.request.url }; } requestErrors.push(new Error(JSON.stringify(errorMessage))); }); } // Finish batch and only then throw all errors in there if (requestErrors.length) { throw new BatchError(`Batch failed`, batch, requestErrors); } } } ]) }; }); const confirm = async function (options) { if (options.skipConfirmation) { return { applyMigration: true }; } return inquirer_1.default.prompt([ { type: 'confirm', message: 'Do you want to apply the migration', name: 'applyMigration' } ]); }; const answers = await confirm({ skipConfirmation: argv.yes }); if (answers.applyMigration) { try { const successfulMigration = await new listr2_1.Listr(tasks).run(); console.log((0, chalk_1.default) `🎉 {bold.green Migration successful}`); return successfulMigration; } catch (err) { console.error((0, chalk_1.default) `🚨 {bold.red Migration unsuccessful}`); if (err instanceof Error) { console.error((0, chalk_1.default) `{red ${err.message}}\n`); if (hasManyErrors(err) && Array.isArray(err.errors)) { err.errors.forEach((err) => console.error((0, chalk_1.default) `{red ${err}}\n\n`)); } } else { console.error((0, chalk_1.default) `{red ${err}}\n`); } await Promise.all(serverErrorsWritten); console.error(`Please check the errors log for more details: ${errorsFile}`); terminate(err); } } else { console.warn((0, chalk_1.default) `⚠️ {bold.yellow Migration aborted}`); } }; exports.runMigration = createRun({ shouldThrow: true }); exports.default = createRun({ shouldThrow: false }); //# sourceMappingURL=cli.js.map