diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 83c15d3bff6d3..6e8f002e287e0 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -29,8 +29,10 @@ new Bucket(this, 'MyFirstBucket'); * `arnForObjects(pattern)` - the ARN of an object or objects within the bucket (i.e. `arn:aws:s3:::bucket_name/exampleobject.png` or `arn:aws:s3:::bucket_name/Development/*`) - * `urlForObject(key)` - the URL of an object within the bucket (i.e. + * `urlForObject(key)` - the HTTP URL of an object within the bucket (i.e. `https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey`) + * `s3UrlForObject(key)` - the S3 URL of an object within the bucket (i.e. + `s3://bucket/mykey`) ### Encryption diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 62f38dab11d8e..f4a481f69807c 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -86,6 +86,16 @@ export interface IBucket extends IResource { */ urlForObject(key?: string): string; + /** + * The S3 URL of an S3 object. For example: + * @example s3://onlybucket + * @example s3://bucket/key + * @param key The S3 key of the object. If not specified, the S3 URL of the + * bucket is returned. + * @returns an ObjectS3Url token + */ + s3UrlForObject(key?: string): string; + /** * Returns an ARN that represents all objects within the bucket that match * the key pattern specified. To represent all keys, specify ``"*"``. @@ -429,17 +439,20 @@ abstract class BucketBase extends Resource implements IBucket { */ public urlForObject(key?: string): string { const stack = Stack.of(this); - const components = [ `https://s3.${stack.region}.${stack.urlSuffix}/${this.bucketName}` ]; - if (key) { - // trim prepending '/' - if (typeof key === 'string' && key.startsWith('/')) { - key = key.substr(1); - } - components.push('/'); - components.push(key); - } + const prefix = `https://s3.${stack.region}.${stack.urlSuffix}/`; + return this.buildUrl(prefix, key); + } - return components.join(''); + /** + * The S3 URL of an S3 object. For example: + * @example s3://onlybucket + * @example s3://bucket/key + * @param key The S3 key of the object. If not specified, the S3 URL of the + * bucket is returned. + * @returns an ObjectS3Url token + */ + public s3UrlForObject(key?: string): string { + return this.buildUrl('s3://', key); } /** @@ -569,6 +582,24 @@ abstract class BucketBase extends Resource implements IBucket { }); } + private buildUrl(prefix: string, key?: string): string { + const components = [ + prefix, + this.bucketName, + ]; + + if (key) { + // trim prepending '/' + if (typeof key === 'string' && key.startsWith('/')) { + key = key.substr(1); + } + components.push('/'); + components.push(key); + } + + return components.join(''); + } + private grant( grantee: iam.IGrantable, bucketActions: string[], diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json index b2e67b56a80b9..74f803ae04cb0 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json @@ -36,6 +36,20 @@ ] ] } + }, + "S3ObjectURL": { + "Value": { + "Fn::Join": [ + "", + [ + "s3://", + { + "Ref": "MyBucketF68F3FF0" + }, + "/myfolder/myfile.txt" + ] + ] + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.ts b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.ts index 34ef5319a05eb..34a282ac5dfb3 100644 --- a/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.ts +++ b/packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.ts @@ -12,6 +12,7 @@ class TestStack extends cdk.Stack { new cdk.CfnOutput(this, 'BucketURL', { value: bucket.bucketWebsiteUrl }); new cdk.CfnOutput(this, 'ObjectURL', { value: bucket.urlForObject('myfolder/myfile.txt') }); + new cdk.CfnOutput(this, 'S3ObjectURL', { value: bucket.s3UrlForObject('myfolder/myfile.txt') }); /// !hide } } diff --git a/packages/@aws-cdk/aws-s3/test/test.bucket.ts b/packages/@aws-cdk/aws-s3/test/test.bucket.ts index 31c5634f0060f..0a29617dc5607 100644 --- a/packages/@aws-cdk/aws-s3/test/test.bucket.ts +++ b/packages/@aws-cdk/aws-s3/test/test.bucket.ts @@ -1388,6 +1388,70 @@ export = { test.done(); }, + 's3UrlForObject returns a token with the S3 URL of the token'(test: Test) { + const stack = new cdk.Stack(); + const bucket = new s3.Bucket(stack, 'MyBucket'); + + new cdk.CfnOutput(stack, 'BucketS3URL', { value: bucket.s3UrlForObject() }); + new cdk.CfnOutput(stack, 'MyFileS3URL', { value: bucket.s3UrlForObject('my/file.txt') }); + new cdk.CfnOutput(stack, 'YourFileS3URL', { value: bucket.s3UrlForObject('/your/file.txt') }); // "/" is optional + + expect(stack).toMatch({ + 'Resources': { + 'MyBucketF68F3FF0': { + 'Type': 'AWS::S3::Bucket', + 'DeletionPolicy': 'Retain', + 'UpdateReplacePolicy': 'Retain', + }, + }, + 'Outputs': { + 'BucketS3URL': { + 'Value': { + 'Fn::Join': [ + '', + [ + 's3://', + { + 'Ref': 'MyBucketF68F3FF0', + }, + ], + ], + }, + }, + 'MyFileS3URL': { + 'Value': { + 'Fn::Join': [ + '', + [ + 's3://', + { + 'Ref': 'MyBucketF68F3FF0', + }, + '/my/file.txt', + ], + ], + }, + }, + 'YourFileS3URL': { + 'Value': { + 'Fn::Join': [ + '', + [ + 's3://', + { + 'Ref': 'MyBucketF68F3FF0', + }, + '/your/file.txt', + ], + ], + }, + }, + }, + }); + + test.done(); + }, + 'grantPublicAccess': { 'by default, grants s3:GetObject to all objects'(test: Test) { // GIVEN