Skip to content

Commit ce62098

Browse files
authored
Add support for returning all response headers in node sdk for error responses (#625)
* feat: add support for returning all response headers in node sdk for error responses * Ensure current tests pass with the new change * feat: add support for returning all response headers in node sdk for error responses * Fix: renamed x-flow-id to x-fastly-id * prettier * Fix: tests * code clean-up * fixed lint errors
1 parent 1bf1cf3 commit ce62098

5 files changed

Lines changed: 259 additions & 37 deletions

File tree

src/apiClient.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import { SDK_VERSION } from './version.js';
1111
import FormData from 'form-data';
1212
import { snakeCase } from 'change-case';
1313

14+
/**
15+
* The header key for the debugging flow ID
16+
*/
17+
export const FLOW_ID_HEADER = 'x-fastly-id';
18+
/**
19+
* The header key for the request ID
20+
*/
21+
export const REQUEST_ID_HEADER = 'x-request-id';
22+
1423
/**
1524
* Options for a request to the Nylas API
1625
* @property path The path to the API endpoint
@@ -162,6 +171,12 @@ export default class APIClient {
162171
throw new Error('Failed to fetch response');
163172
}
164173

174+
const headers = response?.headers?.entries
175+
? Object.fromEntries(response.headers.entries())
176+
: {};
177+
const flowId = headers[FLOW_ID_HEADER];
178+
const requestId = headers[REQUEST_ID_HEADER];
179+
165180
if (response.status > 299) {
166181
const text = await response.text();
167182
let error: Error;
@@ -175,13 +190,27 @@ export default class APIClient {
175190
options.path.includes('connect/revoke');
176191

177192
if (isAuthRequest) {
178-
error = new NylasOAuthError(camelCaseError, response.status);
193+
error = new NylasOAuthError(
194+
camelCaseError,
195+
response.status,
196+
requestId,
197+
flowId,
198+
headers
199+
);
179200
} else {
180-
error = new NylasApiError(camelCaseError, response.status);
201+
error = new NylasApiError(
202+
camelCaseError,
203+
response.status,
204+
requestId,
205+
flowId,
206+
headers
207+
);
181208
}
182209
} catch (e) {
183210
throw new Error(
184-
`Received an error but could not parse response from the server: ${text}`
211+
`Received an error but could not parse response from the server${
212+
flowId ? ` with flow ID ${flowId}` : ''
213+
}: ${text}`
185214
);
186215
}
187216

@@ -234,10 +263,17 @@ export default class APIClient {
234263
}
235264

236265
async requestWithResponse<T>(response: Response): Promise<T> {
266+
const headers = response?.headers?.entries
267+
? Object.fromEntries(response.headers.entries())
268+
: {};
269+
const flowId = headers[FLOW_ID_HEADER];
237270
const text = await response.text();
238271

239272
try {
240273
const responseJSON = JSON.parse(text);
274+
// Inject the flow ID and headers into the response
275+
responseJSON.flowId = flowId;
276+
responseJSON.headers = headers;
241277
return objKeysToCamelCase(responseJSON, ['metadata']);
242278
} catch (e) {
243279
throw new Error(`Could not parse response from the server: ${text}`);

src/models/error.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ export abstract class AbstractNylasApiError extends Error {
1010
* The HTTP status code of the error response.
1111
*/
1212
statusCode?: number;
13+
/**
14+
* The flow ID
15+
* Provide this to Nylas support to help trace requests and responses
16+
*/
17+
flowId?: string | null;
18+
/**
19+
* The response headers
20+
*/
21+
headers?: Record<string, string>;
1322
}
1423

1524
/**
@@ -31,10 +40,18 @@ export class NylasApiError extends AbstractNylasApiError
3140
*/
3241
providerError: any;
3342

34-
constructor(apiError: NylasApiErrorResponse, statusCode?: number) {
43+
constructor(
44+
apiError: NylasApiErrorResponse,
45+
statusCode?: number,
46+
requestId?: string,
47+
flowId?: string,
48+
headers?: Record<string, string>
49+
) {
3550
super(apiError.error.message);
3651
this.type = apiError.error.type;
37-
this.requestId = apiError.requestId;
52+
this.requestId = requestId;
53+
this.flowId = flowId;
54+
this.headers = headers;
3855
this.providerError = apiError.error.providerError;
3956
this.statusCode = statusCode;
4057
}
@@ -62,13 +79,22 @@ export class NylasOAuthError extends AbstractNylasApiError
6279
*/
6380
errorUri: string;
6481

65-
constructor(apiError: NylasOAuthErrorResponse, statusCode?: number) {
82+
constructor(
83+
apiError: NylasOAuthErrorResponse,
84+
statusCode?: number,
85+
requestId?: string,
86+
flowId?: string,
87+
headers?: Record<string, string>
88+
) {
6689
super(apiError.errorDescription);
6790
this.error = apiError.error;
6891
this.errorCode = apiError.errorCode;
6992
this.errorDescription = apiError.errorDescription;
7093
this.errorUri = apiError.errorUri;
7194
this.statusCode = statusCode;
95+
this.requestId = requestId;
96+
this.flowId = flowId;
97+
this.headers = headers;
7298
}
7399
}
74100

@@ -84,11 +110,33 @@ export class NylasSdkTimeoutError extends AbstractNylasSdkError {
84110
* The timeout value set in the Nylas SDK, in seconds
85111
*/
86112
timeout: number;
113+
/**
114+
* The request ID
115+
*/
116+
requestId?: string;
117+
/**
118+
* The flow ID
119+
* Provide this to Nylas support to help trace requests and responses
120+
*/
121+
flowId?: string;
122+
/**
123+
* The response headers
124+
*/
125+
headers?: Record<string, string>;
87126

88-
constructor(url: string, timeout: number) {
127+
constructor(
128+
url: string,
129+
timeout: number,
130+
requestId?: string,
131+
flowId?: string,
132+
headers?: Record<string, string>
133+
) {
89134
super('Nylas SDK timed out before receiving a response from the server.');
90135
this.url = url;
91136
this.timeout = timeout;
137+
this.requestId = requestId;
138+
this.flowId = flowId;
139+
this.headers = headers;
92140
}
93141
}
94142

@@ -99,6 +147,8 @@ export class NylasSdkTimeoutError extends AbstractNylasSdkError {
99147
export interface NylasApiErrorResponse {
100148
requestId: string;
101149
error: NylasApiErrorResponseData;
150+
flowId?: string;
151+
headers?: Record<string, string>;
102152
}
103153

104154
/**

src/models/response.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ export interface NylasResponse<T> {
1717
* The request ID
1818
*/
1919
requestId: string;
20+
/**
21+
* The flow ID
22+
* Provide this t
23+
*/
24+
flowId?: string;
25+
/**
26+
* The response headers
27+
*/
28+
headers?: Record<string, string>;
2029
}
2130

2231
/**

0 commit comments

Comments
 (0)