Skip to content

Commit 416285e

Browse files
committed
feat(clients): blob helper tests
1 parent a948899 commit 416285e

File tree

11 files changed

+254
-50
lines changed

11 files changed

+254
-50
lines changed

packages/types/src/blob/blob-types.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@ import { RuntimeBlobTypes } from "./runtime-blob-types.node";
33
/**
44
* @public
55
*
6-
* A set of types that can be used as inputs for the blob type.
6+
* A union of types that can be used as inputs for the service model
7+
* "blob" type when it represents the request's entire payload or body.
8+
*
9+
* For example, in Lambda::invoke, the payload is modeled as a blob type
10+
* and this union applies to it.
11+
* In contrast, in Lambda::createFunction the Zip file option is a blob type,
12+
* but is not the (entire) payload and this union does not apply.
13+
*
14+
* Note: not all types are signable by the standard SignatureV4 signer when
15+
* used as the request body. For example, in Node.js a Readable stream
16+
* is not signable by the default signer.
17+
* They are included in the union because it may work in some cases,
18+
* but the expected types are primarily string and Uint8Array.
19+
*
20+
* Additional details may be found in the internal
21+
* function "getPayloadHash" in the SignatureV4 module.
722
*/
823
export type BlobTypes = string | ArrayBuffer | ArrayBufferView | Uint8Array | RuntimeBlobTypes;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
preset: "ts-jest",
3+
testMatch: ["**/*.e2e.spec.ts"],
4+
};

packages/util-stream/package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"build:types": "tsc -p tsconfig.types.json",
1010
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
1111
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
12-
"test": "jest"
12+
"test": "jest",
13+
"test:e2e": "jest -c jest.config.e2e.js"
1314
},
1415
"main": "./dist-cjs/index.js",
1516
"module": "./dist-es/index.js",
@@ -52,13 +53,11 @@
5253
],
5354
"browser": {
5455
"./dist-es/getAwsChunkedEncodingStream": "./dist-es/getAwsChunkedEncodingStream.browser",
55-
"./dist-es/sdk-stream-mixin": "./dist-es/sdk-stream-mixin.browser",
56-
"./dist-es/blob/decode": "./dist-es/blob/decode.browser"
56+
"./dist-es/sdk-stream-mixin": "./dist-es/sdk-stream-mixin.browser"
5757
},
5858
"react-native": {
5959
"./dist-es/getAwsChunkedEncodingStream": "./dist-es/getAwsChunkedEncodingStream.browser",
60-
"./dist-es/sdk-stream-mixin": "./dist-es/sdk-stream-mixin.browser",
61-
"./dist-es/blob/decode": "./dist-es/blob/decode.browser"
60+
"./dist-es/sdk-stream-mixin": "./dist-es/sdk-stream-mixin.browser"
6261
},
6362
"homepage": "https://github.com/aws/aws-sdk-js-v3/tree/main/packages/util-stream",
6463
"repository": {

packages/util-stream/src/blob/Uint8ArrayBlobAdapter.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { BlobAdapter } from "./BlobAdapter";
1+
import { Uint8ArrayBlobAdapter } from "./Uint8ArrayBlobAdapter";
22

3-
describe(BlobAdapter.name, () => {
3+
describe(Uint8ArrayBlobAdapter.name, () => {
44
it("extends Uint8Array", () => {
5-
const blobby = new BlobAdapter(5);
5+
const blobby = new Uint8ArrayBlobAdapter(5);
66

77
blobby[-1] = 8;
88
blobby[0] = 8;
@@ -26,6 +26,7 @@ describe(BlobAdapter.name, () => {
2626
});
2727

2828
it("should transform to string synchronously", () => {
29-
throw new Error("NYI");
29+
const blob = Uint8ArrayBlobAdapter.fromString("test-123");
30+
expect(blob.transformToString()).toEqual("test-123");
3031
});
3132
});

packages/util-stream/src/blob/Uint8ArrayBlobAdapter.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { transformFromObject, transformFromString, transformToString } from "./transforms";
1+
import { transformFromString, transformToString } from "./transforms";
22

33
/**
44
* Adapter for conversions of the native Uint8Array type.
@@ -9,12 +9,10 @@ export class Uint8ArrayBlobAdapter extends Uint8Array {
99
* @param source - such as a string or Stream.
1010
* @returns a new Uint8ArrayBlobAdapter extending Uint8Array.
1111
*/
12-
public static from(source: unknown): Uint8ArrayBlobAdapter {
12+
public static fromString(source: string, encoding = "utf-8"): Uint8ArrayBlobAdapter {
1313
switch (typeof source) {
1414
case "string":
15-
return transformFromString(source as string);
16-
case "object":
17-
return transformFromObject(source as object);
15+
return transformFromString(source, encoding);
1816
default:
1917
throw new Error(`Unsupported conversion from ${typeof source} to Uint8ArrayBlobAdapter.`);
2018
}

packages/util-stream/src/blob/transforms.browser.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import { Readable } from "stream";
1+
import { fromBase64, toBase64 } from "@aws-sdk/util-base64";
2+
import { fromUtf8, toUtf8 } from "@aws-sdk/util-utf8";
23

34
import { Uint8ArrayBlobAdapter } from "./Uint8ArrayBlobAdapter";
45

56
/**
67
* @internal
78
*/
89
export function transformToString(payload: Uint8Array, encoding = "utf-8"): string {
9-
return Buffer.from(payload).toString(encoding as BufferEncoding);
10+
if (encoding === "base64") {
11+
return toBase64(payload);
12+
}
13+
return toUtf8(payload);
1014
}
1115

1216
/**
1317
* @internal
1418
*/
15-
export function transformFromString(str: string): Uint8ArrayBlobAdapter {
16-
return new Uint8ArrayBlobAdapter(Buffer.from(str));
17-
}
18-
19-
/**
20-
* @internal
21-
*/
22-
export function transformFromObject(obj: object): Uint8ArrayBlobAdapter {
23-
return new Uint8ArrayBlobAdapter(obj as any);
19+
export function transformFromString(str: string, encoding?: string): Uint8ArrayBlobAdapter {
20+
if (encoding === "base64") {
21+
return Uint8ArrayBlobAdapter.mutate(fromBase64(str));
22+
}
23+
return Uint8ArrayBlobAdapter.mutate(fromUtf8(str));
2424
}
228 Bytes
Binary file not shown.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* function.zip contains this file.
3+
*/
4+
export const handler = async (event) => {
5+
return event;
6+
};

packages/util-stream/test/setup.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { AttachedPolicy, CreateRoleResponse, GetRoleResponse, IAM } from "@aws-sdk/client-iam";
2+
import {
3+
GetFunctionConfigurationCommandOutput,
4+
Lambda,
5+
Runtime,
6+
waitUntilFunctionActiveV2,
7+
waitUntilFunctionUpdated,
8+
} from "@aws-sdk/client-lambda";
9+
import fs from "fs";
10+
export const FunctionName = "aws-sdk-js-v3-e2e-echo";
11+
export const Handler = "index.handler";
12+
const LAMBDA_ROLE_NAME = "aws-sdk-js-v3-e2e-LambdaRole";
13+
14+
export async function setup() {
15+
const lambda = new Lambda({
16+
region: "us-west-2",
17+
});
18+
const getFn: null | GetFunctionConfigurationCommandOutput = await lambda
19+
.getFunctionConfiguration({
20+
FunctionName,
21+
})
22+
.catch(() => null);
23+
24+
if (getFn) {
25+
return;
26+
}
27+
28+
const iam = new IAM({
29+
region: "us-west-2",
30+
});
31+
32+
const roleName = LAMBDA_ROLE_NAME;
33+
const role: null | GetRoleResponse | CreateRoleResponse = await iam
34+
.getRole({
35+
RoleName: roleName,
36+
})
37+
.catch(() => null);
38+
39+
if (!role) {
40+
console.info("Creating role", roleName);
41+
await iam.createRole({
42+
RoleName: roleName,
43+
Path: "/",
44+
Description: "aws sdk js v3 lambda test role",
45+
AssumeRolePolicyDocument: JSON.stringify({
46+
Version: "2012-10-17",
47+
Statement: [
48+
{
49+
Effect: "Allow",
50+
Action: ["sts:AssumeRole"],
51+
Principal: {
52+
Service: ["lambda.amazonaws.com"],
53+
},
54+
},
55+
],
56+
}),
57+
});
58+
} else {
59+
console.info("Role exists", roleName);
60+
}
61+
62+
const listAttachedRolePolicies = await iam.listAttachedRolePolicies({
63+
RoleName: roleName,
64+
});
65+
const policies = listAttachedRolePolicies.AttachedPolicies || [];
66+
67+
const existingPolicies = policies.reduce((acc: Record<string, boolean>, cur: AttachedPolicy) => {
68+
if (cur.PolicyName) {
69+
acc[cur.PolicyName] = true;
70+
}
71+
return acc;
72+
}, {} as Record<string, boolean>);
73+
74+
const required = ["AWSLambda_FullAccess"];
75+
76+
for (const requiredPolicy of required) {
77+
if (!existingPolicies[requiredPolicy]) {
78+
console.info("Attaching policy to role", requiredPolicy, roleName);
79+
await iam.attachRolePolicy({
80+
RoleName: roleName,
81+
PolicyArn: `arn:aws:iam::aws:policy/${requiredPolicy}`,
82+
});
83+
} else {
84+
console.info("Policy exists on role", requiredPolicy, roleName);
85+
}
86+
}
87+
88+
const getRole: null | GetRoleResponse = await iam
89+
.getRole({
90+
RoleName: roleName,
91+
})
92+
.catch(() => null);
93+
if (!getRole) {
94+
throw new Error("Role not found.");
95+
} else {
96+
console.info("Role found", roleName);
97+
}
98+
99+
const roleArn = getRole.Role!.Arn!;
100+
101+
if (getFn) {
102+
console.info("Function exists:", FunctionName);
103+
104+
if ((getFn.Timeout ?? 0) < 5 * 60 || getFn?.Handler !== Handler) {
105+
await lambda.updateFunctionConfiguration({
106+
FunctionName,
107+
Handler,
108+
Timeout: 5 * 60,
109+
});
110+
await waitUntilFunctionUpdated(
111+
{
112+
client: lambda,
113+
maxWaitTime: 40,
114+
},
115+
{
116+
FunctionName,
117+
}
118+
);
119+
}
120+
// await lambda.updateFunctionCode({
121+
// FunctionName,
122+
// ZipFile: fs.readFileSync(require.resolve("./function.zip")),
123+
// });
124+
// console.info("Function code/configuration updated:", FunctionName);
125+
} else {
126+
await lambda.createFunction({
127+
FunctionName,
128+
Role: roleArn,
129+
Code: {
130+
ZipFile: fs.readFileSync(require.resolve("./function.zip")),
131+
},
132+
Runtime: Runtime.nodejs16x,
133+
Description: `aws sdk js v3 e2e test echo`,
134+
Timeout: 300,
135+
Handler,
136+
});
137+
console.info("Function created:", FunctionName);
138+
}
139+
140+
await waitUntilFunctionActiveV2(
141+
{
142+
maxWaitTime: 40,
143+
client: lambda,
144+
},
145+
{
146+
FunctionName,
147+
}
148+
);
149+
}

0 commit comments

Comments
 (0)