Skip to content

Commit e314a9a

Browse files
authored
feat(kinesisfirehose): support S3 file extension format (#33776)
### Issue # (if applicable) Closes #32154. ### Reason for this change The file extension can be overridden by specifying `FileExtension`. For details, see **S3 file extension format (optional)** in [Configure destination settings for Amazon S3](https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-s3) ### Description of changes Added `fileExtension` prop to `S3BucketProps`. ### Describe any new or updated permissions being added N/A ### Description of how you validated changes Added unit tests and updated an integ test to include `fileExtension`. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent d8bbc1c commit e314a9a

File tree

11 files changed

+114
-20
lines changed

11 files changed

+114
-20
lines changed
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/aws-cdk-firehose-delivery-stream-s3-all-properties.assets.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/aws-cdk-firehose-delivery-stream-s3-all-properties.template.json

+1
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@
590590
}
591591
},
592592
"ErrorOutputPrefix": "errorPrefix",
593+
"FileExtension": ".log.gz",
593594
"Prefix": "regularPrefix",
594595
"ProcessingConfiguration": {
595596
"Enabled": true,

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.assets.json

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/integtestsDefaultTestDeployAssert44C8D370.template.json

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/manifest.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.js.snapshot/tree.json

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-kinesisfirehose/test/integ.s3-bucket.lit.ts

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const deliveryStream = new firehose.DeliveryStream(stack, 'DeliveryStream', {
5151
compression: firehose.Compression.GZIP,
5252
dataOutputPrefix: 'regularPrefix',
5353
errorOutputPrefix: 'errorPrefix',
54+
fileExtension: '.log.gz',
5455
bufferingInterval: cdk.Duration.seconds(60),
5556
bufferingSize: cdk.Size.mebibytes(1),
5657
encryptionKey: key,

packages/aws-cdk-lib/aws-kinesisfirehose/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ const s3Destination = new firehose.S3Bucket(bucket, {
112112
See: [Custom S3 Prefixes](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)
113113
in the *Amazon Data Firehose Developer Guide*.
114114

115+
To override default file extension appended by Data Format Conversion or S3 compression features, specify `fileExtension`.
116+
117+
```ts
118+
declare const bucket: s3.Bucket;
119+
const s3Destination = new firehose.S3Bucket(bucket, {
120+
compression: firehose.Compression.GZIP,
121+
fileExtension: '.json.gz',
122+
});
123+
```
124+
115125
## Server-side Encryption
116126

117127
Enabling server-side encryption (SSE) requires Amazon Data Firehose to encrypt all data

packages/aws-cdk-lib/aws-kinesisfirehose/lib/s3-bucket.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ import { DestinationBindOptions, DestinationConfig, IDestination } from './desti
44
import * as iam from '../../aws-iam';
55
import * as s3 from '../../aws-s3';
66
import { createBackupConfig, createBufferingHints, createEncryptionConfig, createLoggingOptions, createProcessingConfig } from './private/helpers';
7-
import { UnscopedValidationError } from '../../core';
7+
import { Token, UnscopedValidationError, ValidationError } from '../../core';
88

99
/**
1010
* Props for defining an S3 destination of an Amazon Data Firehose delivery stream.
1111
*/
1212
export interface S3BucketProps extends CommonDestinationS3Props, CommonDestinationProps {
13+
/**
14+
* Specify a file extension.
15+
* It will override the default file extension appended by Data Format Conversion or S3 compression features such as `.parquet` or `.gz`.
16+
*
17+
* File extension must start with a period (`.`) and can contain allowed characters: `0-9a-z!-_.*'()`.
18+
*
19+
* @see https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-s3
20+
* @default - The default file extension appended by Data Format Conversion or S3 compression features
21+
*/
22+
readonly fileExtension?: string;
1323
}
1424

1525
/**
@@ -36,6 +46,17 @@ export class S3Bucket implements IDestination {
3646
}) ?? {};
3747

3848
const { backupConfig, dependables: backupDependables } = createBackupConfig(scope, role, this.props.s3Backup) ?? {};
49+
50+
const fileExtension = this.props.fileExtension;
51+
if (fileExtension && !Token.isUnresolved(fileExtension)) {
52+
if (!fileExtension.startsWith('.')) {
53+
throw new ValidationError("fileExtension must start with '.'", scope);
54+
}
55+
if (/[^0-9a-z!\-_.*'()]/.test(fileExtension)) {
56+
throw new ValidationError("fileExtension can contain allowed characters: 0-9a-z!-_.*'()", scope);
57+
}
58+
}
59+
3960
return {
4061
extendedS3DestinationConfiguration: {
4162
cloudWatchLoggingOptions: loggingOptions,
@@ -49,6 +70,7 @@ export class S3Bucket implements IDestination {
4970
encryptionConfiguration: createEncryptionConfig(role, this.props.encryptionKey),
5071
errorOutputPrefix: this.props.errorOutputPrefix,
5172
prefix: this.props.dataOutputPrefix,
73+
fileExtension: this.props.fileExtension,
5274
},
5375
dependables: [bucketGrant, ...(loggingDependables ?? []), ...(backupDependables ?? [])],
5476
};

packages/aws-cdk-lib/aws-kinesisfirehose/test/s3-bucket.test.ts

+61
Original file line numberDiff line numberDiff line change
@@ -597,4 +597,65 @@ describe('S3 destination', () => {
597597
});
598598
});
599599
});
600+
601+
describe('file extension', () => {
602+
it('sets fileExtension', () => {
603+
const destination = new firehose.S3Bucket(bucket, {
604+
role: destinationRole,
605+
fileExtension: '.json',
606+
});
607+
new firehose.DeliveryStream(stack, 'DeliveryStream', {
608+
destination: destination,
609+
});
610+
611+
Template.fromStack(stack).hasResourceProperties('AWS::KinesisFirehose::DeliveryStream', {
612+
ExtendedS3DestinationConfiguration: {
613+
FileExtension: '.json',
614+
},
615+
});
616+
});
617+
618+
it('sets fileExtension from a token', () => {
619+
const fileExtension = new cdk.CfnParameter(stack, 'FileExtension');
620+
const destination = new firehose.S3Bucket(bucket, {
621+
role: destinationRole,
622+
fileExtension: fileExtension.valueAsString,
623+
});
624+
new firehose.DeliveryStream(stack, 'DeliveryStream', {
625+
destination: destination,
626+
});
627+
628+
Template.fromStack(stack).hasResourceProperties('AWS::KinesisFirehose::DeliveryStream', {
629+
ExtendedS3DestinationConfiguration: {
630+
FileExtension: { Ref: 'FileExtension' },
631+
},
632+
});
633+
});
634+
635+
it('throws when fileExtension does not start with a period', () => {
636+
const destination = new firehose.S3Bucket(bucket, {
637+
role: destinationRole,
638+
fileExtension: 'json',
639+
});
640+
641+
expect(() => {
642+
new firehose.DeliveryStream(stack, 'DeliveryStream', {
643+
destination: destination,
644+
});
645+
}).toThrow("fileExtension must start with '.'");
646+
});
647+
648+
it('throws when fileExtension contains unallowed characters', () => {
649+
const destination = new firehose.S3Bucket(bucket, {
650+
role: destinationRole,
651+
fileExtension: '.json?',
652+
});
653+
654+
expect(() => {
655+
new firehose.DeliveryStream(stack, 'DeliveryStream', {
656+
destination: destination,
657+
});
658+
}).toThrow("fileExtension can contain allowed characters: 0-9a-z!-_.*'()");
659+
});
660+
});
600661
});

0 commit comments

Comments
 (0)