Commit f43dbc16a329b2ffedd2103dc9ddbcb1343ef668
1 parent
4051d4be68
Exists in
master
New modem selector
Showing 7 changed files with 167 additions and 104 deletions Side-by-side Diff
lib/apiserver/index.js
... | ... | @@ -16,6 +16,7 @@ const logger = require('komodo-sdk/logger'); |
16 | 16 | const transport = require('../transport'); |
17 | 17 | const partnerLastSeen = require('../partner-last-seen'); |
18 | 18 | const history = require('../history'); |
19 | +const modems = require('../modems2'); | |
19 | 20 | |
20 | 21 | const app = express(); |
21 | 22 | messagingService.setTransport(transport); |
... | ... | @@ -52,6 +53,15 @@ function onIncomingSms(req, res) { |
52 | 53 | message: req.query.msg, |
53 | 54 | }); |
54 | 55 | |
56 | + modems.set({ | |
57 | + name: req.query.modem, | |
58 | + imsi: req.query.modem_imsi, | |
59 | + msisdn: req.query.modem_msisdn, | |
60 | + reportIp: req.query.report_ip || req.ip, | |
61 | + reportPort: req.query.report_port, | |
62 | + reportApikey: req.query.report_apikey, | |
63 | + }); | |
64 | + | |
55 | 65 | logger.info('APISERVER: Incoming SMS', { modem: req.query.modem, from: req.query.number, from_with_suffix: numberWithSuffix, msg: req.query.msg }); |
56 | 66 | messagingService.onIncomingMessage({ |
57 | 67 | me: req.query.modem, |
lib/modemSelect.js
... | ... | @@ -0,0 +1,34 @@ |
1 | +"use strict"; | |
2 | + | |
3 | +function getModemConfig(modemName, modemsConfig) { | |
4 | + if (!modemsConfig) return; | |
5 | + if (!modemName) return; | |
6 | + if (typeof modemName === 'string' && !modemName.trim()) return; | |
7 | + | |
8 | + return modemsConfig[modemName.trim()]; | |
9 | +} | |
10 | + | |
11 | +function getModemUrl(modemName, modemsConfig) { | |
12 | + const modemConfig = getModemConfig(modemName, modemsConfig); | |
13 | + return modemConfig ? modemConfig.url : null; | |
14 | +} | |
15 | + | |
16 | +function getModemApikey(modemName, modemsConfig) { | |
17 | + const modemConfig = getModemConfig(modemName, modemsConfig); | |
18 | + return modemConfig ? modemConfig.apikey : null; | |
19 | +} | |
20 | + | |
21 | +function removeSuffixFromNumber(number, config) { | |
22 | + if (!config) { | |
23 | + config = {}; | |
24 | + } | |
25 | + | |
26 | + const suffix = config && config.number_suffix ? config.number_suffix : '@.*'; | |
27 | + const re = new RegExp(suffix + '$'); | |
28 | + return number.replace(re, ''); | |
29 | +} | |
30 | + | |
31 | +exports.getModemConfig = getModemConfig; | |
32 | +exports.getModemUrl = getModemUrl; | |
33 | +exports.getModemApikey = getModemApikey; | |
34 | +exports.removeSuffixFromNumber = removeSuffixFromNumber; | |
0 | 35 | \ No newline at end of file |
lib/modems.js
... | ... | @@ -1,34 +0,0 @@ |
1 | -"use strict"; | |
2 | - | |
3 | -function getModemConfig(modemName, modemsConfig) { | |
4 | - if (!modemsConfig) return; | |
5 | - if (!modemName) return; | |
6 | - if (typeof modemName === 'string' && !modemName.trim()) return; | |
7 | - | |
8 | - return modemsConfig[modemName.trim()]; | |
9 | -} | |
10 | - | |
11 | -function getModemUrl(modemName, modemsConfig) { | |
12 | - const modemConfig = getModemConfig(modemName, modemsConfig); | |
13 | - return modemConfig ? modemConfig.url : null; | |
14 | -} | |
15 | - | |
16 | -function getModemApikey(modemName, modemsConfig) { | |
17 | - const modemConfig = getModemConfig(modemName, modemsConfig); | |
18 | - return modemConfig ? modemConfig.apikey : null; | |
19 | -} | |
20 | - | |
21 | -function removeSuffixFromNumber(number, config) { | |
22 | - if (!config) { | |
23 | - config = {}; | |
24 | - } | |
25 | - | |
26 | - const suffix = config && config.number_suffix ? config.number_suffix : '@.*'; | |
27 | - const re = new RegExp(suffix + '$'); | |
28 | - return number.replace(re, ''); | |
29 | -} | |
30 | - | |
31 | -exports.getModemConfig = getModemConfig; | |
32 | -exports.getModemUrl = getModemUrl; | |
33 | -exports.getModemApikey = getModemApikey; | |
34 | -exports.removeSuffixFromNumber = removeSuffixFromNumber; | |
35 | 0 | \ No newline at end of file |
lib/modems2.js
... | ... | @@ -0,0 +1,91 @@ |
1 | +'use strict'; | |
2 | + | |
3 | +const modemList = { | |
4 | + by_name: {}, | |
5 | + by_imsi: {}, | |
6 | + by_msisdn: {}, | |
7 | +}; | |
8 | + | |
9 | +/** | |
10 | + * Objek data sebuah modem. | |
11 | + * | |
12 | + * @typedef {Object} ModemData | |
13 | + * @property {string} name - nama modem | |
14 | + * @property {string} imsi - IMSI modem | |
15 | + * @property {string} msisdn - MSISDN modem | |
16 | + * @property {string} reportIp - IP modem | |
17 | + * @property {number} reportPort - TCP port modem | |
18 | + * @property {string} reportApikey - APIKEY modem | |
19 | + */ | |
20 | + | |
21 | +/** | |
22 | + * Update data sebuah modem berdasarkan nama modem. | |
23 | + * | |
24 | + * @param {ModemData} val - objek data modem | |
25 | + */ | |
26 | +function touchByName(val) { | |
27 | + if (!val || !val.name) return; | |
28 | + if (typeof val.name !== 'string') return; | |
29 | + if (!val.name.trim()) return; | |
30 | + | |
31 | + modemList.by_name[val.name] = val; | |
32 | +} | |
33 | +/** | |
34 | + * Update data sebuah modem berdasarkan IMSI. | |
35 | + * | |
36 | + * @param {ModemData} val - objek data modem | |
37 | + */ | |
38 | +function touchByIMSI(val) { | |
39 | + if (!val || !val.imsi) return; | |
40 | + if (typeof val.imsi !== 'string') return; | |
41 | + if (!val.imsi.trim()) return; | |
42 | + | |
43 | + modemList.by_imsi[val.imsi] = val; | |
44 | +} | |
45 | + | |
46 | +/** | |
47 | + * Update data sebuah modem berdasarkan MSISDN. | |
48 | + * | |
49 | + * @param {ModemData} val - objek data modem | |
50 | + */ | |
51 | +function touchByMSISDN(val) { | |
52 | + if (!val || !val.msisdn) return; | |
53 | + if (typeof val.msisdn !== 'string') return; | |
54 | + if (!val.msisdn.trim()) return; | |
55 | + | |
56 | + modemList.by_msisdn[val.msisdn] = val; | |
57 | +} | |
58 | + | |
59 | +/** | |
60 | + * Update data sebuah modem. | |
61 | + * | |
62 | + * @param {ModemData} val - objek data modem | |
63 | + * @see ModemData | |
64 | + */ | |
65 | +function touch(val) { | |
66 | + if (!val) return; | |
67 | + | |
68 | + if (!val.reportIp) { | |
69 | + val.reportIp = '127.0.0.1'; | |
70 | + } | |
71 | + | |
72 | + touchByName(val); | |
73 | + touchByIMSI(val); | |
74 | + touchByMSISDN(val); | |
75 | +} | |
76 | +/** | |
77 | + * Ambil data sebuah modem. | |
78 | + * | |
79 | + * @param {string} selector - selector pencarian, valid jika bernilai salah satu dari: name, imsi, msisdn | |
80 | + * @param {string} keyword - kata kunci modem yang ingin diambil | |
81 | + * @returns {ModemData} data modem terkait | |
82 | + */ | |
83 | +function get(selector, keyword) { | |
84 | + if (!selector || !keyword) return null; | |
85 | + | |
86 | + return modemList[`by_${selector}`] ? modemList[`by_${selector}`][keyword] : null; | |
87 | +} | |
88 | + | |
89 | +exports.touch = touch; | |
90 | +exports.set = touch; | |
91 | +exports.get = get; | |
0 | 92 | \ No newline at end of file |
lib/partner-last-seen.js
lib/transport.js
1 | 1 | "use strict"; |
2 | 2 | |
3 | +const url = require('url'); | |
3 | 4 | const request = require('request'); |
4 | 5 | const uuidv4 = require('uuid/v4'); |
5 | 6 | const moment = require('moment'); |
... | ... | @@ -7,60 +8,13 @@ const moment = require('moment'); |
7 | 8 | const config = require('komodo-sdk/config'); |
8 | 9 | const logger = require('komodo-sdk/logger'); |
9 | 10 | |
10 | -const modems = require('./modems'); | |
11 | +const modemSelect = require('./modemSelect'); | |
12 | +const modems = require('./modems2'); | |
11 | 13 | const partnerLastSeen = require('./partner-last-seen'); |
12 | 14 | const history = require('./history'); |
13 | 15 | |
14 | -async function _getApproriateHandlerByLastSeen(partnerNumber) { | |
15 | - logger.verbose('Looking for last seen on for partner number ' + partnerNumber); | |
16 | - const lastSeenFrom = await partnerLastSeen.get(partnerNumber); | |
17 | - return lastSeenFrom; | |
18 | -} | |
19 | - | |
20 | -function _getApproriateHandlerByForced() { | |
21 | - if (!config.sending_handler || !config.sending_handler.length) return; | |
22 | - | |
23 | - const sendingHandlerCount = config.sending_handler.length; | |
24 | - const idx = Math.floor(Math.random() * sendingHandlerCount); | |
25 | - return config.sending_handler[idx]; | |
26 | -} | |
27 | - | |
28 | -async function _getApproriateHandler(partnerNumber, origin) { | |
29 | - let handlerToUse; | |
30 | - | |
31 | - if (config.handler_chooser_algorithm === 'FORCED') { | |
32 | - handlerToUse = _getApproriateHandlerByForced(); | |
33 | - logger.verbose('Config file mentioned to using FORCED handler chooser algorithm', { handler_to_use: handlerToUse}); | |
34 | - } | |
35 | - else { | |
36 | - handlerToUse = await _getApproriateHandlerByLastSeen(partnerNumber); | |
37 | - logger.verbose('Config file mentioned to using LAST-SEEN handler chooser algorithm', { handler_to_use: handlerToUse}); | |
38 | - } | |
39 | - | |
40 | - if (!modems.getModemConfig(handlerToUse, config.modems)) { | |
41 | - const handlerWithSameOrigin = modems.getModemConfig(origin, config.modems); | |
42 | - if (handlerWithSameOrigin) { | |
43 | - logger.verbose('Invalid approriate handler, using handler from the same ORIGIN request by CORE to send sms') | |
44 | - handlerToUse = origin; | |
45 | - } | |
46 | - else { | |
47 | - logger.verbose('Invalid approriate handler, using default handler to send sms') | |
48 | - handlerToUse = config.default_modem; | |
49 | - } | |
50 | - } | |
51 | - | |
52 | - return handlerToUse; | |
53 | -} | |
54 | - | |
55 | 16 | function _send(destinationNumber, msg, handlerName) { |
56 | 17 | |
57 | - /* | |
58 | - if (msg.length > 160 && !config.do_not_trim_long_sms) { | |
59 | - logger.verbose('Message trim to 160 chars'); | |
60 | - msg = msg.slice(0, 156) + ' ...'; | |
61 | - } | |
62 | - */ | |
63 | - | |
64 | 18 | if (msg.length > 160) { |
65 | 19 | const newMsg = msg.slice(0, 160); |
66 | 20 | const remainingMsg = msg.slice(160); |
... | ... | @@ -73,14 +27,14 @@ function _send(destinationNumber, msg, handlerName) { |
73 | 27 | return; |
74 | 28 | } |
75 | 29 | |
76 | - const modem = modems.getModemConfig(handlerName, config.modems); | |
30 | + const modem = modems.get('name', handlerName); | |
77 | 31 | if (!modem) { |
78 | 32 | logger.warn('Not knowing modem to use. Ignoring message', { destination_number: destinationNumber, msg: msg, handler_name: handlerName }); |
79 | 33 | return; |
80 | 34 | } |
81 | 35 | |
82 | - if (!modem.url || !modem.apikey) { | |
83 | - logger.warn('Invalid modem configuration', { config: modem, handler_name: handlerName }); | |
36 | + if (!modem.reportIp || !modem.reportPort || !modem.reportApikey) { | |
37 | + logger.warn('Invalid modem configuration', { modem }); | |
84 | 38 | return; |
85 | 39 | } |
86 | 40 | |
... | ... | @@ -96,14 +50,17 @@ function _send(destinationNumber, msg, handlerName) { |
96 | 50 | message: msg, |
97 | 51 | }); |
98 | 52 | |
99 | - | |
100 | 53 | const requestOptions = { |
101 | - url: modem.url, | |
54 | + url: url.format({ | |
55 | + protocol: 'http', | |
56 | + hostname: modem.reportIp, | |
57 | + port: modem.reportPort, | |
58 | + }), | |
102 | 59 | qs: { |
103 | 60 | msg: msg, |
104 | 61 | number: destinationNumber, |
105 | 62 | reqid: reqId, |
106 | - apikey: modem.apikey | |
63 | + apikey: modem.reportApikey | |
107 | 64 | } |
108 | 65 | } |
109 | 66 | |
... | ... | @@ -134,10 +91,15 @@ async function send(partner, msg, origin) { |
134 | 91 | msg = msg.trim(); |
135 | 92 | if (!msg) return; |
136 | 93 | |
137 | - const destinationNumber = modems.removeSuffixFromNumber(partner, config); | |
94 | + const destinationNumber = modemSelect.removeSuffixFromNumber(partner, config); | |
95 | + | |
96 | + // logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin }); | |
97 | + let handlerName = await partnerLastSeen.get(destinationNumber) ; | |
138 | 98 | |
139 | - logger.verbose('Choosing handler name', { partner, destinationNumber, msg, origin }); | |
140 | - let handlerName = ( await _getApproriateHandler(destinationNumber) ); | |
99 | + if (!handlerName) { | |
100 | + logger.warn(`Unknown handler for sending message to partner`, { partner, destinationNumber }); | |
101 | + return; | |
102 | + } | |
141 | 103 | |
142 | 104 | _send(destinationNumber, msg, handlerName); |
143 | 105 | } |
test/modems.js
... | ... | @@ -4,9 +4,9 @@ |
4 | 4 | |
5 | 5 | const should = require('should'); |
6 | 6 | |
7 | -const modems = require('../lib/modems'); | |
7 | +const modemSelect = require('../lib/modemSelect'); | |
8 | 8 | |
9 | -describe('#modems', function() { | |
9 | +describe('#modemSelect', function() { | |
10 | 10 | |
11 | 11 | describe('#getModemUrl', function() { |
12 | 12 | |
... | ... | @@ -17,13 +17,13 @@ describe('#modems', function() { |
17 | 17 | } |
18 | 18 | |
19 | 19 | it('should return correct url', function() { |
20 | - modems.getModemUrl('SMS0', modemsConfig).should.equal('http://localhost:8888/'); | |
20 | + modemSelect.getModemUrl('SMS0', modemsConfig).should.equal('http://localhost:8888/'); | |
21 | 21 | }) |
22 | 22 | |
23 | 23 | it ('should handle missing modems', function() { |
24 | - should.not.exists(modems.getModemUrl('SMS0', null)); | |
25 | - should.not.exists(modems.getModemUrl('SMS0', {})); | |
26 | - should.not.exists(modems.getModemUrl('SMS1', modemsConfig)); | |
24 | + should.not.exists(modemSelect.getModemUrl('SMS0', null)); | |
25 | + should.not.exists(modemSelect.getModemUrl('SMS0', {})); | |
26 | + should.not.exists(modemSelect.getModemUrl('SMS1', modemsConfig)); | |
27 | 27 | }) |
28 | 28 | }) |
29 | 29 | |
... | ... | @@ -33,17 +33,17 @@ describe('#modems', function() { |
33 | 33 | } |
34 | 34 | |
35 | 35 | it('should return correct number', function() { |
36 | - modems.removeSuffixFromNumber('08181234@phonenumber', config).should.equal('08181234'); | |
36 | + modemSelect.removeSuffixFromNumber('08181234@phonenumber', config).should.equal('08181234'); | |
37 | 37 | }) |
38 | 38 | |
39 | 39 | it ('should return correct number without suffix in the number', function() { |
40 | - modems.removeSuffixFromNumber('08181234', config).should.equal('08181234'); | |
40 | + modemSelect.removeSuffixFromNumber('08181234', config).should.equal('08181234'); | |
41 | 41 | }) |
42 | 42 | |
43 | 43 | it ('should return correct number without suffix in config', function() { |
44 | - modems.removeSuffixFromNumber('08181234', null).should.equal('08181234'); | |
45 | - modems.removeSuffixFromNumber('08181234', {}).should.equal('08181234'); | |
46 | - modems.removeSuffixFromNumber('08181234@phonenumber', {}).should.equal('08181234'); | |
44 | + modemSelect.removeSuffixFromNumber('08181234', null).should.equal('08181234'); | |
45 | + modemSelect.removeSuffixFromNumber('08181234', {}).should.equal('08181234'); | |
46 | + modemSelect.removeSuffixFromNumber('08181234@phonenumber', {}).should.equal('08181234'); | |
47 | 47 | }) |
48 | 48 | }) |
49 | 49 |