Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-s3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 41 additions & 10 deletions packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ``"*"``.
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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[],
Expand Down
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@
]
]
}
},
"S3ObjectURL": {
"Value": {
"Fn::Join": [
"",
[
"s3://",
{
"Ref": "MyBucketF68F3FF0"
},
"/myfolder/myfile.txt"
]
]
}
}
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-s3/test/integ.bucket.url.lit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
64 changes: 64 additions & 0 deletions packages/@aws-cdk/aws-s3/test/test.bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down