Merge branch 'dev' into 6184-saveCmr
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
6701f70a33
|
@ -0,0 +1,77 @@
|
|||
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', {
|
||||
description:
|
||||
'Find all postcodes of the model matched by postcode, town, province or country.',
|
||||
accessType: 'READ',
|
||||
returns: {
|
||||
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 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
|
||||
`);
|
||||
|
||||
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];
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
let UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/postcode/filter.js')(Self);
|
||||
Self.rewriteDbError(function(err) {
|
||||
if (err.code === 'ER_DUP_ENTRY')
|
||||
return new UserError(`This postcode already exists`);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = class SalixError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -42,14 +42,15 @@
|
|||
translate-attr="{title: 'Set as default'}">
|
||||
</vn-icon-button>
|
||||
</vn-none>
|
||||
<vn-one
|
||||
<vn-one
|
||||
style="overflow: hidden; min-width: 14em;">
|
||||
<div class="ellipsize"><b>{{::address.nickname}} - #{{::address.id}}</b></div>
|
||||
<div class="ellipsize" name="street">{{::address.street}}</div>
|
||||
<div class="ellipsize">
|
||||
<span ng-show="::address.postalCode">{{::address.postalCode}} -</span>
|
||||
<span ng-show="::address.city">{{::address.city}},</span>
|
||||
{{::address.province.name}}
|
||||
<span ng-show="::address.postalCode">{{::address.postalCode}} -</span>
|
||||
<span ng-show="::address.city">{{::address.city}},</span>
|
||||
<span ng-show="::address.province.name">{{::address.province.name}},</span>
|
||||
{{::address.province.country.country}}
|
||||
</div>
|
||||
<div class="ellipsize">
|
||||
{{::address.phone}}<span ng-if="::address.mobile">, </span>
|
||||
|
@ -72,7 +73,7 @@
|
|||
class="vn-hide-narrow vn-px-md border-solid-left"
|
||||
style="height: 6em; overflow: auto;">
|
||||
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'vn-pt-sm': $index}">
|
||||
<b>{{::observation.observationType.description}}:</b>
|
||||
<b>{{::observation.observationType.description}}:</b>
|
||||
<span>{{::observation.description}}</span>
|
||||
</vn-one>
|
||||
</vn-vertical>
|
||||
|
|
|
@ -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']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue