commit
e1a9e016e9
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const cli = require('../server/cli');
|
||||
|
||||
cli();
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
require('../server/cli')(process.argv);
|
12
package.json
12
package.json
|
@ -10,9 +10,8 @@
|
|||
"required": false,
|
||||
"kind": "plugin"
|
||||
},
|
||||
"main": "server/cli.js",
|
||||
"bin": {
|
||||
"config-sync": "./bin/config-sync.js"
|
||||
"config-sync": "./bin/config-sync"
|
||||
},
|
||||
"scripts": {
|
||||
"eslint": "eslint --max-warnings=0 './**/*.{js,jsx}'",
|
||||
|
@ -48,10 +47,11 @@
|
|||
],
|
||||
"devDependencies": {
|
||||
"@fortawesome/react-fontawesome": "^0.1.16",
|
||||
"@strapi/design-system": "0.0.1-alpha.51",
|
||||
"@strapi/helper-plugin": "4.0.0-beta.6",
|
||||
"@strapi/icons": "0.0.1-alpha.51",
|
||||
"@strapi/utils": "4.0.0-beta.6",
|
||||
"@strapi/design-system": "0.0.1-alpha.64",
|
||||
"@strapi/helper-plugin": "4.0.0-beta.12",
|
||||
"@strapi/icons": "0.0.1-alpha.64",
|
||||
"@strapi/strapi": "^4.0.0-beta.12",
|
||||
"@strapi/utils": "4.0.0-beta.12",
|
||||
"babel-eslint": "9.0.0",
|
||||
"codecov": "^3.8.3",
|
||||
"eslint": "^5.16.0",
|
||||
|
|
188
server/cli.js
188
server/cli.js
|
@ -4,14 +4,17 @@ const { Command } = require('commander');
|
|||
const Table = require('cli-table');
|
||||
const chalk = require('chalk');
|
||||
const inquirer = require('inquirer');
|
||||
const { isEmpty } = require('lodash');
|
||||
const strapi = require('@strapi/strapi');
|
||||
|
||||
const packageJSON = require('../package.json');
|
||||
|
||||
const program = new Command();
|
||||
|
||||
const initTable = () => {
|
||||
const initTable = (head) => {
|
||||
return new Table({
|
||||
head: [chalk.green('Name'), chalk.green('State')],
|
||||
head: [chalk.green('Name'), chalk.green(head || 'State')],
|
||||
colWidths: [50, 15],
|
||||
chars: { top: '═',
|
||||
'top-mid': '╤',
|
||||
'top-left': '╔',
|
||||
|
@ -31,6 +34,93 @@ const initTable = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const getConfigState = (diff, configName, syncType) => {
|
||||
if (
|
||||
diff.fileConfig[configName]
|
||||
&& diff.databaseConfig[configName]
|
||||
) {
|
||||
return chalk.yellow(syncType ? 'Update' : 'Different');
|
||||
} else if (
|
||||
diff.fileConfig[configName]
|
||||
&& !diff.databaseConfig[configName]
|
||||
) {
|
||||
if (syncType === 'import') {
|
||||
return chalk.green('Create');
|
||||
} else if (syncType === 'export') {
|
||||
return chalk.red('Delete');
|
||||
} else {
|
||||
return chalk.red('Only in sync dir');
|
||||
}
|
||||
} else if (
|
||||
!diff.fileConfig[configName]
|
||||
&& diff.databaseConfig[configName]
|
||||
) {
|
||||
if (syncType === 'import') {
|
||||
return chalk.red('Delete');
|
||||
} else if (syncType === 'export') {
|
||||
return chalk.green('Create');
|
||||
} else {
|
||||
return chalk.green('Only in DB');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAction = async (type, skipConfirm) => {
|
||||
const app = await strapi().load();
|
||||
const diff = await app.plugin('config-sync').service('main').getFormattedDiff();
|
||||
|
||||
// No changes.
|
||||
if (isEmpty(diff.diff)) {
|
||||
console.log(`${chalk.cyan('[notice]')} There are no changes to ${type}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Init table.
|
||||
const table = initTable('Action');
|
||||
|
||||
// Add diff to table.
|
||||
Object.keys(diff.diff).map((configName) => {
|
||||
table.push([configName, getConfigState(diff, configName, type)]);
|
||||
});
|
||||
|
||||
// Print table.
|
||||
console.log(table.toString(), '\n');
|
||||
|
||||
// Prompt to confirm.
|
||||
let answer = {};
|
||||
if (!skipConfirm) {
|
||||
answer = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Are you sure you want to ${type} the config changes?`,
|
||||
}]);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Preform the action.
|
||||
if (skipConfirm || answer.confirm) {
|
||||
if (type === 'import') {
|
||||
const onSuccess = (name) => console.log(`${chalk.green('[success]')} Imported ${name}`);
|
||||
|
||||
try {
|
||||
await app.plugin('config-sync').service('main').importAllConfig(null, onSuccess);
|
||||
} catch (e) {
|
||||
console.log(`${chalk.red('[error]')} Something went wrong during the import. ${e}`);
|
||||
}
|
||||
}
|
||||
if (type === 'export') {
|
||||
try {
|
||||
await app.plugin('config-sync').service('main').exportAllConfig();
|
||||
console.log(`${chalk.green('[success]')} Exported config`);
|
||||
} catch (e) {
|
||||
console.log(`${chalk.red('[error]')} Something went wrong during the export. ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
// Initial program setup
|
||||
program.storeOptionsAsProperties(false).allowUnknownOption(true);
|
||||
|
||||
|
@ -50,88 +140,44 @@ program
|
|||
// `$ config-sync import`
|
||||
program
|
||||
.command('import')
|
||||
.option('-t, --type <type>', 'The type of config')
|
||||
// .option('-t, --type <type>', 'The type of config') // TODO: partial import
|
||||
.option('-y', 'Skip the confirm prompt')
|
||||
.description('Import the config')
|
||||
.action(async (args) => {
|
||||
console.log('import', args);
|
||||
|
||||
// Init table.
|
||||
const table = initTable();
|
||||
|
||||
// Fill table.
|
||||
table.push(
|
||||
['admin-role.author', chalk.yellow('different')],
|
||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
||||
);
|
||||
|
||||
// Print table.
|
||||
console.log(table.toString());
|
||||
|
||||
// Prompt to confirm.
|
||||
const answer = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Are you sure you want to import the config changes?',
|
||||
}]);
|
||||
|
||||
// Make the import.
|
||||
if (answer.confirm) {
|
||||
console.log(chalk.magenta('IMPORT!!'));
|
||||
}
|
||||
.action(async ({ y, type }) => {
|
||||
return handleAction('import', y, type);
|
||||
});
|
||||
|
||||
// `$ config-sync export`
|
||||
program
|
||||
.command('export')
|
||||
.option('-t, --type <type>', 'The type of config')
|
||||
// .option('-t, --type <type>', 'The type of config') // TODO: partial export
|
||||
.option('-y', 'Skip the confirm prompt')
|
||||
.description('Export the config')
|
||||
.action(async (args) => {
|
||||
console.log('export', args);
|
||||
|
||||
// Init table.
|
||||
const table = initTable();
|
||||
|
||||
// Fill table.
|
||||
table.push(
|
||||
['admin-role.author', chalk.yellow('different')],
|
||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
||||
);
|
||||
|
||||
// Print table.
|
||||
console.log(table.toString());
|
||||
|
||||
// Prompt to confirm.
|
||||
const answer = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Are you sure you want to export the config changes?',
|
||||
}]);
|
||||
|
||||
// Make the export.
|
||||
if (answer.confirm) {
|
||||
console.log(chalk.magenta('EXPORT!!'));
|
||||
}
|
||||
.action(async ({ y, type }) => {
|
||||
return handleAction('export', y, type);
|
||||
});
|
||||
|
||||
// `$ config-sync diff`
|
||||
program
|
||||
.command('diff')
|
||||
.option('-t, --type <type>', 'The type of config')
|
||||
.description('The config diff')
|
||||
.action(async (args) => {
|
||||
console.log('diff', args);
|
||||
.command('diff')
|
||||
// .option('-t, --type <type>', 'The type of config') // TODO: partial diff
|
||||
.description('The config diff')
|
||||
.action(async ({ type }) => {
|
||||
const app = await strapi().load();
|
||||
const diff = await app.plugin('config-sync').service('main').getFormattedDiff();
|
||||
|
||||
// Init table.
|
||||
const table = initTable();
|
||||
// Init table.
|
||||
const table = initTable();
|
||||
|
||||
// Fill table.
|
||||
table.push(
|
||||
['admin-role.author', chalk.yellow('different')],
|
||||
['core-store.plugin_i18n_default_locale', chalk.green('Only in DB')],
|
||||
);
|
||||
// Add diff to table.
|
||||
Object.keys(diff.diff).map((configName) => {
|
||||
table.push([configName, getConfigState(diff, configName)]);
|
||||
});
|
||||
|
||||
// Print table.
|
||||
console.log(table.toString());
|
||||
});
|
||||
// Print table.
|
||||
console.log(table.toString());
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
program.parseAsync(process.argv);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const difference = require('../utils/getObjectDiff');
|
||||
|
||||
/**
|
||||
* Main controllers for config import/export.
|
||||
|
@ -64,24 +63,6 @@ module.exports = {
|
|||
return;
|
||||
}
|
||||
|
||||
const formattedDiff = {
|
||||
fileConfig: {},
|
||||
databaseConfig: {},
|
||||
diff: {},
|
||||
};
|
||||
|
||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
|
||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
|
||||
|
||||
const diff = difference(databaseConfig, fileConfig);
|
||||
|
||||
formattedDiff.diff = diff;
|
||||
|
||||
Object.keys(diff).map((changedConfigName) => {
|
||||
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
||||
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
||||
});
|
||||
|
||||
return formattedDiff;
|
||||
return strapi.plugin('config-sync').service('main').getFormattedDiff();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -144,9 +144,10 @@ module.exports = () => ({
|
|||
* Import all config files into the db.
|
||||
*
|
||||
* @param {string} configType - Type of config to impor. Leave empty to import all config.
|
||||
* @param {object} onSuccess - Success callback to run on each single successfull import.
|
||||
* @returns {void}
|
||||
*/
|
||||
importAllConfig: async (configType = null) => {
|
||||
importAllConfig: async (configType = null, onSuccess) => {
|
||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
|
||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
|
||||
|
||||
|
@ -160,7 +161,14 @@ module.exports = () => ({
|
|||
return;
|
||||
}
|
||||
|
||||
await strapi.plugin('config-sync').service('main').importSingleConfig(type, name);
|
||||
try {
|
||||
await strapi.plugin('config-sync').service('main').importSingleConfig(type, name);
|
||||
if (onSuccess) {
|
||||
onSuccess(`${type}.${name}`);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -207,4 +215,33 @@ module.exports = () => ({
|
|||
exportSingleConfig: async (configType, configName) => {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the formatted diff.
|
||||
*
|
||||
* @param {string} configType - Type of config to get the diff of. Leave empty to get the diff of all config.
|
||||
*
|
||||
* @returns {object} - the formatted diff.
|
||||
*/
|
||||
getFormattedDiff: async (configType = null) => {
|
||||
const formattedDiff = {
|
||||
fileConfig: {},
|
||||
databaseConfig: {},
|
||||
diff: {},
|
||||
};
|
||||
|
||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles(configType);
|
||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase(configType);
|
||||
|
||||
const diff = difference(databaseConfig, fileConfig);
|
||||
|
||||
formattedDiff.diff = diff;
|
||||
|
||||
Object.keys(diff).map((changedConfigName) => {
|
||||
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
||||
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
||||
});
|
||||
|
||||
return formattedDiff;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
const { logMessage, sanitizeConfig, dynamicSort } = require('../utils');
|
||||
const difference = require('../utils/getObjectDiff');
|
||||
const arrayDifference = require('../utils/getArrayDiff');
|
||||
|
||||
const difference = require('../utils/getArrayDiff');
|
||||
|
||||
const ConfigType = class ConfigType {
|
||||
constructor(queryString, configPrefix, uid, jsonFields, relations) {
|
||||
|
@ -21,24 +19,9 @@ const ConfigType = class ConfigType {
|
|||
* @returns {void}
|
||||
*/
|
||||
exportAll = async () => {
|
||||
const formattedDiff = {
|
||||
fileConfig: {},
|
||||
databaseConfig: {},
|
||||
diff: {},
|
||||
};
|
||||
const formattedDiff = await strapi.plugin('config-sync').service('main').getFormattedDiff(this.configPrefix);
|
||||
|
||||
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles(this.configPrefix);
|
||||
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase(this.configPrefix);
|
||||
const diff = difference(databaseConfig, fileConfig);
|
||||
|
||||
formattedDiff.diff = diff;
|
||||
|
||||
Object.keys(diff).map((changedConfigName) => {
|
||||
formattedDiff.fileConfig[changedConfigName] = fileConfig[changedConfigName];
|
||||
formattedDiff.databaseConfig[changedConfigName] = databaseConfig[changedConfigName];
|
||||
});
|
||||
|
||||
await Promise.all(Object.entries(diff).map(async ([configName, config]) => {
|
||||
await Promise.all(Object.entries(formattedDiff.diff).map(async ([configName, config]) => {
|
||||
// Check if the config should be excluded.
|
||||
const shouldExclude = strapi.config.get('plugin.config-sync.exclude').includes(`${configName}`);
|
||||
if (shouldExclude) return;
|
||||
|
@ -132,29 +115,29 @@ const ConfigType = class ConfigType {
|
|||
const entity = await queryAPI.update({ where: { [this.uid]: configName }, data: query });
|
||||
|
||||
// Delete/create relations.
|
||||
this.relations.map(async ({ queryString, relationName, parentName, relationSortField }) => {
|
||||
await Promise.all(this.relations.map(async ({ queryString, relationName, parentName, relationSortField }) => {
|
||||
const relationQueryApi = strapi.query(queryString);
|
||||
existingConfig = sanitizeConfig(existingConfig, relationName, relationSortField);
|
||||
configContent = sanitizeConfig(configContent, relationName, relationSortField);
|
||||
|
||||
const configToAdd = arrayDifference(configContent[relationName], existingConfig[relationName], relationSortField);
|
||||
const configToDelete = arrayDifference(existingConfig[relationName], configContent[relationName], relationSortField);
|
||||
const configToAdd = difference(configContent[relationName], existingConfig[relationName], relationSortField);
|
||||
const configToDelete = difference(existingConfig[relationName], configContent[relationName], relationSortField);
|
||||
|
||||
configToDelete.map(async (config) => {
|
||||
await Promise.all(configToDelete.map(async (config) => {
|
||||
await relationQueryApi.delete({
|
||||
where: {
|
||||
[relationSortField]: config[relationSortField],
|
||||
[parentName]: entity.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
configToAdd.map(async (config) => {
|
||||
await Promise.all(configToAdd.map(async (config) => {
|
||||
await relationQueryApi.create({
|
||||
data: { ...config, [parentName]: entity.id },
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue