Commit 00208dea14cae319fd0851c773b947d4f86e8ae4
1 parent
4c826b27e1
Exists in
master
messageExtractor buat extract price dan endingBalance
Showing 4 changed files with 150 additions and 4 deletions Inline Diff
lib/message-extractor.js
File was created | 1 | /* eslint-disable no-console */ | |
2 | const MODULE_NAME = 'MESSAGE-PARSER.FINDER'; | ||
3 | |||
4 | /** | ||
5 | * Mencari nilai berdasarkan array of pattern | ||
6 | * @date 2021-07-05 | ||
7 | * @param {Array} rules | ||
8 | * @param {string} originalMessage | ||
9 | * @param {any} isDebug | ||
10 | * @returns {string} string yang sesuai pattern, null jika tidak ditemukan | ||
11 | */ | ||
12 | module.exports = (rules, originalMessage, isDebug) => { | ||
13 | if (!Array.isArray(rules)) return null; | ||
14 | if (typeof originalMessage !== 'string') return null; | ||
15 | |||
16 | const msg = originalMessage.trim(); | ||
17 | |||
18 | if (!rules || !msg) return null; | ||
19 | |||
20 | const ruleCount = rules.length; | ||
21 | for (let i = 0; i < ruleCount; i += 1) { | ||
22 | const rule = rules[i]; | ||
23 | |||
24 | // eslint-disable-next-line no-continue | ||
25 | if (rule.disable || rule.disabled) continue; | ||
26 | |||
27 | const re = new RegExp(rule.pattern, rule.flags || ''); | ||
28 | |||
29 | const matches = msg.match(re); | ||
30 | let result = matches && matches[rule.idx]; | ||
31 | |||
32 | if (result && rule.postprocessing && typeof rule.postprocessing === 'function') { | ||
33 | result = rule.postprocessing(result); | ||
34 | } | ||
35 | |||
36 | if (result) { | ||
37 | if (isDebug) { | ||
38 | console.log(`${MODULE_NAME} 9FAF9286: Found matches`, { isDebug, result, rule }); | ||
39 | } | ||
40 | |||
41 | return result; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | if (isDebug) { | ||
46 | console.log(`${MODULE_NAME} 0240860D: Matches not found`, { isDebug }); | ||
47 | } | ||
48 | |||
49 | return null; | ||
50 | }; | ||
51 |
lib/parse-result.js
1 | const MODULE_NAME = 'PARSE-RESULT'; | 1 | const MODULE_NAME = 'PARSE-RESULT'; |
2 | 2 | ||
3 | const config = require('komodo-sdk/config'); | 3 | const config = require('komodo-sdk/config'); |
4 | const logger = require('komodo-sdk/logger'); | 4 | const logger = require('komodo-sdk/logger'); |
5 | 5 | ||
6 | const getParamsMemberObj = require('./generic-xmlrpc/get-params-members-obj'); | 6 | const getParamsMemberObj = require('./generic-xmlrpc/get-params-members-obj'); |
7 | const parseXml = require('./parse-xml'); | 7 | const parseXml = require('./parse-xml'); |
8 | 8 | ||
9 | const report = require('./report'); | 9 | const report = require('./report'); |
10 | const translateRc = require('./translate-rc'); | 10 | const translateRc = require('./translate-rc'); |
11 | const messageExtractor = require('./message-extractor'); | ||
12 | const patternRules = require('./pattern-rules'); | ||
11 | 13 | ||
12 | module.exports = (xid, trxIdFromCaller, xml, isCallback) => { | 14 | module.exports = (xid, trxIdFromCaller, xml, isCallback) => { |
13 | logger.verbose(`${MODULE_NAME} 58547863: Processing XML message`, { xid, isCallback }); | 15 | logger.verbose(`${MODULE_NAME} 58547863: Processing XML message`, { xid, isCallback }); |
14 | 16 | ||
15 | const responseType = isCallback ? 'CALLBACK' : 'DIRECT-RESPONSE'; | 17 | const responseType = isCallback ? 'CALLBACK' : 'DIRECT-RESPONSE'; |
16 | 18 | ||
17 | if (!trxIdFromCaller && !isCallback) { | 19 | if (!trxIdFromCaller && !isCallback) { |
18 | if (logger) logger.warn(`${MODULE_NAME} 8CAAD7C9: No trx id on non callback`, { xid }); | 20 | if (logger) logger.warn(`${MODULE_NAME} 8CAAD7C9: No trx id on non callback`, { xid }); |
19 | return; | 21 | return; |
20 | } | 22 | } |
21 | 23 | ||
22 | const obj = parseXml(xid, xml); | 24 | const obj = parseXml(xid, xml); |
23 | if (!obj) { | 25 | if (!obj) { |
24 | const e = new Error(`${MODULE_NAME} B46CAAD1: Invalid XML`); | 26 | const e = new Error(`${MODULE_NAME} B46CAAD1: Invalid XML`); |
25 | logger.warn(e.message, { xid }); | 27 | logger.warn(e.message, { xid }); |
26 | 28 | ||
27 | if (trxIdFromCaller) { | 29 | if (trxIdFromCaller) { |
28 | report(xid, { | 30 | report(xid, { |
29 | trx_id: trxIdFromCaller.toString(), | 31 | trx_id: trxIdFromCaller.toString(), |
30 | rc: '68', | 32 | rc: '68', |
31 | message: { | 33 | message: { |
32 | xid, | 34 | xid, |
33 | responseType, | 35 | responseType, |
34 | 'GW-ERROR': { | 36 | 'GW-ERROR': { |
35 | message: e.message, | 37 | message: e.message, |
36 | responseFromPartner: xml, | 38 | responseFromPartner: xml, |
37 | }, | 39 | }, |
38 | }, | 40 | }, |
39 | }); | 41 | }); |
40 | } | 42 | } |
41 | 43 | ||
42 | return; | 44 | return; |
43 | } | 45 | } |
44 | 46 | ||
45 | const params = getParamsMemberObj(obj, false); | 47 | const params = getParamsMemberObj(obj, false); |
46 | 48 | ||
47 | if (!params) { | 49 | if (!params) { |
48 | const e = new Error(`${MODULE_NAME} A7B7EB30: Can not get params from message`); | 50 | const e = new Error(`${MODULE_NAME} A7B7EB30: Can not get params from message`); |
49 | logger.warn(e.message, { xid }); | 51 | logger.warn(e.message, { xid }); |
50 | 52 | ||
51 | if (trxIdFromCaller) { | 53 | if (trxIdFromCaller) { |
52 | report(xid, { | 54 | report(xid, { |
53 | trx_id: trxIdFromCaller.toString(), | 55 | trx_id: trxIdFromCaller.toString(), |
54 | rc: '68', | 56 | rc: '68', |
55 | message: { | 57 | message: { |
56 | xid, | 58 | xid, |
57 | responseType, | 59 | responseType, |
58 | 'GW-ERROR': { | 60 | 'GW-ERROR': { |
59 | message: e.message, | 61 | message: e.message, |
60 | responseFromPartner: obj, | 62 | responseFromPartner: obj, |
61 | }, | 63 | }, |
62 | }, | 64 | }, |
63 | }); | 65 | }); |
64 | } | 66 | } |
65 | } | 67 | } |
66 | 68 | ||
67 | logger.verbose(`${MODULE_NAME} 3D5400C1: Params extracted from response`, { xid, params }); | 69 | logger.verbose(`${MODULE_NAME} 3D5400C1: Params extracted from response`, { xid, params }); |
68 | 70 | ||
69 | const requestIdField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.requestId) || 'REQUESTID'; | 71 | const requestIdField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.requestId) || 'REQUESTID'; |
70 | const requestIdFromResponse = params[requestIdField]; | 72 | const requestIdFromResponse = params[requestIdField]; |
71 | 73 | ||
72 | const responseCodeField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.responseCode) || 'RESPONSECODE'; | 74 | const responseCodeField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.responseCode) || 'RESPONSECODE'; |
73 | const responseCodeFromResponse = params[responseCodeField]; | 75 | const responseCodeFromResponse = params[responseCodeField]; |
74 | 76 | ||
75 | // eslint-disable-next-line max-len | 77 | const messageField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.message) || 'MESSAGE'; |
76 | // const messageField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.message) || 'MESSAGE'; | 78 | const messageFromResponse = params[messageField]; |
77 | // const messageFromResponse = params[messageField]; | ||
78 | 79 | ||
79 | const snField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.sn) || 'SN'; | 80 | const snField = (config.partner.xmlrpc_field && config.partner.xmlrpc_field.sn) || 'SN'; |
80 | const snFromResponse = params[snField]; | 81 | const snFromResponse = params[snField]; |
81 | 82 | ||
82 | const trxId = trxIdFromCaller || requestIdFromResponse; | 83 | const trxId = trxIdFromCaller || requestIdFromResponse; |
83 | if (!trxId) { | 84 | if (!trxId) { |
84 | logger.warn(`${MODULE_NAME} 534E684A: Unknown trxId`, { xid }); | 85 | logger.warn(`${MODULE_NAME} 534E684A: Unknown trxId`, { xid }); |
85 | return; | 86 | return; |
86 | } | 87 | } |
87 | 88 | ||
88 | const rc = translateRc(xid, responseCodeFromResponse) || '68'; | 89 | const rc = translateRc(xid, responseCodeFromResponse) || '68'; |
89 | 90 | ||
90 | const sn = (rc === '00' && snFromResponse) || null; | 91 | const sn = ( |
92 | rc === '00' && ( | ||
93 | snFromResponse | ||
94 | || messageExtractor(patternRules.sn, messageFromResponse) | ||
95 | ) | ||
96 | ) || null; | ||
97 | |||
98 | const amount = Number(messageExtractor(patternRules.price, messageFromResponse) || '') || null; | ||
99 | const balance = Number(messageExtractor(patternRules.balance, messageFromResponse) || '') || null; | ||
91 | 100 | ||
92 | report(xid, { | 101 | report(xid, { |
93 | trx_id: trxId.toString(), | 102 | trx_id: trxId.toString(), |
94 | rc, | 103 | rc, |
95 | sn, | 104 | sn, |
105 | amount, | ||
106 | balance, | ||
96 | message: { | 107 | message: { |
97 | xid, | 108 | xid, |
98 | responseType: isCallback ? 'CALLBACK' : 'DIRECT-RESPONSE', | 109 | responseType: isCallback ? 'CALLBACK' : 'DIRECT-RESPONSE', |
99 | message: params, | 110 | message: params, |
100 | }, | 111 | }, |
101 | }); | 112 | }); |
102 | }; | 113 | }; |
lib/pattern-rules.js
File was created | 1 | /** | |
2 | * Rule pattern to be used by message extractor | ||
3 | */ | ||
4 | module.exports = { | ||
5 | sn: [ | ||
6 | { | ||
7 | pattern: '(?:^|,|\\.|;|\\s)SN\\s*[=: ]\\s*(.+?)(?:;|$)', | ||
8 | idx: 1, | ||
9 | disable: false, | ||
10 | }, | ||
11 | ], | ||
12 | price: [ | ||
13 | { | ||
14 | pattern: '(?:,|\\.|;|\\s)(?:HRG|HARGA)\\s*[=: ]\\s*(?:RP)*\\s*(([0-9]|\\.||,)+)', | ||
15 | idx: 1, | ||
16 | disable: false, | ||
17 | postprocessing: (val) => (val || '').replace(/[,.]/g, ''), | ||
18 | }, | ||
19 | ], | ||
20 | balance: [ | ||
21 | { | ||
22 | pattern: '(?:,|\\.|;|\\s)SAL(?:DO)*\\s*[=: ]\\s*(?:RP)*\\s*(([0-9]|\\.||,)+)', | ||
23 | idx: 1, | ||
24 | disable: false, | ||
25 | postprocessing: (val) => (val || '').replace(/[,.]/g, ''), | ||
26 | }, | ||
27 | ], | ||
28 | }; | ||
29 |
test/message-extractor.js
File was created | 1 | /* global describe it */ | |
2 | |||
3 | // eslint-disable-next-line no-unused-vars | ||
4 | const should = require('should'); | ||
5 | const finder = require('../lib/message-extractor'); | ||
6 | const organicRules = require('../lib/pattern-rules'); | ||
7 | |||
8 | describe('#message-extractor', () => { | ||
9 | it('should handle garbage input', () => { | ||
10 | should.not.exists(finder(null, null, '39A9909D')); | ||
11 | should.not.exists(finder(null, '', '8C046E19')); | ||
12 | should.not.exists(finder(null, 'ada', '4154D1DC')); | ||
13 | should.not.exists(finder([], 'ada'), '5511A78C'); | ||
14 | }); | ||
15 | |||
16 | it('should return correct sn', () => { | ||
17 | finder( | ||
18 | organicRules.sn, | ||
19 | 'SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5;05/07/21 19:05 TRANSAKSI PLNB50 KE 14336230686, (ISO00) BERHASIL. SAL=RP 75.856.585 ,HRG=RP 50.020 ,ID=91280176,SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5@,REQID=2794362 *Channel Telegram : https://t.me/metroreloadinfoh2h', | ||
20 | false, | ||
21 | ).should.equal('3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5', 'ST24 metro-reload 5B164B64'); | ||
22 | |||
23 | finder( | ||
24 | organicRules.sn, | ||
25 | 'ISI TS10 KE 082324881979 , SUKSES. SAL=489700,HRG=10300,ID=1625459089388,SN=0061003777417733', | ||
26 | false, | ||
27 | ).should.equal('0061003777417733', 'PLINK 00FC0A03'); | ||
28 | }); | ||
29 | |||
30 | it('should return correct price', () => { | ||
31 | finder( | ||
32 | organicRules.price, | ||
33 | 'SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5;05/07/21 19:05 TRANSAKSI PLNB50 KE 14336230686, (ISO00) BERHASIL. SAL=RP 75.856.585 ,HRG=RP 50.020 ,ID=91280176,SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5@,REQID=2794362 *Channel Telegram : https://t.me/metroreloadinfoh2h', | ||
34 | false, | ||
35 | ).should.equal('50020', 'ST24 metro-reload 572DAAE1'); | ||
36 | |||
37 | finder( | ||
38 | organicRules.price, | ||
39 | 'ISI TS10 KE 082324881979 , SUKSES. SAL=489700,HRG=10300,ID=1625459089388,SN=0061003777417733', | ||
40 | false, | ||
41 | ).should.equal('10300', 'PLINK DB7F7B1C'); | ||
42 | }); | ||
43 | |||
44 | it('should return correct ending balance', () => { | ||
45 | finder( | ||
46 | organicRules.balance, | ||
47 | 'SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5;05/07/21 19:05 TRANSAKSI PLNB50 KE 14336230686, (ISO00) BERHASIL. SAL=RP 75.856.585 ,HRG=RP 50.020 ,ID=91280176,SN=3438-2805-5518-4885-2577/A HANIS SACHRA/R1 /1300 VA/31,5@,REQID=2794362 *Channel Telegram : https://t.me/metroreloadinfoh2h', | ||
48 | false, | ||
49 | ).should.equal('75856585', 'ST24 metro-reload 5E6CA14F'); | ||
50 | |||
51 | finder( | ||
52 | organicRules.balance, | ||
53 | 'ISI TS10 KE 082324881979 , SUKSES. SAL=489700,HRG=10300,ID=1625459089388,SN=0061003777417733', | ||
54 | false, | ||
55 | ).should.equal('489700', 'PLINK 29C24CEA'); | ||
56 | }); | ||
57 | }); | ||
58 |