Skip to content

Commit

Permalink
Merge pull request #141 from fireblocks/return-api-response
Browse files Browse the repository at this point in the history
feat: add support for axios response interceptor, remove deprecated methods
  • Loading branch information
yarinvak authored Feb 16, 2023
2 parents 7c075d4 + 0f27495 commit f3aeb3d
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 132 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [12.x, 14.x, 16.x, 18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand Down
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
This repository contains the official Javascript & Typescript SDK for Fireblocks API.
For the complete API reference, go to [API reference](https://docs.fireblocks.com/api/swagger-ui/).

## V4 Migration
Please read the [following](./docs/V4-MIGRATION.md) guide for migration

## Usage
#### Before You Begin
Make sure you have the credentials for Fireblocks API Services. Otherwise, please contact Fireblocks support for further instructions on how to obtain your API credentials.

#### Requirements
- [node.js](https://nodejs.org) v6.3.1 or newer
- [node.js](https://nodejs.org) v12 or newer

#### Installation
`npm install fireblocks-sdk --save`
Expand Down Expand Up @@ -54,3 +57,39 @@ interface SDKOptions {
userAgent?: string;
}
```

#### Axios Interceptor
You can provide the sdk options with an [axios response interceptor](https://axios-http.com/docs/interceptors):
```ts
new FireblocksSDK(privateKey, userId, serverAddress, undefined, {
customAxiosOptions: {
interceptors: {
response: {
onFulfilled: (response) => {
console.log(`Request ID: ${response.headers["x-request-id"]}`);
return response;
},
onRejected: (error) => {
console.log(`Request ID: ${error.response.headers["x-request-id"]}`);
throw error;
}
}
}
}
});
```

#### Error Handling
The SDK throws `AxiosError` upon http errors for API requests.

You can read more about axios error handling [here](https://axios-http.com/docs/handling_errors).

You can get more data on the Fireblocks error using the following fields:

- `error.response.data.code`: The Fireblocks error code, should be provided on support tickets
- `error.response.data.message`: Explanation of the Fireblocks error
- `error.response.headers['x-request-id']`: The request ID correlated to the API request, should be provided on support tickets / Github issues




40 changes: 40 additions & 0 deletions docs/V4-MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## V4 Migration
### `X-REQUEST-ID` Response Header
Using v4 you can now use the response header `x-request-id` which correlates the request to the Fireblocks operation.

You can provide the value of this header in case you have support tickets related to an API operation, or a Github issue.

In case of API request failures the SDK throws an AxiosError that contains the following fields:
```ts
error.response.data; // the error body
error.response.status; // the error status code
error.response.headers; // the error headers
```

- You can get the request-id by using the `error.response.headers['x-request-id']` field
- Another way of getting the request-id for successful operations as well, will be to provide an axios response interceptor

For example, you can provide the sdk options with an axios response interceptor:
```ts
new FireblocksSDK(privateKey, userId, serverAddress, undefined, {
customAxiosOptions: {
interceptors: {
response: {
onFulfilled: (response) => {
console.log(`Request ID: ${response.headers["x-request-id"]}`);
return response;
},
onRejected: (error) => {
console.log(`Request ID: ${error.response.headers["x-request-id"]}`);
throw error;
}
}
}
}
});
```

### Removed deprecated methods
- `getVaultAccounts` method was removed. It is replaced by the `getVaultAccountsWithPageInfo` method
- `getVaultAccount` method was removed. It is replaced by the `getVaultAccountById` method
- `getExchangeAccount` was removed in favour of `getExchangeAccountById`
57 changes: 35 additions & 22 deletions src/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os from "os";
import platform from "platform";
import { IAuthProvider } from "./iauth-provider";
import { RequestOptions } from "./types";
import { RequestOptions, TransactionPageResponse } from "./types";
import { SDKOptions } from "./fireblocks-sdk";
import axios, { AxiosInstance } from "axios";
import { version as SDK_VERSION } from "../package.json";
Expand All @@ -19,6 +19,10 @@ export class ApiClient {
"User-Agent": this.getUserAgent()
}
});

if (options.customAxiosOptions?.interceptors?.response) {
this.axiosInstance.interceptors.response.use(options.customAxiosOptions.interceptors.response.onFulfilled, options.customAxiosOptions.interceptors.response.onRejected);
}
}

private getUserAgent(): string {
Expand All @@ -32,51 +36,60 @@ export class ApiClient {
return userAgent;
}

public async issueGetRequest(path: string, pageMode: boolean = false) {
public async issueGetRequestForTransactionPages(path: string): Promise<TransactionPageResponse> {
const token = this.authProvider.signJwt(path);
const res = await this.axiosInstance.get(path, {
headers: {"Authorization": `Bearer ${token}`}
});
return {
transactions: res.data,
pageDetails: {
prevPage: res.headers["prev-page"] ? res.headers["prev-page"].toString() : "",
nextPage: res.headers["next-page"] ? res.headers["next-page"].toString() : "",
}
};
}

public async issueGetRequest<T>(path: string): Promise<T> {
const token = this.authProvider.signJwt(path);
const res = await this.axiosInstance.get(path, {
headers: {"Authorization": `Bearer ${token}`}
});
if (pageMode) {
return {
transactions: res.data,
pageDetails: {
prevPage: res.headers["prev-page"] ? res.headers["prev-page"].toString() : "",
nextPage: res.headers["next-page"] ? res.headers["next-page"].toString() : "",
}
};
}
return res.data;
}

public async issuePostRequest(path: string, body: any, requestOptions?: RequestOptions) {
public async issuePostRequest<T>(path: string, body: any, requestOptions?: RequestOptions): Promise<T> {
const token = this.authProvider.signJwt(path, body);
const headers: any = {"Authorization": `Bearer ${token}`};
const idempotencyKey = requestOptions?.idempotencyKey;
if (idempotencyKey) {
headers["Idempotency-Key"] = idempotencyKey;
}
return (await this.axiosInstance.post(path, body, {headers})).data;
const response = await this.axiosInstance.post<T>(path, body, {headers});
return response.data;
}

public async issuePutRequest(path: string, body: any) {
public async issuePutRequest<T>(path: string, body: any): Promise<T> {
const token = this.authProvider.signJwt(path, body);
return (await this.axiosInstance.put(path, body, {
const res = (await this.axiosInstance.put<T>(path, body, {
headers: {"Authorization": `Bearer ${token}`}
})).data;
}));
return res.data;
}

public async issuePatchRequest(path: string, body: any) {
public async issuePatchRequest<T>(path: string, body: any): Promise<T> {
const token = this.authProvider.signJwt(path, body);
return (await this.axiosInstance.patch(path, body, {
const res = (await this.axiosInstance.patch<T>(path, body, {
headers: {"Authorization": `Bearer ${token}`}
})).data;
}));
return res.data;
}

public async issueDeleteRequest(path: string) {
public async issueDeleteRequest<T>(path: string): Promise<T> {
const token = this.authProvider.signJwt(path);
return (await this.axiosInstance.delete(path, {
const res = (await this.axiosInstance.delete<T>(path, {
headers: {"Authorization": `Bearer ${token}`}
})).data;
}));
return res.data;
}
}
Loading

0 comments on commit f3aeb3d

Please sign in to comment.