33const querystring = require ( 'querystring' ) ;
44const fs = require ( 'fs' ) ;
55const path = require ( 'path' ) ;
6- const request = require ( 'request ' ) ;
6+ const axios = require ( 'axios ' ) ;
77const crypto = require ( 'crypto' ) ;
8+ const FormData = require ( 'form-data' ) ;
89const os = require ( 'os' ) ;
910
1011class TestingBot {
@@ -14,18 +15,22 @@ class TestingBot {
1415 this . options . api_secret = this . options . api_secret || process . env . TESTINGBOT_SECRET || process . env . TB_SECRET || null ;
1516
1617 if ( ! this . options . api_key || ! this . options . api_secret ) {
17- try {
18- const tbFile = path . join ( os . homedir ( ) , '.testingbot' ) ;
19- if ( fs . statSync ( tbFile ) . isFile ( ) === true ) {
20- const data = fs . readFileSync ( tbFile ) ;
21- if ( data !== null ) {
22- const arr = data . toString ( ) . replace ( '\n' , '' ) . split ( ':' ) ;
23- this . options . api_key = arr [ 0 ] ;
24- this . options . api_secret = arr [ 1 ] ;
25- }
18+ this . _loadConfig ( ) ;
19+ }
20+ }
21+
22+ _loadConfig ( ) {
23+ try {
24+ const tbFile = path . join ( os . homedir ( ) , '.testingbot' ) ;
25+ if ( fs . statSync ( tbFile ) . isFile ( ) === true ) {
26+ const data = fs . readFileSync ( tbFile ) ;
27+ if ( data !== null ) {
28+ const arr = data . toString ( ) . replace ( '\n' , '' ) . split ( ':' ) ;
29+ this . options . api_key = arr [ 0 ] ;
30+ this . options . api_secret = arr [ 1 ] ;
2631 }
27- } catch ( e ) {
2832 }
33+ } catch ( e ) {
2934 }
3035 }
3136
@@ -264,31 +269,32 @@ class TestingBot {
264269 }
265270
266271 uploadFile ( localFilePath , callback ) {
267- const executeRequest = ( resolve , reject ) => {
272+ const executeRequest = async ( resolve , reject ) => {
273+ const form = new FormData ( ) ;
274+ form . append ( 'file' , fs . createReadStream ( localFilePath ) ) ;
275+
268276 const requestOptions = {
269277 method : 'POST' ,
270- uri : 'https://api.testingbot.com/v1/storage' ,
278+ url : 'https://api.testingbot.com/v1/storage' ,
271279 auth : {
272- user : this . options . api_key ,
273- pass : this . options . api_secret ,
274- sendImmediately : true
280+ username : this . options . api_key ,
281+ password : this . options . api_secret
275282 } ,
276- formData : {
277- file : fs . createReadStream ( localFilePath )
283+ data : form ,
284+ headers : form . getHeaders ( ) ,
285+ validateStatus : function ( status ) {
286+ return true ; // Accept any status code to handle errors ourselves
278287 }
279288 } ;
280289
281- request ( requestOptions , function ( error , response ) {
282- let responseBody = null ;
283- if ( response ) {
284- if ( response . body && typeof ( response . body ) === 'string' ) {
285- response . body = JSON . parse ( response . body ) ;
286- }
287- if ( response . statusCode . toString ( ) . substring ( 0 , 1 ) === '2' ) {
288- responseBody = response . body ;
289- } else {
290- error = response . body ;
291- }
290+ try {
291+ const response = await axios ( requestOptions ) ;
292+ let responseBody = response . data ;
293+ let error = null ;
294+
295+ if ( response . status . toString ( ) . substring ( 0 , 1 ) !== '2' ) {
296+ error = responseBody ;
297+ responseBody = null ;
292298 }
293299
294300 if ( callback ) {
@@ -300,11 +306,19 @@ class TestingBot {
300306 resolve ( responseBody ) ;
301307 }
302308 }
303- } ) ;
309+ } catch ( err ) {
310+ const error = err . response ? err . response . data : err . message ;
311+ if ( callback ) {
312+ callback ( error , null ) ;
313+ } else if ( reject ) {
314+ reject ( error ) ;
315+ }
316+ }
304317 } ;
305318
306319 if ( callback ) {
307- return executeRequest ( null , null ) ;
320+ executeRequest ( null , null ) ;
321+ return ;
308322 }
309323
310324 return new Promise ( executeRequest ) ;
@@ -402,46 +416,56 @@ class TestingBot {
402416 }
403417
404418 request ( reqData , callback ) {
405- const executeRequest = ( resolve , reject ) => {
406- let requestPath = '/v1' + reqData . url ;
407- if ( reqData . method === 'GET' && reqData . data ) {
408- requestPath = requestPath + '?' + querystring . stringify ( reqData . data ) ;
409- }
419+ const executeRequest = async ( resolve , reject ) => {
420+ const requestPath = '/v1' + reqData . url ;
410421 const requestOptions = {
411422 method : reqData . method ,
412- uri : 'https://api.testingbot.com' + requestPath ,
423+ url : 'https://api.testingbot.com' + requestPath ,
413424 auth : {
414- user : this . options . api_key ,
415- pass : this . options . api_secret ,
416- sendImmediately : true
425+ username : this . options . api_key ,
426+ password : this . options . api_secret
427+ } ,
428+ validateStatus : function ( status ) {
429+ return true ; // Accept any status code to handle errors ourselves
417430 }
418431 } ;
419432
420- if ( reqData . method !== 'GET' ) {
433+ if ( reqData . method === 'GET' && reqData . data ) {
434+ requestOptions . params = reqData . data ;
435+ } else if ( reqData . method !== 'GET' ) {
421436 if ( reqData . json ) {
422- requestOptions . json = true ;
423- requestOptions . body = reqData . data ;
437+ requestOptions . headers = { 'Content-Type' : 'application/json' } ;
438+ requestOptions . data = reqData . data ;
424439 } else {
425- requestOptions . form = reqData . data ;
440+ requestOptions . headers = { 'Content-Type' : 'application/x-www-form-urlencoded' } ;
441+ // Flatten nested objects for form-encoded data
442+ const flattenObject = ( obj , prefix = '' ) => {
443+ const flattened = { } ;
444+ Object . keys ( obj ) . forEach ( key => {
445+ const value = obj [ key ] ;
446+ const newKey = prefix ? prefix + '[' + key + ']' : key ;
447+ if ( typeof value === 'object' && value !== null && ! Array . isArray ( value ) ) {
448+ Object . assign ( flattened , flattenObject ( value , newKey ) ) ;
449+ } else {
450+ flattened [ newKey ] = value ;
451+ }
452+ } ) ;
453+ return flattened ;
454+ } ;
455+ requestOptions . data = querystring . stringify ( flattenObject ( reqData . data || { } ) ) ;
426456 }
427457 }
428458
429- request ( requestOptions , function ( error , response ) {
430- let responseBody = null ;
431- if ( response ) {
432- if ( ! reqData . json && response . body && typeof ( response . body ) === 'string' ) {
433- try {
434- response . body = JSON . parse ( response . body ) ;
435- } catch ( e ) {
436- // keep body as string if JSON parsing fails
437- }
438- }
439- if ( response . statusCode . toString ( ) . substring ( 0 , 1 ) === '2' ) {
440- responseBody = response . body ;
441- } else {
442- error = response . body ;
443- }
459+ try {
460+ const response = await axios ( requestOptions ) ;
461+ let responseBody = response . data ;
462+ let error = null ;
463+
464+ if ( response . status . toString ( ) . substring ( 0 , 1 ) !== '2' ) {
465+ error = responseBody ;
466+ responseBody = null ;
444467 }
468+
445469 if ( callback ) {
446470 callback ( error , responseBody ) ;
447471 } else if ( resolve && reject ) {
@@ -451,11 +475,19 @@ class TestingBot {
451475 resolve ( responseBody ) ;
452476 }
453477 }
454- } ) ;
478+ } catch ( err ) {
479+ const error = err . response ? err . response . data : err . message ;
480+ if ( callback ) {
481+ callback ( error , null ) ;
482+ } else if ( reject ) {
483+ reject ( error ) ;
484+ }
485+ }
455486 } ;
456487
457488 if ( callback ) {
458- return executeRequest ( null , null ) ;
489+ executeRequest ( null , null ) ;
490+ return ;
459491 }
460492
461493 return new Promise ( executeRequest ) ;
0 commit comments