Skip to content

Commit

Permalink
feat(aws-s3objectlambda): add L2 construct for S3 Object Lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
duarten committed Jul 30, 2021
1 parent 58fda91 commit 1cce38a
Show file tree
Hide file tree
Showing 6 changed files with 482 additions and 3 deletions.
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-s3objectlambda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
>
> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib
![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)

> The APIs of higher level constructs in this module are experimental and under active development.
> They are subject to non-backward compatible changes or removal in any future version. These are
> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be
> announced in the release notes. This means that while you may use them, you may need to update
> your source code when upgrading to a newer version of this package.
---

<!--END STABILITY BANNER-->
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-s3objectlambda/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './object-lambda';

// AWS::S3ObjectLambda CloudFormation Resources:
export * from './s3objectlambda.generated';
166 changes: 166 additions & 0 deletions packages/@aws-cdk/aws-s3objectlambda/lib/object-lambda.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as s3 from '@aws-cdk/aws-s3';
import { Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnAccessPoint } from './s3objectlambda.generated';

// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
// eslint-disable-next-line no-duplicate-imports, import/order
import { Construct as CoreConstruct } from '@aws-cdk/core';

/**
* Creates an S3 Object Lambda, which can intercept and transform
* `GetObject` requests.
*
* @param fn The Lambda function
* @param props Configuration for this Object Lambda
*/
export interface ObjectLambdaProps {
/**
* The bucket to which this Object Lambda belongs
*/
readonly bucket: s3.IBucket

/**
* The Lambda function used to transform objects.
*/
readonly fn: lambda.IFunction

/**
* The name of the Object Lambda access point.
*/
readonly name: string

/**
* Whether CloudWatch metrics are enabled for the Object Lambda.
*
* @default false
*/
readonly cloudWatchMetricsEnabled?: boolean

/**
* Whether the Lambda function can process `GetObject-Range` requests.
*
* @default false
*/
readonly supportsGetObjectRange?: boolean

/**
* Whether the Lambda function can process `GetObject-PartNumber` requests.
*
* @default false
*/
readonly supportsGetObjectPartNumber?: boolean

/**
* Additional JSON that provides supplemental data passed to the
* Lambda function on every request.
*
* @default - No data.
*/
readonly payload?: string
}

/**
* An S3 Object Lambda for intercepting and transforming `GetObject` requests.
*/
export class ObjectLambda extends CoreConstruct {
private readonly objectLambda: CfnAccessPoint
private readonly stack: Stack

constructor(scope: Construct, id: string, props: ObjectLambdaProps) {
super(scope, id);

this.stack = props.bucket.stack;

const supporting = new s3.CfnAccessPoint(this, 'AccessPoint', {
bucket: props.bucket.bucketName,
// TODO: configure publicAccessBlockConfiguration?
});
supporting.addPropertyOverride('Name', `${props.name}-access-point`);

const allowedFeatures = [];
if (props.supportsGetObjectPartNumber) {
allowedFeatures.push('GetObject-PartNumber');
}
if (props.supportsGetObjectRange) {
allowedFeatures.push('GetObject-Range');
}

this.objectLambda = new CfnAccessPoint(this, 'LambdaAccessPoint', {
name: props.name.toLowerCase(),
objectLambdaConfiguration: {
allowedFeatures,
cloudWatchMetricsEnabled: props.cloudWatchMetricsEnabled,
supportingAccessPoint: supporting.getAtt('Arn').toString(),
transformationConfigurations: [
{
actions: ['GetObject'],
contentTransformation: {
AwsLambda: {
FunctionArn: props.fn.functionArn,
FunctionPayload: props.payload ?? '',
},
},
},
],
},
});
this.objectLambda.addDependsOn(supporting);

props.fn.addToRolePolicy(
new iam.PolicyStatement({
actions: ['s3-object-lambda:WriteGetObjectResponse'],
resources: ['*'],
}),
);
}

/**
* The ARN of the Object Lambda access point.
*/
get arn(): string {
return this.objectLambda.getAtt('Arn').toString();
}

/**
* The IPv4 DNS name of the Object Lambda access point.
*/
get domainName(): string {
const urlSuffix = this.stack.urlSuffix;
return `${this.objectLambda.name}-${this.stack.account}.s3.${urlSuffix}`;
}

/**
* The regional domain name of the Object Lambda access point.
*/
get regionalDomainName(): string {
const urlSuffix = this.stack.urlSuffix;
const region = this.stack.region;
return `${this.objectLambda.name}-${this.stack.account}.s3.${region}.${urlSuffix}`;
}

/**
* The virtual hosted-style URL of an S3 object through this access point.
* Specify `regional: false` at the options for non-regional URL.
* @param key The S3 key of the object. If not specified, the URL of the
* bucket is returned.
* @param options Options for generating URL.
* @returns an ObjectS3Url token
*/
public virtualHostedUrlForObject(key?: string, options?: s3.VirtualHostedStyleUrlOptions): string {
const domainName = options?.regional ?? true ? this.regionalDomainName : this.domainName;
const prefix = `https://${domainName}`;
if (typeof key !== 'string') {
return prefix;
}
if (key.startsWith('/')) {
key = key.slice(1);
}
if (key.endsWith('/')) {
key = key.slice(0, -1);
}
return `${prefix}/${key}`;
}
}
16 changes: 13 additions & 3 deletions packages/@aws-cdk/aws-s3objectlambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,31 @@
"devDependencies": {
"@types/jest": "^26.0.24",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"cfn2ts": "0.0.0",
"jest": "^26.6.3",
"pkglint": "0.0.0",
"@aws-cdk/assert-internal": "0.0.0"
},
"dependencies": {
"@aws-cdk/core": "0.0.0"
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
"peerDependencies": {
"@aws-cdk/core": "0.0.0"
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
"engines": {
"node": ">= 10.13.0 <13 || >=13.7.0"
},
"stability": "experimental",
"maturity": "cfn-only",
"maturity": "experimental",
"awscdkio": {
"announce": false
},
Expand Down
Loading

0 comments on commit 1cce38a

Please sign in to comment.