diff --git a/src/client.ts b/src/client.ts index e08aa61..c33d4cc 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,9 @@ import { AWSSignerV4 } from "../deps.ts"; -import type { CreateBucketOptions } from "./types.ts"; +import type { + CreateBucketOptions, + HeadBucketOptions, + HeadBucketResponse, +} from "./types.ts"; import { S3Error } from "./error.ts"; import { S3Bucket } from "./bucket.ts"; import { doRequest, encoder } from "./request.ts"; @@ -28,6 +32,37 @@ export class S3 { this.#config = { ...config }; } + async headBucket( + bucket: string, + options?: HeadBucketOptions, + ): Promise { + const headers: Params = {}; + + if (options?.expectedBucketOwner) { + headers["x-amz-expected-bucket-owner "] = options.expectedBucketOwner; + } + + const resp = await doRequest({ + host: this.#host, + signer: this.#signer, + path: bucket, + method: "HEAD", + headers, + }); + + if (resp.status !== 200) { + throw new S3Error( + `Failed to get bucket "${bucket}": ${resp.status} ${resp.statusText}`, + await resp.text(), + ); + } + + return { + bucketRegion: resp.headers.get("x-amz-bucket-region") ?? undefined, + accessPointAlias: resp.headers.get("x-amz-access-point-alias") === "true", + }; + } + async createBucket( bucket: string, options?: CreateBucketOptions, diff --git a/src/client_test.ts b/src/client_test.ts index 3fbf76d..0f0fcb5 100644 --- a/src/client_test.ts +++ b/src/client_test.ts @@ -11,6 +11,29 @@ const s3 = new S3({ endpointURL: Deno.env.get("S3_ENDPOINT_URL"), }); +Deno.test({ + name: "[client] should get a bucket", + async fn() { + const resp = await s3.headBucket("test"); + assertEquals(resp, { + bucketRegion: undefined, + accessPointAlias: false, + }); + }, +}); + +Deno.test({ + name: + "[client] should throw when getting a bucket if the bucket does not exist", + async fn() { + await assertThrowsAsync( + () => s3.headBucket("not-existing-bucket"), + S3Error, + 'Failed to get bucket "not-existing-bucket": 404 Not Found', + ); + }, +}); + Deno.test({ name: "[client] should create a new bucket", async fn() { diff --git a/src/types.ts b/src/types.ts index babb30b..fb19ea8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -537,6 +537,15 @@ export interface DeleteObjectResponse { deleteMarker: boolean; } +export interface HeadBucketOptions { + expectedBucketOwner?: string; +} + +export interface HeadBucketResponse { + bucketRegion?: string; + accessPointAlias: boolean; +} + export interface CreateBucketOptions { /** The canned ACL to apply to the bucket */ acl?: