Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-s3objectlambda): add L2 construct for S3 Object Lambda #15833

Merged
merged 5 commits into from
Mar 4, 2022

Conversation

duarten
Copy link
Contributor

@duarten duarten commented Jul 30, 2021

This PR adds an L2 construct for the S3 Object Lambda.

To avoid a circular dependency, the construct lives outside of the aws-s3 package.

Fixes #13675

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@gitpod-io
Copy link

gitpod-io bot commented Jul 30, 2021

@duarten duarten force-pushed the feature/object-lambda-l2 branch 3 times, most recently from cf82e8e to af83e6c Compare July 30, 2021 06:20
@duarten
Copy link
Contributor Author

duarten commented Jul 30, 2021

I wonder if the construct should implement IBucket?

@nija-at
Copy link
Contributor

nija-at commented Aug 12, 2021

Thanks so much for submitting this pull request. I am marking this pull request as p2, which means that we are unable to work on it immediately, but it's definitely still on our radar.

We use +1s to help prioritize our work, and are happy to revaluate this pull request based on community feedback. You can reach out to the cdk.dev community on Slack to solicit support for reprioritization.

@nija-at nija-at added effort/medium Medium work item – several days of effort p2 labels Aug 12, 2021
@nija-at nija-at removed their assignment Aug 12, 2021
/**
* An S3 Object Lambda for intercepting and transforming `GetObject` requests.
*/
export class ObjectLambda extends CoreConstruct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@duarten thanks for the PR and sorry it took so long to review! The main comment I have is that I think this PR should introduce an AccessPoint construct instead of ObjectLambda since that is the name of the corresponding L1. Also instead of extending CoreConstruct this should extend Resource. I would recommend taking a look at the example here which has the base setup we would be looking for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, @corymhall! Isn't an AccessPoint different than an ObjectLambda?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah and they are in different libraries, so you would use them like this.

import * as objectlambda from '@aws-cdk/aws-s3objectlambda';
import * as s3 from '@aws-cdk/aws-s3';

new objectlambda.AccessPoint();
new s3.AccessPoint();

In this case S3ObjectLambda is the name of the library and AccessPoint is the name of the class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense! I'll update the PR asap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

@mergify mergify bot dismissed corymhall’s stale review February 17, 2022 02:21

Pull request has been modified.

@duarten duarten closed this Feb 17, 2022
@duarten duarten deleted the feature/object-lambda-l2 branch February 17, 2022 03:22
@duarten duarten reopened this Feb 17, 2022
@duarten duarten restored the feature/object-lambda-l2 branch February 17, 2022 03:27
Copy link
Contributor

@corymhall corymhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@duarten updates look good! Just some minor comments, and we need to add

  • README documentation
  • unit tests

/**
* The name of the access point access point.
*/
readonly accessPointName: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optional and I think the doc should say The name of the s3objectlambda access point.

*
* @default - No data.
*/
readonly payload?: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is json data can we allow object input that we convert to string?

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') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are just checking to see if key is provided you can do

if (!key) {}

public abstract readonly accessPointArn: string
public abstract readonly accessPointCreationDate: string

protected abstract readonly name: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected abstract readonly name: string;
public abstract readonly accessPointName: string;

Let's just go ahead and make this public.

objectLambdaConfiguration: {
allowedFeatures,
cloudWatchMetricsEnabled: props.cloudWatchMetricsEnabled,
supportingAccessPoint: supporting.getAtt('Arn').toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
supportingAccessPoint: supporting.getAtt('Arn').toString(),
supportingAccessPoint: supporting.attrArn,

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
readonly fn: lambda.IFunction
readonly handler: lambda.IFunction

lets call this handler instead.

],
},
});
this.accessPoint.addDependsOn(supporting);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is required since an implicit dependency should be added by the GetAttr.

}

/** Implement the {@link IAccessPoint.domainName} field. */
get domainName(): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should either be here or on the abstract class, you don't need them both places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, forgot to remove them.

@corymhall corymhall added the pr/needs-tests This PR is missing tests. label Feb 17, 2022
@mergify mergify bot dismissed corymhall’s stale review February 18, 2022 04:43

Pull request has been modified.

@duarten
Copy link
Contributor Author

duarten commented Feb 18, 2022

@duarten updates look good! Just some minor comments, and we need to add

  • README documentation
  • unit tests

Thanks for all the feedback! :)

I've added the documentation, but I'm struggling a bit with the unit tests. Because now we're doing this.accessPointName = this.accessPoint.ref, accessPointName is an unresolved token (because .ref will get us the logicalId), which is awkward to handle in unit tests. Do you have some suggestion about this?

@duarten duarten force-pushed the feature/object-lambda-l2 branch 2 times, most recently from 015d52a to 10b3fc7 Compare February 18, 2022 05:29

constructor(scope: Construct, id: string, props: AccessPointProps) {
super(scope, id, {
physicalName: props.accessPointName ?? core.Lazy.string({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I wasn't thinking on this one. We don't need to have CDK generate a name so we can just do

physicalName: props.accessPointName,

*
* @default - No data.
*/
readonly payload?: Record<string, unknown>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do { [key: string]: any } instead. I think that is the convention we use elsewhere.

@corymhall
Copy link
Contributor

@duarten it's looking really good! Just two minor comments.

I've added the documentation, but I'm struggling a bit with the unit tests. Because now we're doing this.accessPointName = this.accessPoint.ref, accessPointName is an unresolved token (because .ref will get us the logicalId), which is awkward to handle in unit tests. Do you have some suggestion about this?

Unit tests with ids can be tricky. If you need to validate a value that is only available after synth (you don't know what it will be when writing the test), I usually do something like this which will give you a failing test with the content of the template.

expect(Template.from_stack(stack).toJSON()).toEqual({});

@mergify mergify bot dismissed corymhall’s stale review February 19, 2022 01:46

Pull request has been modified.

@duarten
Copy link
Contributor Author

duarten commented Feb 23, 2022

I added the unit test.

Copy link
Contributor

@corymhall corymhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! Just a couple last comments.

allowedFeatures.push('GetObject-Range');
}

this.accessPoint = new CfnAccessPoint(this, 'LambdaAccessPoint', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    const accessPoint = new CfnAccessPoint(this, 'Resource', {

The default resource should have the id of Resource. Also can you just assign accessPoint to a const.

},
"awslint": {
"exclude": [
"attribute-tag:@aws-cdk/aws-s3objectlambda.AccessPoint.accessPointName"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this I get a lint error.

regional: false,
}),
});
expect(Template.fromStack(stack).toJSON()).toEqual({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update these tests to just use the assertions methods like

Template.fromStack(stack).hasResourceProperties('AWS::S3ObjectLambda::AccessPoint', {
  ObjectLambdaConfiguration: {},
});

Sorry if my comment about using expect(Template.fromStack(stack).toJSON()) was misleading. I was meaning that that method can be useful to find out logical ids of resources if the normal test output isn't giving you what you need.

Also to make things easier you might want to just create the resources in a beforeEach and then have a separate test to assert individual things. Combined with using the assertions methods each test can be smaller and validate a smaller piece of the template, which makes it easier to review.

let stack: cdk.Stack;
let bucket: s3.Bucket;
let handler: lambda.Function;
beforeEach(() => {
  stack = new cdk.Stack();
  bucket = new s3.Bucket(stack, 'MyBucket');
  handler = new lambda.Function(stack, 'MyFunction', {
    runtime: lambda.Runtime.NODEJS_14_X,
    handler: 'index.hello',
    code: new lambda.InlineCode('def hello(): pass'),
  });
});

test('can create a valid access point');
test('regional virtual hosted URL');
test('non-regional virtual hosted URL');
...etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

@mergify mergify bot dismissed corymhall’s stale review February 28, 2022 18:52

Pull request has been modified.

Copy link
Contributor

@corymhall corymhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@duarten looks great!

@mergify
Copy link
Contributor

mergify bot commented Mar 4, 2022

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: 79e2e63
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit fe9f750 into aws:master Mar 4, 2022
@mergify
Copy link
Contributor

mergify bot commented Mar 4, 2022

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

TheRealAmazonKendra pushed a commit to TheRealAmazonKendra/aws-cdk that referenced this pull request Mar 11, 2022
…15833)

This PR adds an L2 construct for the S3 Object Lambda.

To avoid a circular dependency, the construct lives outside of the aws-s3 package.

Fixes aws#13675

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-s3objectlambda effort/medium Medium work item – several days of effort p2 pr/needs-tests This PR is missing tests.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(s3): L2 Construct for S3 Object Lambda
5 participants