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: Add FirehoseToS3Stage and tests #151

Merged

Conversation

jaidisido
Copy link
Contributor

  • Add utils and defaults modules. Helper methods for the first, defaults for resources for the second
  • Add FirehoseToS3Stage
  • Add tests for aforementioned stage (100% coverage) and modify basic pipeline to include this stage instead

Copy link
Contributor Author

@jaidisido jaidisido left a comment

Choose a reason for hiding this comment

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

Clarifications

.projenrc.js Outdated Show resolved Hide resolved
],
});

// Experimental modules
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might want to discuss how we wish to handle additional deps in .projenrc.js

@@ -0,0 +1,11 @@
import { Compression, S3BucketProps } from '@aws-cdk/aws-kinesisfirehose-destinations-alpha';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is how defaults are handled in the construct-hub library, what do we think about this approach? I like how it enables us to centralise these defaults instead of having them all over the place at the moment

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a fan, this will make precedence of settings (config, explicit props, defaults) a lot more readable in the stages as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't create a separate file for the defaults tbh but otherwise looks good to me

return target;
}

export function overrideProps(DefaultProps: object, userProps: object, concatArray: boolean = false): any {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some useful util functions to manipulate props (i.e. override and/or combine them)

Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nice to have some documentation for them - they do look useful but some are far less obvious than others


export interface FirehoseToS3StageProps extends DataStageProps {
readonly s3Bucket?: s3.IBucket;
readonly s3BucketProps?: s3.BucketProps;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have used the existing BucketProps and destinations.S3BucketProps interfaces instead of defining my own. Is there are reason why we have adopted a different strategy in other stages (i.e. defining our own interfaces for these)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Mostly I was copying these definitions as they were in Python. I figured the reasoning was that we didn't want them messing with some important defaults (like making a function fully open to the public or something). Or if they really did, they would have to pass their own Function/Bucket.

Another concern I have is that when a user reads the documentation for what goes into FirehoseToS3StageProps, they'll look at the properties of s3.BucketProps and then they'll read that if they don't set the encryption, it will be set to unencrypted. This will be confusing if we're offering different default values, such as S3_MANAGED for encryption. A benefit of defining our own set of properties is that at least our documentation will be accurate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair points. I suppose my concern about restricting the arguments of Props is that there will always be a user who needs a specific argument that we wouldn't be supporting (c.f. Tracing in the Lambda Function). I find the idea of asking them to create their own Lambda function to be a bad UX and kind of defeats the point of DDK a bit. I suppose it goes back to the discussion on props and escape hatches again...

Copy link
Contributor

Choose a reason for hiding this comment

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

Typescript does let us hack around this a little bit. We could have something like:

interface OurBucketProps extends Omit<s3.BucketProps, "encryption"> {
  /**
    Our custom documentation, and here we can mention what our default is.
  */
  readonly encryption: s3.EncryptionOption;
}

This way, OurBucketProps extends a version of the BucketProps that omits the encryption property. This allows us to redefine it ourself, with our own documentation, while still inheriting all other properties. And if we then decide to do the same thing with enforceSsl, we simply inherit from Omit<s3.BucketProps, 'encryption', 'enforceSsl'> , and define our own enforceSsl.

This solution would technically solve the problem for us, although my primary concern here is I'm not sure how well this would play with other languages as it gets translated with JSII.

Copy link
Contributor

Choose a reason for hiding this comment

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

A pattern that I've seen is to extend the type even if there are no additions to the type at the moment i.e.

interface OurBucketProps extends s3.BucketProps {}

This way we can still keep the code backward-compatible:

  1. Add properties when required
  2. Break the inheritance if we have to divert from the original definition of a property at some point without changing the type of what the user has to provide

Copy link
Contributor

Choose a reason for hiding this comment

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

Omit & other utility types are cool but I would avoid them unless absolutely necessary as they're a bit of a hack

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is that without Omit, JSII will not let you redefine a property, even to just rewrite the property documentation. When I tried, it said it would break compatibility with C# (and Java I believe).

@jaidisido jaidisido changed the base branch from typescript-conversion to main August 19, 2022 14:25
@jaidisido jaidisido changed the base branch from main to typescript-conversion August 19, 2022 14:25
@jaidisido jaidisido force-pushed the typescript-conversion-kinesis-s3-stage branch from 90733f5 to 183adae Compare August 19, 2022 14:28
@jaidisido jaidisido changed the title (feat): Add FirehoseToS3Stage and tests feat: Add FirehoseToS3Stage and tests Aug 19, 2022

export interface FirehoseToS3StageProps extends DataStageProps {
readonly s3Bucket?: s3.IBucket;
readonly s3BucketProps?: s3.BucketProps;
Copy link
Contributor

Choose a reason for hiding this comment

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

Mostly I was copying these definitions as they were in Python. I figured the reasoning was that we didn't want them messing with some important defaults (like making a function fully open to the public or something). Or if they really did, they would have to pass their own Function/Bucket.

Another concern I have is that when a user reads the documentation for what goes into FirehoseToS3StageProps, they'll look at the properties of s3.BucketProps and then they'll read that if they don't set the encryption, it will be set to unencrypted. This will be confusing if we're offering different default values, such as S3_MANAGED for encryption. A benefit of defining our own set of properties is that at least our documentation will be accurate.

import { Compression, S3BucketProps } from '@aws-cdk/aws-kinesisfirehose-destinations-alpha';
import { Duration, Size } from 'aws-cdk-lib';

export function DefaultDestinationsS3BucketProps(_dataOutputPrefix?: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: Why is Default capitalized? I thought the convention in Typescript was that functions are camelCase rather than PascalCase. We'll also need to figure out how to enforce these conventions. I think this rule should be good. Obviously this can be a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, not sure why they are doing that. Perhaps because it's supposed to mimick a Props interface. But I agree, I will switch to lower case

removalPolicy: RemovalPolicy.RETAIN,
enforceSSL: true,
eventBridgeEnabled: false,
...((lifecycleRules !== undefined) && { lifecycleRules }),
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this syntax do exactly? Does it check if lifecycleRules is set, and if it is, it puts it in the props?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is my understanding indeed. If lifecycle rules is passed, add it to the object, otherwise ignore

Copy link
Contributor

Choose a reason for hiding this comment

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

This is very cool, I've been missing that in Python a LOT

throw TypeError("'s3Bucket' or 's3BucketProps' must be set to instantiate this stage");
}

const dataStreamEnabled = props.dataStreamEnabled ?? false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we have a function that assigns default, in order to avoid having to do it this way? Or are we only using that structure for original CDK constructs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I felt that creating a function to handle defaults for a single variable to be a bit of an overkill as it could quickly get out of hands

@jaidisido jaidisido added this to In Review in Roadmap Aug 19, 2022
Copy link
Contributor

@kukushking kukushking left a comment

Choose a reason for hiding this comment

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

LGTM! Some ongoing discussions in threads but overall looks great!

@jaidisido jaidisido added this to the 1.0.0 milestone Aug 22, 2022
@jaidisido jaidisido merged commit 03216e5 into typescript-conversion Aug 23, 2022
@jaidisido jaidisido deleted the typescript-conversion-kinesis-s3-stage branch August 23, 2022 10:36
@malachi-constant malachi-constant moved this from In Review to Done in Roadmap Sep 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

None yet

4 participants