Skip to content

Commit

Permalink
improvement: ensure custom storage adapter has required functions (#7234
Browse files Browse the repository at this point in the history
)

refs #2852

- improvement: ensure custom storage adapter has required functions
- serve, save and exists are from now on required functions for a custom storage adapter
- add delete as required storage function
  • Loading branch information
kirrg001 authored and ErisDS committed Aug 22, 2016
1 parent 41ae8c0 commit 6a1c105
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 12 deletions.
4 changes: 4 additions & 0 deletions core/server/storage/base.js
Expand Up @@ -2,6 +2,10 @@ var moment = require('moment'),
path = require('path');

function StorageBase() {
Object.defineProperty(this, 'requiredFns', {
value: ['exists', 'save', 'serve', 'delete'],
writable: false
});
}

StorageBase.prototype.getTargetDir = function (baseDir) {
Expand Down
16 changes: 14 additions & 2 deletions core/server/storage/index.js
@@ -1,5 +1,7 @@
var errors = require('../errors'),
config = require('../config'),
Base = require('./base'),
_ = require('lodash'),
storage = {};

/**
Expand Down Expand Up @@ -33,10 +35,20 @@ function getStorage(type) {
}
}

// TODO: determine if storage has all the necessary methods.
// Instantiate and cache the storage module instance.
storage[storageChoice] = new storage[storageChoice](storageConfig);

if (!(storage[storageChoice] instanceof Base)) {
throw new errors.IncorrectUsage('Your storage adapter does not inherit from the Storage Base.');
}

if (!storage[storageChoice].requiredFns) {
throw new errors.IncorrectUsage('Your storage adapter does not provide the minimum required functions.');
}

if (_.xor(storage[storageChoice].requiredFns, Object.keys(_.pick(Object.getPrototypeOf(storage[storageChoice]), storage[storageChoice].requiredFns))).length) {
throw new errors.IncorrectUsage('Your storage adapter does not provide the minimum required functions.');
}

return storage[storageChoice];
}

Expand Down
9 changes: 7 additions & 2 deletions core/server/storage/local-file-store.js
Expand Up @@ -9,11 +9,12 @@ var serveStatic = require('express').static,
errors = require('../errors'),
config = require('../config'),
utils = require('../utils'),
baseStore = require('./base');
BaseStore = require('./base');

function LocalFileStore() {
BaseStore.call(this);
}
util.inherits(LocalFileStore, baseStore);
util.inherits(LocalFileStore, BaseStore);

// ### Save
// Saves the image to storage (the file system)
Expand Down Expand Up @@ -56,4 +57,8 @@ LocalFileStore.prototype.serve = function () {
return serveStatic(config.paths.imagesPath, {maxAge: utils.ONE_YEAR_MS, fallthrough: false});
};

LocalFileStore.prototype.delete = function () {
return Promise.reject('not implemented');
};

module.exports = LocalFileStore;
91 changes: 85 additions & 6 deletions core/test/unit/storage/index_spec.js
Expand Up @@ -9,6 +9,23 @@ var fs = require('fs-extra'),
should.equal(true, true);

describe('storage: index_spec', function () {
var scope = {adapter: null};

before(function () {
if (!fs.existsSync(configUtils.config.paths.storagePath.custom)) {
fs.mkdirSync(configUtils.config.paths.storagePath.custom);
}
});

afterEach(function () {
if (scope.adapter) {
fs.unlinkSync(scope.adapter);
scope.adapter = null;
}

configUtils.restore();
});

describe('default ghost storage config', function () {
it('load without a type', function () {
var chosenStorage = storage.getStorage();
Expand All @@ -31,6 +48,8 @@ describe('storage: index_spec', function () {

describe('custom ghost storage config', function () {
it('images storage adapter is custom, themes is default', function () {
scope.adapter = configUtils.config.paths.storagePath.custom + 'custom-adapter.js';

configUtils.set({
storage: {
active: {
Expand All @@ -46,21 +65,81 @@ describe('storage: index_spec', function () {
'util.inherits(AnotherAdapter, StorageBase);' +
'AnotherAdapter.prototype.exists = function (){};' +
'AnotherAdapter.prototype.save = function (){};' +
'AnotherAdapter.prototype.serve = function (){};' +
'AnotherAdapter.prototype.delete = function (){};' +
'module.exports = AnotherAdapter', chosenStorage;

if (!fs.existsSync(configUtils.config.paths.storagePath.custom)) {
fs.mkdirSync(configUtils.config.paths.storagePath.custom);
}

fs.writeFileSync(configUtils.config.paths.storagePath.custom + 'custom-adapter.js', jsFile);
fs.writeFileSync(scope.adapter, jsFile);

chosenStorage = storage.getStorage('themes');
(chosenStorage instanceof localFileStorage).should.eql(true);

chosenStorage = storage.getStorage('images');
(chosenStorage instanceof localFileStorage).should.eql(false);
});
});

describe('adapter validation', function () {
it('create good adapter', function () {
scope.adapter = configUtils.config.paths.storagePath.custom + 'another-storage.js';

configUtils.set({
storage: {
active: 'another-storage'
},
paths: {
storage: __dirname + '/another-storage.js'
}
});

var jsFile = '' +
'var util = require(\'util\');' +
'var StorageBase = require(__dirname + \'/../../core/server/storage/base\');' +
'var AnotherAdapter = function (){ StorageBase.call(this); };' +
'util.inherits(AnotherAdapter, StorageBase);' +
'AnotherAdapter.prototype.exists = function (){};' +
'AnotherAdapter.prototype.save = function (){};' +
'AnotherAdapter.prototype.serve = function (){};' +
'AnotherAdapter.prototype.delete = function (){};' +
'module.exports = AnotherAdapter', adapter;

fs.writeFileSync(scope.adapter, jsFile);

adapter = storage.getStorage();
should.exist(adapter);
(adapter instanceof localFileStorage).should.eql(false);
});

it('create bad adapter: exists fn is missing', function () {
scope.adapter = configUtils.config.paths.storagePath.custom + 'broken-storage.js';

fs.unlinkSync(configUtils.config.paths.storagePath.custom + 'custom-adapter.js');
configUtils.set({
storage: {
active: 'broken-storage'
},
paths: {
storage: __dirname + '/broken-storage.js'
}
});

var jsFile = '' +
'var util = require(\'util\');' +
'var StorageBase = require(__dirname + \'/../../core/server/storage/base\');' +
'var AnotherAdapter = function (){ StorageBase.call(this); };' +
'util.inherits(AnotherAdapter, StorageBase);' +
'AnotherAdapter.prototype.save = function (){};' +
'AnotherAdapter.prototype.serve = function (){};' +
'AnotherAdapter.prototype.delete = function (){};' +
'module.exports = AnotherAdapter';

fs.writeFileSync(scope.adapter, jsFile);

try {
storage.getStorage();
} catch (err) {
should.exist(err);
(err instanceof errors.IncorrectUsage).should.eql(true);
}
});
});
});
Expand Up @@ -4,10 +4,10 @@ var fs = require('fs-extra'),
should = require('should'),
sinon = require('sinon'),
_ = require('lodash'),
LocalFileStore = require('../../server/storage/local-file-store'),
LocalFileStore = require('../../../server/storage/local-file-store'),
localFileStore,

configUtils = require('../utils/configUtils');
configUtils = require('../../utils/configUtils');

// To stop jshint complaining
should.equal(true, true);
Expand Down

0 comments on commit 6a1c105

Please sign in to comment.