Skip to content

Commit e6374a1

Browse files
committed
Add method for fetching Data Taps client token
1 parent 1e65346 commit e6374a1

File tree

7 files changed

+98
-930
lines changed

7 files changed

+98
-930
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v18.16
1+
v20

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ async function main() {
8989
}
9090
```
9191

92+
`getTapClientToken()` method can be used to fetch [Data Taps client token](https://www.taps.boilingdata.com/). You need fresh token when sending data to a Data Tap shared to you (or with your own Data Tap too). This API call does not require `connect()` method to be called as the request goes through via REST API rather than WebSocket API.
93+
94+
```typescript
95+
const bdInstance = new BoilingData({ username: process.env["BD_USERNAME"], password: process.env["BD_PASSWORD"] });
96+
// first argument is token lifetime, max "24h", 2nd argument is the sharing user (unless your own Tap). Both arguments are optional.
97+
const tapClientToken = await bdInstance.getTapClientToken("24h", "[email protected]");
98+
```
99+
92100
This repository contains JS/TS BoilingData client SDK that can be used both with NodeJS and in browser. Please see the integration tests on `tests/query.test.ts` for for more examples.
93101

94102
### Callbacks

browser/boilingdata.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/boilingdata/boilingdata.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BDCredentials, getBoilingDataCredentials } from "../common/identity";
1+
import { BDCredentials, getBoilingDataCredentials, getIdToken } from "../common/identity";
22
import { EEngineTypes, EEvent, EMessageTypes, IBDDataQuery, IBDDataResponse } from "./boilingdata.api";
33
import { v4 as uuidv4 } from "uuid";
44
import WebSocket from "isomorphic-ws";
@@ -128,13 +128,31 @@ export function isDataResponse(data: IBDDataResponse | unknown): data is IBDData
128128
return (data as IBDDataResponse).messageType !== undefined && (data as IBDDataResponse).messageType === "DATA";
129129
}
130130

131+
// FIXME: Make org specific
132+
export const apiKey = "Ak7itOEG1N1I7XpFfmYO97NWHRZwEYDmYBL4y0lb";
133+
134+
export function getApiKey(): Promise<string> {
135+
return Promise.resolve(apiKey); // FIXME: Get API key..
136+
}
137+
138+
export async function getReqHeaders(token: string): Promise<{ [k: string]: string }> {
139+
const apikey = await getApiKey();
140+
return {
141+
Authorization: token,
142+
"x-api-key": apikey,
143+
"Content-Type": "application/json",
144+
Accept: "application/json",
145+
};
146+
}
147+
131148
export class BoilingData {
132149
private region: BDAWSRegion;
133150
private creds?: BDCredentials;
134151
private socketInstance: ISocketInstance;
135152
private logger: Console;
136153
private closedPromise?: Promise<void>;
137154
private authcontext!: any;
155+
private cognitoIdToken!: string;
138156

139157
constructor(public props: IBoilingData) {
140158
this.logger = createLogger({ name: "boilingdata", logLevel: this.props.logLevel ?? "info" });
@@ -187,6 +205,32 @@ export class BoilingData {
187205
return this.authcontext;
188206
}
189207

208+
public async getTapClientToken(tokenLifetime = "12h", sharingUser?: string): Promise<string> {
209+
if (this.authcontext?.idToken) {
210+
this.cognitoIdToken = this.authcontext.idToken;
211+
} else if (this.props.username && this.props.password) {
212+
this.cognitoIdToken = (await getIdToken(this.props.username, this.props.password)).getJwtToken();
213+
}
214+
const headers = await getReqHeaders(this.cognitoIdToken); // , { tokenLifetime, vendingSchedule, shareId });
215+
const method = "POST";
216+
const tapTokenUrl = "https://rest.api.test.boilingdata.com/taptoken";
217+
const body = JSON.stringify({ tokenLifetime, sharingUser });
218+
this.logger.debug({ method, tapTokenUrl, headers, body });
219+
const res = await fetch(tapTokenUrl, { method, headers, body });
220+
const resBody = await res.json();
221+
this.logger.debug({ getTapToken: { body: resBody } });
222+
if (!resBody.ResponseCode || !resBody.ResponseText) {
223+
throw new Error("Malformed response from BD API");
224+
}
225+
if (resBody.ResponseCode != "00") {
226+
throw new Error(`Failed to fetch token: ${resBody.ResponseText}`);
227+
}
228+
if (!resBody.bdTapToken) {
229+
throw new Error("Missing bdStsToken in BD API Response");
230+
}
231+
return resBody.bdTapToken;
232+
}
233+
190234
public async connect(): Promise<void> {
191235
return new Promise((resolve, reject) => {
192236
const sock = this.socketInstance;

src/common/identity.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ export interface BDCredentials {
2323
idToken: any;
2424
}
2525

26-
function getIdToken(Username: string, Password: string, mfa?: number, logger?: Console): Promise<CognitoIdToken> {
26+
export function getIdToken(
27+
Username: string,
28+
Password: string,
29+
mfa?: number,
30+
logger?: Console,
31+
): Promise<CognitoIdToken> {
2732
return new Promise((resolve, reject) => {
2833
try {
2934
const loginDetails = { Username, Password };

src/tests/taptoken.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { BoilingData } from "../boilingdata/boilingdata";
2+
3+
const username = process.env["BD_USERNAME"];
4+
const password = process.env["BD_PASSWORD"];
5+
if (!password || !username) throw new Error("Set BD_USERNAME and BD_PASSWORD envs");
6+
7+
describe("boilingdata with DuckDB", () => {
8+
const bdInstance: BoilingData = new BoilingData({ username, password, region: "eu-west-1" });
9+
10+
it("can get client TapToken with default arguments", async () => {
11+
const tapClientToken = await bdInstance.getTapClientToken();
12+
console.log({ tapClientToken });
13+
expect(tapClientToken.length).toBeGreaterThan(0);
14+
});
15+
16+
it("can get client TapToken with default arguments", async () => {
17+
const tapClientToken = await bdInstance.getTapClientToken("24h", "[email protected]");
18+
console.log({ tapClientToken });
19+
expect(tapClientToken.length).toBeGreaterThan(0);
20+
});
21+
});

0 commit comments

Comments
 (0)