Merge pull request #15169 from strapi/deits/strategies-cleanup

This commit is contained in:
Ben Irvin 2022-12-16 16:23:37 +01:00 committed by GitHub
commit 0444bb9929
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 41 additions and 76 deletions

View File

@ -299,9 +299,8 @@ describe('Transfer engine', () => {
} as IDestinationProvider; } as IDestinationProvider;
const defaultOptions = { const defaultOptions = {
strategy: 'restore', versionStrategy: 'exact',
versionMatching: 'exact', schemaStrategy: 'exact',
schemasMatching: 'exact',
exclude: [], exclude: [],
} as ITransferEngineOptions; } as ITransferEngineOptions;
@ -416,9 +415,8 @@ describe('Transfer engine', () => {
describe('schema matching', () => { describe('schema matching', () => {
describe('exact', () => { describe('exact', () => {
const engineOptions = { const engineOptions = {
strategy: 'restore', versionStrategy: 'exact',
versionMatching: 'exact', schemaStrategy: 'exact',
schemasMatching: 'exact',
exclude: [], exclude: [],
} as ITransferEngineOptions; } as ITransferEngineOptions;
test('source with source schema missing in destination fails', async () => { test('source with source schema missing in destination fails', async () => {
@ -465,7 +463,7 @@ describe('Transfer engine', () => {
const versionsThatFail = ['foo', 'z1.2.3', '1.2.3z']; const versionsThatFail = ['foo', 'z1.2.3', '1.2.3z'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'exact', versionStrategy: 'exact',
}; };
versionsThatFail.forEach((version) => { versionsThatFail.forEach((version) => {
@ -487,7 +485,7 @@ describe('Transfer engine', () => {
const versionsThatSucceed = ['1.2.3']; const versionsThatSucceed = ['1.2.3'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'exact', versionStrategy: 'exact',
}; };
versionsThatFail.forEach((version) => { versionsThatFail.forEach((version) => {
@ -522,7 +520,7 @@ describe('Transfer engine', () => {
const versionsThatSucceed = ['1.2.3', '1.3.4', '1.4.4-alpha']; const versionsThatSucceed = ['1.2.3', '1.3.4', '1.4.4-alpha'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'major', versionStrategy: 'major',
}; };
await Promise.all( await Promise.all(
@ -561,7 +559,7 @@ describe('Transfer engine', () => {
const versionsThatSucceed = ['1.2.3', '1.2.40', '1.2.4-alpha']; const versionsThatSucceed = ['1.2.3', '1.2.40', '1.2.4-alpha'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'minor', versionStrategy: 'minor',
}; };
await Promise.all( await Promise.all(
@ -600,7 +598,7 @@ describe('Transfer engine', () => {
const versionsThatSucceed = ['1.2.3']; const versionsThatSucceed = ['1.2.3'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'patch', versionStrategy: 'patch',
}; };
await Promise.all( await Promise.all(
@ -638,7 +636,7 @@ describe('Transfer engine', () => {
const versionsThatSucceed = ['1.2.3', '1.3.4', '5.24.44-alpha']; const versionsThatSucceed = ['1.2.3', '1.3.4', '5.24.44-alpha'];
const options: ITransferEngineOptions = { const options: ITransferEngineOptions = {
...defaultOptions, ...defaultOptions,
versionMatching: 'ignore', versionStrategy: 'ignore',
}; };
await Promise.all( await Promise.all(

View File

@ -1,5 +1,4 @@
import { PassThrough, Transform, Readable, Writable } from 'stream'; import { PassThrough, Transform, Readable, Writable } from 'stream';
import * as path from 'path';
import { extname } from 'path'; import { extname } from 'path';
import { isEmpty, uniq } from 'lodash/fp'; import { isEmpty, uniq } from 'lodash/fp';
import { diff as semverDiff } from 'semver'; import { diff as semverDiff } from 'semver';
@ -24,6 +23,9 @@ import type { Diff } from '../utils/json';
import compareSchemas from '../strategies'; import compareSchemas from '../strategies';
import { filter, map } from '../utils/stream'; import { filter, map } from '../utils/stream';
export const DEFAULT_VERSION_STRATEGY = 'ignore';
export const DEFAULT_SCHEMA_STRATEGY = 'strict';
type SchemaMap = Record<string, Schema>; type SchemaMap = Record<string, Schema>;
class TransferEngine< class TransferEngine<
@ -158,7 +160,7 @@ class TransferEngine<
} }
#assertStrapiVersionIntegrity(sourceVersion?: string, destinationVersion?: string) { #assertStrapiVersionIntegrity(sourceVersion?: string, destinationVersion?: string) {
const strategy = this.options.versionMatching; const strategy = this.options.versionStrategy || DEFAULT_VERSION_STRATEGY;
if ( if (
!sourceVersion || !sourceVersion ||
@ -200,7 +202,11 @@ class TransferEngine<
} }
#assertSchemasMatching(sourceSchemas: SchemaMap, destinationSchemas: SchemaMap) { #assertSchemasMatching(sourceSchemas: SchemaMap, destinationSchemas: SchemaMap) {
const strategy = this.options.schemasMatching || 'strict'; const strategy = this.options.schemaStrategy || DEFAULT_SCHEMA_STRATEGY;
if (strategy === 'ignore') {
return;
}
const keys = uniq(Object.keys(sourceSchemas).concat(Object.keys(destinationSchemas))); const keys = uniq(Object.keys(sourceSchemas).concat(Object.keys(destinationSchemas)));
const diffs: { [key: string]: Diff[] } = {}; const diffs: { [key: string]: Diff[] } = {};

View File

@ -6,7 +6,8 @@ import type { IAsset, IDestinationProvider, IMetadata, ProviderType } from '../.
import { restore } from './strategies'; import { restore } from './strategies';
import * as utils from '../../utils'; import * as utils from '../../utils';
export const VALID_STRATEGIES = ['restore', 'merge']; export const VALID_CONFLICT_STRATEGIES = ['restore', 'merge'];
export const DEFAULT_CONFLICT_STRATEGY = 'restore';
interface ILocalStrapiDestinationProviderOptions { interface ILocalStrapiDestinationProviderOptions {
getStrapi(): Strapi.Strapi | Promise<Strapi.Strapi>; getStrapi(): Strapi.Strapi | Promise<Strapi.Strapi>;
@ -40,7 +41,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
} }
#validateOptions() { #validateOptions() {
if (!VALID_STRATEGIES.includes(this.options.strategy)) { if (!VALID_CONFLICT_STRATEGIES.includes(this.options.strategy)) {
throw new Error(`Invalid stategy ${this.options.strategy}`); throw new Error(`Invalid stategy ${this.options.strategy}`);
} }
} }

View File

@ -112,12 +112,12 @@ export interface ITransferEngineOptions {
* "minor" // both the major and minor version should match. 4.3.9 and 4.3.11 will work, while 4.3.9 and 4.4.1 won't * "minor" // both the major and minor version should match. 4.3.9 and 4.3.11 will work, while 4.3.9 and 4.4.1 won't
* "patch" // every part of the version should match. Similar to "exact" but only work on semver. * "patch" // every part of the version should match. Similar to "exact" but only work on semver.
*/ */
versionMatching: 'exact' | 'ignore' | 'major' | 'minor' | 'patch'; versionStrategy: 'exact' | 'ignore' | 'major' | 'minor' | 'patch';
/** /**
* Strategy used to do the schema matching in the integrity checks * Strategy used to do the schema matching in the integrity checks
*/ */
schemasMatching: 'exact' | 'strict'; schemaStrategy: 'exact' | 'strict' | 'ignore';
// List of rules to integrate into the final pipelines // List of rules to integrate into the final pipelines
transforms?: { transforms?: {

View File

@ -14,11 +14,7 @@ const inquirer = require('inquirer');
const program = new Command(); const program = new Command();
const packageJSON = require('../package.json'); const packageJSON = require('../package.json');
const { const { promptEncryptionKey, confirmMessage } = require('../lib/commands/utils/commander');
parseInputList,
promptEncryptionKey,
confirmKeyValue,
} = require('../lib/commands/utils/commander');
const checkCwdIsStrapiApp = (name) => { const checkCwdIsStrapiApp = (name) => {
const logErrorAndExit = () => { const logErrorAndExit = () => {
@ -273,45 +269,15 @@ program
.addOption( .addOption(
new Option('--key <string>', 'Provide encryption key in command instead of using a prompt') new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
) )
.addOption(
new Option(
'--max-size-jsonl <max MB per internal backup file>',
'split internal jsonl files when exceeding max size in MB'
)
.argParser(parseFloat)
.default(256)
)
.addOption(new Option('-f, --file <file>', 'name to use for exported file (without extensions)')) .addOption(new Option('-f, --file <file>', 'name to use for exported file (without extensions)'))
.allowExcessArguments(false) .allowExcessArguments(false)
.hook('preAction', promptEncryptionKey) .hook('preAction', promptEncryptionKey)
// validate inputs
.hook('preAction', (thisCommand) => {
const opts = thisCommand.opts();
if (!opts.maxSizeJsonl) {
console.error('Invalid max-size-jsonl provided. Must be a number value.');
process.exit(1);
}
})
.action(getLocalScript('transfer/export')); .action(getLocalScript('transfer/export'));
// `$ strapi import` // `$ strapi import`
program program
.command('import') .command('import')
.description('Import data from file to Strapi') .description('Import data from file to Strapi')
.addOption(
new Option('--conflictStrategy <conflictStrategy>', 'Which strategy to use for ID conflicts')
.choices(['restore', 'abort', 'keep', 'replace'])
.default('restore')
)
.addOption(
new Option(
'--schemaComparison <schemaComparison>',
'exact requires every field to match, strict requires Strapi version and content type schema fields do not break, subset requires source schema to exist in destination, bypass skips checks',
parseInputList
)
.choices(['exact', 'strict', 'subset', 'bypass'])
.default('exact')
)
.requiredOption( .requiredOption(
'-f, --file <file>', '-f, --file <file>',
'path and filename to the Strapi export file you want to import' 'path and filename to the Strapi export file you want to import'
@ -344,10 +310,8 @@ program
}) })
.hook( .hook(
'preAction', 'preAction',
confirmKeyValue( confirmMessage(
'conflictStrategy', 'The import will delete all data in your database. Are you sure you want to proceed?'
'restore',
"Using strategy 'restore' will delete all data in your database. Are you sure you want to proceed?"
) )
) )
.action(getLocalScript('transfer/import')); .action(getLocalScript('transfer/import'));

View File

@ -22,7 +22,6 @@ const {
* @typedef ImportCommandOptions Options given to the CLI import command * @typedef ImportCommandOptions Options given to the CLI import command
* *
* @property {string} [file] The file path to import * @property {string} [file] The file path to import
* @property {number} [maxSizeJsonl] Maximum size for each .jsonl file
* @property {boolean} [encrypt] Used to encrypt the final archive * @property {boolean} [encrypt] Used to encrypt the final archive
* @property {string} [key] Encryption key, only useful when encryption is enabled * @property {string} [key] Encryption key, only useful when encryption is enabled
* @property {boolean} [compress] Used to compress the final archive * @property {boolean} [compress] Used to compress the final archive
@ -52,8 +51,8 @@ module.exports = async (opts) => {
const destination = createDestinationProvider(opts); const destination = createDestinationProvider(opts);
const engine = createTransferEngine(source, destination, { const engine = createTransferEngine(source, destination, {
strategy: 'restore', // for an export to file, strategy will always be 'restore' versionStrategy: 'ignore', // for an export to file, versionStrategy will always be skipped
versionMatching: 'ignore', // for an export to file, versionMatching will always be skipped schemaStrategy: 'ignore', // for an export to file, schemaStrategy will always be skipped
transforms: { transforms: {
links: [ links: [
{ {

View File

@ -4,6 +4,9 @@ const {
createLocalFileSourceProvider, createLocalFileSourceProvider,
createLocalStrapiDestinationProvider, createLocalStrapiDestinationProvider,
createTransferEngine, createTransferEngine,
DEFAULT_VERSION_STRATEGY,
DEFAULT_SCHEMA_STRATEGY,
DEFAULT_CONFLICT_STRATEGY,
// TODO: we need to solve this issue with typescript modules // TODO: we need to solve this issue with typescript modules
// eslint-disable-next-line import/no-unresolved, node/no-missing-require // eslint-disable-next-line import/no-unresolved, node/no-missing-require
} = require('@strapi/data-transfer'); } = require('@strapi/data-transfer');
@ -42,7 +45,7 @@ module.exports = async (opts) => {
async getStrapi() { async getStrapi() {
return strapiInstance; return strapiInstance;
}, },
strategy: opts.conflictStrategy, strategy: opts.conflictStrategy || DEFAULT_CONFLICT_STRATEGY,
restore: { restore: {
entities: { exclude: DEFAULT_IGNORED_CONTENT_TYPES }, entities: { exclude: DEFAULT_IGNORED_CONTENT_TYPES },
}, },
@ -53,8 +56,8 @@ module.exports = async (opts) => {
* Configure and run the transfer engine * Configure and run the transfer engine
*/ */
const engineOptions = { const engineOptions = {
strategy: opts.conflictStrategy, versionStrategy: opts.versionStrategy || DEFAULT_VERSION_STRATEGY,
versionMatching: opts.schemaComparison, schemaStrategy: opts.schemaStrategy || DEFAULT_SCHEMA_STRATEGY,
exclude: opts.exclude, exclude: opts.exclude,
rules: { rules: {
links: [ links: [

View File

@ -48,25 +48,19 @@ const promptEncryptionKey = async (thisCommand) => {
}; };
/** /**
* hook: confirm that key has a value with a provided message * hook: require a confirmation message to be accepted
*/ */
const confirmKeyValue = (key, value, message) => { const confirmMessage = (message) => {
return async (thisCommand) => { return async () => {
const opts = thisCommand.opts();
if (!opts[key] || opts[key] !== value) {
console.error(`Could not confirm key ${key}, halting operation.`);
process.exit(1);
}
const answers = await inquirer.prompt([ const answers = await inquirer.prompt([
{ {
type: 'confirm', type: 'confirm',
message, message,
name: `confirm_${key}`, name: `confirm`,
default: false, default: false,
}, },
]); ]);
if (!answers[`confirm_${key}`]) { if (!answers.confirm) {
process.exit(0); process.exit(0);
} }
}; };
@ -75,5 +69,5 @@ const confirmKeyValue = (key, value, message) => {
module.exports = { module.exports = {
parseInputList, parseInputList,
promptEncryptionKey, promptEncryptionKey,
confirmKeyValue, confirmMessage,
}; };