From a746bbc4c9cbe201b93f42b4358a0ae74239e51e Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Fri, 29 May 2020 09:54:26 -0400 Subject: [PATCH] fix for service provider api --- lib/models/account.js | 1 + lib/models/service-provider.js | 58 ++++++++++++++++++- lib/routes/api/service-providers.js | 87 ++++++++++++++++++++++++++++- lib/swagger/swagger.yaml | 19 ++----- test/accounts.js | 4 +- test/service-providers.js | 9 ++- 6 files changed, 156 insertions(+), 22 deletions(-) diff --git a/lib/models/account.js b/lib/models/account.js index c05350e..db52e60 100644 --- a/lib/models/account.js +++ b/lib/models/account.js @@ -10,6 +10,7 @@ function transmogrifyResults(results) { const obj = row.acc; if (row.rh && Object.keys(row.rh).length && row.rh.url !== null) { Object.assign(obj, {registration_hook: row.rh}); + delete obj.registration_hook.webhook_sid; } else obj.registration_hook = null; delete obj.registration_hook_sid; diff --git a/lib/models/service-provider.js b/lib/models/service-provider.js index 232782c..b48ab35 100644 --- a/lib/models/service-provider.js +++ b/lib/models/service-provider.js @@ -1,9 +1,65 @@ const Model = require('./model'); +const {getMysqlConnection} = require('../db'); + +const retrieveSql = `SELECT * from service_providers sp +LEFT JOIN webhooks AS rh +ON sp.registration_hook_sid = rh.webhook_sid`; + + +function transmogrifyResults(results) { + return results.map((row) => { + const obj = row.sp; + if (row.rh && Object.keys(row.rh).length && row.rh.url !== null) { + Object.assign(obj, {registration_hook: row.rh}); + delete obj.registration_hook.webhook_sid; + } + else obj.registration_hook = null; + delete obj.registration_hook_sid; + return obj; + }); +} class ServiceProvider extends Model { constructor() { super(); } + + /** + * list all service providers + */ + static retrieveAll() { + const sql = retrieveSql; + return new Promise((resolve, reject) => { + getMysqlConnection((err, conn) => { + if (err) return reject(err); + conn.query({sql, nestTables: true}, [], (err, results, fields) => { + conn.release(); + if (err) return reject(err); + const r = transmogrifyResults(results); + resolve(r); + }); + }); + }); + } + + /** + * retrieve a service provider + */ + static retrieve(sid) { + const args = [sid]; + const sql = `${retrieveSql} WHERE sp.service_provider_sid = ?`; + return new Promise((resolve, reject) => { + getMysqlConnection((err, conn) => { + if (err) return reject(err); + conn.query({sql, nestTables: true}, args, (err, results, fields) => { + conn.release(); + if (err) return reject(err); + const r = transmogrifyResults(results); + resolve(r); + }); + }); + }); + } } ServiceProvider.table = 'service_providers'; @@ -27,7 +83,7 @@ ServiceProvider.fields = [ type: 'string', }, { - name: 'registration_hook', + name: 'registration_hook_sid', type: 'string', }, { diff --git a/lib/routes/api/service-providers.js b/lib/routes/api/service-providers.js index e6f0e67..50de45e 100644 --- a/lib/routes/api/service-providers.js +++ b/lib/routes/api/service-providers.js @@ -1,6 +1,8 @@ const router = require('express').Router(); const {DbErrorUnprocessableRequest} = require('../../utils/errors'); +const Webhook = require('../../models/webhook'); const ServiceProvider = require('../../models/service-provider'); +const sysError = require('./error'); const decorate = require('./decorate'); const preconditions = { 'delete': noActiveAccounts @@ -12,6 +14,89 @@ async function noActiveAccounts(req, sid) { if (activeAccounts > 0) throw new DbErrorUnprocessableRequest('cannot delete service provider with active accounts'); } -decorate(router, ServiceProvider, ['*'], preconditions); +decorate(router, ServiceProvider, ['delete'], preconditions); + +/* add */ +router.post('/', async(req, res) => { + const logger = req.app.locals.logger; + try { + + // create webhooks if provided + const obj = Object.assign({}, req.body); + for (const prop of ['registration_hook']) { + if (obj[prop]) { + obj[`${prop}_sid`] = await Webhook.make(obj[prop]); + delete obj[prop]; + } + } + + //logger.debug(`Attempting to add account ${JSON.stringify(obj)}`); + const uuid = await ServiceProvider.make(obj); + res.status(201).json({sid: uuid}); + } catch (err) { + sysError(logger, res, err); + } +}); + +/* list */ +router.get('/', async(req, res) => { + const logger = req.app.locals.logger; + try { + const results = await ServiceProvider.retrieveAll(); + res.status(200).json(results); + } catch (err) { + sysError(logger, res, err); + } +}); + +/* retrieve */ +router.get('/:sid', async(req, res) => { + const logger = req.app.locals.logger; + try { + const results = await ServiceProvider.retrieve(req.params.sid); + if (results.length === 0) return res.status(404).end(); + return res.status(200).json(results[0]); + } + catch (err) { + sysError(logger, res, err); + } +}); + +/* update */ +router.put('/:sid', async(req, res) => { + const sid = req.params.sid; + const logger = req.app.locals.logger; + try { + // create webhooks if provided + const obj = Object.assign({}, req.body); + for (const prop of ['registration_hook']) { + if (prop in obj && Object.keys(obj[prop]).length) { + if ('webhook_sid' in obj[prop]) { + const sid = obj[prop]['webhook_sid']; + delete obj[prop]['webhook_sid']; + await Webhook.update(sid, obj[prop]); + } + else { + const sid = await Webhook.make(obj[prop]); + obj[`${prop}_sid`] = sid; + } + } + else { + obj[`${prop}_sid`] = null; + } + delete obj[prop]; + } + + const rowsAffected = await ServiceProvider.update(sid, obj); + if (rowsAffected === 0) { + return res.status(404).end(); + } + res.status(204).end(); + } catch (err) { + sysError(logger, res, err); + } +}); + + module.exports = router; diff --git a/lib/swagger/swagger.yaml b/lib/swagger/swagger.yaml index edf9a44..4bf0786 100644 --- a/lib/swagger/swagger.yaml +++ b/lib/swagger/swagger.yaml @@ -687,16 +687,8 @@ paths: description: root domain for group of accounts that share a registration hook example: example.com registration_hook: - type: string - format: url + $ref: '#/components/schemas/Webhook' description: authentication webhook for registration - example: https://mycompany.com - hook_basic_auth_user: - type: string - description: username to use for http basic auth when calling hook - hook_basic_auth_password: - type: string - description: password to use for http basic auth when calling hook ms_teams_fqdn: type: string description: SBC domain name for Microsoft Teams @@ -1603,12 +1595,9 @@ components: type: string root_domain: type: string - hook_basic_auth_user: - type: string - format: url - hook_basic_auth_password: - type: string - format: url + registration_hook: + $ref: '#/components/schemas/Webhook' + description: authentication webhook for registration ms_teams_fqdn: type: string required: diff --git a/test/accounts.js b/test/accounts.js index c294f2a..789295a 100644 --- a/test/accounts.js +++ b/test/accounts.js @@ -45,7 +45,7 @@ test('account tests', async(t) => { }); let regHook = result[0].registration_hook; t.ok(result.length === 1 && - Object.keys(regHook).length == 5, 'successfully queried all accounts'); + Object.keys(regHook).length == 4, 'successfully queried all accounts'); /* query one accounts */ result = await request.get(`/Accounts/${sid}`, { @@ -74,7 +74,7 @@ test('account tests', async(t) => { json: true, }); //console.log(`retrieved account after update: ${JSON.stringify(result)}`); - t.ok(Object.keys(result.registration_hook).length === 5, 'successfully removed a hook from account'); + t.ok(Object.keys(result.registration_hook).length === 4, 'successfully removed a hook from account'); /* assign phone number to account */ result = await request.put(`/PhoneNumbers/${phone_number_sid}`, { diff --git a/test/service-providers.js b/test/service-providers.js index b9d3bc5..339bcf7 100644 --- a/test/service-providers.js +++ b/test/service-providers.js @@ -44,7 +44,10 @@ test('service provider tests', async(t) => { json: true, body: { name: 'johndoe', - root_domain: 'example.com' + root_domain: 'example.com', + registration_hook: { + url: 'http://a.com' + } } }); t.ok(result.statusCode === 201, 'successfully created service provider with a root domain'); @@ -85,11 +88,11 @@ test('service provider tests', async(t) => { t.ok(result.length === 2 , 'successfully queried all service providers'); /* query one service providers */ - result = await request.get(`/ServiceProviders/${sid}`, { + result = await request.get(`/ServiceProviders/${sid2}`, { auth: authAdmin, json: true, }); - t.ok(result.name === 'daveh' && result.ms_teams_fqdn === 'contoso.com', 'successfully retrieved service provider by sid'); + t.ok(result.name === 'johndoe' && result.root_domain === 'example.com', 'successfully retrieved service provider by sid'); /* update service providers */