You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.2 KiB
200 lines
6.2 KiB
'use strict';
|
|
|
|
var Controllers = require('./Controllers'),
|
|
hasOneResource = require('./associations/has-one'),
|
|
hasManyResource = require('./associations/has-many'),
|
|
belongsToResource = require('./associations/belongs-to'),
|
|
belongsToManyResource = require('./associations/belongs-to-many'),
|
|
_ = require('lodash');
|
|
|
|
var Resource = function(options) {
|
|
_.defaults(options, {
|
|
actions: ['create', 'read', 'update', 'delete', 'list'],
|
|
pagination: true,
|
|
reloadInstances: true,
|
|
include: [],
|
|
excludeAttributes: []
|
|
});
|
|
|
|
_.defaultsDeep(options, {
|
|
search: {
|
|
param: 'q'
|
|
},
|
|
sort: {
|
|
param: 'sort'
|
|
}
|
|
});
|
|
|
|
this.app = options.app;
|
|
this.sequelize = options.sequelize;
|
|
this.model = options.model;
|
|
this.include = options.include.map(function(include) {
|
|
return include instanceof options.sequelize.Model ? { model: include } : include;
|
|
});
|
|
if (!!options.readOnlyAttributes) this.readOnlyAttributes = options.readOnlyAttributes;
|
|
if (!!options.excludeAttributes) this.excludeAttributes = options.excludeAttributes;
|
|
this.attributes = (!options.excludeAttributes.length) ?
|
|
Object.keys(this.model.rawAttributes) :
|
|
Object.keys(this.model.rawAttributes).filter(function(attr) {
|
|
return options.excludeAttributes.indexOf(attr) === -1;
|
|
});
|
|
|
|
this.actions = options.actions;
|
|
this.endpoints = {
|
|
plural: options.endpoints[0],
|
|
singular: options.endpoints[1] || options.endpoints[0]
|
|
};
|
|
this.updateMethod = options.updateMethod;
|
|
this.pagination = options.pagination;
|
|
this.search = options.search;
|
|
this.sort = options.sort;
|
|
this.reloadInstances = options.reloadInstances;
|
|
|
|
this.associationOptions = {
|
|
removeForeignKeys: false
|
|
};
|
|
|
|
if (!!options.associations) {
|
|
if (_.isObject(options.associations)) {
|
|
this.associationOptions = _.extend(this.associationOptions, options.associations);
|
|
}
|
|
autoAssociate(this);
|
|
}
|
|
|
|
this.controllers = {};
|
|
this.actions.forEach(function(action) {
|
|
var Controller = Controllers[action];
|
|
var endpoint = this.endpoints[Controller.prototype.plurality];
|
|
|
|
this.controllers[action] = new Controller({
|
|
endpoint: endpoint,
|
|
app: options.app,
|
|
model: this.model,
|
|
include: this.include,
|
|
resource: this
|
|
});
|
|
|
|
}.bind(this));
|
|
|
|
var hooks = Controllers.base.hooks,
|
|
self = this;
|
|
|
|
this.actions.forEach(function(action) {
|
|
self[action] = self[action] || {};
|
|
hooks.forEach(function(hook) {
|
|
self[action][hook] = function(f) {
|
|
self.controllers[action].milestone(hook, f);
|
|
};
|
|
|
|
self[action][hook].before = function(f) {
|
|
self.controllers[action].milestone(hook + '_before', f);
|
|
};
|
|
|
|
self[action][hook].after = function(f) {
|
|
self.controllers[action].milestone(hook + '_after', f);
|
|
};
|
|
});
|
|
});
|
|
|
|
this.all = {};
|
|
|
|
hooks.forEach(function(hook) {
|
|
self.all[hook] = function(f) {
|
|
self.actions.forEach(function(action) {
|
|
self.controllers[action].milestone(hook, f);
|
|
});
|
|
};
|
|
|
|
self.all[hook].before = function(f) {
|
|
self.actions.forEach(function(action) {
|
|
self.controllers[action].milestone(hook + '_before', f);
|
|
});
|
|
};
|
|
|
|
self.all[hook].after = function(f) {
|
|
self.actions.forEach(function(action) {
|
|
self.controllers[action].milestone(hook + '_after', f);
|
|
});
|
|
};
|
|
|
|
});
|
|
|
|
// cache association data for later use
|
|
self.associationsInfo = {};
|
|
if (self.include && self.include.length) {
|
|
self.include.forEach(function(i) {
|
|
var primaryKey = i.model.primaryKeyField,
|
|
associations = _.values(self.model.associations).filter(function(a) {
|
|
return a.target === i.model;
|
|
});
|
|
|
|
associations.forEach(function(association) {
|
|
self.associationsInfo[association.identifier] = {
|
|
identifier: association.identifier,
|
|
primaryKey: primaryKey,
|
|
as: association.as
|
|
};
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
Resource.prototype.use = function(middleware) {
|
|
var self = this,
|
|
actions = _.clone(self.actions);
|
|
|
|
actions.push('all');
|
|
actions.forEach(function(action) {
|
|
if (_.has(middleware, action)) {
|
|
_.forOwn(middleware[action], function(definition, milestone) {
|
|
if (_.isFunction(definition)) {
|
|
self[action][milestone](definition);
|
|
} else {
|
|
if (_.has(definition, 'action')) self[action][milestone](definition.action);
|
|
if (_.has(definition, 'before')) self[action][milestone].before(definition.before);
|
|
if (_.has(definition, 'after')) self[action][milestone].after(definition.after);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
if (_.has(middleware, 'extraConfiguration') && _.isFunction(middleware.extraConfiguration))
|
|
middleware.extraConfiguration(this);
|
|
};
|
|
|
|
function autoAssociate(resource) {
|
|
if (!resource.model.associations)
|
|
return;
|
|
|
|
_.forEach(resource.model.associations, function(association) {
|
|
// for prefetched data in list and read
|
|
if (!_.has(resource.associationOptions, 'autoPopulate') || !!resource.associationOptions.autoPopulate) {
|
|
if (!!association.as) {
|
|
resource.include.push({model: association.target, as: association.as});
|
|
} else {
|
|
resource.include.push(association.target);
|
|
}
|
|
}
|
|
|
|
var subResourceName;
|
|
if (association.associationType === 'HasOne') {
|
|
subResourceName =
|
|
association.target.options.name.singular.toLowerCase();
|
|
resource[subResourceName] = hasOneResource(Resource, resource, association);
|
|
} else if (association.associationType === 'HasMany') {
|
|
subResourceName =
|
|
association.target.options.name.plural.toLowerCase();
|
|
resource[subResourceName] = hasManyResource(Resource, resource, association);
|
|
} else if (association.associationType === 'BelongsTo') {
|
|
subResourceName =
|
|
association.target.options.name.singular.toLowerCase();
|
|
resource[subResourceName] = belongsToResource(Resource, resource, association);
|
|
} else if (association.associationType === 'BelongsToMany') {
|
|
subResourceName =
|
|
association.target.options.name.plural.toLowerCase();
|
|
resource[subResourceName] = belongsToManyResource(Resource, resource, association);
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = Resource;
|
|
|