1- import * as http from 'http' ;
2- import * as https from 'https' ;
31import { version } from '../package.json' ;
42import Transaction from './request/transaction' ;
53import TransactionReport from './request/transaction-report' ;
@@ -13,6 +11,11 @@ interface ResponseError {
1311
1412type servicePath = 'factors' | 'insights' | 'score' | 'transactions/report' ;
1513
14+ const invalidResponseBody = {
15+ code : 'INVALID_RESPONSE_BODY' ,
16+ error : 'Received an invalid or unparseable response body' ,
17+ } ;
18+
1619export default class WebServiceClient {
1720 private accountID : string ;
1821 private host : string ;
@@ -65,7 +68,7 @@ export default class WebServiceClient {
6568 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6669 modelClass ?: any
6770 ) : Promise < T > ;
68- private responseFor (
71+ private async responseFor (
6972 path : servicePath ,
7073 postData : string ,
7174 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -74,97 +77,90 @@ export default class WebServiceClient {
7477 const parsedPath = `/minfraud/v2.0/${ path } ` ;
7578 const url = `https://${ this . host } ${ parsedPath } ` ;
7679
77- const options = {
78- auth : `${ this . accountID } :${ this . licenseKey } ` ,
80+ const controller = new AbortController ( ) ;
81+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , this . timeout ) ;
82+
83+ const options : RequestInit = {
7984 headers : {
8085 Accept : 'application/json' ,
81- 'Content-Length' : Buffer . byteLength ( postData ) ,
86+ Authorization :
87+ 'Basic ' +
88+ Buffer . from ( `${ this . accountID } :${ this . licenseKey } ` ) . toString (
89+ 'base64'
90+ ) ,
91+ 'Content-Length' : Buffer . byteLength ( postData ) . toString ( ) ,
8292 'Content-Type' : 'application/json' ,
8393 'User-Agent' : `minfraud-api-node/${ version } ` ,
8494 } ,
85- host : this . host ,
8695 method : 'POST' ,
87- path : parsedPath ,
88- timeout : this . timeout ,
96+ signal : controller . signal ,
8997 } ;
9098
91- return new Promise ( ( resolve , reject ) => {
92- const req = https . request ( options , ( response ) => {
93- let data = '' ;
94-
95- response . on ( 'data' , ( chunk ) => {
96- data += chunk ;
97- } ) ;
98-
99- response . on ( 'end' , ( ) => {
100- if ( response . statusCode && response . statusCode === 204 ) {
101- return resolve ( ) ;
102- }
103-
104- try {
105- data = JSON . parse ( data ) ;
106- } catch {
107- return reject ( this . handleError ( { } , response , url ) ) ;
108- }
109-
110- if ( response . statusCode && response . statusCode !== 200 ) {
111- return reject (
112- this . handleError ( data as ResponseError , response , url )
113- ) ;
114- }
115-
116- return resolve ( new modelClass ( data ) ) ;
117- } ) ;
118- } ) ;
119-
120- req . on ( 'error' , ( err : NodeJS . ErrnoException ) => {
121- return reject ( {
122- code : err . code ,
123- error : err . message ,
124- url,
125- } as WebServiceClientError ) ;
126- } ) ;
127-
128- req . write ( postData ) ;
129-
130- req . end ( ) ;
131- } ) ;
99+ let data ;
100+ try {
101+ console . log ( url ) ;
102+ const response = await fetch ( url , options ) ;
103+
104+ if ( ! response . ok ) {
105+ return Promise . reject ( await this . handleError ( response , url ) ) ;
106+ }
107+ } catch ( err ) {
108+ const error = err as TypeError ;
109+ switch ( error . name ) {
110+ case 'AbortError' :
111+ return Promise . reject ( {
112+ code : 'NETWORK_TIMEOUT' ,
113+ error : 'The request timed out' ,
114+ url,
115+ } ) ;
116+ case 'SyntaxError' :
117+ return Promise . reject ( {
118+ ...invalidResponseBody ,
119+ url,
120+ } ) ;
121+ default :
122+ return Promise . reject ( {
123+ code : 'FETCH_ERROR' ,
124+ error : `${ error . name } - ${ error . message } ` ,
125+ url,
126+ } ) ;
127+ }
128+ } finally {
129+ clearTimeout ( timeoutId ) ;
130+ }
131+
132+ return new modelClass ( data ) ;
132133 }
133134
134- private handleError (
135- data : ResponseError ,
136- response : http . IncomingMessage ,
135+ private async handleError (
136+ response : Response ,
137137 url : string
138- ) : WebServiceClientError {
139- if (
140- response . statusCode &&
141- response . statusCode >= 500 &&
142- response . statusCode < 600
143- ) {
138+ ) : Promise < WebServiceClientError > {
139+ if ( response . status && response . status >= 500 && response . status < 600 ) {
144140 return {
145141 code : 'SERVER_ERROR' ,
146- error : `Received a server error with HTTP status code: ${ response . statusCode } ` ,
142+ error : `Received a server error with HTTP status code: ${ response . status } ` ,
147143 url,
148144 } ;
149145 }
150146
151- if (
152- response . statusCode &&
153- ( response . statusCode < 400 || response . statusCode >= 600 )
154- ) {
147+ if ( response . status && ( response . status < 400 || response . status >= 600 ) ) {
155148 return {
156149 code : 'HTTP_STATUS_CODE_ERROR' ,
157- error : `Received an unexpected HTTP status code: ${ response . statusCode } ` ,
150+ error : `Received an unexpected HTTP status code: ${ response . status } ` ,
158151 url,
159152 } ;
160153 }
161154
162- if ( ! data . code || ! data . error ) {
163- return {
164- code : 'INVALID_RESPONSE_BODY' ,
165- error : 'Received an invalid or unparseable response body' ,
166- url,
167- } ;
155+ let data ;
156+ try {
157+ data = ( await response . json ( ) ) as ResponseError ;
158+
159+ if ( ! data . code || ! data . error ) {
160+ return { ...invalidResponseBody , url } ;
161+ }
162+ } catch {
163+ return { ...invalidResponseBody , url } ;
168164 }
169165
170166 return { ...data , url } as WebServiceClientError ;
0 commit comments