Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@
}
},
"ErrorOutputPrefix": "errorPrefix",
"FileExtension": ".log.gz",
"Prefix": "regularPrefix",
"ProcessingConfiguration": {
"Enabled": true,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const deliveryStream = new firehose.DeliveryStream(stack, 'DeliveryStream', {
compression: firehose.Compression.GZIP,
dataOutputPrefix: 'regularPrefix',
errorOutputPrefix: 'errorPrefix',
fileExtension: '.log.gz',
bufferingInterval: cdk.Duration.seconds(60),
bufferingSize: cdk.Size.mebibytes(1),
encryptionKey: key,
Expand Down
10 changes: 10 additions & 0 deletions packages/aws-cdk-lib/aws-kinesisfirehose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ const s3Destination = new firehose.S3Bucket(bucket, {
See: [Custom S3 Prefixes](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)
in the *Amazon Data Firehose Developer Guide*.

To override default file extension appended by Data Format Conversion or S3 compression features, specify `fileExtension`.

```ts
declare const bucket: s3.Bucket;
const s3Destination = new firehose.S3Bucket(bucket, {
compression: firehose.Compression.GZIP,
fileExtension: '.json.gz',
});
```

## Server-side Encryption

Enabling server-side encryption (SSE) requires Amazon Data Firehose to encrypt all data
Expand Down
24 changes: 23 additions & 1 deletion packages/aws-cdk-lib/aws-kinesisfirehose/lib/s3-bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ import { DestinationBindOptions, DestinationConfig, IDestination } from './desti
import * as iam from '../../aws-iam';
import * as s3 from '../../aws-s3';
import { createBackupConfig, createBufferingHints, createEncryptionConfig, createLoggingOptions, createProcessingConfig } from './private/helpers';
import { UnscopedValidationError } from '../../core';
import { Token, UnscopedValidationError, ValidationError } from '../../core';

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

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

const { backupConfig, dependables: backupDependables } = createBackupConfig(scope, role, this.props.s3Backup) ?? {};

const fileExtension = this.props.fileExtension;
if (fileExtension && !Token.isUnresolved(fileExtension)) {
if (!fileExtension.startsWith('.')) {
throw new ValidationError("fileExtension must start with '.'", scope);
}
if (/[^0-9a-z!\-_.*'()]/.test(fileExtension)) {
throw new ValidationError("fileExtension can contain allowed characters: 0-9a-z!-_.*'()", scope);
}
}

return {
extendedS3DestinationConfiguration: {
cloudWatchLoggingOptions: loggingOptions,
Expand All @@ -49,6 +70,7 @@ export class S3Bucket implements IDestination {
encryptionConfiguration: createEncryptionConfig(role, this.props.encryptionKey),
errorOutputPrefix: this.props.errorOutputPrefix,
prefix: this.props.dataOutputPrefix,
fileExtension: this.props.fileExtension,
},
dependables: [bucketGrant, ...(loggingDependables ?? []), ...(backupDependables ?? [])],
};
Expand Down
61 changes: 61 additions & 0 deletions packages/aws-cdk-lib/aws-kinesisfirehose/test/s3-bucket.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,4 +597,65 @@ describe('S3 destination', () => {
});
});
});

describe('file extension', () => {
it('sets fileExtension', () => {
const destination = new firehose.S3Bucket(bucket, {
role: destinationRole,
fileExtension: '.json',
});
new firehose.DeliveryStream(stack, 'DeliveryStream', {
destination: destination,
});

Template.fromStack(stack).hasResourceProperties('AWS::KinesisFirehose::DeliveryStream', {
ExtendedS3DestinationConfiguration: {
FileExtension: '.json',
},
});
});

it('sets fileExtension from a token', () => {
const fileExtension = new cdk.CfnParameter(stack, 'FileExtension');
const destination = new firehose.S3Bucket(bucket, {
role: destinationRole,
fileExtension: fileExtension.valueAsString,
});
new firehose.DeliveryStream(stack, 'DeliveryStream', {
destination: destination,
});

Template.fromStack(stack).hasResourceProperties('AWS::KinesisFirehose::DeliveryStream', {
ExtendedS3DestinationConfiguration: {
FileExtension: { Ref: 'FileExtension' },
},
});
});

it('throws when fileExtension does not start with a period', () => {
const destination = new firehose.S3Bucket(bucket, {
role: destinationRole,
fileExtension: 'json',
});

expect(() => {
new firehose.DeliveryStream(stack, 'DeliveryStream', {
destination: destination,
});
}).toThrow("fileExtension must start with '.'");
});

it('throws when fileExtension contains unallowed characters', () => {
const destination = new firehose.S3Bucket(bucket, {
role: destinationRole,
fileExtension: '.json?',
});

expect(() => {
new firehose.DeliveryStream(stack, 'DeliveryStream', {
destination: destination,
});
}).toThrow("fileExtension can contain allowed characters: 0-9a-z!-_.*'()");
});
});
});