flinks-api 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +563 -0
- package/package.json +35 -0
- package/readme.md +219 -0
- package/src/FlinksApi.ts +474 -0
- package/src/index.ts +2 -0
- package/src/types.ts +532 -0
- package/tsconfig.json +13 -0
- package/tsdown.config.ts +8 -0
package/readme.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Flinks API Wrapper for TypeScript / Nest.js
|
|
2
|
+
|
|
3
|
+
A TypeScript wrapper for the [Flinks API](https://docs.flinks.io/), providing a Flinks API service for Canada and the United States.
|
|
4
|
+
This package does not (yet?) provide a wrapper for each endpoint Flinks provides, but instead lets you manage the Axios configuration for both US and CA instances
|
|
5
|
+
along with your secrets and ASYNC response handling. Please see [Examples](#Examples) for usage examples!
|
|
6
|
+
|
|
7
|
+
Flinks API helps developers access bank account data, financial institution information, and authorization workflows for customers in supported countries.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Environment Variables & Constructor Configuration
|
|
12
|
+
|
|
13
|
+
All configuration for `FlinksApi` should be passed **directly to the constructor**. **Environment variables are not automatically read**; you must supply them when creating an instance of the class.
|
|
14
|
+
|
|
15
|
+
### Required Constructor Parameters
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Description |
|
|
18
|
+
| --------------- | ------------------ |----------------------------------------------------------------------|
|
|
19
|
+
| `country` | `CountryShortcode` | The country code for your API instance (e.g., `CA`,`US`). |
|
|
20
|
+
| `customerId` | `string` | Your Flinks customer ID. |
|
|
21
|
+
| `url` | `string` | Base URL for the Flinks API. |
|
|
22
|
+
| `apiKey` | `string` | Your Flinks public API key. ( "x-api-key" ) |
|
|
23
|
+
| `authKey` | `string` | Flinks authentication key. ( "flinks-auth-key" ) |
|
|
24
|
+
| `authorization` | `string` | Authorization token (Bearer token) if required. ( "Authorization" ) |
|
|
25
|
+
|
|
26
|
+
### Optional Constructor Parameters
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
| --------------- | --------------- | ------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `axiosInstance` | `AxiosInstance` | Custom Axios instance. Required if you want to inject Nest.js `HttpService`. |
|
|
31
|
+
| `logger` | `Logger` | Custom logger. Defaults to `console.log`, `console.error`, etc. Must implement `log` and `error`. |
|
|
32
|
+
|
|
33
|
+
<details>
|
|
34
|
+
<summary><i>Logger type</i></summary>
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
export interface Logger {
|
|
38
|
+
log(message: string, ...optionalParams: any[]): void;
|
|
39
|
+
error(message: string, ...optionalParams: any[]): void;
|
|
40
|
+
info?(message: string, ...optionalParams: any[]): void;
|
|
41
|
+
warn?(message: string, ...optionalParams: any[]): void;
|
|
42
|
+
debug?(message: string, ...optionalParams: any[]): void;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
</details>
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Available Methods in FlinksApi
|
|
50
|
+
|
|
51
|
+
The `FlinksApi` class provides methods to interact with the Flinks backend for performing CRUD operations and authorization-related tasks. Users of this package obtain a country-specific instance via `useCountry()` and then call the methods listed below.
|
|
52
|
+
|
|
53
|
+
### Method Overview
|
|
54
|
+
|
|
55
|
+
| Method | Description |
|
|
56
|
+
|--------|----------------------------------------------------------------------------------------------------------------------|
|
|
57
|
+
| `getFlinksUrl()` | Returns the base URL for the registered Flinks account. |
|
|
58
|
+
| `getAuthorizeToken()` | Generates an authorization token from Flinks, used for subsequent requests. |
|
|
59
|
+
| `getRequestId(loginId: string, options?: FlinksAuthorizeOptions)` | Generates an authorize token and returns a request ID associated with the provided login ID. |
|
|
60
|
+
| `receivePendingData<T>(pendingPackage: PendingPackage)` | Polls the Flinks API for pending data until completion (handling 202 responses). |
|
|
61
|
+
| `post<T>(path: string, options: FlinksPostOptions)` | Sends a POST request to the Flinks API and handles pending responses if any. |
|
|
62
|
+
| `patch<T>(path: string, options: FlinksPatchOptions)` | Sends a PATCH request to the Flinks API and handles pending responses if any. |
|
|
63
|
+
| `get(path: string, options: FlinksGetOptions)` | Sends a GET request to the Flinks API and handles pending responses if any. |
|
|
64
|
+
| `delete(path: string, options: FlinksDeleteOptions)` | Sends a DELETE request to the Flinks API and handles pending responses if any. |
|
|
65
|
+
|
|
66
|
+
### Notes
|
|
67
|
+
|
|
68
|
+
- All methods automatically manage headers including API key, auth key, and bearer token when requested.
|
|
69
|
+
- Methods handle asynchronous 202 responses by polling with `receivePendingData` until the operation completes.
|
|
70
|
+
- `useCountry()` must be used first to obtain the correct country-specific instance of `FlinksApi`.
|
|
71
|
+
|
|
72
|
+
### Examples
|
|
73
|
+
These examples come directly from the Nest.js project which I wrote this package for.
|
|
74
|
+
#### Authorize Example:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
/**
|
|
78
|
+
* Fetches the GenerateAuthorizeToken endpoint for a Token that is required for all non-user Flinks operations
|
|
79
|
+
* https://docs.flinks.com/reference/generateauthorizetoken
|
|
80
|
+
* GenerateAuthorizeToken serves the same function as the /authorize/ endpoint, although It returns an AuthorizeToken instead of requestId.
|
|
81
|
+
*
|
|
82
|
+
* @Return AuthorizeToken Token string that is valid for 30 minutes.
|
|
83
|
+
* */
|
|
84
|
+
async getAuthorizeToken(countryShortcode: CountryShortcode): Promise<FlinksAuthorizeToken> {
|
|
85
|
+
const flinksApi = this.flinksApi.useCountry(countryShortcode);
|
|
86
|
+
return flinksApi.getAuthorizeToken();
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### GetAccountsDetail Example:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
/**
|
|
94
|
+
* Gets account details for a Flinks entity.
|
|
95
|
+
*
|
|
96
|
+
* Returns the failed authorization response if there is an error.
|
|
97
|
+
*
|
|
98
|
+
* @param flinksCredentials
|
|
99
|
+
* @param getCachedDetails
|
|
100
|
+
*/
|
|
101
|
+
async getAccountDetails(
|
|
102
|
+
requestId: string,
|
|
103
|
+
country: CountryShortcode,
|
|
104
|
+
options?:BankInfoOptions,
|
|
105
|
+
): Promise<IFlinksDetails> {
|
|
106
|
+
const flinksApi = this.flinksApi.useCountry(country);
|
|
107
|
+
const body = {
|
|
108
|
+
requestId: requestId,
|
|
109
|
+
WithBalance: true,
|
|
110
|
+
...options,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const flinksDetails = await flinksApi.post<IFlinksDetails>(
|
|
114
|
+
"BankingServices/GetAccountsDetail",
|
|
115
|
+
{
|
|
116
|
+
includeHeaders: [FlinksHeaderOptions.ApiKey],
|
|
117
|
+
body,
|
|
118
|
+
customTimeout: 300000, // Timeout set to 5 minutes
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
if (!flinksDetails) {
|
|
122
|
+
throw new Error("Flinks Details fetch failed!");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return flinksDetails;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Using Flinks-api as a Nest.js service
|
|
132
|
+
|
|
133
|
+
You can inject environment variables from a Nest.js `ConfigService`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
137
|
+
import { FlinksApi } from "flinks-api";
|
|
138
|
+
import { CountryShortcode } from "flinks-api/dist/index";
|
|
139
|
+
import { AppConfigService } from "../app-config/app-config.service";
|
|
140
|
+
import { HttpService } from "@nestjs/axios";
|
|
141
|
+
import { ModuleRef } from "@nestjs/core";
|
|
142
|
+
|
|
143
|
+
@Injectable()
|
|
144
|
+
export class FlinksApiService {
|
|
145
|
+
constructor(protected readonly moduleRef: ModuleRef) {}
|
|
146
|
+
|
|
147
|
+
private readonly countryMap = {
|
|
148
|
+
[CountryShortcode.Canada]: FlinksCaApiService,
|
|
149
|
+
[CountryShortcode['United States']]: FlinksUsApiService,
|
|
150
|
+
} as const;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* FlinksApi Factory method.
|
|
154
|
+
*
|
|
155
|
+
* Returns an instance of the FlinksApi class with the specified country's API service.
|
|
156
|
+
* Throws an error if an unsupported CountryShortcode is provided.
|
|
157
|
+
*/
|
|
158
|
+
useCountry(country: CountryShortcode): FlinksCaApiService | FlinksUsApiService {
|
|
159
|
+
const service = this.moduleRef.get(this.countryMap[country], { strict: false });
|
|
160
|
+
if (!service) {
|
|
161
|
+
throw new Error(`Unsupported country: ${country}`);
|
|
162
|
+
}
|
|
163
|
+
return service;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Country-specific services
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
@Injectable()
|
|
174
|
+
export class FlinksCaApiService extends FlinksApi {
|
|
175
|
+
constructor(
|
|
176
|
+
protected readonly configService: AppConfigService,
|
|
177
|
+
protected readonly httpService: HttpService,
|
|
178
|
+
) {
|
|
179
|
+
super({
|
|
180
|
+
country: CountryShortcode.Canada,
|
|
181
|
+
customerId: configService.flinksCaCustomerId,
|
|
182
|
+
url: configService.flinksCaUrl,
|
|
183
|
+
apiKey: configService.flinksCaXApiKey,
|
|
184
|
+
authKey: configService.flinksCaAuthKey,
|
|
185
|
+
bearerToken: configService.flinksCaBearerToken,
|
|
186
|
+
logger: new Logger(FlinksCaApiService.name),
|
|
187
|
+
axiosInstance: httpService.axiosRef,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Injectable()
|
|
193
|
+
export class FlinksUsApiService extends FlinksApi {
|
|
194
|
+
constructor(
|
|
195
|
+
protected readonly configService: AppConfigService,
|
|
196
|
+
protected readonly httpService: HttpService,
|
|
197
|
+
) {
|
|
198
|
+
super({
|
|
199
|
+
country: CountryShortcode["United States"],
|
|
200
|
+
customerId: configService.flinksUsCustomerId,
|
|
201
|
+
url: configService.flinksUsUrl,
|
|
202
|
+
apiKey: configService.flinksUsXApiKey,
|
|
203
|
+
authKey: configService.flinksUsAuthKey,
|
|
204
|
+
bearerToken: configService.flinksUsBearerToken,
|
|
205
|
+
logger: new Logger(FlinksUsApiService.name),
|
|
206
|
+
axiosInstance: httpService.axiosRef,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### References
|
|
215
|
+
|
|
216
|
+
* [Flinks](https://www.flinks.com/)
|
|
217
|
+
* [Flinks API Documentation](https://docs.flinks.com/reference/flinks-api)
|
|
218
|
+
* [Jeremy Cox](https://jcox.ca)
|
|
219
|
+
* [Clove Financial](https://helloclove.com)
|
package/src/FlinksApi.ts
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FlinksGetOptions,
|
|
3
|
+
FlinksResponse,
|
|
4
|
+
FlinksPatchOptions,
|
|
5
|
+
PendingPackage,
|
|
6
|
+
FlinksPostOptions,
|
|
7
|
+
FlinksDataOptions,
|
|
8
|
+
FlinksHeaderOptions,
|
|
9
|
+
FlinksDeleteOptions,
|
|
10
|
+
FlinksAuthorizeOptions,
|
|
11
|
+
SecurityResponses,
|
|
12
|
+
IFlinksAuthorize,
|
|
13
|
+
IFlinksToken,
|
|
14
|
+
IFlinksResponse,
|
|
15
|
+
FlinksAuthorizeToken, CountryShortcode, FlinksApiConfiguration, Logger,
|
|
16
|
+
} from "./types";
|
|
17
|
+
import axios, {AxiosInstance, AxiosResponse} from "axios";
|
|
18
|
+
|
|
19
|
+
const MAX_API_ATTEMPTS: number = 20;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This is our Flinks API class, which aims to standardize the way we:
|
|
23
|
+
* - Post
|
|
24
|
+
* - Patch
|
|
25
|
+
* - Get
|
|
26
|
+
* - Delete
|
|
27
|
+
* Data when integrating with the Flinks Backend.
|
|
28
|
+
*
|
|
29
|
+
* Calling the API class itself will not work, instead you must call the "useCountry" factory method
|
|
30
|
+
* to get an Instance of the FlinksApi specific to the provided CountryCode.
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
export class FlinksApi {
|
|
34
|
+
private static interceptorInstalled = false;
|
|
35
|
+
|
|
36
|
+
constructor(config:FlinksApiConfiguration
|
|
37
|
+
) {
|
|
38
|
+
this.country = config.country;
|
|
39
|
+
this.customerId = config.customerId;
|
|
40
|
+
this.url=config.url;
|
|
41
|
+
this.apiKey = config.apiKey;
|
|
42
|
+
this.authKey = config.authKey;
|
|
43
|
+
this.bearerToken = config.bearerToken;
|
|
44
|
+
this.axios = config.axiosInstance ?? axios.create({
|
|
45
|
+
baseURL: `${this.url}/${this.customerId}`,
|
|
46
|
+
headers: {
|
|
47
|
+
"accept": "application/json",
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
...(this.authKey ? { "flinks-auth-key": this.authKey } : {}),
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
this.logger = {
|
|
53
|
+
log: config.logger?.log ?? console.log,
|
|
54
|
+
error: config.logger?.error ?? console.error,
|
|
55
|
+
info: config.logger?.info ?? console.info,
|
|
56
|
+
warn: config.logger?.warn ?? console.warn,
|
|
57
|
+
debug: config.logger?.debug ?? console.debug,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
protected country: CountryShortcode;
|
|
61
|
+
protected customerId: string;
|
|
62
|
+
protected url: string;
|
|
63
|
+
protected apiKey: string;
|
|
64
|
+
protected authKey: string;
|
|
65
|
+
protected bearerToken: string;
|
|
66
|
+
protected axios: AxiosInstance;
|
|
67
|
+
protected logger: Required<Logger> ;
|
|
68
|
+
|
|
69
|
+
private REQUEST_TIMEOUT = 30000;
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Returns a Flinks URL for being registered under a VoPay account,
|
|
74
|
+
* based on the Country this class is registered to return
|
|
75
|
+
*/
|
|
76
|
+
public getFlinksUrl(): string {
|
|
77
|
+
return `${this.url}/${this.customerId}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private flinksApiUrlBuilder(
|
|
81
|
+
path: string,
|
|
82
|
+
options?: FlinksDataOptions,
|
|
83
|
+
): string {
|
|
84
|
+
let pathParams: string = "";
|
|
85
|
+
options?.pathParams?.map((param:string) => {
|
|
86
|
+
pathParams = `${pathParams}/${param}`;
|
|
87
|
+
});
|
|
88
|
+
const queryParams = new URLSearchParams();
|
|
89
|
+
Object.entries(options?.queryParams || []).map((param) => {
|
|
90
|
+
queryParams.append(param[0], param[1]);
|
|
91
|
+
});
|
|
92
|
+
return `${this.url}/${this.customerId}/${path}${pathParams}${queryParams.size > 0 ? `?${queryParams.toString()}` : ""}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public async getAuthorizeToken(): Promise<FlinksAuthorizeToken> {
|
|
96
|
+
const data = await this.post<IFlinksToken>(
|
|
97
|
+
"BankingServices/GenerateAuthorizeToken",
|
|
98
|
+
{
|
|
99
|
+
includeHeaders: [FlinksHeaderOptions.AuthKey],
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
if (data.HttpStatusCode !== 200) {
|
|
103
|
+
const err = `Status ${data.HttpStatusCode}: ${data.FlinksCode} | ${data.Message}`;
|
|
104
|
+
throw new Error(`Failed to generate authorize token! \n ${err}`);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
token: data.Token,
|
|
108
|
+
timestamp: Date.now(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generates an Authorize token & turns it into a Request ID
|
|
114
|
+
* associated with the provided Login ID
|
|
115
|
+
*
|
|
116
|
+
* Only returns the RequestID
|
|
117
|
+
*
|
|
118
|
+
* @param loginId
|
|
119
|
+
*/
|
|
120
|
+
public async getRequestId(
|
|
121
|
+
loginId: string,
|
|
122
|
+
options?: FlinksAuthorizeOptions,
|
|
123
|
+
): Promise<IFlinksAuthorize> {
|
|
124
|
+
if (!this.country) {
|
|
125
|
+
throw new Error("No locale provided for flinks!!");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const securityResponses: SecurityResponses[] |undefined = options?.mfaResponse;
|
|
129
|
+
const params = {
|
|
130
|
+
LoginId: loginId,
|
|
131
|
+
MostRecentCached:
|
|
132
|
+
options?.mostRecentCached === undefined
|
|
133
|
+
? true
|
|
134
|
+
: options?.mostRecentCached,
|
|
135
|
+
Save: options?.save,
|
|
136
|
+
Tag: options?.tag,
|
|
137
|
+
SecurityResponses: securityResponses,
|
|
138
|
+
requestId: options?.requestId,
|
|
139
|
+
};
|
|
140
|
+
const authorizeToken = await this.getAuthorizeToken();
|
|
141
|
+
|
|
142
|
+
// if (this.configService.flinksDebug) {
|
|
143
|
+
// this.logger.debug("Flinks Debug Info:", {
|
|
144
|
+
// loginId,
|
|
145
|
+
// authorizeToken,
|
|
146
|
+
// date: new Date().toISOString(),
|
|
147
|
+
// });
|
|
148
|
+
// }
|
|
149
|
+
|
|
150
|
+
const response = await this.post<IFlinksAuthorize>(
|
|
151
|
+
"BankingServices/Authorize",
|
|
152
|
+
{
|
|
153
|
+
headers: {
|
|
154
|
+
"flinks-auth-key": authorizeToken.token,
|
|
155
|
+
},
|
|
156
|
+
body: params,
|
|
157
|
+
customTimeout: options?.customTimeout || 180000, // Timeout set to 3 minutes by default
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// TODO: Add debugging to constructor.
|
|
162
|
+
// if (this.configService.flinksDebug) {
|
|
163
|
+
// this.logger.log(response);
|
|
164
|
+
// }
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
response?.Message ===
|
|
168
|
+
// TODO: this string may change. Find a better way to check if loginId no longer exists
|
|
169
|
+
"Request source is unsupported. (Parameter 'requestSource')\nActual value was Unknown."
|
|
170
|
+
) {
|
|
171
|
+
this.logger.log(
|
|
172
|
+
`Flinks Account with login ID ${loginId} no longer exists! This account should be disabled!`,
|
|
173
|
+
);
|
|
174
|
+
return response;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!response?.RequestId) {
|
|
178
|
+
throw new Error("Response does not contain RequestId!");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return response;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public async receivePendingData<T extends IFlinksResponse>(pendingPackage: PendingPackage): Promise<T>{
|
|
185
|
+
const delay = (ms: number) => {
|
|
186
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
|
+
};
|
|
188
|
+
//Initial delay
|
|
189
|
+
await delay(2000);
|
|
190
|
+
|
|
191
|
+
for (let attempts = 1; attempts <= MAX_API_ATTEMPTS; attempts++) {
|
|
192
|
+
// if (this.configService.flinksDebug) {
|
|
193
|
+
// this.logger.log("attempts", attempts);
|
|
194
|
+
// this.logger.log("before request", Date.now());
|
|
195
|
+
// }
|
|
196
|
+
let response: AxiosResponse<T>;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
response = await this.axios.get(
|
|
200
|
+
`${this.url}/${this.customerId}/${pendingPackage.PathParam}Async/${pendingPackage.requestId}`,
|
|
201
|
+
{
|
|
202
|
+
headers: pendingPackage.headers,
|
|
203
|
+
timeout: this.REQUEST_TIMEOUT,
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw new Error("Absolute failure when attempting async fetch.");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
switch (response.status) {
|
|
211
|
+
case 200:
|
|
212
|
+
return response.data;
|
|
213
|
+
|
|
214
|
+
case 202:
|
|
215
|
+
await delay(10000);
|
|
216
|
+
this.logger.log("still waiting 202: ", attempts);
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
default:
|
|
220
|
+
this.logger.error(
|
|
221
|
+
`Flinks async request failed with status ${response.status}, Status text: ${response.statusText}`,
|
|
222
|
+
);
|
|
223
|
+
if (response.data) {
|
|
224
|
+
this.logger.error(
|
|
225
|
+
`Additional information from Flinks: Flinks Code: ${response.data.FlinksCode}, Message: ${response.data.Message}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
await delay(10000);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw new Error("Failed to retrieve pending data");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* This function will create a POST request to flinks, and manage any 202 status responses received.
|
|
237
|
+
*
|
|
238
|
+
* Additional allowed options:
|
|
239
|
+
* Body - JSON body object
|
|
240
|
+
*
|
|
241
|
+
* !! Path should omit the first "/" !!
|
|
242
|
+
*
|
|
243
|
+
*
|
|
244
|
+
* @param path
|
|
245
|
+
* @param body
|
|
246
|
+
*/
|
|
247
|
+
public async post<T extends IFlinksResponse>(
|
|
248
|
+
path: string,
|
|
249
|
+
options: FlinksPostOptions,
|
|
250
|
+
): Promise<T> {
|
|
251
|
+
if (!this.country) {
|
|
252
|
+
throw new Error("No locale set for Flinks API!");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const headers: Record<string, string> = {
|
|
256
|
+
...options.headers,
|
|
257
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.ApiKey) && { "x-api-key": this.apiKey }),
|
|
258
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.AuthKey) && { "flinks-auth-key": this.authKey }),
|
|
259
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.BearerToken) && { "Authorization": this.bearerToken }),
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const response: AxiosResponse<T> =
|
|
263
|
+
await this.axios.post<T>(
|
|
264
|
+
this.flinksApiUrlBuilder(path, options),
|
|
265
|
+
options.body || {},
|
|
266
|
+
// TODO: validate status should eventually actually validate the status
|
|
267
|
+
{
|
|
268
|
+
headers: headers,
|
|
269
|
+
timeout: options?.customTimeout || this.REQUEST_TIMEOUT,
|
|
270
|
+
validateStatus: () => true,
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
if (!(response.status >= 200 && response.status < 300)) {
|
|
275
|
+
if (response.data && response.data.Message) {
|
|
276
|
+
this.logger.error(
|
|
277
|
+
`Flinks Post had status: ${response.status} and returned message ${response.data.Message}`,
|
|
278
|
+
);
|
|
279
|
+
} else {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Flinks Post failed with status ${response.status}: ${response.statusText}`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (response.data.HttpStatusCode === 202) {
|
|
286
|
+
const pendingPackage: PendingPackage = {
|
|
287
|
+
PathParam: path,
|
|
288
|
+
requestId: response.data.RequestId,
|
|
289
|
+
HttpStatusCode: 202,
|
|
290
|
+
headers: headers,
|
|
291
|
+
};
|
|
292
|
+
//
|
|
293
|
+
return await this.receivePendingData(pendingPackage);
|
|
294
|
+
}
|
|
295
|
+
return response.data;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Patch method of the Flinks API.
|
|
300
|
+
*
|
|
301
|
+
* Additional allowed options:
|
|
302
|
+
* Body - JSON body object
|
|
303
|
+
*
|
|
304
|
+
* !! Path should omit the first "/" !!
|
|
305
|
+
*
|
|
306
|
+
* @param path
|
|
307
|
+
* @param options
|
|
308
|
+
*/
|
|
309
|
+
public async patch<T extends IFlinksResponse>(
|
|
310
|
+
path: string,
|
|
311
|
+
options: FlinksPatchOptions,
|
|
312
|
+
): Promise<T> {
|
|
313
|
+
if (!this.country) {
|
|
314
|
+
throw new Error("No locale set for Flinks API!");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const headers: Record<string, string> = {
|
|
318
|
+
...options.headers,
|
|
319
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.ApiKey) && { "x-api-key": this.apiKey }),
|
|
320
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.AuthKey) && { "flinks-auth-key": this.authKey }),
|
|
321
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.BearerToken) && { "Authorization": this.bearerToken }),
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
let pathParams: string = "";
|
|
325
|
+
options.pathParams?.map((param) => {
|
|
326
|
+
pathParams = `${pathParams}/${param}`;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const response: AxiosResponse<T> = await this.axios.patch<T>(
|
|
330
|
+
`${this.url}/${this.customerId}/${path}${pathParams}`,
|
|
331
|
+
options.body || {},
|
|
332
|
+
{
|
|
333
|
+
headers: headers,
|
|
334
|
+
timeout: options?.customTimeout || this.REQUEST_TIMEOUT,
|
|
335
|
+
},
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
if (!(response.status >= 200 && response.status < 300)) {
|
|
339
|
+
if (response.data) {
|
|
340
|
+
this.logger.error(
|
|
341
|
+
`Flinks Patch had status: ${response.status} and returned message ${response.data.Message}`,
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`Flinks Patch failed with status ${response.status}: ${response.statusText}`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (response.data.HttpStatusCode === 202) {
|
|
350
|
+
const pendingPackage: PendingPackage = {
|
|
351
|
+
PathParam: path,
|
|
352
|
+
requestId: options.requestId,
|
|
353
|
+
HttpStatusCode: 202,
|
|
354
|
+
headers: headers,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
return await this.receivePendingData(pendingPackage);
|
|
358
|
+
}
|
|
359
|
+
return response.data;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get method for the Flinks API
|
|
364
|
+
*
|
|
365
|
+
*
|
|
366
|
+
* !! Path should omit the first "/" !!
|
|
367
|
+
*
|
|
368
|
+
* @param path
|
|
369
|
+
* @param options
|
|
370
|
+
*/
|
|
371
|
+
public async get(path: string, options: FlinksGetOptions) {
|
|
372
|
+
if (!this.country) {
|
|
373
|
+
throw new Error("No locale set for Flinks API!");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const headers: Record<string, string> = {
|
|
377
|
+
...options.headers,
|
|
378
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.ApiKey) && { "x-api-key": this.apiKey }),
|
|
379
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.AuthKey) && { "flinks-auth-key": this.authKey }),
|
|
380
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.BearerToken) && { "Authorization": this.bearerToken }),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
const response = await this.axios.get<FlinksResponse>(
|
|
385
|
+
this.flinksApiUrlBuilder(path, options),
|
|
386
|
+
{
|
|
387
|
+
headers: headers,
|
|
388
|
+
timeout: options?.customTimeout || this.REQUEST_TIMEOUT,
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
if (!(response.status >= 200 && response.status < 300)) {
|
|
393
|
+
if (response.data) {
|
|
394
|
+
this.logger.error(
|
|
395
|
+
`Flinks Get had status: ${response.status} and returned message ${response.data.Message}`,
|
|
396
|
+
);
|
|
397
|
+
} else {
|
|
398
|
+
throw new Error(
|
|
399
|
+
`Flinks Get failed with status ${response.status}: ${response.statusText}`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (response.data.HttpStatusCode === 202) {
|
|
405
|
+
const pendingPackage: PendingPackage = {
|
|
406
|
+
PathParam: path,
|
|
407
|
+
requestId: options.requestId,
|
|
408
|
+
HttpStatusCode: 202,
|
|
409
|
+
headers: headers,
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return await this.receivePendingData(pendingPackage);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return response.data;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Delete method for our Flinks API
|
|
420
|
+
*
|
|
421
|
+
* !! Path should omit the first "/" !!
|
|
422
|
+
*
|
|
423
|
+
* @param path
|
|
424
|
+
* @param options
|
|
425
|
+
*/
|
|
426
|
+
public async delete(
|
|
427
|
+
path: string,
|
|
428
|
+
options: FlinksDeleteOptions,
|
|
429
|
+
): Promise<FlinksResponse> {
|
|
430
|
+
if (!this.country) {
|
|
431
|
+
throw new Error("No locale set for Flinks API!");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const headers: Record<string, string> = {
|
|
435
|
+
...options.headers,
|
|
436
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.ApiKey) && { "x-api-key": this.apiKey }),
|
|
437
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.AuthKey) && { "flinks-auth-key": this.authKey }),
|
|
438
|
+
...(options.includeHeaders?.includes(FlinksHeaderOptions.BearerToken) && { "Authorization": this.bearerToken }),
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const response = await this.axios.delete<FlinksResponse>(
|
|
442
|
+
this.flinksApiUrlBuilder(path, options),
|
|
443
|
+
{
|
|
444
|
+
headers: headers,
|
|
445
|
+
timeout: options?.customTimeout || this.REQUEST_TIMEOUT,
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (!(response.status >= 200 && response.status < 300)) {
|
|
450
|
+
if (response.data) {
|
|
451
|
+
this.logger.error(
|
|
452
|
+
`Flinks Delete had status: ${response.status} and returned message ${response.data.Message}`,
|
|
453
|
+
);
|
|
454
|
+
} else {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`Flinks Delete failed with status ${response.status}: ${response.statusText}`,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (response.data.HttpStatusCode === 202) {
|
|
462
|
+
const pendingPackage: PendingPackage = {
|
|
463
|
+
PathParam: path,
|
|
464
|
+
requestId: options.requestId,
|
|
465
|
+
HttpStatusCode: 202,
|
|
466
|
+
headers: headers,
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
return await this.receivePendingData(pendingPackage);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return response.data;
|
|
473
|
+
}
|
|
474
|
+
}
|
package/src/index.ts
ADDED