Commit 543db86435855acfd80235d2022f655f3767c905

Authored by Adhidarma Hadiwinoto
1 parent e9d3b8b13b
Exists in master

insertTaskToMongoDb dan callbackReportWithPushToMongoDb

Showing 2 changed files with 22 additions and 7 deletions Inline Diff

1 var fs = require('fs'); 1 var fs = require('fs');
2 var https = require('https'); 2 var https = require('https');
3 var http = require('http'); 3 var http = require('http');
4 var url = require('url'); 4 var url = require('url');
5 var request = require('request'); 5 var request = require('request');
6 var xml2js = require('xml2js').parseString; 6 var xml2js = require('xml2js').parseString;
7 var strftime = require('strftime'); 7 var strftime = require('strftime');
8 var redis = require('redis'); 8 var redis = require('redis');
9 9
10 var Router = require('node-simple-router'); 10 var Router = require('node-simple-router');
11 11
12 var winston = require('winston'); 12 var winston = require('winston');
13 13
14 var logger; 14 var logger;
15 var config; 15 var config;
16 var httpServer; 16 var httpServer;
17 var redisClient; 17 var redisClient;
18 18
19 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 19 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
20 20
21 var aaa; 21 var aaa;
22 22
23 var logTag = __filename.split('/').reverse()[0]; 23 var logTag = __filename.split('/').reverse()[0];
24 24
25 function getRedisKey(timestamp) { 25 function getRedisKey(timestamp) {
26 var prefix = config.globals.gateway_name; 26 var prefix = config.globals.gateway_name;
27 if (config.globals.redis_prefix) { 27 if (config.globals.redis_prefix) {
28 prefix = config.globals.redis_prefix; 28 prefix = config.globals.redis_prefix;
29 } 29 }
30 return prefix + '.ts:' + timestamp + '.rid'; 30 return prefix + '.ts:' + timestamp + '.rid';
31 } 31 }
32 32
33 function generateTimestamp(request_id) { 33 function generateTimestamp(request_id) {
34 var ts = strftime('%F %T', new Date()); 34 var ts = strftime('%F %T', new Date());
35 35
36 var key = getRedisKey(ts); 36 var key = getRedisKey(ts);
37 redisClient.set(key, request_id); 37 redisClient.set(key, request_id);
38 redisClient.expire(key, 3600*48); 38 redisClient.expire(key, 3600*48);
39 39
40 return ts; 40 return ts;
41 } 41 }
42 42
43 function topupRequest(task) { 43 function topupRequest(task) {
44 aaa.insertTaskToMongoDb(task);
45
44 var ts = strftime('%F %T', new Date()); 46 var ts = strftime('%F %T', new Date());
45 ts = generateTimestamp(task['requestId']); 47 ts = generateTimestamp(task['requestId']);
46 48
47 var options = { 49 var options = {
48 url: config.h2h_out.partner, 50 url: config.h2h_out.partner,
49 qs: { 51 qs: {
50 code: task['remoteProduct'], 52 code: task['remoteProduct'],
51 msisdn: task['destination'], 53 msisdn: task['destination'],
52 user_id: config.h2h_out.userid, 54 user_id: config.h2h_out.userid,
53 password: config.h2h_out.password, 55 password: config.h2h_out.password,
54 ts: ts 56 ts: ts
55 } 57 }
56 }; 58 };
57 59
58 logger.info('Creating http request to gateway', {options: options}); 60 logger.info('Creating http request to gateway', {options: options});
59 61
60 if (aaa) { 62 if (aaa) {
61 aaa.incrementTrxCount(); 63 aaa.incrementTrxCount();
62 aaa.incrementActiveTrxCount(); 64 aaa.incrementActiveTrxCount();
63 } 65 }
64 66
65 request(options, function (error, response, body) { 67 request(options, function (error, response, body) {
66 aaa.decrementActiveTrxCount(); 68 aaa.decrementActiveTrxCount();
67 69
68 if (error) { 70 if (error) {
69 71
70 var error_message = 'Error on http connection to gateway: ' + error; 72 var error_message = 'Error on http connection to gateway: ' + error;
71 logger.warn(error_message); 73 logger.warn(error_message);
72 callbackReport(task['requestId'], '91', error_message); 74 callbackReport(task['requestId'], '91', error_message);
73 return; 75 return;
74 76
75 } 77 }
76 78
77 if (response.statusCode != 200) { 79 if (response.statusCode != 200) {
78 80
79 var error_message = 'Gateway error, http response code: ' + response.statusCode; 81 var error_message = 'Gateway error, http response code: ' + response.statusCode;
80 logger.warn(error_message); 82 logger.warn(error_message);
81 callbackReport(task['requestId'], '91', error_message); 83 callbackReport(task['requestId'], '91', error_message);
82 return; 84 return;
83 } 85 }
84 86
85 var responseCode = 40; 87 var responseCode = 40;
86 var responseMessage; 88 var responseMessage;
87 89
88 xml2js(body, function (err, result) { 90 xml2js(body, function (err, result) {
89 if (err) { 91 if (err) {
90 logger.warn('Error parsing XML', {response_error: err, response_body: body}); 92 logger.warn('Error parsing XML', {response_error: err, response_body: body});
91 callbackReport(task['requestId'], '40', body); 93 callbackReport(task['requestId'], '40', body);
92 return; 94 return;
93 } 95 }
94 96
95 logger.info('Got direct response from request', {result: result}); 97 logger.info('Got direct response from request', {result: result});
96 98
97 try { 99 try {
98 responseMessage = result.direct_ack.info[0]; 100 responseMessage = result.direct_ack.info[0];
99 101
100 if (result.direct_ack.request_status[0] == 'OK') { 102 if (result.direct_ack.request_status[0] == 'OK') {
101 responseCode = 68; 103 responseCode = 68;
102 } else { 104 } else {
103 responseCode = 40; 105 responseCode = 40;
104 106
105 var new_response_code = responseCodeFromMessage(responseMessage); 107 var new_response_code = responseCodeFromMessage(responseMessage);
106 if (new_response_code) { 108 if (new_response_code) {
107 responseCode = new_response_code; 109 responseCode = new_response_code;
108 } 110 }
109 111
110 } 112 }
111 113
112 } 114 }
113 catch(err) { 115 catch(err) {
114 logger.warn('Exception on parsing xml response'); 116 logger.warn('Exception on parsing xml response');
115 responseCode = 40; 117 responseCode = 40;
116 responseMessage = 'Invalid xml response from gateway'; 118 responseMessage = 'Invalid xml response from gateway';
117 } 119 }
118 120
119 callbackReport(task['requestId'], responseCode, responseMessage); 121 callbackReport(task['requestId'], responseCode, responseMessage);
120 122
121 }); 123 });
122 124
123 }); 125 });
124 } 126 }
125 127
126 function createRedisClient() { 128 function createRedisClient() {
127 redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host); 129 redisClient = redis.createClient(config.globals.redis_port, config.globals.redis_host);
128 } 130 }
129 131
130 function paddingSN(sn, _config) { 132 function paddingSN(sn, _config) {
131 133
132 if (!_config) { 134 if (!_config) {
133 _config = config; 135 _config = config;
134 } 136 }
135 137
136 if (_config.h2h_out.sn_min_length && (sn.length < Number(_config.h2h_out.sn_min_length))) { 138 if (_config.h2h_out.sn_min_length && (sn.length < Number(_config.h2h_out.sn_min_length))) {
137 sn = '0000000000000000' + sn; 139 sn = '0000000000000000' + sn;
138 sn = sn.slice(Number(_config.h2h_out.sn_min_length) * -1); 140 sn = sn.slice(Number(_config.h2h_out.sn_min_length) * -1);
139 } 141 }
140 return sn; 142 return sn;
141 } 143 }
142 144
143 function parseSN(message, _config) { 145 function parseSN(message, _config) {
144 146
145 if (!_config) { 147 if (!_config) {
146 _config = config; 148 _config = config;
147 } 149 }
148 150
149 var sn_regex = new RegExp(_config.h2h_out.sn_pattern); 151 var sn_regex = new RegExp(_config.h2h_out.sn_pattern);
150 var sn_match = message.match(sn_regex); 152 var sn_match = message.match(sn_regex);
151 153
152 if (sn_match <= 0) { 154 if (sn_match <= 0) {
153 logger.info('SN Not found: ' + message); 155 logger.info('SN Not found: ' + message);
154 return ''; 156 return '';
155 } 157 }
156 158
157 var match_index = 0; 159 var match_index = 0;
158 if (_config.h2h_out.sn_match_index) { 160 if (_config.h2h_out.sn_match_index) {
159 match_index = Number(_config.h2h_out.sn_match_index) 161 match_index = Number(_config.h2h_out.sn_match_index)
160 } 162 }
161 163
162 var sn = sn_match[match_index]; 164 var sn = sn_match[match_index];
163 165
164 if (_config.h2h_out.sn_remove_whitespace) { 166 if (_config.h2h_out.sn_remove_whitespace) {
165 sn = sn.replace(/\s/g, ''); 167 sn = sn.replace(/\s/g, '');
166 } 168 }
167 169
168 var sn_remove_patterns = _config.h2h_out.sn_remove_patterns.split(_config.h2h_out.sn_remove_patterns_separator); 170 var sn_remove_patterns = _config.h2h_out.sn_remove_patterns.split(_config.h2h_out.sn_remove_patterns_separator);
169 171
170 var count = sn_remove_patterns.length; 172 var count = sn_remove_patterns.length;
171 173
172 for(var i = 0; i < count; i++) { 174 for(var i = 0; i < count; i++) {
173 175
174 //sn = sn.replace(sn_remove_patterns[i], ''); 176 //sn = sn.replace(sn_remove_patterns[i], '');
175 177
176 var re = new RegExp(sn_remove_patterns[i], 'g'); 178 var re = new RegExp(sn_remove_patterns[i], 'g');
177 sn = sn.replace(re, ''); 179 sn = sn.replace(re, '');
178 } 180 }
179 181
180 sn = paddingSN(sn, _config); 182 sn = paddingSN(sn, _config);
181 183
182 return sn.trim(); 184 return sn.trim();
183 } 185 }
184 186
185 function createServer() { 187 function createServer() {
186 var httpServer = http.createServer(function(request, response) { 188 var httpServer = http.createServer(function(request, response) {
187 189
188 var response_code = '68'; 190 var response_code = '68';
189 var sn = ''; 191 var sn = '';
190 192
191 var qs = url.parse(request.url, true).query; 193 var qs = url.parse(request.url, true).query;
192 194
193 logger.info('Got reverse report from gateway', {qs: qs}); 195 logger.info('Got reverse report from gateway', {qs: qs});
194 196
195 if (qs.topup_status == 'S') { 197 if (qs.topup_status == 'S') {
196 response_code = '00'; 198 response_code = '00';
197 if (qs.sn && !config.h2h_out.force_parse_sn && !Number(config.h2h_out.force_parse_sn)) { 199 if (qs.sn && !config.h2h_out.force_parse_sn && !Number(config.h2h_out.force_parse_sn)) {
198 sn = qs.sn; 200 sn = qs.sn;
199 } else { 201 } else {
200 logger.warn('Missing SN from query string. Trying to get SN from message'); 202 logger.warn('Missing SN from query string. Trying to get SN from message');
201 sn = parseSN(qs.info); 203 sn = parseSN(qs.info);
202 } 204 }
203 205
204 if (config.h2h_out.sn_split_index) { 206 if (config.h2h_out.sn_split_index) {
205 sn = splitSN(sn, config); 207 sn = splitSN(sn, config);
206 } 208 }
207 209
208 if (sn) { 210 if (sn) {
209 sn = paddingSN(sn, config); 211 sn = paddingSN(sn, config);
210 } 212 }
211 213
212 } else if (qs.topup_status == 'R') { 214 } else if (qs.topup_status == 'R') {
213 215
214 response_code = '40'; 216 response_code = '40';
215 217
216 } 218 }
217 219
218 try { 220 try {
219 221
220 var new_response_code = responseCodeFromMessage(qs.info); 222 var new_response_code = responseCodeFromMessage(qs.info);
221 if (new_response_code) { 223 if (new_response_code) {
222 response_code = new_response_code; 224 response_code = new_response_code;
223 } 225 }
224 226
225 } 227 }
226 catch(err) { 228 catch(err) {
227 logger.warn('Exception on parsing reverse report', {exception: err} ); 229 logger.warn('Exception on parsing reverse report', {exception: err} );
228 response_code = '40'; 230 response_code = '40';
229 } 231 }
230 232
231 message = qs.info; 233 message = qs.info;
232 //updateBalance(message); 234 //updateBalance(message);
233 if (sn) { 235 if (sn) {
234 message = 'SN=' + sn + '; ' + message; 236 message = 'SN=' + sn + '; ' + message;
235 } 237 }
236 238
237 response.end('OK'); 239 response.end('OK');
238 240
239 var key = getRedisKey(qs.ts); 241 var key = getRedisKey(qs.ts);
240 redisClient.get(key, function(err, request_id) { 242 redisClient.get(key, function(err, request_id) {
241 if (err) { 243 if (err) {
242 logger.warn('Error when requesting request id for ts:' + qs.ts + ' (' + key + ')', {redis_error: err}); 244 logger.warn('Error when requesting request id for ts:' + qs.ts + ' (' + key + ')', {redis_error: err});
243 return; 245 return;
244 } 246 }
245 247
246 callbackReport(request_id, response_code, message); 248 callbackReport(request_id, response_code, message);
247 }); 249 });
248 }); 250 });
249 251
250 httpServer.listen(config.h2h_out.listen_port, function() { 252 httpServer.listen(config.h2h_out.listen_port, function() {
251 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port); 253 logger.info('HTTP Reverse/Report server listen on port ' + config.h2h_out.listen_port);
252 }); 254 });
253 } 255 }
254 256
255 function splitSN(sn, _config) { 257 function splitSN(sn, _config) {
256 var sn_pieces = sn.split(' '); 258 var sn_pieces = sn.split(' ');
257 259
258 if (sn_pieces.length <= 0) { 260 if (sn_pieces.length <= 0) {
259 logger.info('Returning original SN because SN only has one element'); 261 logger.info('Returning original SN because SN only has one element');
260 return sn; 262 return sn;
261 } 263 }
262 264
263 if (!_config.h2h_out.sn_split_index) { 265 if (!_config.h2h_out.sn_split_index) {
264 logger.info('Returning original SN because config.h2h_out.sn_split_index undefined'); 266 logger.info('Returning original SN because config.h2h_out.sn_split_index undefined');
265 return sn; 267 return sn;
266 } 268 }
267 var sn_indexes = _config.h2h_out.sn_split_index.split(','); 269 var sn_indexes = _config.h2h_out.sn_split_index.split(',');
268 270
269 logger.info('Split SN', {sn_pieces: sn_pieces, sn_indexes: sn_indexes}); 271 logger.info('Split SN', {sn_pieces: sn_pieces, sn_indexes: sn_indexes});
270 272
271 var _sn = ''; 273 var _sn = '';
272 274
273 var id_count = sn_indexes.length; 275 var id_count = sn_indexes.length;
274 for(var i = 0; i < id_count; i++) { 276 for(var i = 0; i < id_count; i++) {
275 277
276 var sn_index = sn_indexes[i]; 278 var sn_index = sn_indexes[i];
277 var sn_piece = sn_pieces[sn_index]; 279 var sn_piece = sn_pieces[sn_index];
278 280
279 if (sn_pieces[i]) { 281 if (sn_pieces[i]) {
280 _sn = _sn + sn_piece; 282 _sn = _sn + sn_piece;
281 } else { 283 } else {
282 logger.warn('Undefined value on sn piece ' + sn_index); 284 logger.warn('Undefined value on sn piece ' + sn_index);
283 } 285 }
284 } 286 }
285 287
286 _sn = _sn.trim(); 288 _sn = _sn.trim();
287 289
288 if (_sn) { 290 if (_sn) {
289 sn = _sn; 291 sn = _sn;
290 logger.info('Got new SN: ' + sn); 292 logger.info('Got new SN: ' + sn);
291 } else { 293 } else {
292 logger.warn('Got empty SN when using split SN. Use original SN'); 294 logger.warn('Got empty SN when using split SN. Use original SN');
293 } 295 }
294 296
295 return sn; 297 return sn;
296 } 298 }
297 299
298 function responseCodeFromMessage(message) { 300 function responseCodeFromMessage(message) {
299 if (message.toLowerCase().indexOf('nomor salah/tidak terdaftar') >= 0) { 301 if (message.toLowerCase().indexOf('nomor salah/tidak terdaftar') >= 0) {
300 return '14'; 302 return '14';
301 } 303 }
302 else if (message.toLowerCase().indexOf('nomor tidak di temukan/tidak aktif') >= 0) { 304 else if (message.toLowerCase().indexOf('nomor tidak di temukan/tidak aktif') >= 0) {
303 return '14'; 305 return '14';
304 } 306 }
305 else if (message.toLowerCase().indexOf('kode produk tidak sesuai nomor tujuan') >= 0) { 307 else if (message.toLowerCase().indexOf('kode produk tidak sesuai nomor tujuan') >= 0) {
306 return '14'; 308 return '14';
307 } 309 }
308 else if (message.toLowerCase().indexOf('nomor yang anda masukan salah') >= 0) { 310 else if (message.toLowerCase().indexOf('nomor yang anda masukan salah') >= 0) {
309 return '14'; 311 return '14';
310 } 312 }
311 else if (message.toLowerCase().indexOf('nomor yang anda masukkan salah') >= 0) { 313 else if (message.toLowerCase().indexOf('nomor yang anda masukkan salah') >= 0) {
312 return '14'; 314 return '14';
313 } 315 }
314 else if (message.toLowerCase().indexOf('nomor telepon seluler salah') >= 0) { 316 else if (message.toLowerCase().indexOf('nomor telepon seluler salah') >= 0) {
315 return '14'; 317 return '14';
316 } 318 }
317 else if (message.toLowerCase().indexOf('bulk or forbidden request') >= 0) { 319 else if (message.toLowerCase().indexOf('bulk or forbidden request') >= 0) {
318 return '55'; 320 return '55';
319 } 321 }
320 else if (message.toLowerCase().indexOf('sudah pernah dilakukan') >= 0) { 322 else if (message.toLowerCase().indexOf('sudah pernah dilakukan') >= 0) {
321 return '55'; 323 return '55';
322 } 324 }
323 else if (message.toLowerCase().indexOf('transaksi yg sama sudah pernah dilakukan tunggu dlm') >= 0) { 325 else if (message.toLowerCase().indexOf('transaksi yg sama sudah pernah dilakukan tunggu dlm') >= 0) {
324 return '55'; 326 return '55';
325 } 327 }
326 else if (message.toLowerCase().indexOf('transaksi tsb sudah pernah dilakukan') >= 0) { 328 else if (message.toLowerCase().indexOf('transaksi tsb sudah pernah dilakukan') >= 0) {
327 return '55'; 329 return '55';
328 } 330 }
329 else if (message.toLowerCase().indexOf('mohon maaf saat ini stock belum tersedia') >= 0) { 331 else if (message.toLowerCase().indexOf('mohon maaf saat ini stock belum tersedia') >= 0) {
330 return '13'; 332 return '13';
331 } 333 }
332 else if (message.toLowerCase().indexOf('stock tidak tersedia') >= 0) { 334 else if (message.toLowerCase().indexOf('stock tidak tersedia') >= 0) {
333 return '13'; 335 return '13';
334 } 336 }
335 else if (message.toLowerCase().indexOf('tidak tersedia atau nonaktif') >= 0) { 337 else if (message.toLowerCase().indexOf('tidak tersedia atau nonaktif') >= 0) {
336 return '13'; 338 return '13';
337 } 339 }
338 else if (message.toLowerCase().indexOf('saldo di account anda saat ini tidak mencukupi') >= 0) { 340 else if (message.toLowerCase().indexOf('saldo di account anda saat ini tidak mencukupi') >= 0) {
339 return '40'; 341 return '40';
340 } 342 }
341 else if (message.toLowerCase().indexOf('tidak mencukupi untuk melakukan transaksi') >= 0) { 343 else if (message.toLowerCase().indexOf('tidak mencukupi untuk melakukan transaksi') >= 0) {
342 return '40'; 344 return '40';
343 } 345 }
344 else if (message.toLowerCase().indexOf('saldo anda tidak mencukupi') >= 0) { 346 else if (message.toLowerCase().indexOf('saldo anda tidak mencukupi') >= 0) {
345 return '40'; 347 return '40';
346 } 348 }
347 else if (message.search(/stok product .* tidak tersedia ke nomor .* dari pilihan2 yg tersedia/i) >= 0) { 349 else if (message.search(/stok product .* tidak tersedia ke nomor .* dari pilihan2 yg tersedia/i) >= 0) {
348 return '13'; 350 return '13';
349 } 351 }
350 else if (message.search(/stok produk .* gangguan ke no tujuan/i) >= 0) { 352 else if (message.search(/stok produk .* gangguan ke no tujuan/i) >= 0) {
351 return '13'; 353 return '13';
352 } 354 }
353 355
354 return; 356 return;
355 } 357 }
356 358
357 function start(_config, _callbackReport, options) { 359 function start(options) {
358 config = _config; 360 if (!options) {
359 callbackReport = _callbackReport 361 console.log('Undefined options, terminating....');
362 process.exit(1);
363 }
364
365 if (options.config) {
366 config = options.config;
367 } else {
368 console.log('Undefined options.config, terminating....')
369 process.exit(1);
370 }
360 371
361 if (options && options.aaa) { 372 if (options.aaa) {
362 aaa = options.aaa; 373 aaa = options.aaa;
374 callbackReport = options.aaa.callbackReportWithPushToMongoDb;
375 } else {
376 console.log('Undefined options.aaa, terminating....')
377 process.exit(1);
363 } 378 }
364 379
365 if (options && options.logger) { 380 if (options.logger) {
366 logger = options.logger; 381 logger = options.logger;
367 } else { 382 } else {
368 logger = new winston.Logger({ 383 logger = new winston.Logger({
369 transports: [ 384 transports: [
370 new (winston.transports.Console)() 385 new (winston.transports.Console)()
371 ] 386 ]
372 }); 387 });
373 } 388 }
374 389
375 createRedisClient(); 390 createRedisClient();
376 createServer(); 391 createServer();
377 } 392 }
378 393
379 exports.start = start; 394 exports.start = start;
380 exports.topupRequest = topupRequest; 395 exports.topupRequest = topupRequest;
381 exports.parseSN = parseSN; 396 exports.parseSN = parseSN;
382 397
1 var fs = require('fs'); 1 var fs = require('fs');
2 var ini = require('ini'); 2 var ini = require('ini');
3 var expresso = require('sate24-expresso'); 3 var expresso = require('sate24-expresso');
4 var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8')); 4 var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8'));
5 5
6 process.chdir(__dirname); 6 process.chdir(__dirname);
7 7
8 //var iniparser = require('iniparser'); 8 //var iniparser = require('iniparser');
9 //var config = iniparser.parseSync('./config.ini'); 9 //var config = iniparser.parseSync('./config.ini');
10 10
11 var aaaHost = config.globals.aaa_host; 11 var aaaHost = config.globals.aaa_host;
12 12
13 var logger = require('sate24/logger.js').start(); 13 var logger = require('sate24/logger.js').start();
14 var HttpServer = require('sate24/httpserver.js'); 14 var HttpServer = require('sate24/httpserver.js');
15 var aaa = require('sate24/aaa.js'); 15 var aaa = require('sate24/aaa.js');
16 var partner = require('./gentong.js'); 16 var partner = require('./gentong.js');
17 17
18 var matrix = aaa.prepareMatrix(); 18 var matrix = aaa.prepareMatrix();
19 19
20 var options = { 20 var options = {
21 'aaa': aaa, 21 'aaa': aaa,
22 'logger': logger, 22 'logger': logger,
23 'config': config, 23 'config': config,
24 'matrix': matrix, 24 'matrix': matrix,
25 //'expresso_views': 'node_modules/sate24-expresso/views' 25 //'expresso_views': 'node_modules/sate24-expresso/views'
26 } 26 }
27 27
28 //var httpServer = HttpServer.start(config, {logger: logger}); 28 //var httpServer = HttpServer.start(config, {logger: logger});
29 var httpServer = HttpServer.start(config, options); 29 var httpServer = HttpServer.start(config, options);
30 //HttpServer.setAaa(aaa); 30 //HttpServer.setAaa(aaa);
31 31
32 partner.start(config, aaa.callbackReport, options); 32 partner.start(options);
33 aaa.start(config, partner, options); 33 aaa.start(config, partner, options);
34 expresso.start(options); 34 expresso.start(options);
35 35