mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Refactored ensure settings module into a class with DI
refs https://linear.app/tryghost/issue/CORE-35/refactor-route-and-redirect-settings - Ensure settings had only one method but would benefit from class+DI pattern before extracting it into an outside module. - The logic is now also less coupled with "routes" and single source/destination paths. It's all configureable instead and might be reused if similar pattern is needed for example with redirect settings defaults.
This commit is contained in:
parent
fd20f90cca
commit
d4cd1bb865
5 changed files with 156 additions and 111 deletions
|
@ -0,0 +1,62 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const Promise = require('bluebird');
|
||||||
|
const path = require('path');
|
||||||
|
const debug = require('@tryghost/debug')('frontend:services:settings:ensure-settings');
|
||||||
|
const tpl = require('@tryghost/tpl');
|
||||||
|
const errors = require('@tryghost/errors');
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
ensureSettings: 'Error trying to access settings files in {path}.'
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultSettingsManager {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {String} options.type - name of the setting file
|
||||||
|
* @param {String} options.extension - settings file extension
|
||||||
|
* @param {String} options.destinationFolderPath - path to store the default setting config
|
||||||
|
* @param {String} options.sourceFolderPath - path where the default config can be seeded from
|
||||||
|
*/
|
||||||
|
constructor({type, extension, destinationFolderPath, sourceFolderPath}) {
|
||||||
|
this.type = type;
|
||||||
|
this.extension = extension;
|
||||||
|
this.destinationFolderPath = destinationFolderPath;
|
||||||
|
this.sourceFolderPath = sourceFolderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Makes sure the destination folder either contains a file or copies over a default file.
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
async ensureSettingsFileExists() {
|
||||||
|
const fileName = this.type + this.extension;
|
||||||
|
const defaultFileName = `default-${fileName}`;
|
||||||
|
|
||||||
|
const destinationFilePath = path.join(this.destinationFolderPath, fileName);
|
||||||
|
const defaultFilePath = path.join(this.sourceFolderPath, defaultFileName);
|
||||||
|
|
||||||
|
return Promise.resolve(fs.readFile(destinationFilePath, 'utf8'))
|
||||||
|
.catch({code: 'ENOENT'}, () => {
|
||||||
|
// CASE: file doesn't exist, copy it from our defaults
|
||||||
|
return fs.copy(
|
||||||
|
defaultFilePath,
|
||||||
|
destinationFilePath
|
||||||
|
).then(() => {
|
||||||
|
debug(`'${defaultFileName}' copied to ${this.destinationFolderPath}.`);
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
// CASE: we might have a permission error, as we can't access the directory
|
||||||
|
throw new errors.GhostError({
|
||||||
|
message: tpl(messages.ensureSettings, {
|
||||||
|
path: this.destinationFolderPath
|
||||||
|
}),
|
||||||
|
err: error,
|
||||||
|
context: error.path
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DefaultSettingsManager;
|
|
@ -1,43 +0,0 @@
|
||||||
const fs = require('fs-extra');
|
|
||||||
const Promise = require('bluebird');
|
|
||||||
const path = require('path');
|
|
||||||
const debug = require('@tryghost/debug')('frontend:services:settings:ensure-settings');
|
|
||||||
const tpl = require('@tryghost/tpl');
|
|
||||||
const errors = require('@tryghost/errors');
|
|
||||||
const config = require('../../../shared/config');
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
ensureSettings: 'Error trying to access settings files in {path}.'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sure file is in the `/content/settings` directory. If not, copy the default over.
|
|
||||||
* @param {String} fileName - name of the setting file
|
|
||||||
* @returns {Promise<any>}
|
|
||||||
*/
|
|
||||||
module.exports = function ensureSettingsFile(fileName) {
|
|
||||||
const contentPath = config.getContentPath('settings');
|
|
||||||
const defaultSettingsPath = config.get('paths').defaultSettings;
|
|
||||||
|
|
||||||
const defaultFileName = `default-${fileName}`;
|
|
||||||
const filePath = path.join(contentPath, fileName);
|
|
||||||
|
|
||||||
return Promise.resolve(fs.readFile(filePath, 'utf8'))
|
|
||||||
.catch({code: 'ENOENT'}, () => {
|
|
||||||
const defaultFilePath = path.join(defaultSettingsPath, defaultFileName);
|
|
||||||
// CASE: file doesn't exist, copy it from our defaults
|
|
||||||
return fs.copy(
|
|
||||||
defaultFilePath,
|
|
||||||
filePath
|
|
||||||
).then(() => {
|
|
||||||
debug(`'${defaultFileName}' copied to ${contentPath}.`);
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
|
||||||
// CASE: we might have a permission error, as we can't access the directory
|
|
||||||
throw new errors.GhostError({
|
|
||||||
message: tpl(messages.ensureSettings, {path: contentPath}),
|
|
||||||
err: error,
|
|
||||||
context: error.path
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,12 +1,18 @@
|
||||||
const routeSettings = require('./route-settings');
|
const routeSettings = require('./route-settings');
|
||||||
const SettingsLoader = require('./loader');
|
const SettingsLoader = require('./loader');
|
||||||
const ensureSettingsFile = require('./ensure-settings');
|
const config = require('../../../shared/config');
|
||||||
|
const DefaultSettingsManager = require('./default-settings-manager');
|
||||||
|
|
||||||
|
const defaultSettingsManager = new DefaultSettingsManager({
|
||||||
|
type: 'routes',
|
||||||
|
extension: '.yaml',
|
||||||
|
destinationFolderPath: config.getContentPath('settings'),
|
||||||
|
sourceFolderPath: config.get('paths').defaultSettings
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: async () => {
|
init: async () => {
|
||||||
// Make sure that supported settings files are available
|
return await defaultSettingsManager.ensureSettingsFileExists();
|
||||||
// inside of the `content/setting` directory
|
|
||||||
return ensureSettingsFile('routes.yaml');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
loadRouteSettingsSync: SettingsLoader.loadSettingsSync,
|
loadRouteSettingsSync: SettingsLoader.loadSettingsSync,
|
||||||
|
|
84
test/unit/services/settings/default-settings-manager.test.js
Normal file
84
test/unit/services/settings/default-settings-manager.test.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
const sinon = require('sinon');
|
||||||
|
const should = require('should');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const DefaultSettingsManager = require('../../../../core/server/services/route-settings/default-settings-manager');
|
||||||
|
|
||||||
|
describe('UNIT > Settings Service DefaultSettingsManager:', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(fs, 'readFile');
|
||||||
|
sinon.stub(fs, 'copy');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Ensure settings files', function () {
|
||||||
|
it('returns yaml file from settings folder if it exists', async function () {
|
||||||
|
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('content');
|
||||||
|
|
||||||
|
const defaultSettingsManager = new DefaultSettingsManager({
|
||||||
|
type: 'routes',
|
||||||
|
extension: '.yaml',
|
||||||
|
destinationFolderPath: path.join(__dirname, '../../../utils/fixtures/settings/'),
|
||||||
|
sourceFolderPath: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
await defaultSettingsManager.ensureSettingsFileExists();
|
||||||
|
|
||||||
|
// Assert did not attempt to copy the default config
|
||||||
|
fs.copy.called.should.be.false();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copies default settings file if no file found', async function () {
|
||||||
|
const destinationFolderPath = path.join(__dirname, '../../../utils/fixtures/settings/');
|
||||||
|
const sourceFolderPath = path.join(__dirname, '../../../../core/server/services/route-settings/');
|
||||||
|
|
||||||
|
const defaultSettingsManager = new DefaultSettingsManager({
|
||||||
|
type: 'routes',
|
||||||
|
extension: '.yaml',
|
||||||
|
destinationFolderPath: destinationFolderPath,
|
||||||
|
sourceFolderPath: sourceFolderPath
|
||||||
|
});
|
||||||
|
|
||||||
|
const fsError = new Error('not found');
|
||||||
|
fsError.code = 'ENOENT';
|
||||||
|
|
||||||
|
const settingsDestinationPath = path.join(destinationFolderPath, 'routes.yaml');
|
||||||
|
fs.readFile.withArgs(settingsDestinationPath, 'utf8').rejects(fsError);
|
||||||
|
fs.copy.withArgs(path.join(sourceFolderPath, 'default-routes.yaml'), settingsDestinationPath).resolves();
|
||||||
|
|
||||||
|
await defaultSettingsManager.ensureSettingsFileExists();
|
||||||
|
|
||||||
|
// Assert attempt to copy the default config
|
||||||
|
fs.copy.calledOnce.should.be.true();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects, if error is not a not found error', async function () {
|
||||||
|
const destinationFolderPath = path.join(__dirname, '../../../utils/fixtures/settings/');
|
||||||
|
|
||||||
|
const defaultSettingsManager = new DefaultSettingsManager({
|
||||||
|
type: 'routes',
|
||||||
|
extension: '.yaml',
|
||||||
|
destinationFolderPath: destinationFolderPath,
|
||||||
|
sourceFolderPath: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const fsError = new Error('no permission');
|
||||||
|
fsError.code = 'EPERM';
|
||||||
|
|
||||||
|
fs.readFile.withArgs(path.join(destinationFolderPath, 'routes.yaml'), 'utf8').rejects(fsError);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await defaultSettingsManager.ensureSettingsFileExists('routes.yaml');
|
||||||
|
throw new Error('Expected test to fail');
|
||||||
|
} catch (error) {
|
||||||
|
should.exist(error);
|
||||||
|
error.message.should.be.eql(`Error trying to access settings files in ${destinationFolderPath}.`);
|
||||||
|
fs.readFile.calledOnce.should.be.true();
|
||||||
|
fs.copy.called.should.be.false();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,64 +0,0 @@
|
||||||
const sinon = require('sinon');
|
|
||||||
const should = require('should');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
|
||||||
const configUtils = require('../../../utils/configUtils');
|
|
||||||
const ensureSettings = require('../../../../core/server/services/route-settings/ensure-settings');
|
|
||||||
|
|
||||||
describe('UNIT > Settings Service ensure settings:', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../utils/fixtures/'));
|
|
||||||
sinon.stub(fs, 'readFile');
|
|
||||||
sinon.stub(fs, 'copy');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
sinon.restore();
|
|
||||||
configUtils.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Ensure settings files', function () {
|
|
||||||
it('returns yaml file from settings folder if it exists', function () {
|
|
||||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/goodroutes.yaml'), 'utf8').resolves('content');
|
|
||||||
|
|
||||||
return ensureSettings('goodroutes.yaml').then(() => {
|
|
||||||
fs.readFile.callCount.should.be.eql(1);
|
|
||||||
fs.copy.called.should.be.false();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('copies default settings file if no file found', function () {
|
|
||||||
const expectedDefaultSettingsPath = path.join(__dirname, '../../../../core/server/services/route-settings/default-routes.yaml');
|
|
||||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml');
|
|
||||||
const fsError = new Error('not found');
|
|
||||||
fsError.code = 'ENOENT';
|
|
||||||
|
|
||||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').rejects(fsError);
|
|
||||||
fs.copy.withArgs(expectedDefaultSettingsPath, expectedContentPath).resolves();
|
|
||||||
|
|
||||||
return ensureSettings('routes.yaml').then(() => {
|
|
||||||
fs.readFile.calledOnce.should.be.true();
|
|
||||||
fs.copy.calledOnce.should.be.true();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rejects, if error is not a not found error', function () {
|
|
||||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/');
|
|
||||||
const fsError = new Error('no permission');
|
|
||||||
fsError.code = 'EPERM';
|
|
||||||
|
|
||||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').rejects(fsError);
|
|
||||||
|
|
||||||
return ensureSettings('routes.yaml')
|
|
||||||
.then(() => {
|
|
||||||
throw new Error('Expected test to fail');
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
should.exist(error);
|
|
||||||
error.message.should.be.eql(`Error trying to access settings files in ${expectedContentPath}.`);
|
|
||||||
fs.readFile.calledOnce.should.be.true();
|
|
||||||
fs.copy.called.should.be.false();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Add table
Reference in a new issue