refs #5858 feat: handle Error from middleware
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Javier Segarra 2023-12-11 07:36:39 +01:00
parent 081a339b5a
commit 94147bd2b2
10 changed files with 195 additions and 684 deletions

View File

@ -1,6 +1,4 @@
Are you sure exit without saving?: ¿Seguro que quieres salir sin guardar?
Unsaved changes will be lost: Los cambios que no hayas guardado se perderán
No changes to save: No hay cambios que guardar
Field are invalid: El campo {{tag}} no es válido
Some fields are invalid: Algunos campos no son válidos

View File

@ -317,10 +317,8 @@ export default class Watcher extends Component {
* Checks if the form is valid.
*/
isInvalid() {
if (this.form && this.form.$invalid) {
const tag = Object.values(this.form.$error)[0][0].$$attr.label;
throw new UserError(this.$t('Field are invalid', {tag: this.$t(tag)}));
}
if (this.form && this.form.$invalid)
throw new UserError('Some fields are invalid');
}
/**

View File

@ -351,5 +351,5 @@
"sageWithholdingFk" : "Sage con tenencia",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"Field are invalid": "El campo {{tag}} no es válido"
"Field are invalid": "El campo '{{tag}}' no es válido"
}

View File

@ -35,7 +35,6 @@
}
},
"auth:after": {
"./middleware/validate-model": {},
"./middleware/current-user": {},
"./middleware/salix-version": {}
},

View File

@ -1,8 +1,36 @@
const UserError = require('../../util/user-error');
const logToConsole = require('strong-error-handler/lib/logger');
const validations = [{
validation: message => String(message).startsWith('Value is not'),
message: ({__: $t, body}) => $t('Field are invalid', {tag: $t(Object.keys(body)[0])})
message: ({__: $t}, _tag) =>
$t('Field are invalid', {tag: $t(_tag)}),
handleError: ({method, originalUrl, body}) => {
const {models} = require('vn-loopback/server/server');
let tag = null;
try {
let [module, id, path] = originalUrl.split('api/')[1].split('/');
let model = models[module];
if (!model) {
module = module.substring(0, module.length - 1);
model = models[module];
}
if (!model) throw new UserError('');
const {accepts} = model.sharedClass.methods().find(method => method.name === path);
for (const [key, value] of Object.entries(body)) {
const accept = accepts.find(acc => acc.arg === key);
if (!value && accept.type !== 'any') {
tag = key;
break;
}
}
return tag;
} catch (error) {
throw new Error(error);
}
}
}];
module.exports = function() {
return function(err, req, res, next) {
@ -16,7 +44,17 @@ module.exports = function() {
if (err.statusCode == 400) {
try {
validations.forEach(validation => {
err.message = validation.validation(err.message) && validation.message(req);
if (validation.validation(err.message)) {
const error = validation.handleError(req);
if (error)
err.message = validation.message(req, error);
// const tag = handleNullProperty(req);
// if (tag) {
// const message = validation.message(req);
// }
// const tag = validateModel(req);
// if (tag) validation.message(req);
}
});
return next(err);

View File

@ -1,33 +0,0 @@
const {models} = require('vn-loopback/server/server');
const validatorBodyModel = require('../../../modules/client/back/methods/client/body_model_validator');
const makeSingular = s => {
if (s == null || s.length == 0)
return s;
return s.substring(0, s.length - 1);
};
function blobToB64(data) {
return new Uint8Array(data).reduce(function(data, byte) {
return data + String.fromCharCode(byte);
}, '');
}
module.exports = function(options) {
return function(req, res, next) {
const {method, originalUrl} = req;
if (['GET', 'DELETE'].includes(method)) return next();
let [module, id, path] = originalUrl.split('api/')[1].split('/');
let model = models[module];
if (!model) {
module = makeSingular(module);
model = models[module];
}
const properties = model.definition.rawProperties;
const data = JSON.parse(blobToB64(req.readableBuffer.tail.data));
let isValid = validatorBodyModel(properties, data);
if (!isValid.isValid)
next();
return next();
};
};

View File

@ -1,42 +0,0 @@
const isNotNull = value => value !== null && value !== undefined && value !== '';
const validatorBodyModel = (model, body) => {
let isValid = true;
let tag = null;
Object.entries(body).forEach(([key, value]) => {
if (!isValid) return;
const bodyArg = model.find(m => m.arg === key);
if (!bodyArg) return;
// throw new Error(`Property ${key} is not defined in this model`);
const {type} = bodyArg;
tag = key;
if (tag !== 'any') {
isValid = isNotNull(value);
return;
}
switch (type) {
case 'number':
isValid = /^[0-9]*$/.test(value);
break;
case 'boolean':
isValid = value instanceof Boolean;
break;
case 'date':
isValid = (new Date(date) !== 'Invalid Date') && !isNaN(new Date(date));
break;
case 'string':
isValid = typeof value == 'string';
break;
default:
break;
}
});
return {isValid, tag};
};
module.exports = validatorBodyModel;

View File

@ -1,414 +0,0 @@
/* eslint-disable jasmine/no-spec-dupes */
const validatorBodyModel = require('../body_model_validator');
fdescribe('Validation Model', () => {
describe('should be number', () => {
it('success', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'number'
}], {property: 1});
expect(isValid).toBeTrue();
});
it('fail', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'number'
}], {property: null});
expect(isValid).toBeFalse();
});
});
describe('should be string', () => {
it('success as number', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'string'
}], {property: '1234'});
expect(isValid).toBeTrue();
});
it('success as string', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'string'
}], {property: 'null'});
expect(isValid).toBeTrue();
});
it('fail', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'string'
}], {property: null});
expect(isValid).toBeFalse();
});
});
describe('should be date', () => {
it('success', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'date'
}], {property: new Date()});
expect(isValid).toBeTrue();
});
it('fail', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'date'
}], {property: null});
expect(isValid).toBeFalse();
});
});
describe('should be boolean', () => {
it('success as true', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'boolean'
}], {property: true});
expect(isValid).toBeTrue();
});
it('success as false', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'boolean'
}], {property: false});
expect(isValid).toBeTrue();
});
it('fail', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'boolean'
}], {property: null});
expect(isValid).toBeFalse();
});
});
describe('should be any', () => {
it('success', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'any'
}], {property: '1234'});
expect(isValid).toBeTrue();
});
it('fail', () => {
let {isValid} = validatorBodyModel([{
arg: 'property',
type: 'any'
}], {property: null});
expect(isValid).toBeFalse();
});
});
});
describe('Validation Client Create', () => {
const newAccount = {
userName: 'Deadpool',
email: 'Deadpool@marvel.com',
fi: '16195279J',
name: 'Wade',
socialName: 'DEADPOOL MARVEL',
street: 'WALL STREET',
city: 'New York',
businessTypeFk: 'florist',
provinceFk: 1
};
const CLIENT_MODEL = [
{
arg: 'socialName',
type: 'string'
},
{
arg: 'fi',
type: 'string'
},
{
arg: 'street',
type: 'string'
},
{
arg: 'postcode',
type: 'string'
},
{
arg: 'city',
type: 'string'
},
{
arg: 'countryFk',
type: 'number'
},
{
arg: 'provinceFk',
type: 'number'
},
{
arg: 'sageTaxTypeFk',
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'any'
},
{
arg: 'transferorFk',
type: 'any'
},
{
arg: 'hasToInvoiceByAddress',
type: 'boolean'
},
{
arg: 'hasToInvoice',
type: 'boolean'
},
{
arg: 'isActive',
type: 'boolean'
},
{
arg: 'isFreezed',
type: 'boolean'
},
{
arg: 'isVies',
type: 'boolean'
},
{
arg: 'isToBeMailed',
type: 'boolean'
},
{
arg: 'isEqualizated',
type: 'boolean'
},
{
arg: 'isTaxDataVerified',
type: 'boolean'
},
{
arg: 'isTaxDataChecked',
type: 'boolean'
},
{
arg: 'despiteOfClient',
type: 'number'
},
{
arg: 'hasIncoterms',
type: 'boolean'
},
{
arg: 'hasElectronicInvoice',
type: 'boolean'
}
];
it(`should not find Deadpool as he's not created yet`, async() => {
let isValid = false;
isValid = validatorBodyModel(CLIENT_MODEL, newAccount).isValid;
expect(isValid).toBeTrue();
});
it('should create a new account', async() => {
let isValid = false;
isValid = validatorBodyModel(CLIENT_MODEL, newAccount).isValid;
expect(isValid).toBeTrue();
});
it('should not be able to create a user if exists', async() => {
let isValid = false;
isValid = validatorBodyModel(CLIENT_MODEL, newAccount).isValid;
expect(isValid).toBeTrue();
});
});
fdescribe('Validation Worker Create', () => {
const defaultWorker = {
// fi: '78457139E',
// name: 'DEFAULTERWORKER',
// firstName: 'DEFAULT',
// lastNames: 'WORKER',
// email: 'defaultWorker@mydomain.com',
// street: 'S/ DEFAULTWORKERSTREET',
// city: 'defaultWorkerCity',
// provinceFk: 1,
// countryFk: 1,
// companyFk: 442,
// postcode: '46680',
// phone: '123456789',
// code: 'DWW',
// bossFk: 9,
// birth: '2022-12-11T23:00:00.000Z',
// payMethodFk: 1,
// roleFk: 1
};
const WORKER_MODEL = [
{
arg: 'fi',
type: 'string',
description: `The worker fi`,
required: true,
},
{
arg: 'name',
type: 'string',
description: `The user name`,
required: true,
},
{
arg: 'firstName',
type: 'string',
description: `The worker firstname`,
required: true,
},
{
arg: 'lastNames',
type: 'string',
description: `The worker lastnames`,
required: true,
},
{
arg: 'email',
type: 'string',
description: `The worker email`,
required: true,
},
{
arg: 'street',
type: 'string',
description: `The worker address`,
required: true,
},
{
arg: 'city',
type: 'string',
description: `The worker city`,
required: true,
},
{
arg: 'provinceFk',
type: 'number',
description: `The worker province`,
required: true,
},
{
arg: 'companyFk',
type: 'number',
description: `The worker company`,
required: true,
},
{
arg: 'postcode',
type: 'string',
description: `The worker postcode`,
required: true,
},
{
arg: 'phone',
type: 'string',
description: `The worker phone`,
required: true,
},
{
arg: 'code',
type: 'string',
description: `The worker code`,
required: true,
},
{
arg: 'bossFk',
type: 'number',
description: `The worker boss`,
required: true,
},
{
arg: 'birth',
type: 'date',
description: `The worker birth`,
required: true,
},
{
arg: 'payMethodFk',
type: 'number',
description: `The client payMethod`,
required: true,
},
{
arg: 'iban',
type: 'string',
description: `The client iban`,
},
{
arg: 'bankEntityFk',
type: 'number',
description: `The client bank entity`,
}
];
it(`should not find worker as he's not created yet`, async() => {
let isValid = false;
isValid = validatorBodyModel(WORKER_MODEL, defaultWorker).isValid;
expect(isValid).toBeTrue();
});
it('should create a new worker', async() => {
let isValid = false;
isValid = validatorBodyModel(WORKER_MODEL, defaultWorker).isValid;
expect(isValid).toBeTrue();
});
it('should update a new worker', async() => {
let isValid = false;
isValid = validatorBodyModel(WORKER_MODEL, defaultWorker).isValid;
expect(isValid).toBeTrue();
});
it('should not be able to create a worker if exists', async() => {
let isValid = false;
let error;
isValid = validatorBodyModel(WORKER_MODEL, defaultWorker).isValid;
expect(isValid).toBeTrue();
});
});

View File

@ -1,95 +1,4 @@
let UserError = require('vn-loopback/util/user-error');
const validatorBodyModel = require('./body_model_validator');
const BODY_MODEL = [
{
arg: 'socialName',
type: 'string'
},
{
arg: 'fi',
type: 'string'
},
{
arg: 'street',
type: 'string'
},
{
arg: 'postcode',
type: 'string'
},
{
arg: 'city',
type: 'string'
},
{
arg: 'countryFk',
type: 'number'
},
{
arg: 'provinceFk',
type: 'number'
},
{
arg: 'sageTaxTypeFk',
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'any'
},
{
arg: 'transferorFk',
type: 'any'
},
{
arg: 'hasToInvoiceByAddress',
type: 'boolean'
},
{
arg: 'hasToInvoice',
type: 'boolean'
},
{
arg: 'isActive',
type: 'boolean'
},
{
arg: 'isFreezed',
type: 'boolean'
},
{
arg: 'isVies',
type: 'boolean'
},
{
arg: 'isToBeMailed',
type: 'boolean'
},
{
arg: 'isEqualizated',
type: 'boolean'
},
{
arg: 'isTaxDataVerified',
type: 'boolean'
},
{
arg: 'isTaxDataChecked',
type: 'boolean'
},
{
arg: 'despiteOfClient',
type: 'number'
},
{
arg: 'hasIncoterms',
type: 'boolean'
},
{
arg: 'hasElectronicInvoice',
type: 'boolean'
}
];
module.exports = Self => {
Self.remoteMethod('updateFiscalData', {
@ -108,9 +17,92 @@ module.exports = Self => {
http: {source: 'path'}
},
{
arg: 'data',
type: 'object',
http: {source: 'body'}
arg: 'socialName',
type: 'string'
},
{
arg: 'fi',
type: 'string'
},
{
arg: 'street',
type: 'string'
},
{
arg: 'postcode',
type: 'string'
},
{
arg: 'city',
type: 'string'
},
{
arg: 'countryFk',
type: 'number'
},
{
arg: 'provinceFk',
type: 'number'
},
{
arg: 'sageTaxTypeFk',
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'any'
},
{
arg: 'transferorFk',
type: 'any'
},
{
arg: 'hasToInvoiceByAddress',
type: 'boolean'
},
{
arg: 'hasToInvoice',
type: 'boolean'
},
{
arg: 'isActive',
type: 'boolean'
},
{
arg: 'isFreezed',
type: 'boolean'
},
{
arg: 'isVies',
type: 'boolean'
},
{
arg: 'isToBeMailed',
type: 'boolean'
},
{
arg: 'isEqualizated',
type: 'boolean'
},
{
arg: 'isTaxDataVerified',
type: 'boolean'
},
{
arg: 'isTaxDataChecked',
type: 'boolean'
},
{
arg: 'despiteOfClient',
type: 'number'
},
{
arg: 'hasIncoterms',
type: 'boolean'
},
{
arg: 'hasElectronicInvoice',
type: 'boolean'
}
],
returns: {
@ -128,7 +120,9 @@ module.exports = Self => {
let tx;
const myOptions = {};
const models = Self.app.models;
const {args, req: {__: $t}} = ctx;
const args = ctx.args;
const userId = ctx.req.accessToken.userId;
const $t = ctx.req.__;
if (typeof options == 'object')
Object.assign(myOptions, options);
@ -154,22 +148,8 @@ module.exports = Self => {
// Remove unwanted properties
delete args.ctx;
delete args.id;
const {isValid, tag} = validatorBodyModel(BODY_MODEL, args.data);
if (!isValid) {
const key = $t(`${tag}`);
throw new Error($t('Model is not valid', {key})
);
}
// const isValid = client.isValid(async function(valid) {
// if (!valid) {
// cb(new ValidationError(client), client);
// return;
// }
// // triggerSave();
// }, args.data, myOptions);
// client.isValid(client.constructor.validations, client.data, {});
const updatedClient = await client.updateAttributes(args.data, myOptions);
const updatedClient = await client.updateAttributes(args, myOptions);
if (tx) await tx.commit();

View File

@ -1,65 +1,3 @@
const validatorBodyModel = require('../../../../client/back/methods/client/body_model_validator');
const BODY_MODEL = [{
arg: 'name',
type: 'string'
},
{
arg: 'nif',
type: 'string'
},
{
arg: 'account',
type: 'any'
},
{
arg: 'sageTaxTypeFk',
type: 'any'
},
{
arg: 'sageWithholdingFk',
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'any'
},
{
arg: 'postCode',
type: 'any'
},
{
arg: 'street',
type: 'any'
},
{
arg: 'city',
type: 'string'
},
{
arg: 'provinceFk',
type: 'any'
},
{
arg: 'countryFk',
type: 'any'
},
{
arg: 'supplierActivityFk',
type: 'string'
},
{
arg: 'healthRegister',
type: 'string'
},
{
arg: 'isVies',
type: 'boolean'
},
{
arg: 'isTrucker',
type: 'boolean'
}];
module.exports = Self => {
Self.remoteMethod('updateFiscalData', {
description: 'Updates fiscal data of a supplier',
@ -76,11 +14,65 @@ module.exports = Self => {
http: {source: 'path'}
},
{
arg: 'data',
type: 'object',
http: {source: 'body'}
}
],
arg: 'name',
type: 'string'
},
{
arg: 'nif',
type: 'string'
},
{
arg: 'account',
type: 'any'
},
{
arg: 'sageTaxTypeFk',
type: 'any'
},
{
arg: 'sageWithholdingFk',
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'any'
},
{
arg: 'postCode',
type: 'any'
},
{
arg: 'street',
type: 'any'
},
{
arg: 'city',
type: 'string'
},
{
arg: 'provinceFk',
type: 'any'
},
{
arg: 'countryFk',
type: 'any'
},
{
arg: 'supplierActivityFk',
type: 'string'
},
{
arg: 'healthRegister',
type: 'string'
},
{
arg: 'isVies',
type: 'boolean'
},
{
arg: 'isTrucker',
type: 'boolean'
}],
returns: {
arg: 'res',
type: 'string',
@ -94,18 +86,13 @@ module.exports = Self => {
Self.updateFiscalData = async(ctx, supplierId) => {
const models = Self.app.models;
const {args} = ctx;
const args = ctx.args;
const supplier = await models.Supplier.findById(supplierId);
// Remove unwanted properties
delete args.ctx;
delete args.id;
const {isValid, tag} = validatorBodyModel(BODY_MODEL, args.data);
if (!isValid) {
const key = $t(`${tag}`);
throw new Error($t('Model is not valid', {key})
);
}
return supplier.updateAttributes(args.data);
return supplier.updateAttributes(args);
};
};