Skip to content

Commit 33da7e0

Browse files
committed
get rid of amplify dependency, add tests
1 parent 4c15071 commit 33da7e0

6 files changed

Lines changed: 3185 additions & 3540 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@
4848
}
4949
},
5050
"dependencies": {
51-
"@aws-amplify/core": "^4.5.2",
51+
"@aws-crypto/sha256-browser": "^2.0.1",
52+
"@aws-sdk/protocol-http": "^3.58.0",
53+
"@aws-sdk/signature-v4": "^3.58.0",
5254
"amazon-cognito-identity-js": "^5.2.8",
53-
"aws-sdk": "^2.1119.0",
5455
"bunyan": "^1.8.15",
5556
"uuid": "^8.3.2",
5657
"ws": "^8.5.0"

src/common/identity.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { Signer } from "@aws-amplify/core";
21
import { CognitoIdentity, CognitoIdentityCredentials } from "aws-sdk";
32
import { CognitoIdToken, CognitoUserPool, CognitoUser, AuthenticationDetails } from "amazon-cognito-identity-js";
3+
import { getSignedWssUrl } from "./signature";
44

55
// FIXME: Hard coded
66
const region = "eu-west-1";
77
const UserPoolId = "eu-west-1_0GLV9KO1p";
88
const IdentityPoolId = "eu-west-1:bce21571-e3a6-47a4-8032-fd015213405f";
9-
const webSocketUrl = "wss://m9fhs4t5vh.execute-api.eu-west-1.amazonaws.com/dev";
9+
const webSocketHost = "m9fhs4t5vh.execute-api.eu-west-1.amazonaws.com";
1010
const Logins = `cognito-idp.${region}.amazonaws.com/${UserPoolId}`;
1111
const poolData = { UserPoolId, ClientId: "6timr8knllr4frovfvq8r2o6oo" };
1212
const Pool = new CognitoUserPool(poolData);
@@ -18,38 +18,33 @@ export interface BDCredentials {
1818

1919
function getIdToken(Username: string, Password: string): Promise<CognitoIdToken> {
2020
return new Promise((resolve, reject) => {
21-
const params = { Username, Password };
21+
const loginDetails = { Username, Password };
2222
const userData = { Username, Pool };
2323
const cognitoUser = new CognitoUser(userData);
24-
const authenticationDetails = new AuthenticationDetails(params);
24+
const authenticationDetails = new AuthenticationDetails(loginDetails);
2525
cognitoUser.authenticateUser(authenticationDetails, {
26-
onSuccess: result => resolve(result?.getIdToken()),
27-
onFailure: err => reject(err),
26+
onSuccess: (result: any) => resolve(result?.getIdToken()),
27+
onFailure: (err: any) => reject(err),
2828
});
2929
});
3030
}
3131

3232
async function refreshCredsWithToken(idToken: string): Promise<CognitoIdentityCredentials> {
33-
const creds = new CognitoIdentityCredentials(
34-
{
35-
IdentityPoolId,
36-
Logins: { [Logins]: idToken },
37-
},
38-
{ region },
39-
);
33+
const idParams = { IdentityPoolId, Logins: { [Logins]: idToken } };
34+
const creds = new CognitoIdentityCredentials(idParams, { region });
4035
await creds.getPromise();
4136
return creds;
4237
}
4338

4439
export async function getBoilingDataCredentials(username: string, password: string): Promise<BDCredentials> {
4540
const idToken = await getIdToken(username, password);
4641
const creds = await refreshCredsWithToken(idToken.getJwtToken());
47-
const params = {
48-
access_key: creds.data?.Credentials?.AccessKeyId,
49-
secret_key: (<CognitoIdentity.Types.GetCredentialsForIdentityResponse>creds.data)?.Credentials?.SecretKey,
50-
session_token: creds.data?.Credentials?.SessionToken,
51-
};
52-
const signedWebsocketUrl = await Signer.signUrl(webSocketUrl, params);
42+
const accessKeyId = creds.data?.Credentials?.AccessKeyId;
43+
const secretAccessKey = (<CognitoIdentity.Types.GetCredentialsForIdentityResponse>creds.data)?.Credentials?.SecretKey;
44+
const sessionToken = creds.data?.Credentials?.SessionToken;
45+
if (!accessKeyId || !secretAccessKey) throw new Error("Missing credentials (after refresh)!");
46+
const credentials = { accessKeyId, secretAccessKey, sessionToken };
47+
const signedWebsocketUrl = await getSignedWssUrl(webSocketHost, credentials);
5348
const cognitoUsername = idToken.decodePayload()["cognito:username"];
5449
return { cognitoUsername, signedWebsocketUrl };
5550
}

src/common/signature.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { HttpRequest } from "@aws-sdk/protocol-http";
2+
import { SignatureV4 } from "@aws-sdk/signature-v4";
3+
import { Sha256 } from "@aws-crypto/sha256-browser";
4+
import { Credentials, Provider } from "@aws-sdk/types";
5+
6+
function getSigner(region: string, credentials: any): SignatureV4 {
7+
const service = "execute-api";
8+
return new SignatureV4({ credentials, region, service, sha256: Sha256 });
9+
}
10+
11+
export async function getSignedWssUrl(
12+
host: string,
13+
credentials: Credentials | Provider<Credentials>,
14+
protocol = "wss",
15+
path = "/dev",
16+
region = "eu-west-1",
17+
): Promise<string> {
18+
const request = new HttpRequest({ protocol, headers: { host }, hostname: host, path });
19+
const fiveMinsS = 5 * 60;
20+
const signedRequest = await getSigner(region, credentials).presign(request, { expiresIn: fiveMinsS });
21+
const searchParams = signedRequest.query ? Object.entries(signedRequest.query).map(k => [k[0], `${k[1]}`]) : [];
22+
const signatureParams = new URLSearchParams(searchParams).toString();
23+
return `wss://${host}${path}?${signatureParams}`;
24+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`boilingdata run all meta queries 1`] = `
4+
Array [
5+
Array [],
6+
Array [
7+
Object {
8+
"CreationDate": "2021-12-06T12:06:12.060Z",
9+
"Name": "boilingdata-demo",
10+
},
11+
],
12+
Array [
13+
Object {
14+
"awsAccountId": "589434896614",
15+
"externalId": "OPL0DcV9pKiQg7/YXxiKQkwOmYQk7wj9FoBS4P7GNI8=",
16+
},
17+
],
18+
Array [
19+
Object {
20+
"pragma": "s3AccessRoleArn='arn:aws:iam::123456789012:role/EXAMPLE';",
21+
},
22+
],
23+
Array [
24+
Object {
25+
"ETag": "\\"83cc3a5587b8c0b16873b2485d063da3-93\\"",
26+
"Key": "demo.parquet",
27+
"LastModified": "2021-12-06T12:35:46.000Z",
28+
"Size": 777673114,
29+
"StorageClass": "STANDARD",
30+
},
31+
Object {
32+
"ETag": "\\"e10d77e8fe329838bb8984659e5090cb-103\\"",
33+
"Key": "demo2.duckdb.zst",
34+
"LastModified": "2022-04-17T12:15:24.000Z",
35+
"Size": 858335085,
36+
"StorageClass": "STANDARD",
37+
},
38+
Object {
39+
"ETag": "\\"83cc3a5587b8c0b16873b2485d063da3-93\\"",
40+
"Key": "demo2.parquet",
41+
"LastModified": "2022-04-17T12:13:42.000Z",
42+
"Size": 777673114,
43+
"StorageClass": "STANDARD",
44+
},
45+
Object {
46+
"ETag": "",
47+
"LastModified": "",
48+
"Name": "cc-demo",
49+
"Size": 0,
50+
"StorageClass": "",
51+
},
52+
Object {
53+
"ETag": "",
54+
"LastModified": "",
55+
"Name": "uploads",
56+
"Size": 0,
57+
"StorageClass": "",
58+
},
59+
],
60+
]
61+
`;
62+
63+
exports[`boilingdata run multi-key query 1`] = `
64+
Array [
65+
Object {
66+
"count": 44459136,
67+
"key": "s3://boilingdata-demo/demo.parquet",
68+
},
69+
Object {
70+
"count": 44459136,
71+
"key": "s3://boilingdata-demo/demo2.parquet",
72+
},
73+
]
74+
`;
75+
76+
exports[`boilingdata run single query 1`] = `
77+
Array [
78+
Object {
79+
"DOLocationID": 145,
80+
"PULocationID": 145,
81+
"RatecodeID": 1,
82+
"VendorID": 1,
83+
"congestion_surcharge": 0,
84+
"extra": 0.5,
85+
"fare_amount": 3,
86+
"improvement_surcharge": 0.3,
87+
"mta_tax": 0.5,
88+
"passenger_count": 1,
89+
"payment_type": 2,
90+
"store_and_fwd_flag": "N",
91+
"tip_amount": 0,
92+
"tolls_amount": 0,
93+
"total_amount": 4.3,
94+
"tpep_dropoff_datetime": 1556669808000,
95+
"tpep_pickup_datetime": 1556669690000,
96+
"trip_distance": 0,
97+
},
98+
Object {
99+
"DOLocationID": 145,
100+
"PULocationID": 145,
101+
"RatecodeID": 1,
102+
"VendorID": 1,
103+
"congestion_surcharge": 0,
104+
"extra": 0.5,
105+
"fare_amount": 3,
106+
"improvement_surcharge": 0.3,
107+
"mta_tax": 0.5,
108+
"passenger_count": 1,
109+
"payment_type": 2,
110+
"store_and_fwd_flag": "N",
111+
"tip_amount": 0,
112+
"tolls_amount": 0,
113+
"total_amount": 4.3,
114+
"tpep_dropoff_datetime": 1556671047000,
115+
"tpep_pickup_datetime": 1556670954000,
116+
"trip_distance": 1.5,
117+
},
118+
]
119+
`;

src/tests/query.test.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe("boilingdata", () => {
4949
},
5050
});
5151
});
52-
console.log(rows);
52+
expect(rows.sort()).toMatchSnapshot();
5353
});
5454

5555
it("run multi-key query", async () => {
@@ -67,6 +67,35 @@ describe("boilingdata", () => {
6767
},
6868
});
6969
});
70-
console.log(rows);
70+
expect(rows.sort((a, b) => a.key.localeCompare(b.key))).toMatchSnapshot();
71+
});
72+
73+
it("run all meta queries", async () => {
74+
const metaQueries = [
75+
"SELECT * FROM list('s3://');",
76+
"SELECT * FROM list('s3://boilingdata-demo/');",
77+
"SELECT * FROM boilingdata;",
78+
"SELECT * FROM pragmas;",
79+
"SELECT * FROM status;",
80+
];
81+
const rows = await Promise.all(
82+
metaQueries.map(sql => {
83+
return new Promise<any[]>((resolve, reject) => {
84+
const r: any[] = [];
85+
bdInstance.execQuery({
86+
sql,
87+
keys: [],
88+
callbacks: {
89+
onData: (data: IBDDataResponse | unknown) => {
90+
if (isDataResponse(data)) data.data.map(row => r.push(row));
91+
},
92+
onQueryFinished: () => resolve(r),
93+
onLogError: (data: any) => reject(data),
94+
},
95+
});
96+
});
97+
}),
98+
);
99+
expect(rows.sort()).toMatchSnapshot();
71100
});
72101
});

0 commit comments

Comments
 (0)