-
Notifications
You must be signed in to change notification settings - Fork 580
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
254 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
preset: "ts-jest", | ||
testMatch: ["**/*.e2e.spec.ts"], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,24 @@ | ||
import { Readable } from "stream"; | ||
import { fromBase64, toBase64 } from "@aws-sdk/util-base64"; | ||
import { fromUtf8, toUtf8 } from "@aws-sdk/util-utf8"; | ||
|
||
import { Uint8ArrayBlobAdapter } from "./Uint8ArrayBlobAdapter"; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function transformToString(payload: Uint8Array, encoding = "utf-8"): string { | ||
return Buffer.from(payload).toString(encoding as BufferEncoding); | ||
if (encoding === "base64") { | ||
return toBase64(payload); | ||
} | ||
return toUtf8(payload); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function transformFromString(str: string): Uint8ArrayBlobAdapter { | ||
return new Uint8ArrayBlobAdapter(Buffer.from(str)); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function transformFromObject(obj: object): Uint8ArrayBlobAdapter { | ||
return new Uint8ArrayBlobAdapter(obj as any); | ||
export function transformFromString(str: string, encoding?: string): Uint8ArrayBlobAdapter { | ||
if (encoding === "base64") { | ||
return Uint8ArrayBlobAdapter.mutate(fromBase64(str)); | ||
} | ||
return Uint8ArrayBlobAdapter.mutate(fromUtf8(str)); | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** | ||
* function.zip contains this file. | ||
*/ | ||
export const handler = async (event) => { | ||
return event; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { AttachedPolicy, CreateRoleResponse, GetRoleResponse, IAM } from "@aws-sdk/client-iam"; | ||
import { | ||
GetFunctionConfigurationCommandOutput, | ||
Lambda, | ||
Runtime, | ||
waitUntilFunctionActiveV2, | ||
waitUntilFunctionUpdated, | ||
} from "@aws-sdk/client-lambda"; | ||
import fs from "fs"; | ||
export const FunctionName = "aws-sdk-js-v3-e2e-echo"; | ||
export const Handler = "index.handler"; | ||
const LAMBDA_ROLE_NAME = "aws-sdk-js-v3-e2e-LambdaRole"; | ||
|
||
export async function setup() { | ||
const lambda = new Lambda({ | ||
region: "us-west-2", | ||
}); | ||
const getFn: null | GetFunctionConfigurationCommandOutput = await lambda | ||
.getFunctionConfiguration({ | ||
FunctionName, | ||
}) | ||
.catch(() => null); | ||
|
||
if (getFn) { | ||
return; | ||
} | ||
|
||
const iam = new IAM({ | ||
region: "us-west-2", | ||
}); | ||
|
||
const roleName = LAMBDA_ROLE_NAME; | ||
const role: null | GetRoleResponse | CreateRoleResponse = await iam | ||
.getRole({ | ||
RoleName: roleName, | ||
}) | ||
.catch(() => null); | ||
|
||
if (!role) { | ||
console.info("Creating role", roleName); | ||
await iam.createRole({ | ||
RoleName: roleName, | ||
Path: "/", | ||
Description: "aws sdk js v3 lambda test role", | ||
AssumeRolePolicyDocument: JSON.stringify({ | ||
Version: "2012-10-17", | ||
Statement: [ | ||
{ | ||
Effect: "Allow", | ||
Action: ["sts:AssumeRole"], | ||
Principal: { | ||
Service: ["lambda.amazonaws.com"], | ||
}, | ||
}, | ||
], | ||
}), | ||
}); | ||
} else { | ||
console.info("Role exists", roleName); | ||
} | ||
|
||
const listAttachedRolePolicies = await iam.listAttachedRolePolicies({ | ||
RoleName: roleName, | ||
}); | ||
const policies = listAttachedRolePolicies.AttachedPolicies || []; | ||
|
||
const existingPolicies = policies.reduce((acc: Record<string, boolean>, cur: AttachedPolicy) => { | ||
if (cur.PolicyName) { | ||
acc[cur.PolicyName] = true; | ||
} | ||
return acc; | ||
}, {} as Record<string, boolean>); | ||
|
||
const required = ["AWSLambda_FullAccess"]; | ||
|
||
for (const requiredPolicy of required) { | ||
if (!existingPolicies[requiredPolicy]) { | ||
console.info("Attaching policy to role", requiredPolicy, roleName); | ||
await iam.attachRolePolicy({ | ||
RoleName: roleName, | ||
PolicyArn: `arn:aws:iam::aws:policy/${requiredPolicy}`, | ||
}); | ||
} else { | ||
console.info("Policy exists on role", requiredPolicy, roleName); | ||
} | ||
} | ||
|
||
const getRole: null | GetRoleResponse = await iam | ||
.getRole({ | ||
RoleName: roleName, | ||
}) | ||
.catch(() => null); | ||
if (!getRole) { | ||
throw new Error("Role not found."); | ||
} else { | ||
console.info("Role found", roleName); | ||
} | ||
|
||
const roleArn = getRole.Role!.Arn!; | ||
|
||
if (getFn) { | ||
console.info("Function exists:", FunctionName); | ||
|
||
if ((getFn.Timeout ?? 0) < 5 * 60 || getFn?.Handler !== Handler) { | ||
await lambda.updateFunctionConfiguration({ | ||
FunctionName, | ||
Handler, | ||
Timeout: 5 * 60, | ||
}); | ||
await waitUntilFunctionUpdated( | ||
{ | ||
client: lambda, | ||
maxWaitTime: 40, | ||
}, | ||
{ | ||
FunctionName, | ||
} | ||
); | ||
} | ||
// await lambda.updateFunctionCode({ | ||
// FunctionName, | ||
// ZipFile: fs.readFileSync(require.resolve("./function.zip")), | ||
// }); | ||
// console.info("Function code/configuration updated:", FunctionName); | ||
} else { | ||
await lambda.createFunction({ | ||
FunctionName, | ||
Role: roleArn, | ||
Code: { | ||
ZipFile: fs.readFileSync(require.resolve("./function.zip")), | ||
}, | ||
Runtime: Runtime.nodejs16x, | ||
Description: `aws sdk js v3 e2e test echo`, | ||
Timeout: 300, | ||
Handler, | ||
}); | ||
console.info("Function created:", FunctionName); | ||
} | ||
|
||
await waitUntilFunctionActiveV2( | ||
{ | ||
maxWaitTime: 40, | ||
client: lambda, | ||
}, | ||
{ | ||
FunctionName, | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { Lambda } from "@aws-sdk/client-lambda"; | ||
import { Uint8ArrayBlobAdapter } from "@aws-sdk/util-stream"; | ||
import { Readable } from "stream"; | ||
|
||
import { FunctionName, setup } from "./setup"; | ||
|
||
describe("blob e2e", () => { | ||
jest.setTimeout(100000); | ||
|
||
const lambda = new Lambda({ | ||
region: "us-west-2", | ||
}); | ||
|
||
beforeAll(async () => { | ||
await setup(); | ||
}); | ||
|
||
it("should allow string as payload blob and allow conversion of output payload blob to string", async () => { | ||
const payload = JSON.stringify({ hello: "world" }); | ||
const invoke = await lambda.invoke({ FunctionName, Payload: payload }); | ||
expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); | ||
}); | ||
|
||
it("should allow Uint8Array as payload blob", async () => { | ||
const payload = Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" })); | ||
const invoke = await lambda.invoke({ FunctionName, Payload: payload }); | ||
expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); | ||
}); | ||
|
||
it("should allow buffer as payload blob", async () => { | ||
// note: Buffer extends Uint8Array | ||
const payload = Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))); | ||
const invoke = await lambda.invoke({ FunctionName, Payload: payload }); | ||
expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); | ||
}); | ||
|
||
it("should allow stream as payload blob but not be able to sign it", async () => { | ||
const payload = Readable.from(Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))), { | ||
encoding: "utf-8", | ||
}); | ||
expect(JSON.parse(await streamToString(payload))).toEqual({ hello: "world" }); | ||
await lambda.invoke({ FunctionName, Payload: payload }).catch((e) => { | ||
expect(e.toString()).toContain("InvalidSignatureException"); | ||
}); | ||
expect.hasAssertions(); | ||
}); | ||
}); | ||
|
||
function streamToString(stream: Readable): Promise<string> { | ||
const chunks: any[] = []; | ||
return new Promise((resolve, reject) => { | ||
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))); | ||
stream.on("error", (err) => reject(err)); | ||
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); | ||
}); | ||
} |