mirror of
https://github.com/strapi/strapi.git
synced 2025-12-29 16:16:20 +00:00
add confirmation to transfer and import
This commit is contained in:
parent
4b37d806ca
commit
58b8c18870
@ -413,6 +413,7 @@ class TransferEngine<
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: make these messages friendlier, especially for internal strapi features like review workflows
|
||||
if (!isEmpty(diffs)) {
|
||||
const formattedDiffs = Object.entries(diffs)
|
||||
.map(([uid, ctDiffs]) => {
|
||||
@ -424,11 +425,11 @@ class TransferEngine<
|
||||
const path = diff.path.join('.');
|
||||
|
||||
if (diff.kind === 'added') {
|
||||
return `${path} exists in destination schema but not in source schema`;
|
||||
return `${path} exists in destination schema but not in source schema and the data will not be transfered.`;
|
||||
}
|
||||
|
||||
if (diff.kind === 'deleted') {
|
||||
return `${path} exists in source schema but not in destination schema`;
|
||||
return `${path} exists in source schema but not in destination schema and the data will not be transfered.`;
|
||||
}
|
||||
|
||||
if (diff.kind === 'modified') {
|
||||
@ -630,25 +631,26 @@ class TransferEngine<
|
||||
#schemaDiffs: Record<string, Diff[]> = {};
|
||||
|
||||
async integrityCheck() {
|
||||
const sourceMetadata = await this.sourceProvider.getMetadata();
|
||||
const destinationMetadata = await this.destinationProvider.getMetadata();
|
||||
|
||||
if (sourceMetadata && destinationMetadata) {
|
||||
this.#assertStrapiVersionIntegrity(
|
||||
sourceMetadata?.strapi?.version,
|
||||
destinationMetadata?.strapi?.version
|
||||
);
|
||||
}
|
||||
|
||||
const sourceSchemas = (await this.sourceProvider.getSchemas?.()) as SchemaMap;
|
||||
const destinationSchemas = (await this.destinationProvider.getSchemas?.()) as SchemaMap;
|
||||
|
||||
try {
|
||||
const sourceMetadata = await this.sourceProvider.getMetadata();
|
||||
const destinationMetadata = await this.destinationProvider.getMetadata();
|
||||
|
||||
if (sourceMetadata && destinationMetadata) {
|
||||
this.#assertStrapiVersionIntegrity(
|
||||
sourceMetadata?.strapi?.version,
|
||||
destinationMetadata?.strapi?.version
|
||||
);
|
||||
}
|
||||
|
||||
const sourceSchemas = (await this.sourceProvider.getSchemas?.()) as SchemaMap;
|
||||
const destinationSchemas = (await this.destinationProvider.getSchemas?.()) as SchemaMap;
|
||||
|
||||
if (sourceSchemas && destinationSchemas) {
|
||||
this.#assertSchemasMatching(sourceSchemas, destinationSchemas);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof TransferEngineValidationError) {
|
||||
// if this is a schema matching error
|
||||
if (error instanceof TransferEngineValidationError && error.details?.details?.diffs) {
|
||||
this.#schemaDiffs = error.details?.details?.diffs as Record<string, Diff[]>;
|
||||
|
||||
const context = {
|
||||
@ -670,6 +672,7 @@ class TransferEngine<
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ const {
|
||||
} = require('@strapi/data-transfer');
|
||||
|
||||
const { isObject } = require('lodash/fp');
|
||||
const inquirer = require('inquirer');
|
||||
|
||||
const {
|
||||
buildTransferTable,
|
||||
@ -24,6 +23,7 @@ const {
|
||||
getTransferTelemetryPayload,
|
||||
} = require('../../utils/data-transfer');
|
||||
const { exitWith } = require('../../utils/helpers');
|
||||
const { confirmMessage } = require('../../utils/commander');
|
||||
|
||||
/**
|
||||
* @typedef {import('@strapi/data-transfer/src/file/providers').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
|
||||
@ -113,18 +113,16 @@ module.exports = async (opts) => {
|
||||
const { updateLoader } = loadersFactory();
|
||||
|
||||
engine.onSchemaDiff(async (context, next) => {
|
||||
// TODO: work with "force"
|
||||
// TODO: yes/no prompt should look like commander prompt
|
||||
const answers = await inquirer.prompt([
|
||||
const confirmed = await confirmMessage(
|
||||
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
||||
{
|
||||
type: 'confirm',
|
||||
message: 'Are you sure you want to continue?',
|
||||
name: 'confirmSchemaDiff',
|
||||
},
|
||||
]);
|
||||
if (answers.confirmSchemaDiff) {
|
||||
// diffs have been resolved by user
|
||||
force: opts.force,
|
||||
}
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
context.diffs = [];
|
||||
return next(context);
|
||||
}
|
||||
|
||||
return next(context);
|
||||
|
||||
@ -9,7 +9,7 @@ const {
|
||||
throttleOption,
|
||||
validateExcludeOnly,
|
||||
} = require('../../utils/data-transfer');
|
||||
const { confirmMessage, forceOption } = require('../../utils/commander');
|
||||
const { getCommanderConfirmMessage, forceOption } = require('../../utils/commander');
|
||||
const { getLocalScript, exitWith } = require('../../utils/helpers');
|
||||
|
||||
/**
|
||||
@ -88,7 +88,7 @@ module.exports = ({ command }) => {
|
||||
})
|
||||
.hook(
|
||||
'preAction',
|
||||
confirmMessage(
|
||||
getCommanderConfirmMessage(
|
||||
'The import will delete all assets and data in your database. Are you sure you want to proceed?',
|
||||
{ failMessage: 'Import process aborted' }
|
||||
)
|
||||
|
||||
@ -24,6 +24,7 @@ const {
|
||||
getTransferTelemetryPayload,
|
||||
} = require('../../utils/data-transfer');
|
||||
const { exitWith } = require('../../utils/helpers');
|
||||
const { confirmMessage } = require('../../utils/commander');
|
||||
|
||||
/**
|
||||
* @typedef TransferCommandOptions Options given to the CLI transfer command
|
||||
@ -146,6 +147,22 @@ module.exports = async (opts) => {
|
||||
|
||||
const { updateLoader } = loadersFactory();
|
||||
|
||||
engine.onSchemaDiff(async (context, next) => {
|
||||
const confirmed = await confirmMessage(
|
||||
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
||||
{
|
||||
force: opts.force,
|
||||
}
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
context.diffs = [];
|
||||
return next(context);
|
||||
}
|
||||
|
||||
return next(context);
|
||||
});
|
||||
|
||||
progress.on(`stage::start`, ({ stage, data }) => {
|
||||
updateLoader(stage, data).start();
|
||||
});
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const inquirer = require('inquirer');
|
||||
const { Option } = require('commander');
|
||||
const { confirmMessage, forceOption, parseURL } = require('../../utils/commander');
|
||||
const { getCommanderConfirmMessage, forceOption, parseURL } = require('../../utils/commander');
|
||||
const {
|
||||
getLocalScript,
|
||||
exitWith,
|
||||
@ -76,7 +76,7 @@ module.exports = ({ command }) => {
|
||||
thisCommand.opts().fromToken = answers.fromToken;
|
||||
}
|
||||
|
||||
await confirmMessage(
|
||||
await getCommanderConfirmMessage(
|
||||
'The transfer will delete all the local Strapi assets and its database. Are you sure you want to proceed?',
|
||||
{ failMessage: 'Transfer process aborted' }
|
||||
)(thisCommand);
|
||||
@ -104,7 +104,7 @@ module.exports = ({ command }) => {
|
||||
thisCommand.opts().toToken = answers.toToken;
|
||||
}
|
||||
|
||||
await confirmMessage(
|
||||
await getCommanderConfirmMessage(
|
||||
'The transfer will delete all the remote Strapi assets and its database. Are you sure you want to proceed?',
|
||||
{ failMessage: 'Transfer process aborted' }
|
||||
)(thisCommand);
|
||||
|
||||
@ -111,30 +111,35 @@ const promptEncryptionKey = async (thisCommand) => {
|
||||
* @param {object} options Additional options
|
||||
* @param {string|undefined} options.failMessage The message to display when prompt is not confirmed
|
||||
*/
|
||||
const confirmMessage = (message, { failMessage } = {}) => {
|
||||
const getCommanderConfirmMessage = (message, { failMessage } = {}) => {
|
||||
return async (command) => {
|
||||
// if we have a force option, assume yes
|
||||
const opts = command.opts();
|
||||
if (opts?.force === true) {
|
||||
// attempt to mimic the inquirer prompt exactly
|
||||
console.log(`${green('?')} ${bold(message)} ${cyan('Yes')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
message,
|
||||
name: `confirm`,
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
if (!answers.confirm) {
|
||||
const confirmed = await confirmMessage(message, { force: command.opts().force });
|
||||
if (!confirmed) {
|
||||
exitWith(1, failMessage);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const confirmMessage = async (message, { force } = {}) => {
|
||||
// if we have a force option, respond yes
|
||||
if (force === true) {
|
||||
// attempt to mimic the inquirer prompt exactly
|
||||
console.log(`${green('?')} ${bold(message)} ${cyan('Yes')}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
message,
|
||||
name: `confirm`,
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
return answers.confirm;
|
||||
};
|
||||
|
||||
const forceOption = new Option(
|
||||
'--force',
|
||||
`Automatically answer "yes" to all prompts, including potentially destructive requests, and run non-interactively.`
|
||||
@ -146,6 +151,7 @@ module.exports = {
|
||||
parseURL,
|
||||
parseInteger,
|
||||
promptEncryptionKey,
|
||||
getCommanderConfirmMessage,
|
||||
confirmMessage,
|
||||
forceOption,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user