Splits Adapter loading from AdaptableController

- Adds dynamic prototype conformance check upon setting adapter
- Throws when adapter is undefined, invalid in controller
This commit is contained in:
Florent Vilmart
2016-02-21 23:47:07 -05:00
parent 33fa5a7b2a
commit 23e55e941e
10 changed files with 210 additions and 116 deletions

View File

@@ -8,8 +8,6 @@ based on the parameters passed
*/
const DefaultAdapters = {};
export class AdaptableController {
/**
* Check whether the api call has master key or not.
@@ -22,51 +20,49 @@ export class AdaptableController {
* - object: a plain javascript object (options.constructor === Object), if options.adapter is set, we'll try to load it with the same mechanics.
* - function: we'll create a new instance from that function, and pass the options object
*/
constructor(options) {
let adapter;
// We have options and options have adapter key
if (options) {
// Pass an adapter as a module name, a function or an instance
if (typeof options == "string" || typeof options == "function" || options.constructor != Object) {
adapter = options;
}
if (options.adapter) {
adapter = options.adapter;
}
}
if (!adapter) {
adapter = this.defaultAdapter();
}
// This is a string, require the module
if (typeof adapter === "string") {
adapter = require(adapter);
// If it's define as a module, get the default
if (adapter.default) {
adapter = adapter.default;
}
}
// From there it's either a function or an object
// if it's an function, instanciate and pass the options
if (typeof adapter === "function") {
var Adapter = adapter;
adapter = new Adapter(options);
}
constructor(adapter, options) {
this.setAdapter(adapter, options);
}
setAdapter(adapter, options) {
this.validateAdapter(adapter);
this.adapter = adapter;
this.options = options;
}
defaultAdapter() {
return DefaultAdapters[this.constructor.name];
expectedAdapterType() {
throw new Error("Subclasses should implement expectedAdapterType()");
}
// Sets the default adapter for that Class
static setDefaultAdapter(defaultAdapter) {
DefaultAdapters[this.name] = defaultAdapter;
validateAdapter(adapter) {
if (!adapter) {
throw new Error(this.constructor.name+" requires an adapter");
}
let Type = this.expectedAdapterType();
// Allow skipping for testing
if (!Type) {
return;
}
// Makes sure the prototype matches
let mismatches = Object.getOwnPropertyNames(Type.prototype).reduce( (obj, key) => {
const adapterType = typeof adapter[key];
const expectedType = typeof Type.prototype[key];
if (adapterType !== expectedType) {
obj[key] = {
expected: expectedType,
actual: adapterType
}
}
return obj;
}, {});
if (Object.keys(mismatches).length > 0) {
console.error(adapter, mismatches);
throw new Error("Adapter prototype don't match expected prototype");
}
}
}

View File

@@ -2,6 +2,7 @@
import { Parse } from 'parse/node';
import { randomHexString } from '../cryptoUtils';
import AdaptableController from './AdaptableController';
import { FilesAdapter } from '../Adapters/Files/FilesAdapter';
export class FilesController extends AdaptableController {
@@ -29,7 +30,7 @@ export class FilesController extends AdaptableController {
* with the current mount point and app id.
* Object may be a single object or list of REST-format objects.
*/
expandFilesInObject(config, object) {
expandFilesInObject(config, object) {
if (object instanceof Array) {
object.map((obj) => this.expandFilesInObject(config, obj));
return;
@@ -52,6 +53,10 @@ export class FilesController extends AdaptableController {
}
}
}
expectedAdapterType() {
return FilesAdapter;
}
}
export default FilesController;

View File

@@ -1,6 +1,7 @@
import { Parse } from 'parse/node';
import PromiseRouter from '../PromiseRouter';
import AdaptableController from './AdaptableController';
import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter';
const Promise = Parse.Promise;
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
@@ -70,6 +71,10 @@ export class LoggerController extends AdaptableController {
});
return promise;
}
expectedAdapterType() {
return LoggerAdapter;
}
}
export default LoggerController;

View File

@@ -2,6 +2,7 @@ import { Parse } from 'parse/node';
import PromiseRouter from '../PromiseRouter';
import rest from '../rest';
import AdaptableController from './AdaptableController';
import { PushAdapter } from '../Adapters/Push/PushAdapter';
export class PushController extends AdaptableController {
@@ -25,7 +26,7 @@ export class PushController extends AdaptableController {
deviceType + ' is not supported push type.');
}
}
};
}
/**
* Check whether the api call has master key or not.
@@ -53,7 +54,8 @@ export class PushController extends AdaptableController {
rest.find(config, auth, '_Installation', where).then(function(response) {
return pushAdapter.send(body, response.results);
});
};
}
/**
* Get expiration time from the request body.
* @param {Object} request A request object
@@ -80,7 +82,11 @@ export class PushController extends AdaptableController {
body['expiration_time'] + ' is not valid time.');
}
return expirationTime.valueOf();
};
}
expectedAdapterType() {
return PushAdapter;
}
};
export default PushController;