From cc0c8443bf0c4ff938a6f9a14ed44a3ade04812c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 3 Jan 2024 08:06:20 +0100 Subject: [PATCH 01/12] refs #6613 perf: translate message error --- loopback/server/middleware/error-handler.js | 5 +++-- loopback/util/forbiddenError.js | 5 +++-- loopback/util/salixError.js | 5 +++++ loopback/util/user-error.js | 5 +++-- 4 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 loopback/util/salixError.js diff --git a/loopback/server/middleware/error-handler.js b/loopback/server/middleware/error-handler.js index 725826ae7..cc7b81618 100644 --- a/loopback/server/middleware/error-handler.js +++ b/loopback/server/middleware/error-handler.js @@ -1,10 +1,11 @@ +const SalixError = require('../../util/salixError'); const UserError = require('../../util/user-error'); const logToConsole = require('strong-error-handler/lib/logger'); module.exports = function() { return function(err, req, res, next) { // Thrown user errors - if (err instanceof UserError) { + if (err instanceof SalixError) { err.message = req.__(err.message, ...err.translateArgs); return next(err); } @@ -13,7 +14,7 @@ module.exports = function() { if (err.statusCode == 422) { try { let code; - let messages = err.details.messages; + let {messages} = err.details; for (code in messages) break; err.message = req.__(messages[code][0]); return next(err); diff --git a/loopback/util/forbiddenError.js b/loopback/util/forbiddenError.js index 998cb4593..8f6561059 100644 --- a/loopback/util/forbiddenError.js +++ b/loopback/util/forbiddenError.js @@ -1,7 +1,8 @@ -module.exports = class ForbiddenError extends Error { +const SalixError = require('./salixError'); +module.exports = class ForbiddenError extends SalixError { constructor(message, code, ...translateArgs) { super(message); - this.name = 'ForbiddenError'; + this.name = ForbiddenError.name; this.statusCode = 403; this.code = code; this.translateArgs = translateArgs; diff --git a/loopback/util/salixError.js b/loopback/util/salixError.js new file mode 100644 index 000000000..427b871ab --- /dev/null +++ b/loopback/util/salixError.js @@ -0,0 +1,5 @@ +module.exports = class SalixError extends Error { + constructor(message) { + super(message); + } +}; diff --git a/loopback/util/user-error.js b/loopback/util/user-error.js index c2d01e080..feee208b3 100644 --- a/loopback/util/user-error.js +++ b/loopback/util/user-error.js @@ -4,10 +4,11 @@ * the final user, so they cannot contain sensitive data and must * be understandable by people who do not have a technical profile. */ -module.exports = class UserError extends Error { +const SalixError = require('./salixError'); +module.exports = class UserError extends SalixError { constructor(message, code, ...translateArgs) { super(message); - this.name = 'UserError'; + this.name = UserError.name; this.statusCode = 400; this.code = code; this.translateArgs = translateArgs; From 107dded4114a6d027a172d5d4ea216df961d13e5 Mon Sep 17 00:00:00 2001 From: carlossa Date: Mon, 15 Jan 2024 09:03:56 +0100 Subject: [PATCH 02/12] refs #6296 countryAddress --- modules/client/front/address/index/index.html | 10 +++++----- modules/client/front/address/index/index.js | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/client/front/address/index/index.html b/modules/client/front/address/index/index.html index 8fdfce2bb..989ce13a9 100644 --- a/modules/client/front/address/index/index.html +++ b/modules/client/front/address/index/index.html @@ -42,14 +42,14 @@ translate-attr="{title: 'Set as default'}"> -
{{::address.nickname}} - #{{::address.id}}
{{::address.street}}
- {{::address.postalCode}} - - {{::address.city}}, - {{::address.province.name}} + {{::address.city}}, + {{::address.province.name}}, + {{::address.province.country.country}}
{{::address.phone}}, @@ -72,7 +72,7 @@ class="vn-hide-narrow vn-px-md border-solid-left" style="height: 6em; overflow: auto;"> - {{::observation.observationType.description}}: + {{::observation.observationType.description}}: {{::observation.description}} diff --git a/modules/client/front/address/index/index.js b/modules/client/front/address/index/index.js index 608bbbc20..4bad9d4c8 100644 --- a/modules/client/front/address/index/index.js +++ b/modules/client/front/address/index/index.js @@ -33,7 +33,13 @@ class Controller extends Section { }, { relation: 'province', scope: { - fields: ['id', 'name'] + fields: ['id', 'name', 'countryFk'], + include: { + relation: 'country', + scope: { + fields: ['id', 'country'] + } + } } } ] From 30b505aa8fa4f8b0640f3a233acb0d51fe5d4c32 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 08:34:15 +0100 Subject: [PATCH 03/12] refs #6694 feat: add filter for postcodes --- back/models/postcode.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/back/models/postcode.js b/back/models/postcode.js index b08fdaa40..4c1bf7fd7 100644 --- a/back/models/postcode.js +++ b/back/models/postcode.js @@ -1,6 +1,38 @@ let UserError = require('vn-loopback/util/user-error'); module.exports = Self => { + Self.remoteMethod('filter', { + description: + 'Find all postcodes of the model matched by postcode, town, province or country.', + accessType: 'READ', + returns: { + type: ['object'], + root: true, + }, + http: { + path: `/filter`, + verb: 'GET', + }, + }); + Self.filter = async(ctx, filter, options) => { + const {Postcode} = Self.app.models; + let {value} = ctx.where; + let limit = ctx.where?.limit ?? 30; + return await Postcode.rawSql(` + SELECT pc.code, t.name as town, p.name as province , c.country + FROM postCode pc + JOIN town t on t.id = pc.townFk + JOIN province p on p.id = t.provinceFk + JOIN country c on c.id = p.countryFk + WHERE + pc.code like '%${value}%' + or t.name like '%${value}%' + or p.name like '%${value}%' + or c.country like '%${value}%' + LIMIT ${limit} + `); + }; + Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') return new UserError(`This postcode already exists`); From d47429b4a7db95f8d549ac858652288f218dca71 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 10:01:38 +0100 Subject: [PATCH 04/12] refs #6667 sql --- modules/worker/back/methods/worker/search.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 7fe9e0666..2cef3543b 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -41,13 +41,15 @@ module.exports = Self => { } const stmt = new ParameterizedSQL(` - SELECT * - FROM( - SELECT DISTINCT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk - FROM worker w - JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ) w`); + SELECT * + FROM( + SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + FROM worker w + JOIN account.user u ON u.id = w.id + LEFT JOIN business b ON b.workerFk = w.id + ORDER BY b.id DESC + ) w + GROUP BY w.id`); stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); From d1a3c67d2ea296971c4f7390be827cfe554ff2f9 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 14:07:04 +0100 Subject: [PATCH 05/12] refs #6667 fix limit --- modules/worker/back/methods/worker/search.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 2cef3543b..e423f660d 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -42,14 +42,15 @@ module.exports = Self => { const stmt = new ParameterizedSQL(` SELECT * - FROM( - SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk - FROM worker w - JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ORDER BY b.id DESC - ) w - GROUP BY w.id`); + FROM( + SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + FROM worker w + JOIN account.user u ON u.id = w.id + LEFT JOIN business b ON b.workerFk = w.id + ORDER BY b.id DESC + LIMIT 10000000000000000000 + ) w + GROUP BY w.id`); stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); From 75259984771994568b67a906ef36a2ad320e6656 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 14:13:09 +0100 Subject: [PATCH 06/12] refs #6296 postal code --- modules/client/front/address/index/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/client/front/address/index/index.html b/modules/client/front/address/index/index.html index 989ce13a9..ef3da4051 100644 --- a/modules/client/front/address/index/index.html +++ b/modules/client/front/address/index/index.html @@ -47,6 +47,7 @@
{{::address.nickname}} - #{{::address.id}}
{{::address.street}}
+ {{::address.postalCode}} - {{::address.city}}, {{::address.province.name}}, {{::address.province.country.country}} From 71f387f626add3952755e0c83b6ead88bd435941 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:15:21 +0100 Subject: [PATCH 07/12] refs #6694 feat: move filter remote method to own file --- back/methods/postcode/filter.js | 34 +++++++++++++++++++++++++++++++++ back/models/postcode.js | 33 +------------------------------- 2 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 back/methods/postcode/filter.js diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js new file mode 100644 index 000000000..53f25b7d4 --- /dev/null +++ b/back/methods/postcode/filter.js @@ -0,0 +1,34 @@ + +module.exports = Self => { + Self.remoteMethod('filter', { + description: + 'Find all postcodes of the model matched by postcode, town, province or country.', + accessType: 'READ', + returns: { + type: ['object'], + root: true, + }, + http: { + path: `/filter`, + verb: 'GET', + }, + }); + Self.filter = async(ctx, filter, options) => { + const {Postcode} = Self.app.models; + let {value} = ctx.where; + let limit = ctx.where?.limit ?? 30; + return await Postcode.rawSql(` + SELECT pc.code, t.name as town, p.name as province , c.country + FROM postCode pc + JOIN town t on t.id = pc.townFk + JOIN province p on p.id = t.provinceFk + JOIN country c on c.id = p.countryFk + WHERE + pc.code like '%${value}%' + or t.name like '%${value}%' + or p.name like '%${value}%' + or c.country like '%${value}%' + LIMIT ${limit} + `); + }; +}; diff --git a/back/models/postcode.js b/back/models/postcode.js index 4c1bf7fd7..63fd0657f 100644 --- a/back/models/postcode.js +++ b/back/models/postcode.js @@ -1,38 +1,7 @@ let UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.remoteMethod('filter', { - description: - 'Find all postcodes of the model matched by postcode, town, province or country.', - accessType: 'READ', - returns: { - type: ['object'], - root: true, - }, - http: { - path: `/filter`, - verb: 'GET', - }, - }); - Self.filter = async(ctx, filter, options) => { - const {Postcode} = Self.app.models; - let {value} = ctx.where; - let limit = ctx.where?.limit ?? 30; - return await Postcode.rawSql(` - SELECT pc.code, t.name as town, p.name as province , c.country - FROM postCode pc - JOIN town t on t.id = pc.townFk - JOIN province p on p.id = t.provinceFk - JOIN country c on c.id = p.countryFk - WHERE - pc.code like '%${value}%' - or t.name like '%${value}%' - or p.name like '%${value}%' - or c.country like '%${value}%' - LIMIT ${limit} - `); - }; - + require('../methods/postcode/filter.js')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') return new UserError(`This postcode already exists`); From 92a3e841ed3fe6ec34ea6ff44339808bd6a71e30 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:16:09 +0100 Subject: [PATCH 08/12] refs #6694 perf: change let to const --- back/methods/postcode/filter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index 53f25b7d4..0c97c61c5 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -15,8 +15,8 @@ module.exports = Self => { }); Self.filter = async(ctx, filter, options) => { const {Postcode} = Self.app.models; - let {value} = ctx.where; - let limit = ctx.where?.limit ?? 30; + const {value} = ctx.where; + const limit = ctx.where?.limit ?? 30; return await Postcode.rawSql(` SELECT pc.code, t.name as town, p.name as province , c.country FROM postCode pc From ccdef0458a566849a5381e6357a39843cfe508e3 Mon Sep 17 00:00:00 2001 From: carlossa Date: Wed, 17 Jan 2024 14:39:11 +0100 Subject: [PATCH 09/12] refs #6667 fix groupBy --- modules/worker/back/methods/worker/search.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index e423f660d..59348a9ce 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -49,10 +49,14 @@ module.exports = Self => { LEFT JOIN business b ON b.workerFk = w.id ORDER BY b.id DESC LIMIT 10000000000000000000 - ) w - GROUP BY w.id`); + ) w`); + + stmt.merge(conn.makeSuffix(filter.fields)); + stmt.merge(conn.makeWhere(filter.where)); + stmt.merge(conn.makeGroupBy('w.id')); + stmt.merge(conn.makeOrderBy(filter.order)); + stmt.merge(conn.makeLimit(filter.limit)); - stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); }; From 38c93e8c769ad79387b6a0e30bb92e036a7ffb7a Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:49:35 +0100 Subject: [PATCH 10/12] refs #6694 test: add test --- back/methods/postcode/filter.js | 67 +++++++++++--- back/methods/postcode/specs/filter.spec.js | 103 +++++++++++++++++++++ 2 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 back/methods/postcode/specs/filter.spec.js diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index 0c97c61c5..dcf11f80b 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -1,3 +1,6 @@ +const {ParameterizedSQL} = require('loopback-connector'); +const {buildFilter, mergeFilters} = require('vn-loopback/util/filter'); +// const {models} = require('vn-loopback/server/server'); module.exports = Self => { Self.remoteMethod('filter', { @@ -8,27 +11,67 @@ module.exports = Self => { type: ['object'], root: true, }, + accepts: [ + { + arg: 'filter', + type: 'object', + description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', + http: {source: 'query'} + }, + { + arg: 'search', + type: 'string', + description: 'Value to filter', + http: {source: 'query'} + }, + ], http: { path: `/filter`, verb: 'GET', }, }); Self.filter = async(ctx, filter, options) => { - const {Postcode} = Self.app.models; - const {value} = ctx.where; - const limit = ctx.where?.limit ?? 30; - return await Postcode.rawSql(` - SELECT pc.code, t.name as town, p.name as province , c.country - FROM postCode pc + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const conn = Self.dataSource.connector; + const where = buildFilter(ctx.args, (param, value) => { + switch (param) { + case 'search': + return {or: [ + {'pc.code': {like: `%${value}%`}}, + {'t.name': {like: `%${value}%`}}, + {'p.name': {like: `%${value}%`}}, + {'c.country': {like: `%${value}%`}} + ] + }; + } + }) ?? {}; + + filter = mergeFilters(ctx.args.filter, {where}); + + const stmts = []; + let stmt; + stmt = new ParameterizedSQL(` + SELECT + pc.code, + t.name as town, + p.name as province, + c.country + FROM + postCode pc JOIN town t on t.id = pc.townFk JOIN province p on p.id = t.provinceFk JOIN country c on c.id = p.countryFk - WHERE - pc.code like '%${value}%' - or t.name like '%${value}%' - or p.name like '%${value}%' - or c.country like '%${value}%' - LIMIT ${limit} `); + + stmt.merge(conn.makeSuffix(filter)); + const itemsIndex = stmts.push(stmt) - 1; + + const sql = ParameterizedSQL.join(stmts, ';'); + const result = await conn.executeStmt(sql, myOptions); + return itemsIndex === 0 ? result : result[itemsIndex]; }; }; diff --git a/back/methods/postcode/specs/filter.spec.js b/back/methods/postcode/specs/filter.spec.js new file mode 100644 index 000000000..c393b629a --- /dev/null +++ b/back/methods/postcode/specs/filter.spec.js @@ -0,0 +1,103 @@ +const {models} = require('vn-loopback/server/server'); + +describe('Postcode filter()', () => { + it('should retrieve with no filter', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toBeGreaterThan(0); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as postcode', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 46, + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(4); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as city', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'Alz', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(1); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as province', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'one', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(4); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should retrieve with filter as country', async() => { + const tx = await models.Postcode.beginTransaction({}); + const options = {transaction: tx}; + + try { + const ctx = { + args: { + search: 'Ec', + }, + }; + const results = await models.Postcode.filter(ctx, options); + + expect(results.length).toEqual(1); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); From 145e72022a76e4686f711c3f7b4915218e849707 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 14:58:55 +0100 Subject: [PATCH 11/12] refs #6694 perf: handle minor errors --- back/methods/postcode/filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/methods/postcode/filter.js b/back/methods/postcode/filter.js index dcf11f80b..9986a16c9 100644 --- a/back/methods/postcode/filter.js +++ b/back/methods/postcode/filter.js @@ -50,7 +50,7 @@ module.exports = Self => { } }) ?? {}; - filter = mergeFilters(ctx.args.filter, {where}); + filter = mergeFilters(ctx.args?.filter ?? {}, {where}); const stmts = []; let stmt; From 0f104e54103580f8b879bbfef21227496533c397 Mon Sep 17 00:00:00 2001 From: carlossa Date: Fri, 19 Jan 2024 10:11:33 +0100 Subject: [PATCH 12/12] refs #6667 view workerDepartment --- modules/worker/back/methods/worker/search.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/worker/back/methods/worker/search.js b/modules/worker/back/methods/worker/search.js index 59348a9ce..07bdfe240 100644 --- a/modules/worker/back/methods/worker/search.js +++ b/modules/worker/back/methods/worker/search.js @@ -43,19 +43,13 @@ module.exports = Self => { const stmt = new ParameterizedSQL(` SELECT * FROM( - SELECT w.id, w.code, u.name, u.nickname, u.active, b.departmentFk + SELECT w.id, w.code, u.name, u.nickname, u.active, wd.departmentFk FROM worker w JOIN account.user u ON u.id = w.id - LEFT JOIN business b ON b.workerFk = w.id - ORDER BY b.id DESC - LIMIT 10000000000000000000 + LEFT JOIN workerDepartment wd ON wd.workerFk = w.id ) w`); - stmt.merge(conn.makeSuffix(filter.fields)); - stmt.merge(conn.makeWhere(filter.where)); - stmt.merge(conn.makeGroupBy('w.id')); - stmt.merge(conn.makeOrderBy(filter.order)); - stmt.merge(conn.makeLimit(filter.limit)); + stmt.merge(conn.makeSuffix(filter)); return conn.executeStmt(stmt); };