|
| 1 | +import { IPipe, ISource, SourceConfig } from '@aws-cdk/aws-pipes-alpha'; |
| 2 | +import { Duration } from 'aws-cdk-lib'; |
| 3 | +import { IRole } from 'aws-cdk-lib/aws-iam'; |
| 4 | +import { ITableV2 } from 'aws-cdk-lib/aws-dynamodb'; |
| 5 | +import { DeadLetterConfigParameters } from './deadLetterConfig'; |
| 6 | +import { DynamoDBStartingPosition, OnPartialBatchItemFailure } from './enums'; |
| 7 | + |
| 8 | +/** |
| 9 | + * Parameters for the DynamoDB source. |
| 10 | + */ |
| 11 | +export interface DynamoDBSourceParameters { |
| 12 | + /** |
| 13 | + * The maximum number of records to include in each batch. |
| 14 | + * |
| 15 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-batchsize |
| 16 | + * @default 1 |
| 17 | + */ |
| 18 | + readonly batchSize?: number; |
| 19 | + |
| 20 | + /** |
| 21 | + * Define the target queue to send dead-letter queue events to. |
| 22 | + * |
| 23 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-deadletterconfig |
| 24 | + * @default no dead letter queue |
| 25 | + */ |
| 26 | + readonly deadLetterConfig?: DeadLetterConfigParameters; |
| 27 | + |
| 28 | + /** |
| 29 | + * The maximum length of a time to wait for events. |
| 30 | + * |
| 31 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-maximumbatchingwindowinseconds |
| 32 | + * @default no batching window |
| 33 | + */ |
| 34 | + readonly maximumBatchingWindow?: Duration; |
| 35 | + |
| 36 | + /** |
| 37 | + * (Streams only) Discard records older than the specified age. The default value is -1, which sets the maximum age to infinite. When the value is set to infinite, EventBridge never discards old records. |
| 38 | + * |
| 39 | + * Leave undefined to set the maximum record age to infinite. |
| 40 | + * |
| 41 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-maximumrecordageinseconds |
| 42 | + * @default -1 (infinite) |
| 43 | + */ |
| 44 | + readonly maximumRecordAge?: Duration; |
| 45 | + |
| 46 | + /** |
| 47 | + * (Streams only) Discard records after the specified number of retries. The default value is -1, which sets the maximum number of retries to infinite. When MaximumRetryAttempts is infinite, EventBridge retries failed records until the record expires in the event source. |
| 48 | + * |
| 49 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-maximumretryattempts |
| 50 | + * @default -1 (infinite) |
| 51 | + */ |
| 52 | + readonly maximumRetryAttempts?: number; |
| 53 | + |
| 54 | + /** |
| 55 | + * (Streams only) Define how to handle item process failures. AUTOMATIC_BISECT halves each batch and retry each half until all the records are processed or there is one failed message left in the batch. |
| 56 | + * |
| 57 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-onpartialbatchitemfailure |
| 58 | + * @default off |
| 59 | + */ |
| 60 | + readonly onPartialBatchItemFailure?: OnPartialBatchItemFailure; |
| 61 | + |
| 62 | + /** |
| 63 | + * (Streams only) The number of batches to process concurrently from each shard. The default value is 1. |
| 64 | + * |
| 65 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-parallelizationfactor |
| 66 | + * @default 1 |
| 67 | + */ |
| 68 | + readonly parallelizationFactor?: number; |
| 69 | + |
| 70 | + /** |
| 71 | + * (Streams only) The position in a stream from which to start reading. |
| 72 | + * |
| 73 | + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipesourcedynamodbstreamparameters.html#cfn-pipes-pipe-pipesourcedynamodbstreamparameters-startingposition |
| 74 | + */ |
| 75 | + readonly startingPosition: DynamoDBStartingPosition; |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * A source that reads from an DynamoDB stream. |
| 80 | + */ |
| 81 | +export class DynamoDBSource implements ISource { |
| 82 | + private readonly table: ITableV2; |
| 83 | + readonly sourceArn; |
| 84 | + private sourceParameters; |
| 85 | + |
| 86 | + private batchSize; |
| 87 | + private maximumBatchingWindowInSeconds; |
| 88 | + private maximumRecordAgeInSeconds; |
| 89 | + private maximumRetryAttempts; |
| 90 | + private parallelizationFactor; |
| 91 | + |
| 92 | + constructor(table: ITableV2, parameters: DynamoDBSourceParameters) { |
| 93 | + this.table = table; |
| 94 | + |
| 95 | + if (table.tableStreamArn === undefined) { |
| 96 | + throw new Error('Table does not have a stream defined, cannot create pipes source'); |
| 97 | + } |
| 98 | + |
| 99 | + this.sourceArn = table.tableStreamArn; |
| 100 | + this.sourceParameters = parameters; |
| 101 | + |
| 102 | + this.batchSize = this.sourceParameters.batchSize; |
| 103 | + this.maximumBatchingWindowInSeconds = this.sourceParameters.maximumBatchingWindow?.toSeconds(); |
| 104 | + this.maximumRecordAgeInSeconds = this.sourceParameters.maximumRecordAge?.toSeconds(); |
| 105 | + this.maximumRetryAttempts = this.sourceParameters.maximumRetryAttempts; |
| 106 | + this.parallelizationFactor = this.sourceParameters.parallelizationFactor; |
| 107 | + |
| 108 | + if (this.batchSize !== undefined) { |
| 109 | + if (this.batchSize < 1 || this.batchSize > 10000) { |
| 110 | + throw new Error(`Batch size must be between 1 and 10000, received ${this.batchSize}`); |
| 111 | + } |
| 112 | + } |
| 113 | + if (this.maximumBatchingWindowInSeconds !== undefined) { |
| 114 | + // only need to check upper bound since Duration amounts cannot be negative |
| 115 | + if (this.maximumBatchingWindowInSeconds > 300) { |
| 116 | + throw new Error(`Maximum batching window must be between 0 and 300, received ${this.maximumBatchingWindowInSeconds}`); |
| 117 | + } |
| 118 | + } |
| 119 | + if (this.maximumRecordAgeInSeconds !== undefined) { |
| 120 | + // only need to check upper bound since Duration amounts cannot be negative |
| 121 | + if (this.maximumRecordAgeInSeconds > 604800) { |
| 122 | + throw new Error(`Maximum record age in seconds must be between -1 and 604800, received ${this.maximumRecordAgeInSeconds}`); |
| 123 | + } |
| 124 | + } |
| 125 | + if (this.maximumRetryAttempts !== undefined) { |
| 126 | + if (this.maximumRetryAttempts < -1 || this.maximumRetryAttempts > 10000) { |
| 127 | + throw new Error(`Maximum retry attempts must be between -1 and 10000, received ${this.maximumRetryAttempts}`); |
| 128 | + } |
| 129 | + } |
| 130 | + if (this.parallelizationFactor !== undefined) { |
| 131 | + if (this.parallelizationFactor < 1 || this.parallelizationFactor > 10) { |
| 132 | + throw new Error(`Parallelization factor must be between 1 and 10, received ${this.parallelizationFactor}`); |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + bind(_pipe: IPipe): SourceConfig { |
| 138 | + return { |
| 139 | + sourceParameters: { |
| 140 | + dynamoDbStreamParameters: { |
| 141 | + batchSize: this.batchSize, |
| 142 | + deadLetterConfig: this.sourceParameters.deadLetterConfig, |
| 143 | + maximumBatchingWindowInSeconds: this.maximumBatchingWindowInSeconds, |
| 144 | + maximumRecordAgeInSeconds: this.maximumRecordAgeInSeconds, |
| 145 | + maximumRetryAttempts: this.maximumRetryAttempts, |
| 146 | + onPartialBatchItemFailure: this.sourceParameters.onPartialBatchItemFailure, |
| 147 | + parallelizationFactor: this.sourceParameters.parallelizationFactor, |
| 148 | + startingPosition: this.sourceParameters.startingPosition, |
| 149 | + }, |
| 150 | + }, |
| 151 | + }; |
| 152 | + } |
| 153 | + |
| 154 | + grantRead(grantee: IRole): void { |
| 155 | + this.table.grantStreamRead(grantee); |
| 156 | + } |
| 157 | +} |
| 158 | + |
0 commit comments