Skip to content

Commit d15816a

Browse files
committed
WIP: Use fetch
1 parent 695c835 commit d15816a

File tree

2 files changed

+74
-72
lines changed

2 files changed

+74
-72
lines changed

src/webServiceClient.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@ describe('WebServiceClient', () => {
3636
}),
3737
});
3838

39-
it('handles "full" responses', async () => {
39+
it.only('handles "full" responses', async () => {
4040
expect.assertions(172);
4141

4242
nockInstance
4343
.post(fullPath('factors'), factors.request.basic)
4444
.basicAuth(auth)
4545
.reply(200, factors.response.full);
4646

47+
console.log(nockInstance.activeMocks());
48+
4749
const got: models.Factors = await client.factors(transaction);
4850

4951
expect(got.id).toEqual('5bc5d6c2-b2c8-40af-87f4-6d61af86b6ae');
@@ -879,6 +881,10 @@ describe('WebServiceClient', () => {
879881
});
880882

881883
describe('error handling', () => {
884+
afterEach(() => {
885+
nock.cleanAll();
886+
});
887+
882888
const transaction = new Transaction({
883889
device: new Device({
884890
ipAddress: '1.1.1.1',

src/webServiceClient.ts

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as http from 'http';
2-
import * as https from 'https';
31
import { version } from '../package.json';
42
import Transaction from './request/transaction';
53
import TransactionReport from './request/transaction-report';
@@ -13,6 +11,11 @@ interface ResponseError {
1311

1412
type 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+
1619
export 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

Comments
 (0)