1- import {
2- CreateBucketCommand ,
3- DeleteBucketCommand ,
4- GetBucketAclCommand ,
5- GetBucketLocationCommand ,
6- GetBucketTaggingCommand ,
7- GetBucketVersioningCommand ,
8- HeadBucketCommand ,
9- NoSuchBucket ,
10- PutBucketTaggingCommand ,
11- S3Client ,
12- } from "@aws-sdk/client-s3" ;
1+ import { Effect } from "effect" ;
132import type { Context } from "../context.ts" ;
143import { Resource } from "../resource.ts" ;
154import { ignore } from "../util/ignore.ts" ;
16- import { retry } from "./retry .ts" ;
5+ import { createAwsClient , AwsResourceNotFoundError } from "./client .ts" ;
176
187/**
198 * Properties for creating or updating an S3 bucket
@@ -130,104 +119,85 @@ export interface Bucket extends Resource<"s3::Bucket">, BucketProps {
130119export const Bucket = Resource (
131120 "s3::Bucket" ,
132121 async function ( this : Context < Bucket > , _id : string , props : BucketProps ) {
133- const client = new S3Client ( { } ) ;
122+ const client = await createAwsClient ( { service : "s3" } ) ;
134123
135124 if ( this . phase === "delete" ) {
136- await ignore ( NoSuchBucket . name , ( ) =>
137- retry ( ( ) =>
138- client . send (
139- new DeleteBucketCommand ( {
140- Bucket : props . bucketName ,
141- } ) ,
142- ) ,
143- ) ,
144- ) ;
125+ await ignore ( AwsResourceNotFoundError . name , async ( ) => {
126+ const deleteEffect = client . delete ( `/${ props . bucketName } ` ) ;
127+ await Effect . runPromise ( deleteEffect ) ;
128+ } ) ;
145129 return this . destroy ( ) ;
146130 }
147131 try {
148132 // Check if bucket exists
149- await retry ( ( ) =>
150- client . send (
151- new HeadBucketCommand ( {
152- Bucket : props . bucketName ,
153- } ) ,
154- ) ,
155- ) ;
133+ const headEffect = client . request ( "HEAD" , `/${ props . bucketName } ` ) ;
134+ await Effect . runPromise ( headEffect ) ;
156135
157136 // Update tags if they changed and bucket exists
158137 if ( this . phase === "update" && props . tags ) {
159- await retry ( ( ) =>
160- client . send (
161- new PutBucketTaggingCommand ( {
162- Bucket : props . bucketName ,
163- Tagging : {
164- TagSet : Object . entries ( props . tags ! ) . map ( ( [ Key , Value ] ) => ( {
165- Key,
166- Value,
167- } ) ) ,
168- } ,
169- } ) ,
170- ) ,
171- ) ;
138+ const tagSet = Object . entries ( props . tags ) . map ( ( [ Key , Value ] ) => ( { Key, Value } ) ) ;
139+ const taggingXml = `<Tagging><TagSet>${ tagSet
140+ . map ( ( { Key, Value } ) => `<Tag><Key>${ Key } </Key><Value>${ Value } </Value></Tag>` )
141+ . join ( "" ) } </TagSet></Tagging>`;
142+
143+ const putTagsEffect = client . put ( `/${ props . bucketName } ?tagging` , taggingXml , {
144+ "Content-Type" : "application/xml" ,
145+ } ) ;
146+ await Effect . runPromise ( putTagsEffect ) ;
172147 }
173148 } catch ( error : any ) {
174- if ( error . name === "NotFound" ) {
149+ if ( error instanceof AwsResourceNotFoundError ) {
175150 // Create bucket if it doesn't exist
176- await retry ( ( ) =>
177- client . send (
178- new CreateBucketCommand ( {
179- Bucket : props . bucketName ,
180- // Add tags during creation if specified
181- ...( props . tags && {
182- Tagging : {
183- TagSet : Object . entries ( props . tags ) . map ( ( [ Key , Value ] ) => ( {
184- Key,
185- Value,
186- } ) ) ,
187- } ,
188- } ) ,
189- } ) ,
190- ) ,
191- ) ;
151+ const createEffect = client . put ( `/${ props . bucketName } ` ) ;
152+ await Effect . runPromise ( createEffect ) ;
153+
154+ // Add tags after creation if specified
155+ if ( props . tags ) {
156+ const tagSet = Object . entries ( props . tags ) . map ( ( [ Key , Value ] ) => ( { Key, Value } ) ) ;
157+ const taggingXml = `<Tagging><TagSet>${ tagSet
158+ . map ( ( { Key, Value } ) => `<Tag><Key>${ Key } </Key><Value>${ Value } </Value></Tag>` )
159+ . join ( "" ) } </TagSet></Tagging>`;
160+
161+ const putTagsEffect = client . put ( `/${ props . bucketName } ?tagging` , taggingXml , {
162+ "Content-Type" : "application/xml" ,
163+ } ) ;
164+ await Effect . runPromise ( putTagsEffect ) ;
165+ }
192166 } else {
193167 throw error ;
194168 }
195169 }
196170
197171 // Get bucket details
172+ const locationEffect = client . get ( `/${ props . bucketName } ?location` ) ;
173+ const versioningEffect = client . get ( `/${ props . bucketName } ?versioning` ) ;
174+ const aclEffect = client . get ( `/${ props . bucketName } ?acl` ) ;
175+
198176 const [ locationResponse , versioningResponse , aclResponse ] =
199177 await Promise . all ( [
200- retry ( ( ) =>
201- client . send (
202- new GetBucketLocationCommand ( { Bucket : props . bucketName } ) ,
203- ) ,
204- ) ,
205- retry ( ( ) =>
206- client . send (
207- new GetBucketVersioningCommand ( { Bucket : props . bucketName } ) ,
208- ) ,
209- ) ,
210- retry ( ( ) =>
211- client . send ( new GetBucketAclCommand ( { Bucket : props . bucketName } ) ) ,
212- ) ,
178+ Effect . runPromise ( locationEffect ) ,
179+ Effect . runPromise ( versioningEffect ) ,
180+ Effect . runPromise ( aclEffect ) ,
213181 ] ) ;
214182
215- const region = locationResponse . LocationConstraint || "us-east-1" ;
183+ const region = ( locationResponse as any ) ? .LocationConstraint || "us-east-1" ;
216184
217185 // Get tags if they exist
218186 let tags = props . tags ;
219187 if ( ! tags ) {
220188 try {
221- const taggingResponse = await retry ( ( ) =>
222- client . send (
223- new GetBucketTaggingCommand ( { Bucket : props . bucketName } ) ,
224- ) ,
225- ) ;
226- tags = Object . fromEntries (
227- taggingResponse . TagSet ?. map ( ( { Key, Value } ) => [ Key , Value ] ) || [ ] ,
228- ) ;
189+ const taggingEffect = client . get ( `/${ props . bucketName } ?tagging` ) ;
190+ const taggingResponse = await Effect . runPromise ( taggingEffect ) ;
191+
192+ // Parse XML response to extract tags
193+ const tagSet = ( taggingResponse as any ) ?. Tagging ?. TagSet ;
194+ if ( Array . isArray ( tagSet ) ) {
195+ tags = Object . fromEntries (
196+ tagSet . map ( ( { Key, Value } : any ) => [ Key , Value ] ) || [ ] ,
197+ ) ;
198+ }
229199 } catch ( error : any ) {
230- if ( error . name !== "NoSuchTagSet" ) {
200+ if ( ! ( error instanceof AwsResourceNotFoundError ) ) {
231201 throw error ;
232202 }
233203 }
@@ -240,8 +210,8 @@ export const Bucket = Resource(
240210 bucketRegionalDomainName : `${ props . bucketName } .s3.${ region } .amazonaws.com` ,
241211 region,
242212 hostedZoneId : getHostedZoneId ( region ) ,
243- versioningEnabled : versioningResponse . Status === "Enabled" ,
244- acl : aclResponse . Grants ?. [ 0 ] ?. Permission ?. toLowerCase ( ) ,
213+ versioningEnabled : ( versioningResponse as any ) ?. VersioningConfiguration ? .Status === "Enabled" ,
214+ acl : ( aclResponse as any ) ?. AccessControlPolicy ?. AccessControlList ?. Grant ?. [ 0 ] ?. Permission ?. toLowerCase ( ) ,
245215 ...( tags && { tags } ) ,
246216 } ) ;
247217 } ,
0 commit comments