sails-factory-multitenant
Version:
Simple model factory for Sails.js 1.X with multitenancy features. Based on sails-factory and inspired on Factory-Girl.
304 lines (254 loc) • 15.1 kB
JavaScript
// ███████╗ █████╗ ██╗██╗ ███████╗ ███████╗ █████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗
// ██╔════╝██╔══██╗██║██║ ██╔════╝ ██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗╚██╗ ██╔╝
// ███████╗███████║██║██║ ███████╗ █████╗ ███████║██║ ██║ ██║ ██║██████╔╝ ╚████╔╝
// ╚════██║██╔══██║██║██║ ╚════██║ ██╔══╝ ██╔══██║██║ ██║ ██║ ██║██╔══██╗ ╚██╔╝
// ███████║██║ ██║██║███████╗███████║ ██║ ██║ ██║╚██████╗ ██║ ╚██████╔╝██║ ██║ ██║
// ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
const _ = require('@sailshq/lodash');
const fs = require('fs');
const path = require('upath');
const Factory = require('./factory');
const _datasources = require('sails-hook-multitenant/lib/methods/getdatasources');
const multitenant = require('sails-hook-multitenant');
const factoryMap = new Map();
class SailsFactory {
constructor(fpath = null) {
this.loadFactory(fpath);
}
// ██╗ ███████╗ █████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗
// ██║ ██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗╚██╗ ██╔╝
// ██║ █████╗ ███████║██║ ██║ ██║ ██║██████╔╝ ╚████╔╝
// ██║ ██╔══╝ ██╔══██║██║ ██║ ██║ ██║██╔══██╗ ╚██╔╝
// ███████╗██╗ ██║ ██║ ██║╚██████╗ ██║ ╚██████╔╝██║ ██║ ██║
// ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
loadFactory(fpath) {
if (fpath) {
const modelName = path.trimExt(path.basename(fpath));
Object.defineProperties(this, {
modelName: { value: modelName }
});
//-- load factory
fpath = this.getModelPath(fpath)
if (fpath) {
require(fpath)(this);
}
}
}
// ██████╗ ███████╗████████╗ ██████╗ █████╗ ████████╗██╗ ██╗
// ██╔════╝ ██╔════╝╚══██╔══╝ ██╔══██╗██╔══██╗╚══██╔══╝██║ ██║
// ██║ ███╗█████╗ ██║ ██████╔╝███████║ ██║ ███████║
// ██║ ██║██╔══╝ ██║ ██╔═══╝ ██╔══██║ ██║ ██╔══██║
// ╚██████╔╝███████╗ ██║ ██║ ██║ ██║ ██║ ██║ ██║
// ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
getModelPath(factory, required = true) {
if (!fs.existsSync(factory)) {
let path = `${process.cwd()}/test/factories/${factory}.js`;
try {
if (fs.existsSync(path) && !fs.lstatSync(path).isDirectory()) {
return path;
} else {
if (required) {
throw new Error("Factory '" + factory + "' not exists.");
}
return null;
}
} catch(err) {
if (required) {
throw new Error("Factory '" + path + "' not exists.");
}
return null;
}
} else {
return factory;
}
}
// ██╗ ██████╗ █████╗ ██████╗
// ██║ ██╔═══██╗██╔══██╗██╔══██╗
// ██║ ██║ ██║███████║██║ ██║
// ██║ ██║ ██║██╔══██║██║ ██║
// ███████╗╚██████╔╝██║ ██║██████╔╝
// ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝
load(...args) {
let folder = 'test/factories';
let callback = null;
_.each(args, (arg) => {
_.isString(arg) && (folder = arg);
_.isFunction(arg) && (callback = arg);
});
const dir = path.resolve(folder);
const files = fs.readdirSync(dir);
let count = 0;
_.each(files, (fname) => {
const fpath = path.join(dir, fname);
if (/^[^.].*\.js$/.test(fname) && fs.statSync(fpath).isFile()) {
new SailsFactory(fpath) && ++count;
}
});
callback && callback(count);
// Return the number if factories loaded
return count;
}
getFactory(name) {
const factory = factoryMap.get(name);
if (factory) {
return factory
} else {
return null;
}
}
// ██████╗ ███████╗███████╗██╗███╗ ██╗███████╗
// ██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
// ██║ ██║█████╗ █████╗ ██║██╔██╗ ██║█████╗
// ██║ ██║██╔══╝ ██╔══╝ ██║██║╚██╗██║██╔══╝
// ██████╔╝███████╗██║ ██║██║ ╚████║███████╗
// ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝
//
// 1. Instance define
// 2. Static define
define(name, modelName) {
const factory = new Factory(name, (modelName || this.modelName || name), this);
factoryMap.set(name, factory);
return factory;
}
static define(name, modelName) {
const factory = new Factory(name, (modelName || name), new SailsFactory());
factoryMap.set(name, factory);
return factory;
}
// ██████╗ ██╗ ██╗██╗██╗ ██████╗
// ██╔══██╗██║ ██║██║██║ ██╔══██╗
// ██████╔╝██║ ██║██║██║ ██║ ██║
// ██╔══██╗██║ ██║██║██║ ██║ ██║
// ██████╔╝╚██████╔╝██║███████╗██████╔╝
// ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝
//
// 1. Instance build
// 2. Static build
build(name, ...args) {
let attrs = {};
let callback = null;
_.each(args, (arg) => {
_.isPlainObject(arg) && (attrs = arg);
_.isFunction(arg) && (callback = arg);
});
const factory = factoryMap.get(name);
if (!factory) {
throw new Error("Factory '" + name + "' is undefined.");
}
return factory.build(attrs, callback);
}
static build(name, attrs, callback) {
if (_.isFunction(attrs)) {
callback = attrs;
attrs = {};
}
const sFactory = new SailsFactory();
const factory = sFactory.getFactory(name);
if (!factory) {
const path = sFactory.getModelPath(name);
sFactory.loadFactory(path);
factory = sFactory.getFactory(name);
}
return factory.build(attrs, callback);
}
// ██████╗██████╗ ███████╗ █████╗ ████████╗███████╗
// ██╔════╝██╔══██╗██╔════╝██╔══██╗╚══██╔══╝██╔════╝
// ██║ ██████╔╝█████╗ ███████║ ██║ █████╗
// ██║ ██╔══██╗██╔══╝ ██╔══██║ ██║ ██╔══╝
// ╚██████╗██║ ██║███████╗██║ ██║ ██║ ███████╗
// ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
//
// 1. Instance create
// 2. Static create
async create(name, ...args) {
// ████████╗███████╗███╗ ██╗ █████╗ ███╗ ██╗ ██████╗██╗ ██╗ ██████╗ ███████╗
// ╚══██╔══╝██╔════╝████╗ ██║██╔══██╗████╗ ██║██╔════╝╚██╗ ██╔╝ ██╔══██╗██╔════╝
// ██║ █████╗ ██╔██╗ ██║███████║██╔██╗ ██║██║ ╚████╔╝ ██║ ██║█████╗
// ██║ ██╔══╝ ██║╚██╗██║██╔══██║██║╚██╗██║██║ ╚██╔╝ ██║ ██║██╔══╝
// ██║ ███████╗██║ ╚████║██║ ██║██║ ╚████║╚██████╗ ██║ ██████╔╝███████╗██╗
// ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝
var ignore_tenancy = true;
if (multitenant) {
var genArgs = Array.from(arguments);
var req;
var modelName;
// Determine if factory exists based on tenancy posible request
if (factoryMap.get(genArgs[0])) {
modelName = (factoryMap.get(genArgs[0]).oParent) ?
factoryMap.get(genArgs[0]).oParent.modelId :
factoryMap.get(genArgs[0]).modelId;
} else if (factoryMap.get(genArgs[1])) {
modelName = (factoryMap.get(genArgs[1]).oParent) ?
factoryMap.get(genArgs[1]).oParent.modelId :
factoryMap.get(genArgs[1]).modelId;
} else {
throw new Error("Factory '" + name + "' is undefined.");
}
// Define datasource
let datasources;
if (sails.models[modelName]) {
datasources = _datasources(sails.models[modelName]);
} else {
throw new Error("Model '" + modelName + "' is not a sails model.");
}
// ████████╗███████╗███╗ ██╗ █████╗ ███╗ ██╗ ██████╗██╗ ██╗ ██████╗ ██╗███████╗
// ╚══██╔══╝██╔════╝████╗ ██║██╔══██╗████╗ ██║██╔════╝╚██╗ ██╔╝ ██╔══██╗██║██╔════╝
// ██║ █████╗ ██╔██╗ ██║███████║██╔██╗ ██║██║ ╚████╔╝ ██║ ██║██║███████╗
// ██║ ██╔══╝ ██║╚██╗██║██╔══██║██║╚██╗██║██║ ╚██╔╝ ██║ ██║██║╚════██║
// ██║ ███████╗██║ ╚████║██║ ██║██║ ╚████║╚██████╗ ██║ ██████╔╝██║███████║██╗
// ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝╚══════╝╚═╝
// Tenancy discrimination and arguments settings
if (typeof genArgs[0] === 'string' || typeof genArgs[0] === 'object') {
if (typeof genArgs[0] === 'string' &&
datasources.searchStringIdentity(genArgs[0])) {
req = genArgs[0];
name = genArgs[1];
args.splice(0, 1);
ignore_tenancy = false;
} else if (typeof genArgs[0] === 'object' &&
(genArgs[0].constructor.name === 'IncomingMessage' ||
genArgs[0].constructor.name === 'DataStoreConfig')) {
req = genArgs[0];
name = genArgs[1];
args.splice(0, 1);
ignore_tenancy = false;
}
}
}
const factory = factoryMap.get(name);
if (!factory) {
throw new Error("Factory '" + name + "' is undefined.");
}
let attrs = {};
let callback = null;
_.each(args, (arg) => {
_.isPlainObject(arg) && (attrs = arg);
_.isFunction(arg) && (callback = arg);
});
if (ignore_tenancy) {
return factory.create(null, attrs, callback);
} else {
return factory.create(req, attrs, callback);
}
}
// End Instance create
static create(name, ...args) {
const sFactory = new SailsFactory();
let model;
let factory = sFactory.getFactory(arguments[0]) || sFactory.getFactory(arguments[1]);
if (!factory) {
if (sFactory.getModelPath(arguments[0])) {
sFactory.loadFactory(arguments[0]);
model = arguments[0];
} else if (sFactory.getModelPath(arguments[1])) {
sFactory.loadFactory(arguments[1]);
model = arguments[1];
} else {
throw new Error("Factory '" + name + "' is undefined.");
}
}
return sFactory.create.apply(sFactory, arguments);
}
// End Static create
}
module.exports = SailsFactory;