Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions clients/client-s3/test/e2e/s3-object-features.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,67 @@ describe("@aws-sdk/client-s3", () => {
await deleteObject("hello");
expect(await objectExists("hello")).toBe(false);
});

describe("keys with special characters", () => {
const keys = [
`\r`,
`\n`,
`\x85`,
`\u2028`,
"\n \n",
"a\r\n b\n c\r",
"a\r\u0085 b\u0085",
"a\r\u2028 b\u0085 c\u2028",
];

beforeAll(async () => {
await Promise.all(
keys.map(async (Key) => {
await client.putObject({
Bucket,
Key,
Body: Key,
});
})
);
});

afterAll(async () => {
for (const key of keys) {
await client
.deleteObject({
Bucket,
Key: key,
})
.catch(() => {});
}
});

it("can delete keys containing special characters", async () => {
await client.deleteObjects({
Bucket,
Delete: {
Objects: keys.map((Key) => ({
Key,
})),
},
});

await Promise.all(
keys.map(async (Key) => {
return client
.headObject({
Bucket,
Key,
})
.catch((e: any) => e)
.then((r) => {
expect((r ?? r.$response).$metadata.httpStatusCode).toEqual(404);
});
})
);
});
});
});

describe("Content length", () => {
Expand Down
43 changes: 43 additions & 0 deletions clients/client-s3/test/integ/s3-object-lambda.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, test as it } from "vitest";

import { S3 } from "@aws-sdk/client-s3";
import { AwsCredentialIdentity } from "@smithy/types";
import { requireRequestsFrom } from "@aws-sdk/aws-util-test/src";

describe("S3 Object Lambda", () => {
const region = "us-west-2";
const credentials: AwsCredentialIdentity = {
accessKeyId: "",
secretAccessKey: "",
};

it("can make a GET request to an S3 Object Lambda ARN", async () => {
const s3 = new S3({
region,
credentials,
});

requireRequestsFrom(s3).toMatch({
hostname: "my-access-point-123456789012.s3-object-lambda.us-west-2.amazonaws.com",
query: {
"x-id": "GetObject",
},
headers: {
authorization: /=\/\d+\/us-west-2\/s3-object-lambda\/aws4_request/,
},
path: "/a",
});

// slash delimiter
await s3.getObject({
Bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint/my-access-point",
Key: "a",
});

// colon delimiter
await s3.getObject({
Bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-access-point",
Key: "a",
});
});
});
46 changes: 46 additions & 0 deletions packages/core/integ/service-id.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, test as it, expect } from "vitest";

import { S3Client } from "@aws-sdk/client-s3";
import { LambdaClient } from "@aws-sdk/client-lambda";
import { EMRClient } from "@aws-sdk/client-emr";
import { SageMakerClient } from "@aws-sdk/client-sagemaker";
import { CloudWatchClient } from "@aws-sdk/client-cloudwatch";
import { STSClient } from "@aws-sdk/client-sts";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { CloudFormationClient } from "@aws-sdk/client-cloudformation";
import { SFNClient } from "@aws-sdk/client-sfn";

const clients = [
new S3Client(),
new LambdaClient(),
new EMRClient(),
new SageMakerClient(),
new CloudFormationClient(),
new CloudWatchClient(),
new STSClient(),
new DynamoDBClient(),
new SFNClient(),
];

const serviceIds = [
"S3",
"Lambda",
"EMR",
"SageMaker",
"CloudFormation",
"CloudWatch",
"STS",
"DynamoDB",
"SFN",
] as string[];

describe("service ids (various clients)", () => {
for (const [i, client] of Object.entries(clients)) {
it("should have a service id matching its class name", () => {
const index = parseInt(i);

expect(client.config.serviceId).toEqual(serviceIds[index]);
expect(client.constructor.name.replace(/Client$/, "")).toEqual(serviceIds[index]);
});
}
});
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"extract:docs": "api-extractor run --local",
"test": "yarn g:vitest run",
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
"test:watch": "yarn g:vitest watch",
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.ts"
},
"main": "./dist-cjs/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ describe("pagination", () => {
}

expect(pages).toEqual(2);
/**
* As of writing, paginators mutate the input object.
* In case anyone is relying on this behavior to observe the pagination token,
* we are enforcing it with this assertion.
*/
expect(requestParams.ExclusiveStartKey).toEqual({
id: { S: "2" },
});
Expand Down
99 changes: 99 additions & 0 deletions packages/core/src/submodules/client/waiters.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { requireRequestsFrom } from "@aws-sdk/aws-util-test/src";
import {
type DescribeClusterCommandInput,
Route53RecoveryControlConfig,
waitUntilClusterCreated,
} from "@aws-sdk/client-route53-recovery-control-config";
import { type HeadObjectCommandInput, S3, waitUntilObjectExists } from "@aws-sdk/client-s3";
import { HttpResponse } from "@smithy/protocol-http";
import { Readable } from "node:stream";
import { describe, test as it } from "vitest";

describe("waiters", () => {
it("should poll until a return condition is met (http status)", async () => {
const s3 = new S3({
credentials: {
accessKeyId: "INTEG_TEST",
secretAccessKey: "INTEG_TEST",
},
region: "us-west-2",
});

requireRequestsFrom(s3)
.toMatch(
{
hostname: /s3\.us-west-2\.amazonaws\.com/,
},
{
hostname: /s3\.us-west-2\.amazonaws\.com/,
}
)
.respondWith(
new HttpResponse({
statusCode: 404,
headers: {},
}),
new HttpResponse({
statusCode: 200,
headers: {},
})
);

const waiterParams: HeadObjectCommandInput = {
Bucket: "@@@@",
Key: "####",
};

await waitUntilObjectExists({ client: s3, maxDelay: 5, maxWaitTime: 20 }, waiterParams);
});

it("should poll until a return condition is met (nested property)", async () => {
const r53 = new Route53RecoveryControlConfig({
credentials: {
accessKeyId: "INTEG_TEST",
secretAccessKey: "INTEG_TEST",
},
region: "us-west-2",
});

requireRequestsFrom(r53)
.toMatch(
{
hostname: /amazon/,
},
{
hostname: /amazon/,
}
)
.respondWith(
new HttpResponse({
statusCode: 200,
headers: {},
body: Readable.from(
JSON.stringify({
Cluster: {
Status: "PENDING",
},
})
),
}),
new HttpResponse({
statusCode: 200,
headers: {},
body: Readable.from(
JSON.stringify({
Cluster: {
Status: "DEPLOYED",
},
})
),
})
);

const waiterParams: DescribeClusterCommandInput = {
ClusterArn: "@@@@",
};

await waitUntilClusterCreated({ client: r53, maxDelay: 5, maxWaitTime: 20 }, waiterParams);
});
}, 60_000);
15 changes: 10 additions & 5 deletions packages/dsql-signer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,36 @@ const { DsqlSigner } = require("@aws-sdk/dsql-signer");
### Generate Authentication Token for Dsql IAM Authentication

```js
import { Hash } from "@smithy/hash-node";
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";

const signer = new DsqlSigner({
/**
* Required: The hostname of the database to connect to.
*/
hostname: "foo0bar1baz2quux3quux4.dsql.us-east-1.on.aws";
hostname: "foo0bar1baz2quux3quux4.dsql.us-east-1.on.aws",

/**
* Optional: The region the database is located in. Uses the region inferred from the runtime if omitted.
*/
region?: "us-east-1";
region: "us-east-1",

/**
* Optional: The SHA256 hasher constructor to sign the request.
*/
sha256?: HashCtor;
sha256: Hash.bind(null, "sha256"),

/**
* Optional: The amount of time in seconds the generated token is valid
*/
expiresIn?: 3600;
expiresIn: 3600,

/**
* Optional: The AWS credentials to sign requests with. Uses the default credential provider chain if not specified.
* You can use any credential provider from https://www.npmjs.com/package/@aws-sdk/credential-providers,
* or provide a credentials object.
*/
credentials?: fromNodeCredentialProvider();
credentials: fromNodeProviderChain(),
});

// Creates auth token.
Expand Down
5 changes: 4 additions & 1 deletion packages/dsql-signer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"extract:docs": "api-extractor run --local",
"test": "yarn g:vitest run"
"test": "yarn g:vitest run",
"test:watch": "yarn g:vitest watch",
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.ts"
},
"engines": {
"node": ">=18.0.0"
Expand Down
33 changes: 33 additions & 0 deletions packages/dsql-signer/src/dsql-signer.integ.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Sha256 } from "@aws-crypto/sha256-js";
import { describe, expect, test as it } from "vitest";

import { DsqlSigner, DsqlSignerConfig } from "./Signer";

describe("dsql-signer integration", () => {
it("creates a token", async () => {
const allParamsConfig: DsqlSignerConfig = {
hostname: "localhost",
region: "us-east-2",
credentials: {
accessKeyId: "allParamsAccessKey",
secretAccessKey: "allParamsSecretAccessKey",
sessionToken: "allParamsSessionToken",
},
expiresIn: 1000,
sha256: Sha256,
};
const signer = new DsqlSigner(allParamsConfig);
const token = await signer.getDbConnectAdminAuthToken();

expect(token.split("&").sort()).toMatchObject([
/localhost\/\?Action=DbConnectAdmin/,
/X-Amz-Algorithm=AWS4-HMAC-SHA256/,
/X-Amz-Credential=allParamsAccessKey%2F(\d+)%2Fus-east-2%2Fdsql%2Faws4_request/,
/X-Amz-Date=(\d+T\d+Z)/,
/X-Amz-Expires=1000/,
/X-Amz-Security-Token=allParamsSessionToken/,
/X-Amz-Signature=(.*?)/,
/X-Amz-SignedHeaders=host/,
]);
});
});
8 changes: 8 additions & 0 deletions packages/dsql-signer/vitest.config.integ.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
include: ["**/*.integ.spec.ts"],
environment: "node",
},
});
4 changes: 3 additions & 1 deletion packages/polly-request-presigner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"extract:docs": "api-extractor run --local",
"test": "yarn g:vitest run",
"test:watch": "yarn g:vitest watch"
"test:watch": "yarn g:vitest watch",
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.ts"
},
"main": "./dist-cjs/index.js",
"module": "./dist-es/index.js",
Expand Down
Loading
Loading