support fetching application with pagination (#450)

* support fetching application with pagination

* pagination for voip carrier

* wip

* wip

* wip

* support phone number pagination

* wip

* wip

* wip
This commit is contained in:
Hoan Luu Huu
2025-05-28 18:28:48 +07:00
committed by GitHub
parent 3f2a304830
commit 6700ff35be
8 changed files with 241 additions and 69 deletions

View File

@@ -36,24 +36,55 @@ class Application extends Model {
super(); super();
} }
static _criteriaBuilder(obj, args) {
let sql = '';
if (obj.account_sid) {
sql += ' AND app.account_sid = ?';
args.push(obj.account_sid);
}
if (obj.service_provider_sid) {
sql += ' AND app.account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)';
args.push(obj.service_provider_sid);
}
if (obj.name) {
sql += ' AND app.name LIKE ?';
args.push(`%${obj.name}%`);
}
return sql;
}
static countAll(obj) {
let sql = 'SELECT COUNT(*) AS count FROM applications app WHERE 1 = 1';
const args = [];
sql += Application._criteriaBuilder(obj, args);
return new Promise((resolve, reject) => {
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query({sql}, args, (err, results) => {
conn.release();
if (err) return reject(err);
resolve(results[0].count);
});
});
});
}
/** /**
* list all applications - for all service providers, for one service provider, or for one account, * list all applications - for all service providers, for one service provider, or for one account,
* or by an optional name * or by an optional name
*/ */
static retrieveAll(service_provider_sid, account_sid, name) { static retrieveAll(obj) {
const { page, page_size = 50 } = obj || {};
let sql = retrieveSql + ' WHERE 1 = 1'; let sql = retrieveSql + ' WHERE 1 = 1';
const args = []; const args = [];
if (account_sid) { sql += Application._criteriaBuilder(obj, args);
sql = `${sql} AND app.account_sid = ?`;
args.push(account_sid); if (page !== null && page !== undefined) {
} const limit = Number(page_size);
else if (service_provider_sid) { const offset = Number(page > 0 ? (page - 1) : page) * limit;
sql = `${sql} AND account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)`; sql += ' LIMIT ? OFFSET ?';
args.push(service_provider_sid); args.push(limit);
} args.push(offset);
if (name) {
sql = `${sql} AND app.name = ?`;
args.push(name);
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getMysqlConnection((err, conn) => { getMysqlConnection((err, conn) => {

View File

@@ -26,24 +26,45 @@ class PhoneNumber extends Model {
return rows; return rows;
} }
static async retrieveAllByCriteria({ static _criteriaBuilder(obj, params) {
service_provider_sid, account_sid, filter let sql = '';
}) { if (obj.service_provider_sid) {
sql += ' AND account_sid IN (SELECT account_sid FROM accounts WHERE service_provider_sid = ?)';
params.push(obj.service_provider_sid);
}
if (obj.account_sid) {
sql += ' AND account_sid = ?';
params.push(obj.account_sid);
}
if (obj.filter) {
sql += ' AND number LIKE ?';
params.push(`%${obj.filter}%`);
}
return sql;
}
static async countAll(obj) {
let sql = 'SELECT COUNT(*) AS count FROM phone_numbers WHERE 1 = 1';
const args = [];
sql += PhoneNumber._criteriaBuilder(obj, args);
const [rows] = await promisePool.query(sql, args);
return rows[0].count;
}
static async retrieveAllByCriteria(obj) {
let sql = 'SELECT * FROM phone_numbers WHERE 1=1'; let sql = 'SELECT * FROM phone_numbers WHERE 1=1';
const params = []; const params = [];
if (service_provider_sid) { const { page, page_size = 50 } = obj || {};
sql += ' AND account_sid IN (SELECT account_sid FROM accounts WHERE service_provider_sid = ?)'; sql += PhoneNumber._criteriaBuilder(obj, params);
params.push(service_provider_sid);
}
if (account_sid) {
sql += ' AND account_sid = ?';
params.push(account_sid);
}
if (filter) {
sql += ' AND number LIKE ?';
params.push(`%${filter}%`);
}
sql += ' ORDER BY number'; sql += ' ORDER BY number';
if (page !== null && page !== undefined) {
const limit = Number(page_size);
const offset = Number(page > 0 ? (page - 1) : page) * limit;
sql += ' LIMIT ? OFFSET ?';
params.push(limit);
params.push(offset);
}
const [rows] = await promisePool.query(sql, params); const [rows] = await promisePool.query(sql, params);
return rows; return rows;
} }

View File

@@ -8,6 +8,52 @@ class VoipCarrier extends Model {
constructor() { constructor() {
super(); super();
} }
static _criteriaBuilder(obj, args) {
let sql = '';
if (obj.account_sid) {
sql += ' AND vc.account_sid = ?';
args.push(obj.account_sid);
} else {
sql += ' AND vc.account_sid IS NULL';
}
if (obj.service_provider_sid) {
sql += ' AND vc.service_provider_sid = ?';
args.push(obj.service_provider_sid);
}
if (obj.name) {
sql += ' AND vc.name LIKE ?';
args.push(`%${obj.name}%`);
}
return sql;
}
static async countAll(obj) {
let sql = 'SELECT COUNT(*) AS count FROM voip_carriers vc WHERE 1 = 1';
const args = [];
sql += VoipCarrier._criteriaBuilder(obj, args);
const [rows] = await promisePool.query(sql, args);
return rows[0].count;
}
static async retrieveByCriteria(obj) {
let sql = 'SELECT * from voip_carriers vc WHERE 1 =1';
const args = [];
sql += VoipCarrier._criteriaBuilder(obj, args);
if (obj.page !== null && obj.page !== undefined) {
const limit = Number(obj.page_size || 50);
const offset = (Number(obj.page) - 1) * limit;
sql += ' LIMIT ? OFFSET ?';
args.push(limit, offset);
}
const [rows] = await promisePool.query(sql, args);
if (rows) {
rows.map((r) => r.register_status = JSON.parse(r.register_status || '{}'));
}
return rows;
}
static async retrieveAll(account_sid) { static async retrieveAll(account_sid) {
if (!account_sid) return super.retrieveAll(); if (!account_sid) return super.retrieveAll();
const [rows] = await promisePool.query(retrieveSql, account_sid); const [rows] = await promisePool.query(retrieveSql, account_sid);

View File

@@ -23,7 +23,8 @@ const {
} = require('./utils'); } = require('./utils');
const short = require('short-uuid'); const short = require('short-uuid');
const VoipCarrier = require('../../models/voip-carrier'); const VoipCarrier = require('../../models/voip-carrier');
const { encrypt, obscureBucketCredentialsSensitiveData, isObscureKey } = require('../../utils/encrypt-decrypt'); const { encrypt, obscureBucketCredentialsSensitiveData,
isObscureKey, decrypt } = require('../../utils/encrypt-decrypt');
const { testS3Storage, testGoogleStorage, testAzureStorage } = require('../../utils/storage-utils'); const { testS3Storage, testGoogleStorage, testAzureStorage } = require('../../utils/storage-utils');
const translator = short(); const translator = short();
@@ -92,8 +93,34 @@ router.get('/:sid/Applications', async(req, res) => {
try { try {
const account_sid = parseAccountSid(req); const account_sid = parseAccountSid(req);
await validateRequest(req, account_sid); await validateRequest(req, account_sid);
const results = await Application.retrieveAll(null, account_sid); const {page, page_size, name} = req.query || {};
res.status(200).json(results); const isPaginationRequest = page !== null && page !== undefined;
let results = [];
let total = 0;
if (isPaginationRequest) {
total = await Application.countAll({account_sid, name});
results = await Application.retrieveAll({
account_sid, name, page, page_size
});
} else {
results = await Application.retrieveAll({account_sid});
}
const ret = results.map((a) => {
if (a.env_vars) {
a.env_vars = JSON.parse(decrypt(a.env_vars));
return a;
} else {
return a;
}
});
const body = isPaginationRequest ? {
total,
page: Number(page),
page_size: Number(page_size),
data: ret
} : ret;
res.status(200).json(body);
} catch (err) { } catch (err) {
sysError(logger, res, err); sysError(logger, res, err);
} }

View File

@@ -173,11 +173,19 @@ router.post('/', async(req, res) => {
/* list */ /* list */
router.get('/', async(req, res) => { router.get('/', async(req, res) => {
const logger = req.app.locals.logger; const logger = req.app.locals.logger;
const {page, page_size, name} = req.query || {};
const isPaginationRequest = page !== null && page !== undefined;
try { try {
const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null; const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
const account_sid = req.user.hasAccountAuth ? req.user.account_sid : null; const account_sid = req.user.hasAccountAuth ? req.user.account_sid : null;
const name = req.query.name; let results = [];
const results = await Application.retrieveAll(service_provider_sid, account_sid, name); let total = 0;
if (isPaginationRequest) {
total = await Application.countAll({service_provider_sid, account_sid, name});
}
results = await Application.retrieveAll({
service_provider_sid, account_sid, name, page, page_size
});
const ret = results.map((a) => { const ret = results.map((a) => {
if (a.env_vars) { if (a.env_vars) {
a.env_vars = JSON.parse(decrypt(a.env_vars)); a.env_vars = JSON.parse(decrypt(a.env_vars));
@@ -186,7 +194,13 @@ router.get('/', async(req, res) => {
return a; return a;
} }
}); });
res.status(200).json(ret); const body = isPaginationRequest ? {
total,
page: Number(page),
page_size: Number(page_size),
data: ret
} : ret;
res.status(200).json(body);
} catch (err) { } catch (err) {
sysError(logger, res, err); sysError(logger, res, err);
} }

View File

@@ -97,28 +97,30 @@ decorate(router, PhoneNumber, ['add', 'update', 'delete'], preconditions);
/* list */ /* list */
router.get('/', async(req, res) => { router.get('/', async(req, res) => {
const logger = req.app.locals.logger; const logger = req.app.locals.logger;
const {account_sid, filter} = req.query; const {account_sid: query_account_sid, filter, page, page_size} = req.query;
const isPaginationRequest = page !== null && page !== undefined;
let service_provider_sid = null, account_sid = query_account_sid;
if (req.user.hasAccountAuth) {
account_sid = req.user.account_sid;
} else if (req.user.hasServiceProviderAuth) {
service_provider_sid = req.user.service_provider_sid;
}
try { try {
let results = []; let total = 0;
if (req.user.hasAccountAuth) { if (isPaginationRequest) {
results = await PhoneNumber.retrieveAllByCriteria({ total = await PhoneNumber.countAll({service_provider_sid, account_sid, filter});
account_sid: req.user.account_sid,
filter
});
} else if (req.user.hasServiceProviderAuth) {
results = await PhoneNumber.retrieveAllByCriteria({
service_provider_sid: req.user.service_provider_sid,
account_sid,
filter
});
} else if (req.user.hasAdminAuth) {
results = await PhoneNumber.retrieveAllByCriteria({
account_sid,
filter
});
} }
const results = await PhoneNumber.retrieveAllByCriteria({
service_provider_sid, account_sid, filter, page, page_size
});
const body = isPaginationRequest ? {
total,
page: Number(page),
page_size: Number(page_size),
data: results,
} : results;
res.status(200).json(results); res.status(200).json(body);
} catch (err) { } catch (err) {
sysError(logger, res, err); sysError(logger, res, err);
} }

View File

@@ -149,13 +149,30 @@ router.get('/:sid/VoipCarriers', async(req, res) => {
try { try {
await validateRetrieve(req); await validateRetrieve(req);
const service_provider_sid = parseServiceProviderSid(req); const service_provider_sid = parseServiceProviderSid(req);
const carriers = await VoipCarrier.retrieveAllForSP(service_provider_sid); const {account_sid: query_account_sid, name, page, page_size} = req.query || {};
const isPaginationRequest = page !== null && page !== undefined;
if (req.user.hasScope('account')) { const account_sid = req.user.hasAccountAuth ? req.user.account_sid : query_account_sid || null;
return res.status(200).json(carriers.filter((c) => c.account_sid === req.user.account_sid || !c.account_sid)); let carriers = [];
let total = 0;
if (isPaginationRequest) {
total = await VoipCarrier.countAll({service_provider_sid, account_sid, name});
} }
carriers = await VoipCarrier.retrieveByCriteria({
service_provider_sid,
account_sid,
name,
page,
page_size,
});
res.status(200).json(carriers); const body = isPaginationRequest ? {
total,
page: Number(page),
page_size: Number(page_size),
data: carriers,
} : carriers;
res.status(200).json(body);
} catch (err) { } catch (err) {
sysError(logger, res, err); sysError(logger, res, err);
} }

View File

@@ -73,22 +73,36 @@ decorate(router, VoipCarrier, ['add', 'update', 'delete'], preconditions);
/* list */ /* list */
router.get('/', async(req, res) => { router.get('/', async(req, res) => {
const logger = req.app.locals.logger; const logger = req.app.locals.logger;
const {lookupAccountBySid} = req.app.locals; const {account_sid: query_account_sid, name, page, page_size} = req.query || {};
const isPaginationRequest = page !== null && page !== undefined;
let service_provider_sid = null, account_sid = query_account_sid;
if (req.user.hasAccountAuth) {
account_sid = req.user.account_sid;
} else if (req.user.hasServiceProviderAuth) {
service_provider_sid = req.user.service_provider_sid;
}
try { try {
let results = []; let total = 0;
if (req.user.hasAdminAuth) { if (isPaginationRequest) {
results = await VoipCarrier.retrieveAll(req.user.hasAccountAuth ? req.user.account_sid : null); total = await VoipCarrier.countAll({service_provider_sid, account_sid, name});
} else {
const account = req.user.service_provider_sid ? req.user : await lookupAccountBySid(req.user.account_sid);
results = await VoipCarrier.retrieveAllForSP(account.service_provider_sid);
} }
if (req.user.hasScope('account')) { const carriers = await VoipCarrier.retrieveByCriteria({
return res.status(200).json(results.filter((c) => c.account_sid === req.user.account_sid || !c.account_sid)); service_provider_sid,
} account_sid,
name,
page,
page_size,
});
res.status(200).json(results); const body = isPaginationRequest ? {
total,
page: Number(page),
page_size: Number(page_size),
data: carriers,
} : carriers;
res.status(200).json(body);
} catch (err) { } catch (err) {
sysError(logger, res, err); sysError(logger, res, err);
} }