strapi-plugin-config-sync/server/config/type.js

274 lines
10 KiB
JavaScript

import isEmpty from 'lodash/isEmpty';
import { logMessage, sanitizeConfig, dynamicSort, noLimit, getCombinedUid, getCombinedUidWhereFilter, getUidParamsFromName } from '../utils';
import { difference, same } from '../utils/getArrayDiff';
import queryFallBack from '../utils/queryFallBack';
const ConfigType = class ConfigType {
constructor({ queryString, configName, uid, jsonFields, relations, components }) {
if (!configName) {
strapi.log.error(logMessage('A config type was registered without a config name.'));
process.exit(0);
}
if (!queryString) {
strapi.log.error(logMessage(`No query string found for the '${configName}' config type.`));
process.exit(0);
}
// uid could be a single key or an array for a combined uid. So the type of uid is either string or string[]
if (typeof uid === "string") {
this.uidKeys = [uid];
} else if (Array.isArray(uid)) {
this.uidKeys = uid.sort();
} else {
strapi.log.error(logMessage(`Wrong uid config for the '${configName}' config type.`));
process.exit(0);
}
this.queryString = queryString;
this.configPrefix = configName;
this.jsonFields = jsonFields || [];
this.relations = relations || [];
this.components = components || null;
}
/**
* Import a single role-permissions config file into the db.
*
* @param {string} configName - The name of the config file.
* @param {string} configContent - The JSON content of the config file.
* @param {boolean} force - Ignore the soft setting.
* @returns {void}
*/
importSingle = async (configName, configContent, force) => {
// Check if the config should be excluded.
const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${this.configPrefix}.${configName}`.startsWith(option)));
if (shouldExclude) return;
const softImport = strapi.config.get('plugin::config-sync.soft');
const queryAPI = strapi.query(this.queryString);
const uidParams = getUidParamsFromName(this.uidKeys, configName);
const combinedUidWhereFilter = getCombinedUidWhereFilter(this.uidKeys, uidParams);
let existingConfig = await queryAPI
.findOne({
where: combinedUidWhereFilter,
populate: this.relations.map(({ relationName }) => relationName),
});
// Config exists in DB but no configfile content --> delete config from DB
if (existingConfig && configContent === null) {
// Don't preform action when soft setting is true.
if (softImport && !force) return false;
// Exit when trying to delete the super-admin role.
if (this.configPrefix === 'admin-role' && configName === 'strapi-super-admin') {
return false;
}
await Promise.all(this.relations.map(async ({ queryString, parentName }) => {
const relations = await noLimit(strapi.query(queryString), {
where: {
[parentName]: existingConfig.id,
},
});
await Promise.all(relations.map(async (relation) => {
await queryFallBack.delete(queryString, { where: {
id: relation.id,
}});
}));
}));
await queryFallBack.delete(this.queryString, { where: {
id: existingConfig.id,
}});
return;
}
// Config does not exist in DB --> create config in DB
if (!existingConfig) {
// Format JSON fields.
const query = { ...configContent };
this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
// Create entity.
this.relations.map(({ relationName }) => delete query[relationName]);
const newEntity = await queryFallBack.create(this.queryString, {
data: query,
});
// Create relation entities.
await Promise.all(this.relations.map(async ({ queryString, relationName, parentName }) => {
await Promise.all(configContent[relationName].map(async (relationEntity) => {
const relationQuery = { ...relationEntity, [parentName]: newEntity };
await queryFallBack.create(queryString, {
data: relationQuery,
});
}));
}));
} else { // Config does exist in DB --> update config in DB
// Don't preform action when soft setting is true.
if (softImport && !force) return false;
// Format JSON fields.
configContent = sanitizeConfig({
config: configContent,
configName,
});
const query = { ...configContent };
this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
// Update entity.
this.relations.map(({ relationName }) => delete query[relationName]);
const entity = await queryFallBack.update(this.queryString, { where: combinedUidWhereFilter, data: query });
// Delete/create relations.
await Promise.all(this.relations.map(async ({ queryString, relationName, parentName, relationSortFields }) => {
const relationQueryApi = strapi.query(queryString);
existingConfig = sanitizeConfig({ config: existingConfig, configName, relation: relationName, relationSortFields });
configContent = sanitizeConfig({ config: configContent, configName, relation: relationName, relationSortFields });
const configToAdd = difference(configContent[relationName], existingConfig[relationName], relationSortFields);
const configToDelete = difference(existingConfig[relationName], configContent[relationName], relationSortFields);
const configToUpdate = same(configContent[relationName], existingConfig[relationName], relationSortFields);
await Promise.all(configToDelete.map(async (config) => {
const whereClause = {};
relationSortFields.map((sortField) => {
whereClause[sortField] = config[sortField];
});
await relationQueryApi.delete({
where: {
...whereClause,
[parentName]: entity.id,
},
});
}));
await Promise.all(configToAdd.map(async (config) => {
await queryFallBack.create(queryString, {
data: { ...config, [parentName]: entity.id },
});
}));
await Promise.all(configToUpdate.map(async (config, index) => {
const whereClause = {};
relationSortFields.map((sortField) => {
whereClause[sortField] = config[sortField];
});
await relationQueryApi.update({
where: {
...whereClause,
[parentName]: entity.id,
},
data: { ...config, [parentName]: entity.id },
});
}));
}));
}
}
/**
* Export a single core-store config to a file.
*
* @param {string} configName - The name of the config file.
* @returns {void}
*/
exportSingle = async (configName) => {
const formattedDiff = await strapi.plugin('config-sync').service('main').getFormattedDiff(this.configPrefix);
// Check if the config should be excluded.
const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
if (shouldExclude) return;
const currentConfig = formattedDiff.databaseConfig[configName];
if (
!currentConfig
&& formattedDiff.fileConfig[configName]
) {
await strapi.plugin('config-sync').service('main').deleteConfigFile(configName);
} else {
const combinedUid = getCombinedUid(this.uidKeys, currentConfig);
await strapi.plugin('config-sync').service('main').writeConfigFile(this.configPrefix, combinedUid, currentConfig);
}
}
/**
* Zip config files
*
* @param {string} configName - The name of the zip archive.
* @returns {void}
*/
zipConfig = async () => {
return strapi.plugin('config-sync').service('main').zipConfigFiles();
}
/**
* Get all role-permissions config from the db.
*
* @returns {object} Object with key value pairs of configs.
*/
getAllFromDatabase = async () => {
const AllConfig = await noLimit(strapi.query(this.queryString), {
populate: this.components,
});
const configs = {};
await Promise.all(Object.entries(AllConfig).map(async ([configName, config]) => {
const combinedUid = getCombinedUid(this.uidKeys, config);
const combinedUidWhereFilter = getCombinedUidWhereFilter(this.uidKeys, config);
if (!combinedUid) {
strapi.log.warn(logMessage(`Missing data for entity with id ${config.id} of type ${this.configPrefix}`));
return;
}
// Check if the config should be excluded.
const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${this.configPrefix}.${combinedUid}`.startsWith(option)));
if (shouldExclude) return;
const formattedConfig = { ...sanitizeConfig({ config, configName }) };
await Promise.all(this.relations.map(async ({ queryString, relationName, relationSortFields, parentName }) => {
const relations = await noLimit(strapi.query(queryString), {
where: { [parentName]: combinedUidWhereFilter },
});
relations.map((relation) => sanitizeConfig({ config: relation, configName: relationName }));
relationSortFields.map((sortField) => {
relations.sort(dynamicSort(sortField));
});
formattedConfig[relationName] = relations;
}));
this.jsonFields.map((field) => formattedConfig[field] = JSON.parse(config[field]));
configs[`${this.configPrefix}.${combinedUid}`] = formattedConfig;
}));
return configs;
}
/**
* Import all core-store config files into the db.
*
* @returns {void}
*/
importAll = async () => {
// The main.importAllConfig service will loop the core-store.importSingle service.
await strapi.plugin('config-sync').service('main').importAllConfig(this.configPrefix);
}
/**
* Export all core-store config to files.
*
* @returns {void}
*/
exportAll = async () => {
// The main.importAllConfig service will loop the core-store.importSingle service.
await strapi.plugin('config-sync').service('main').exportAllConfig(this.configPrefix);
}
};
export default ConfigType;