VnModel.crud() bugs fixed (#784) and code refactor, VnMysql refactor
This commit is contained in:
parent
c3401a3d9a
commit
a50a98668c
|
@ -135,14 +135,14 @@ export default class CrudModel extends ModelProxy {
|
||||||
if (!this.isChanged)
|
if (!this.isChanged)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
let create = [];
|
let deletes = [];
|
||||||
let update = [];
|
let updates = [];
|
||||||
let remove = [];
|
let creates = [];
|
||||||
|
|
||||||
let pk = this.primaryKey;
|
let pk = this.primaryKey;
|
||||||
|
|
||||||
for (let row of this.removed)
|
for (let row of this.removed)
|
||||||
remove.push(row.$orgRow[pk]);
|
deletes.push(row.$orgRow[pk]);
|
||||||
|
|
||||||
for (let row of this._data)
|
for (let row of this._data)
|
||||||
if (row.$isNew) {
|
if (row.$isNew) {
|
||||||
|
@ -150,22 +150,22 @@ export default class CrudModel extends ModelProxy {
|
||||||
for (let prop in row)
|
for (let prop in row)
|
||||||
if (prop.charAt(0) !== '$')
|
if (prop.charAt(0) !== '$')
|
||||||
data[prop] = row[prop];
|
data[prop] = row[prop];
|
||||||
create.push(data);
|
creates.push(data);
|
||||||
} else if (row.$oldData) {
|
} else if (row.$oldData) {
|
||||||
let data = {};
|
let data = {};
|
||||||
for (let prop in row.$oldData)
|
for (let prop in row.$oldData)
|
||||||
data[prop] = row[prop];
|
data[prop] = row[prop];
|
||||||
update.push({
|
updates.push({
|
||||||
data,
|
data,
|
||||||
where: {[pk]: row.$orgRow[pk]}
|
where: {[pk]: row.$orgRow[pk]}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let changes = {
|
let changes = {deletes, updates, creates};
|
||||||
create: create,
|
|
||||||
update: update,
|
for (let prop in changes)
|
||||||
delete: remove
|
if (changes[prop].length === 0)
|
||||||
};
|
changes[prop] = undefined;
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,6 @@
|
||||||
auto-save="true"
|
auto-save="true"
|
||||||
on-save="$ctrl.onSave()">
|
on-save="$ctrl.onSave()">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-watcher
|
|
||||||
vn-id="watcher"
|
|
||||||
data="weeklies"
|
|
||||||
form="form">
|
|
||||||
</vn-watcher>
|
|
||||||
<form name="form">
|
<form name="form">
|
||||||
<div margin-medium>
|
<div margin-medium>
|
||||||
<vn-card margin-medium-v pad-medium>
|
<vn-card margin-medium-v pad-medium>
|
||||||
|
|
|
@ -2,30 +2,36 @@ import ngModule from '../module';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
constructor($scope) {
|
constructor($scope, vnApp, $translate) {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
|
this.vnApp = vnApp;
|
||||||
|
this._ = $translate;
|
||||||
|
|
||||||
this.ticketSelected = null;
|
this.ticketSelected = null;
|
||||||
this.filter = {
|
this.filter = {
|
||||||
include: [
|
include: {
|
||||||
{relation: 'ticket',
|
relation: 'ticket',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'clientFk', 'companyFk', 'warehouseFk'],
|
fields: ['id', 'clientFk', 'companyFk', 'warehouseFk'],
|
||||||
include: [
|
include: [
|
||||||
{relation: 'client',
|
{
|
||||||
scope: {
|
relation: 'client',
|
||||||
fields: ['salesPersonFk', 'name'],
|
scope: {
|
||||||
include: {
|
fields: ['salesPersonFk', 'name'],
|
||||||
relation: 'salesPerson',
|
include: {
|
||||||
fields: ['firstName', 'name']
|
relation: 'salesPerson',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'firstName', 'name']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{relation: 'warehouse'}
|
},
|
||||||
]
|
{relation: 'warehouse'}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.weekdays = [
|
this.weekdays = [
|
||||||
{id: 0, name: 'Monday'},
|
{id: 0, name: 'Monday'},
|
||||||
{id: 1, name: 'Tuesday'},
|
{id: 1, name: 'Tuesday'},
|
||||||
|
@ -38,7 +44,7 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave() {
|
onSave() {
|
||||||
this.$scope.watcher.notifySaved();
|
this.vnApp.showSuccess(this._.instant('Data saved!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
showClientDescriptor(event, clientFk) {
|
showClientDescriptor(event, clientFk) {
|
||||||
|
@ -65,17 +71,9 @@ export default class Controller {
|
||||||
this.expeditionId = expedition.id;
|
this.expeditionId = expedition.id;
|
||||||
this.$scope.deleteWeekly.show();
|
this.$scope.deleteWeekly.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
this.$scope.watcher.check();
|
|
||||||
this.$scope.model.save().then(() => {
|
|
||||||
this.$scope.watcher.notifySaved();
|
|
||||||
this.$scope.model.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope'];
|
Controller.$inject = ['$scope', 'vnApp', '$translate'];
|
||||||
|
|
||||||
ngModule.component('vnTicketWeekly', {
|
ngModule.component('vnTicketWeekly', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
module.exports = State => {
|
|
||||||
var serverFilter = {where: {order: {gt: 0}}, order: 'order, name'};
|
|
||||||
State.defineScope(serverFilter);
|
|
||||||
};
|
|
|
@ -1,58 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
/**
|
|
||||||
* Catches database errors overriding
|
|
||||||
* model.create() and model.upsert() methods
|
|
||||||
* @param {Object} replaceErrFunc - Callback
|
|
||||||
*/
|
|
||||||
Self.rewriteDbError = function(replaceErrFunc) {
|
|
||||||
this.once('attached', () => {
|
|
||||||
let realUpsert = this.upsert;
|
|
||||||
this.upsert = async(data, options, cb) => {
|
|
||||||
if (options instanceof Function) {
|
|
||||||
cb = options;
|
|
||||||
options = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await realUpsert.call(this, data, options);
|
|
||||||
if (cb) cb();
|
|
||||||
} catch (err) {
|
|
||||||
let myErr = replaceErr(err, replaceErrFunc);
|
|
||||||
if (cb)
|
|
||||||
cb(myErr);
|
|
||||||
else
|
|
||||||
throw myErr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let realCreate = this.create;
|
|
||||||
this.create = async(data, options, cb) => {
|
|
||||||
if (options instanceof Function) {
|
|
||||||
cb = options;
|
|
||||||
options = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await realCreate.call(this, data, options);
|
|
||||||
if (cb) cb();
|
|
||||||
} catch (err) {
|
|
||||||
let myErr = replaceErr(err, replaceErrFunc);
|
|
||||||
if (cb)
|
|
||||||
cb(myErr);
|
|
||||||
else
|
|
||||||
throw myErr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function replaceErr(err, replaceErrFunc) {
|
|
||||||
if (Array.isArray(err)) {
|
|
||||||
let errs = [];
|
|
||||||
for (let e of err)
|
|
||||||
errs.push(replaceErrFunc(e));
|
|
||||||
return errs;
|
|
||||||
}
|
|
||||||
return replaceErrFunc(err);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -8,13 +8,11 @@ describe('Model crud()', () => {
|
||||||
expect(ItemBarcode.crud).toBeDefined();
|
expect(ItemBarcode.crud).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a new instance', async() => {
|
it('should create a new instance', async () => {
|
||||||
let data = {code: '500', itemFk: '1'};
|
let data = {code: '500', itemFk: '1'};
|
||||||
|
let creates = [data];
|
||||||
|
|
||||||
crudObject = {
|
await ItemBarcode.crud(null, null, creates);
|
||||||
create: [data]
|
|
||||||
};
|
|
||||||
await ItemBarcode.crud(crudObject);
|
|
||||||
let instance = await ItemBarcode.findOne({where: data});
|
let instance = await ItemBarcode.findOne({where: data});
|
||||||
insertId = instance.id;
|
insertId = instance.id;
|
||||||
|
|
||||||
|
@ -22,24 +20,21 @@ describe('Model crud()', () => {
|
||||||
expect(instance.code).toEqual('500');
|
expect(instance.code).toEqual('500');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the instance', async() => {
|
it('should update the instance', async () => {
|
||||||
crudObject = {
|
let updates = [{
|
||||||
update: [{
|
where: {id: insertId},
|
||||||
where: {id: insertId},
|
data: {code: '501', itemFk: 1}
|
||||||
data: {code: '501', itemFk: 1}
|
}];
|
||||||
}]
|
|
||||||
};
|
await ItemBarcode.crud(null, updates);
|
||||||
await ItemBarcode.crud(crudObject);
|
|
||||||
let instance = await ItemBarcode.findById(insertId);
|
let instance = await ItemBarcode.findById(insertId);
|
||||||
|
|
||||||
expect(instance.code).toEqual('501');
|
expect(instance.code).toEqual('501');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the created instance', async() => {
|
it('should delete the created instance', async () => {
|
||||||
crudObject = {
|
let deletes = [insertId];
|
||||||
delete: [insertId]
|
await ItemBarcode.crud(deletes);
|
||||||
};
|
|
||||||
await ItemBarcode.crud(crudObject);
|
|
||||||
let instance = await ItemBarcode.findById(insertId);
|
let instance = await ItemBarcode.findById(insertId);
|
||||||
|
|
||||||
expect(instance).toEqual(null);
|
expect(instance).toEqual(null);
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
module.exports = function(Self) {
|
|
||||||
Self.validateBinded = function(propertyName, validatorFn, options) {
|
|
||||||
var customValidator = function(err) {
|
|
||||||
if (!validatorFn(this[propertyName])) err();
|
|
||||||
};
|
|
||||||
options.isExportable = true;
|
|
||||||
options.bindedFunction = validatorFn;
|
|
||||||
this.validate(propertyName, customValidator, options);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,5 +1,4 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.defineScope({where: {isManaged: {neq: 0}}});
|
|
||||||
require('../methods/agency/landsThatDay')(Self);
|
require('../methods/agency/landsThatDay')(Self);
|
||||||
require('../methods/agency/getFirstShipped')(Self);
|
require('../methods/agency/getFirstShipped')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/state/list')(Self);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the alertLevel of a state is 0.
|
* Checks if the alertLevel of a state is 0.
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,261 +5,267 @@ const UserError = require('../helpers').UserError;
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
Self.ParameterizedSQL = ParameterizedSQL;
|
Self.ParameterizedSQL = ParameterizedSQL;
|
||||||
|
|
||||||
require('../methods/vn-model/validateBinded')(Self);
|
|
||||||
require('../methods/vn-model/rewriteDbError')(Self);
|
|
||||||
require('../methods/vn-model/getSetValues')(Self);
|
require('../methods/vn-model/getSetValues')(Self);
|
||||||
|
|
||||||
Self.setup = function() {
|
Object.assign(Self, {
|
||||||
Self.super_.setup.call(this);
|
setup() {
|
||||||
|
Self.super_.setup.call(this);
|
||||||
|
|
||||||
// Register field ACL validation
|
// Register field ACL validation
|
||||||
this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
|
this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
|
||||||
this.beforeRemote('updateAll', ctx => this.checkUpdateAcls(ctx));
|
this.beforeRemote('updateAll', ctx => this.checkUpdateAcls(ctx));
|
||||||
this.beforeRemote('patchOrCreate', ctx => this.checkInsertAcls(ctx));
|
this.beforeRemote('patchOrCreate', ctx => this.checkInsertAcls(ctx));
|
||||||
this.beforeRemote('create', ctx => this.checkInsertAcls(ctx));
|
this.beforeRemote('create', ctx => this.checkInsertAcls(ctx));
|
||||||
this.beforeRemote('replaceById', ctx => this.checkInsertAcls(ctx));
|
this.beforeRemote('replaceById', ctx => this.checkInsertAcls(ctx));
|
||||||
this.beforeRemote('replaceOrCreate', ctx => this.checkInsertAcls(ctx));
|
this.beforeRemote('replaceOrCreate', ctx => this.checkInsertAcls(ctx));
|
||||||
|
|
||||||
this.remoteMethod('crud', {
|
this.remoteMethod('crud', {
|
||||||
description: 'Create, update or/and delete instances from model with a single request',
|
description: `Create, update or/and delete instances from model with a single request`,
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'actions',
|
arg: 'deletes',
|
||||||
type: 'Object',
|
description: `Identifiers of instances to delete`,
|
||||||
require: true,
|
type: ['Integer']
|
||||||
description: 'Instances to update, example: {create: [instances], update: [instances], delete: [ids]}',
|
}, {
|
||||||
http: {source: 'body'}
|
arg: 'updates',
|
||||||
}
|
description: `Instances to update with it's identifier {where, data}`,
|
||||||
],
|
type: ['Object']
|
||||||
http: {
|
}, {
|
||||||
path: `/crud`,
|
arg: 'creates',
|
||||||
verb: 'POST'
|
description: `Instances to create`,
|
||||||
}
|
type: ['Object']
|
||||||
});
|
}
|
||||||
};
|
]
|
||||||
|
|
||||||
Self.defineScope = function(serverFilter) {
|
|
||||||
this.remoteMethodCtx('list', {
|
|
||||||
accepts: [
|
|
||||||
{
|
|
||||||
arg: 'filter',
|
|
||||||
type: 'object',
|
|
||||||
description: 'Filter defining where'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: [this.modelName],
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
verb: 'get',
|
|
||||||
path: '/list'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list = function(ctx, clientFilter, cb) {
|
|
||||||
let clientFields = (clientFilter && clientFilter.fields) ? clientFilter.fields : [];
|
|
||||||
let serverFields = (serverFilter && serverFilter.fields) ? serverFilter.fields : [];
|
|
||||||
let fields = clientFields.filter(itemC => {
|
|
||||||
return serverFields.some(itemS => itemS === itemC);
|
|
||||||
});
|
});
|
||||||
let and = [];
|
},
|
||||||
let order;
|
|
||||||
let limit;
|
|
||||||
let filter = {order: order, limit: limit};
|
|
||||||
|
|
||||||
if (clientFilter && clientFilter.where)
|
async crud(deletes, updates, creates) {
|
||||||
and.push(clientFilter.where);
|
let transaction = await this.beginTransaction({});
|
||||||
if (serverFilter && serverFilter.where)
|
let options = {transaction};
|
||||||
and.push(serverFilter.where);
|
|
||||||
|
|
||||||
if (clientFilter && clientFilter.order)
|
try {
|
||||||
order = clientFilter.order;
|
if (deletes) {
|
||||||
else if (serverFilter && serverFilter.order)
|
|
||||||
order = serverFilter.order;
|
|
||||||
|
|
||||||
if (serverFilter && serverFilter.limit)
|
|
||||||
limit = serverFilter.limit;
|
|
||||||
else if (clientFilter && clientFilter.limit)
|
|
||||||
limit = clientFilter.limit;
|
|
||||||
|
|
||||||
filter.where = (and.length > 0) && {and: and};
|
|
||||||
filter.fields = fields;
|
|
||||||
|
|
||||||
this.find(filter, function(err, states) {
|
|
||||||
if (err)
|
|
||||||
cb(err, null);
|
|
||||||
else
|
|
||||||
cb(null, states);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.remoteMethodCtx = function(methodName, args) {
|
|
||||||
let ctx = {
|
|
||||||
arg: 'context',
|
|
||||||
type: 'object',
|
|
||||||
http: function(ctx) {
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (args.accepts === undefined)
|
|
||||||
args.accepts = [];
|
|
||||||
else if (!Array.isArray(args.accepts))
|
|
||||||
args.accepts = [args.accepts];
|
|
||||||
args.accepts.unshift(ctx);
|
|
||||||
this.remoteMethod(methodName, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.getConnection = function(cb) {
|
|
||||||
this.dataSource.connector.client.getConnection(cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.connectToService = function(ctx, dataSource) {
|
|
||||||
this.app.dataSources[dataSource].connector.remotes.auth = {
|
|
||||||
bearer: new Buffer(ctx.req.accessToken.id).toString('base64'),
|
|
||||||
sendImmediately: true
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.disconnectFromService = function(dataSource) {
|
|
||||||
this.app.dataSources[dataSource].connector.remotes.auth = {
|
|
||||||
bearer: new Buffer('').toString('base64'),
|
|
||||||
sendImmediately: true
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.crud = async function(actions) {
|
|
||||||
let transaction = await this.beginTransaction({});
|
|
||||||
let options = {transaction: transaction};
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (actions.delete && actions.delete.length)
|
|
||||||
await this.destroyAll({id: {inq: actions.delete}}, options);
|
|
||||||
|
|
||||||
if (actions.update) {
|
|
||||||
try {
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
actions.update.forEach(toUpdate => {
|
for (let id of deletes)
|
||||||
promises.push(this.upsertWithWhere(toUpdate.where, toUpdate.data, options));
|
promises.push(this.destroyById(id, options));
|
||||||
});
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
if (updates) {
|
||||||
if (actions.create && actions.create.length) {
|
let promises = [];
|
||||||
try {
|
for (let update of updates)
|
||||||
await this.create(actions.create, options);
|
promises.push(this.upsertWithWhere(update.where, update.data, options));
|
||||||
} catch (error) {
|
await Promise.all(promises);
|
||||||
throw error[error.length - 1];
|
|
||||||
}
|
}
|
||||||
|
if (creates && creates.length)
|
||||||
|
try {
|
||||||
|
await this.create(creates, options);
|
||||||
|
} catch (error) {
|
||||||
|
throw error[error.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
await transaction.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await transaction.rollback();
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
await transaction.commit();
|
},
|
||||||
} catch (error) {
|
|
||||||
await transaction.rollback();
|
/**
|
||||||
throw error;
|
* Wrapper for remoteMethod() but adding the context as
|
||||||
|
* extra argument at the beginning of arguments list.
|
||||||
|
*
|
||||||
|
* @param {String} methodName The method name
|
||||||
|
* @param {Object} options The method options
|
||||||
|
*/
|
||||||
|
remoteMethodCtx(methodName, options) {
|
||||||
|
if (options.accepts === undefined)
|
||||||
|
options.accepts = [];
|
||||||
|
else if (!Array.isArray(options.accepts))
|
||||||
|
options.accepts = [options.accepts];
|
||||||
|
|
||||||
|
options.accepts.unshift({
|
||||||
|
arg: 'ctx',
|
||||||
|
type: 'Object',
|
||||||
|
http: {source: 'context'}
|
||||||
|
});
|
||||||
|
this.remoteMethod(methodName, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a validation, marking it as exportable to the browser.
|
||||||
|
* Exportable validation functions should be synchronous and totally
|
||||||
|
* independent from other code because they are parsed in the browser
|
||||||
|
* using eval().
|
||||||
|
*
|
||||||
|
* @param {String} propertyName The property name
|
||||||
|
* @param {Function} validatorFn The validation function
|
||||||
|
* @param {Object} options The validation options
|
||||||
|
*/
|
||||||
|
validateBinded(propertyName, validatorFn, options) {
|
||||||
|
let customValidator = function(err) {
|
||||||
|
if (!validatorFn(this[propertyName])) err();
|
||||||
|
};
|
||||||
|
options.isExportable = true;
|
||||||
|
options.bindedFunction = validatorFn;
|
||||||
|
this.validate(propertyName, customValidator, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Catches database errors overriding create() and upsert() methods.
|
||||||
|
*
|
||||||
|
* @param {Function} replaceErrFunc - Callback
|
||||||
|
*/
|
||||||
|
rewriteDbError(replaceErrFunc) {
|
||||||
|
function replaceErr(err, replaceErrFunc) {
|
||||||
|
if (Array.isArray(err)) {
|
||||||
|
let errs = [];
|
||||||
|
for (let e of err)
|
||||||
|
errs.push(replaceErrFunc(e));
|
||||||
|
return errs;
|
||||||
|
}
|
||||||
|
return replaceErrFunc(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.once('attached', () => {
|
||||||
|
let realUpsert = this.upsert;
|
||||||
|
this.upsert = async (data, options, cb) => {
|
||||||
|
if (options instanceof Function) {
|
||||||
|
cb = options;
|
||||||
|
options = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await realUpsert.call(this, data, options);
|
||||||
|
if (cb) cb();
|
||||||
|
} catch (err) {
|
||||||
|
let myErr = replaceErr(err, replaceErrFunc);
|
||||||
|
if (cb)
|
||||||
|
cb(myErr);
|
||||||
|
else
|
||||||
|
throw myErr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let realCreate = this.create;
|
||||||
|
this.create = async (data, options, cb) => {
|
||||||
|
if (options instanceof Function) {
|
||||||
|
cb = options;
|
||||||
|
options = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await realCreate.call(this, data, options);
|
||||||
|
if (cb) cb();
|
||||||
|
} catch (err) {
|
||||||
|
let myErr = replaceErr(err, replaceErrFunc);
|
||||||
|
if (cb)
|
||||||
|
cb(myErr);
|
||||||
|
else
|
||||||
|
throw myErr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut to VnMySQL.executeP()
|
||||||
|
*/
|
||||||
|
rawSql(query, params, options, cb) {
|
||||||
|
return this.dataSource.connector.executeP(query, params, options, cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut to VnMySQL.executeStmt()
|
||||||
|
*/
|
||||||
|
rawStmt(stmt, options) {
|
||||||
|
return this.dataSource.connector.executeStmt(stmt, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut to VnMySQL.makeLimit()
|
||||||
|
*/
|
||||||
|
makeLimit(filter) {
|
||||||
|
return this.dataSource.connector.makeLimit(filter);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut to VnMySQL.makeSuffix()
|
||||||
|
*/
|
||||||
|
makeSuffix(filter) {
|
||||||
|
return this.dataSource.connector.makeSuffix(filter);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shortcut to VnMySQL.buildModelSuffix()
|
||||||
|
*/
|
||||||
|
buildSuffix(filter, tableAlias) {
|
||||||
|
return this.dataSource.connector.buildModelSuffix(this.modelName, filter, tableAlias);
|
||||||
|
},
|
||||||
|
|
||||||
|
async checkAcls(ctx, actionType) {
|
||||||
|
let userId = ctx.req.accessToken.userId;
|
||||||
|
let models = this.app.models;
|
||||||
|
let userRoles = await models.Account.getRoles(userId);
|
||||||
|
let data = ctx.args.data;
|
||||||
|
let modelAcls;
|
||||||
|
|
||||||
|
function modifiedProperties(data) {
|
||||||
|
let properties = [];
|
||||||
|
|
||||||
|
for (property in data)
|
||||||
|
properties.push(property);
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
modelAcls = await models.FieldAcl.find({
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{model: this.modelName},
|
||||||
|
{role: {inq: userRoles}},
|
||||||
|
{property: '*'},
|
||||||
|
{or: [{actionType: '*'}, {actionType: actionType}]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let allowedAll = modelAcls.find(acl => {
|
||||||
|
return acl.property == '*';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allowedAll)
|
||||||
|
return;
|
||||||
|
|
||||||
|
modelAcls = await models.FieldAcl.find({
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{model: this.modelName},
|
||||||
|
{role: {inq: userRoles}},
|
||||||
|
{property: {inq: modifiedProperties(data)}},
|
||||||
|
{or: [{actionType: '*'}, {actionType: actionType}]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let propsHash = {};
|
||||||
|
for (let acl of modelAcls)
|
||||||
|
propsHash[acl.property] = true;
|
||||||
|
|
||||||
|
let allowedProperties = Object.keys(data).every(property => {
|
||||||
|
return propsHash[property];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!allowedProperties)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
},
|
||||||
|
|
||||||
|
checkUpdateAcls(ctx) {
|
||||||
|
return this.checkAcls(ctx, 'update');
|
||||||
|
},
|
||||||
|
|
||||||
|
checkInsertAcls(ctx) {
|
||||||
|
return this.checkAcls(ctx, 'insert');
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
Self.checkAcls = async function(ctx, actionType) {
|
|
||||||
let userId = ctx.req.accessToken.userId;
|
|
||||||
let models = this.app.models;
|
|
||||||
let userRoles = await models.Account.getRoles(userId);
|
|
||||||
let data = ctx.args.data;
|
|
||||||
let modelAcls;
|
|
||||||
|
|
||||||
function modifiedProperties(data) {
|
|
||||||
let properties = [];
|
|
||||||
|
|
||||||
for (property in data)
|
|
||||||
properties.push(property);
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
modelAcls = await models.FieldAcl.find({
|
|
||||||
where: {
|
|
||||||
and: [
|
|
||||||
{model: this.modelName},
|
|
||||||
{role: {inq: userRoles}},
|
|
||||||
{property: '*'},
|
|
||||||
{or: [{actionType: '*'}, {actionType: actionType}]}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let allowedAll = modelAcls.find(acl => {
|
|
||||||
return acl.property == '*';
|
|
||||||
});
|
|
||||||
|
|
||||||
if (allowedAll)
|
|
||||||
return;
|
|
||||||
|
|
||||||
modelAcls = await models.FieldAcl.find({
|
|
||||||
where: {
|
|
||||||
and: [
|
|
||||||
{model: this.modelName},
|
|
||||||
{role: {inq: userRoles}},
|
|
||||||
{property: {inq: modifiedProperties(data)}},
|
|
||||||
{or: [{actionType: '*'}, {actionType: actionType}]}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let propsHash = {};
|
|
||||||
for (let acl of modelAcls)
|
|
||||||
propsHash[acl.property] = true;
|
|
||||||
|
|
||||||
let allowedProperties = Object.keys(data).every(property => {
|
|
||||||
return propsHash[property];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!allowedProperties)
|
|
||||||
throw new UserError(`You don't have enough privileges`);
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.checkUpdateAcls = function(ctx) {
|
|
||||||
return this.checkAcls(ctx, 'update');
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.checkInsertAcls = function(ctx) {
|
|
||||||
return this.checkAcls(ctx, 'insert');
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shortcut to VnMySQL.executeP()
|
|
||||||
*/
|
|
||||||
Self.rawSql = function(query, params, options, cb) {
|
|
||||||
return this.dataSource.connector.executeP(query, params, options, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shortcut to VnMySQL.executeStmt()
|
|
||||||
*/
|
|
||||||
Self.rawStmt = function(stmt, options) {
|
|
||||||
return this.dataSource.connector.executeStmt(stmt, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shortcut to VnMySQL.makeLimit()
|
|
||||||
*/
|
|
||||||
Self.makeLimit = function(filter) {
|
|
||||||
return this.dataSource.connector.makeLimit(filter);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shortcut to VnMySQL.makeSuffix()
|
|
||||||
*/
|
|
||||||
Self.makeSuffix = function(filter) {
|
|
||||||
return this.dataSource.connector.makeSuffix(filter);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shortcut to VnMySQL.buildModelSuffix()
|
|
||||||
*/
|
|
||||||
Self.buildSuffix = function(filter, tableAlias) {
|
|
||||||
return this.dataSource.connector.buildModelSuffix(this.modelName, filter, tableAlias);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
|
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
const loopbackConnector = require('loopback-connector');
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
const SqlConnector = loopbackConnector.SqlConnector;
|
|
||||||
const ParameterizedSQL = loopbackConnector.ParameterizedSQL;
|
|
||||||
const MySQL = require('loopback-connector-mysql').MySQL;
|
const MySQL = require('loopback-connector-mysql').MySQL;
|
||||||
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
class VnMySQL extends MySQL {
|
class VnMySQL extends MySQL {
|
||||||
constructor(settings) {
|
|
||||||
super();
|
|
||||||
SqlConnector.call(this, 'mysql', settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
toColumnValue(prop, val) {
|
toColumnValue(prop, val) {
|
||||||
if (val == null || !prop || prop.type !== Date)
|
if (val == null || !prop || prop.type !== Date)
|
||||||
return MySQL.prototype.toColumnValue.call(this, prop, val);
|
return MySQL.prototype.toColumnValue.call(this, prop, val);
|
||||||
|
@ -237,12 +230,11 @@ exports.initialize = function initialize(dataSource, callback) {
|
||||||
|
|
||||||
dataSource.EnumFactory = EnumFactory;
|
dataSource.EnumFactory = EnumFactory;
|
||||||
|
|
||||||
if (callback) {
|
if (callback)
|
||||||
if (dataSource.settings.lazyConnect) {
|
if (dataSource.settings.lazyConnect) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
dataSource.connector.connect(callback);
|
dataSource.connector.connect(callback);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue