From 1fafbab1c8b847cbafed474721f5f562416d709f Mon Sep 17 00:00:00 2001 From: Rizbir Fahmid Khan Date: Mon, 11 Mar 2024 16:19:03 +0000 Subject: [PATCH 01/30] intial commit --- .../redshift-query/integ.redshift-query.ts | 70 +++++++++++ .../aws-events-targets/lib/index.ts | 1 + .../aws-events-targets/lib/redshift-query.ts | 112 ++++++++++++++++++ .../redshift-query/redshift-query.test.ts | 97 +++++++++++++++ packages/aws-cdk-lib/aws-events/lib/target.ts | 15 ++- .../aws-cdk-lib/aws-events/test/rule.test.ts | 40 +++++++ 6 files changed, 330 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts create mode 100644 packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts create mode 100644 packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts new file mode 100644 index 0000000000000..ea890d183a9b6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -0,0 +1,70 @@ +import * as events from 'aws-cdk-lib/aws-events'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; +import * as redshift from '@aws-cdk/aws-redshift-alpha' +import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'log-group-events'); + +const vpc = new ec2.Vpc(stack, 'VPC'); + +const cluster = new redshift.Cluster(stack, 'Cluster', { + vpc, + masterUser: { + masterUsername: 'admin', + }, + defaultDatabaseName: 'dev', + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +const workGroup = new redshiftserverless.CfnWorkgroup(stack, 'WorkGroup', { + workgroupName: 'workgroup', +}); + +const importedCluster = redshift.Cluster.fromClusterAttributes(stack, 'ImportedCluster', { + clusterName: 'imported-cluster', + clusterEndpointAddress: 'imported-cluster-endpoint', + clusterEndpointPort: 5439, + securityGroups: [], +}); + +const timer = new events.Rule(stack, 'Timer', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), +}); +timer.addTarget(new targets.RedshiftQuery(`arn:aws:redshift:${cluster.env.region}:${cluster.env.account}:cluster:${cluster.clusterName}`, {})); + +const customRule = new events.Rule(stack, 'CustomRule', { + eventPattern: { + source: ['cdk-integ'], + detailType: ['cdk-integ-custom-rule'], + }, +}); +customRule.addTarget(new targets.RedshiftQuery(`arn:aws:redshift:${importedCluster.env.region}:${importedCluster.env.account}:cluster:${importedCluster.clusterName}`, { + database: 'dev', +})); + +const queue = new sqs.Queue(stack, 'dlq'); + +const timer3 = new events.Rule(stack, 'Timer3', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), +}); +timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { + database: 'dev', + deadLetterQueue: queue, + sql: [ + 'SELECT * FROM foo', + 'SELECT * FROM bar', + ], +})); + +new IntegTest(app, 'LogGroup', { + testCases: [stack], + diffAssets: true, +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts index 6c91810ebca33..8107b3ef93391 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts @@ -15,3 +15,4 @@ export * from './kinesis-firehose-stream'; export * from './api-gateway'; export * from './api-destination'; export * from './util'; +export * from './redshift-query'; \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts new file mode 100644 index 0000000000000..166c80c3658e9 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -0,0 +1,112 @@ +import { singletonEventRole, addToDeadLetterQueueResourcePolicy } from './util'; +import * as events from '../../aws-events'; +import * as iam from '../../aws-iam'; +import * as secretsmanager from '../../aws-secretsmanager'; +import * as sqs from '../../aws-sqs'; + +/** + * Configuration properties of an Amazon Redshift Query event. + */ +export interface RedshiftQueryProps { + + /** + * The Amazon Redshift database to run the query against. + * + * @default - dev + */ + readonly database?: string; + + /** + * The Amazon Redshift database user to run the query as. This is required when authenticating via temporary credentials. + */ + readonly dbUser?: string; + + /** + * The secret containing the password for the database user. This is required when authenticating via Secrets Manager. + * If the full secret ARN is not specified, this will instead use the secret name. + */ + readonly secret?: secretsmanager.ISecret; + + /** + * The SQL query to run. This will use the `executeStatement` API. + */ + readonly sql?: string; + + /** + * The SQL queries to run. All statements are executed as a single transaction. They each run serially, as appeared in the array; the next sql statement will not run until the previous statement completes. + * If any statement fails, the entire transaction is rolled back. This will use the `batchExecuteStatement` API. + */ + readonly batchSQL?: string[]; + + /** + * The name of the SQL statement. You can name the SQL statement for identitfication purposes. It is recommended to append a `QS2-` prefix to the statement name, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. + */ + readonly statementName?: string; + + /** + * Should an event be sent back to Event Bridge when the SQL statement is executed. + */ + readonly withEvent?: boolean; + + /** + * The queue to be used as dead letter queue. + */ + readonly deadLetterQueue?: sqs.IQueue; + + /** + * The IAM role to be used to execute the SQL statement. + * + * @default - a new role will be created. + */ + readonly role?: iam.IRole; +} + +/** + * Schedule an Amazon Redshift Query to be run, using the Redshift Data API. + * It is recommended to append a `QS2-` prefix to both `statementName` and `ruleName`, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. + */ +export class RedshiftQuery implements events.IRuleTarget { + constructor(private readonly clusterArn: string, private readonly props: RedshiftQueryProps = { + database: 'dev', + }) { + } + + bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { + const role = this.props.role ?? singletonEventRole(rule); + if (this.props.sql) { + role.addToPrincipalPolicy(this.putEventStatement()); + } + if (this.props.batchSQL) { + role.addToPrincipalPolicy(this.putBatchEventStatement()); + } + + return { + arn: this.clusterArn, + deadLetterConfig: this.props.deadLetterQueue ? { arn: this.props.deadLetterQueue?.queueArn } : undefined, + role, + redshiftDataParameters: { + database: this.props.database ?? 'dev', + dbUser: this.props.dbUser, + secretManagerArn: this.props.secret?.secretFullArn ?? this.props.secret?.secretName, + sql: this.props.sql, + sqls: this.props.batchSQL, + statementName: this.props.statementName, + withEvent: this.props.withEvent, + }, + }; + } + + private putEventStatement() { + return new iam.PolicyStatement({ + actions: ['redshift-data:ExecuteStatement'], + resources: [this.clusterArn], + }); + } + + private putBatchEventStatement() { + return new iam.PolicyStatement({ + actions: ['redshift-data:BatchExecuteStatement'], + resources: [this.clusterArn], + }); + } +} diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts new file mode 100644 index 0000000000000..a4ba3275fb825 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -0,0 +1,97 @@ +import { Template } from '../../../assertions'; +import * as events from '../../../aws-events'; +import { Stack } from '../../../core'; +import * as targets from '../../lib'; + +describe('KinesisFirehoseStream event target', () => { + let stack: Stack; + let clusterArn: string; + let streamArn: any; + + beforeEach(() => { + stack = new Stack(); + clusterArn = 'arn:aws:redshift:us-west-2:123456789012:cluster:my-cluster'; + streamArn = { 'Fn::GetAtt': ['MyStream', 'Arn'] }; + }); + + describe('when added to an event rule as a target', () => { + let rule: events.Rule; + + beforeEach(() => { + rule = new events.Rule(stack, 'rule', { + schedule: events.Schedule.expression('rate(1 minute)'), + }); + }); + + describe('with default settings', () => { + beforeEach(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn)); + }); + + test('adds the clusters ARN and role to the targets of the rule', () => { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RoleArn: { 'Fn::GetAtt': ['MyClusterEventsRole5B6CC6AF', 'Arn'] }, + }, + ], + }); + }); + + test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: ['redshift:ExecuteStatement'], + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('assigns the `dev` database to the RedshiftQuery', () => { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + RedshiftDataParameters: { + Database: 'dev', + }, + }, + ], + }); + }); + }); + + describe('with explicity SQL statements', () => { + beforeEach(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + sql: 'SELECT * FROM foo', + batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + }); + + test('sets the SQL statements', () => { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: streamArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + Sqls: ['SELECT * FROM foo', 'SELECT * FROM bar'], + }, + }, + ], + }); + }); + }); + }); +}); + diff --git a/packages/aws-cdk-lib/aws-events/lib/target.ts b/packages/aws-cdk-lib/aws-events/lib/target.ts index e836bcf75e53d..c1cfb0e6bcce5 100644 --- a/packages/aws-cdk-lib/aws-events/lib/target.ts +++ b/packages/aws-cdk-lib/aws-events/lib/target.ts @@ -1,8 +1,8 @@ -import { IConstruct } from 'constructs'; -import { CfnRule } from './events.generated'; -import { RuleTargetInput } from './input'; -import { IRule } from './rule-ref'; -import * as iam from '../../aws-iam'; +import { IConstruct } from "constructs"; +import * as iam from "../../aws-iam"; +import { CfnRule } from "./events.generated"; +import { RuleTargetInput } from "./input"; +import { IRule } from "./rule-ref"; /** * An abstract target for EventRules. @@ -92,6 +92,11 @@ export interface RuleTargetConfig { */ readonly sqsParameters?: CfnRule.SqsParametersProperty; + /** + * Parameters used when the rule invokes Amazon Redshift Queries + */ + readonly redshiftDataParameters?: CfnRule.RedshiftDataParametersProperty; + /** * What input to send to the event target * diff --git a/packages/aws-cdk-lib/aws-events/test/rule.test.ts b/packages/aws-cdk-lib/aws-events/test/rule.test.ts index 80f3c5cbe53d6..8eda0bd66be87 100644 --- a/packages/aws-cdk-lib/aws-events/test/rule.test.ts +++ b/packages/aws-cdk-lib/aws-events/test/rule.test.ts @@ -667,6 +667,46 @@ describe('rule', () => { }); }); + test('redshiftDataParameters are generated when they are specified in target props', () => { + const stack = new cdk.Stack(); + const t1: IRuleTarget = { + bind: () => ({ + id: '', + arn: 'ARN1', + redshiftDataParameters: { + database: 'database', + dbUser: 'dbUser', + secretManagerArn: 'secretManagerArn', + sqls: ['sqls'], + statementName: 'statementName', + withEvent: true, + }, + }), + }; + + new Rule(stack, 'EventRule', { + schedule: Schedule.rate(cdk.Duration.minutes(5)), + targets: [t1], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + 'Arn': 'ARN1', + 'Id': 'Target0', + 'RedshiftDataParameters': { + 'Database': 'database', + 'DbUser': 'dbUser', + 'SecretManagerArn': 'secretManagerArn', + 'Sqls': ['sqls'], + 'StatementName': 'statementName', + 'WithEvent': true, + }, + }, + ], + }); + }); + test('associate rule with event bus', () => { // GIVEN const stack = new cdk.Stack(); From da25e090ffb327660ed52c95e88308c3ae0ee941 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Mon, 11 Mar 2024 17:56:20 +0000 Subject: [PATCH 02/30] integ test --- ...efaultTestDeployAssert353EE07A.assets.json | 19 ++ ...aultTestDeployAssert353EE07A.template.json | 36 +++ .../integ.redshift-query.js.snapshot/cdk.out | 1 + .../integ.json | 13 + .../manifest.json | 143 +++++++++ .../redshift-query-events.assets.json | 19 ++ .../redshift-query-events.template.json | 145 +++++++++ .../tree.json | 300 ++++++++++++++++++ .../redshift-query/integ.redshift-query.ts | 44 +-- .../aws-events-targets/lib/redshift-query.ts | 2 +- packages/aws-cdk-lib/aws-events/lib/target.ts | 10 +- 11 files changed, 691 insertions(+), 41 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json new file mode 100644 index 0000000000000..469352596e9b7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "LogGroupDefaultTestDeployAssert353EE07A.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json new file mode 100644 index 0000000000000..7d0ae285b7b56 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "36.0.0", + "testCases": { + "LogGroup/DefaultTest": { + "stacks": [ + "redshift-query-events" + ], + "diffAssets": true, + "assertionStack": "LogGroup/DefaultTest/DeployAssert", + "assertionStackName": "LogGroupDefaultTestDeployAssert353EE07A" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json new file mode 100644 index 0000000000000..79d856b103c2a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json @@ -0,0 +1,143 @@ +{ + "version": "36.0.0", + "artifacts": { + "redshift-query-events.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "redshift-query-events.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "redshift-query-events": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "redshift-query-events.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "redshift-query-events.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "redshift-query-events.assets" + ], + "metadata": { + "/redshift-query-events/Namespace": [ + { + "type": "aws:cdk:logicalId", + "data": "Namespace" + } + ], + "/redshift-query-events/WorkGroup": [ + { + "type": "aws:cdk:logicalId", + "data": "WorkGroup" + } + ], + "/redshift-query-events/dlq/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "dlq09C78ACC" + } + ], + "/redshift-query-events/Timer3/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer30894E3BB" + } + ], + "/redshift-query-events/Timer3/EventsRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer3EventsRole909B99A1" + } + ], + "/redshift-query-events/Timer3/EventsRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer3EventsRoleDefaultPolicy3A2ECE32" + } + ], + "/redshift-query-events/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/redshift-query-events/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "redshift-query-events" + }, + "LogGroupDefaultTestDeployAssert353EE07A.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "LogGroupDefaultTestDeployAssert353EE07A.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "LogGroupDefaultTestDeployAssert353EE07A": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "LogGroupDefaultTestDeployAssert353EE07A.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "LogGroupDefaultTestDeployAssert353EE07A.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "LogGroupDefaultTestDeployAssert353EE07A.assets" + ], + "metadata": { + "/LogGroup/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/LogGroup/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "LogGroup/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json new file mode 100644 index 0000000000000..161274fde1edd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162": { + "source": { + "path": "redshift-query-events.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json new file mode 100644 index 0000000000000..0f8c691cc7f0d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json @@ -0,0 +1,145 @@ +{ + "Resources": { + "Namespace": { + "Type": "AWS::RedshiftServerless::Namespace", + "Properties": { + "NamespaceName": "namespace" + } + }, + "WorkGroup": { + "Type": "AWS::RedshiftServerless::Workgroup", + "Properties": { + "NamespaceName": "namespace", + "SecurityGroupIds": [ + "sg-0f3ee03c20cc6056c" + ], + "SubnetIds": [ + "subnet-06c91b5d4c16df0ff", + "subnet-04b90752f12ed5174", + "subnet-0d42bcb68396ffd19" + ], + "WorkgroupName": "workgroup" + }, + "DependsOn": [ + "Namespace" + ] + }, + "dlq09C78ACC": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "Timer30894E3BB": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "Id": "Target0", + "RoleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + } + } + ] + } + }, + "Timer3EventsRole909B99A1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Timer3EventsRoleDefaultPolicy3A2ECE32": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Timer3EventsRoleDefaultPolicy3A2ECE32", + "Roles": [ + { + "Ref": "Timer3EventsRole909B99A1" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json new file mode 100644 index 0000000000000..db69e906b74db --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json @@ -0,0 +1,300 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "redshift-query-events": { + "id": "redshift-query-events", + "path": "redshift-query-events", + "children": { + "Namespace": { + "id": "Namespace", + "path": "redshift-query-events/Namespace", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RedshiftServerless::Namespace", + "aws:cdk:cloudformation:props": { + "namespaceName": "namespace" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_redshiftserverless.CfnNamespace", + "version": "0.0.0" + } + }, + "WorkGroup": { + "id": "WorkGroup", + "path": "redshift-query-events/WorkGroup", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RedshiftServerless::Workgroup", + "aws:cdk:cloudformation:props": { + "namespaceName": "namespace", + "securityGroupIds": [ + "sg-0f3ee03c20cc6056c" + ], + "subnetIds": [ + "subnet-06c91b5d4c16df0ff", + "subnet-04b90752f12ed5174", + "subnet-0d42bcb68396ffd19" + ], + "workgroupName": "workgroup" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_redshiftserverless.CfnWorkgroup", + "version": "0.0.0" + } + }, + "dlq": { + "id": "dlq", + "path": "redshift-query-events/dlq", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/dlq/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "Timer3": { + "id": "Timer3", + "path": "redshift-query-events/Timer3", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Events::Rule", + "aws:cdk:cloudformation:props": { + "scheduleExpression": "rate(1 minute)", + "state": "ENABLED", + "targets": [ + { + "id": "Target0", + "arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + }, + "deadLetterConfig": { + "arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_events.CfnRule", + "version": "0.0.0" + } + }, + "EventsRole": { + "id": "EventsRole", + "path": "redshift-query-events/Timer3/EventsRole", + "children": { + "ImportEventsRole": { + "id": "ImportEventsRole", + "path": "redshift-query-events/Timer3/EventsRole/ImportEventsRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/EventsRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "redshift-query-events/Timer3/EventsRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/EventsRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "Timer3EventsRoleDefaultPolicy3A2ECE32", + "roles": [ + { + "Ref": "Timer3EventsRole909B99A1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_events.Rule", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "redshift-query-events/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "redshift-query-events/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "LogGroup": { + "id": "LogGroup", + "path": "LogGroup", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "LogGroup/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "LogGroup/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "LogGroup/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "LogGroup/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "LogGroup/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts index ea890d183a9b6..fa50513125b35 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -3,50 +3,23 @@ import * as sqs from 'aws-cdk-lib/aws-sqs'; import * as cdk from 'aws-cdk-lib'; import { IntegTest } from '@aws-cdk/integ-tests-alpha'; import * as targets from 'aws-cdk-lib/aws-events-targets'; -import * as redshift from '@aws-cdk/aws-redshift-alpha' import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless'; -import * as ec2 from 'aws-cdk-lib/aws-ec2'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'log-group-events'); +const stack = new cdk.Stack(app, 'redshift-query-events'); -const vpc = new ec2.Vpc(stack, 'VPC'); - -const cluster = new redshift.Cluster(stack, 'Cluster', { - vpc, - masterUser: { - masterUsername: 'admin', - }, - defaultDatabaseName: 'dev', - removalPolicy: cdk.RemovalPolicy.DESTROY, +const namespace = new redshiftserverless.CfnNamespace(stack, 'Namespace', { + namespaceName: 'namespace', }); const workGroup = new redshiftserverless.CfnWorkgroup(stack, 'WorkGroup', { workgroupName: 'workgroup', + namespaceName: namespace.namespaceName, + subnetIds: ['subnet-06c91b5d4c16df0ff', 'subnet-04b90752f12ed5174', 'subnet-0d42bcb68396ffd19'], + securityGroupIds: ['sg-0f3ee03c20cc6056c'], }); - -const importedCluster = redshift.Cluster.fromClusterAttributes(stack, 'ImportedCluster', { - clusterName: 'imported-cluster', - clusterEndpointAddress: 'imported-cluster-endpoint', - clusterEndpointPort: 5439, - securityGroups: [], -}); - -const timer = new events.Rule(stack, 'Timer', { - schedule: events.Schedule.rate(cdk.Duration.minutes(1)), -}); -timer.addTarget(new targets.RedshiftQuery(`arn:aws:redshift:${cluster.env.region}:${cluster.env.account}:cluster:${cluster.clusterName}`, {})); - -const customRule = new events.Rule(stack, 'CustomRule', { - eventPattern: { - source: ['cdk-integ'], - detailType: ['cdk-integ-custom-rule'], - }, -}); -customRule.addTarget(new targets.RedshiftQuery(`arn:aws:redshift:${importedCluster.env.region}:${importedCluster.env.account}:cluster:${importedCluster.clusterName}`, { - database: 'dev', -})); +workGroup.addDependency(namespace); const queue = new sqs.Queue(stack, 'dlq'); @@ -56,10 +29,11 @@ const timer3 = new events.Rule(stack, 'Timer3', { timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { database: 'dev', deadLetterQueue: queue, - sql: [ + batchSQL: [ 'SELECT * FROM foo', 'SELECT * FROM bar', ], + sql: 'SELECT * FROM baz', })); new IntegTest(app, 'LogGroup', { diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 166c80c3658e9..cf324e9146058 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -1,4 +1,4 @@ -import { singletonEventRole, addToDeadLetterQueueResourcePolicy } from './util'; +import { singletonEventRole } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as secretsmanager from '../../aws-secretsmanager'; diff --git a/packages/aws-cdk-lib/aws-events/lib/target.ts b/packages/aws-cdk-lib/aws-events/lib/target.ts index c1cfb0e6bcce5..991c78dc52133 100644 --- a/packages/aws-cdk-lib/aws-events/lib/target.ts +++ b/packages/aws-cdk-lib/aws-events/lib/target.ts @@ -1,8 +1,8 @@ -import { IConstruct } from "constructs"; -import * as iam from "../../aws-iam"; -import { CfnRule } from "./events.generated"; -import { RuleTargetInput } from "./input"; -import { IRule } from "./rule-ref"; +import { IConstruct } from 'constructs'; +import { CfnRule } from './events.generated'; +import { RuleTargetInput } from './input'; +import { IRule } from './rule-ref'; +import * as iam from '../../aws-iam'; /** * An abstract target for EventRules. From da3c7f4662e2eb51a372ef054a9adf8289f85523 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 15:05:01 +0000 Subject: [PATCH 03/30] adding defaults --- .../aws-events-targets/lib/redshift-query.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index cf324e9146058..45f6fa5c8538a 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -18,38 +18,52 @@ export interface RedshiftQueryProps { /** * The Amazon Redshift database user to run the query as. This is required when authenticating via temporary credentials. + * + * @default - No Database user is specified */ readonly dbUser?: string; /** * The secret containing the password for the database user. This is required when authenticating via Secrets Manager. * If the full secret ARN is not specified, this will instead use the secret name. + * + * @default - No secret is specified */ readonly secret?: secretsmanager.ISecret; /** * The SQL query to run. This will use the `executeStatement` API. + * + * @default - No SQL query is specified */ readonly sql?: string; /** * The SQL queries to run. All statements are executed as a single transaction. They each run serially, as appeared in the array; the next sql statement will not run until the previous statement completes. * If any statement fails, the entire transaction is rolled back. This will use the `batchExecuteStatement` API. + * + * @default - No SQL queries are specified */ readonly batchSQL?: string[]; /** * The name of the SQL statement. You can name the SQL statement for identitfication purposes. It is recommended to append a `QS2-` prefix to the statement name, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. + * + * @default - No statement name is specified */ readonly statementName?: string; /** * Should an event be sent back to Event Bridge when the SQL statement is executed. + * + * @default - false */ readonly withEvent?: boolean; /** * The queue to be used as dead letter queue. + * + * @default - No dead letter queue is specified */ readonly deadLetterQueue?: sqs.IQueue; From 493b08907bd28e84f881439d3b9575ba344ed1a4 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 15:05:06 +0000 Subject: [PATCH 04/30] updating readme --- .../aws-cdk-lib/aws-events-targets/README.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 781e257857cd4..3a9f4c89e9b5e 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -23,6 +23,7 @@ Currently supported are: - [Launch type for ECS Task](#launch-type-for-ecs-task) - [Assign public IP addresses to tasks](#assign-public-ip-addresses-to-tasks) - [Enable Amazon ECS Exec for ECS Task](#enable-amazon-ecs-exec-for-ecs-task) + - [Run a Redshift query](#schedule-a-redshift-query-(serverless-or-cluster)) See the README of the `aws-cdk-lib/aws-events` library for more information on EventBridge. @@ -465,3 +466,30 @@ rule.addTarget(new targets.EcsTask({ enableExecuteCommand: true, })); ``` + +## Schedule a Redshift query (serverless or cluster) + +Use the `RedshiftQuery` target to schedule an Amazon Redshift Query. + +The code snippet below creates the scheduled event rule that route events to an Amazon Redshift Query + +```ts +import * as redshiftserverless from 'aws-cdk-lib/aws-redshift-serverless' +declare const workgroup: redshiftserverless.CfnWorkgroup; + +const rule = new events.Rule(this, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), +}); + +const dlq = new sqs.Queue(this, 'DeadLetterQueue'); + +rule.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn{ + database: 'dev', + deadLetterQueue: dlq, + batchSQL: [ + 'SELECT * FROM foo', + 'SELECT * FROM bar', + ], + sql: 'SELECT * FROM baz', +})); +``` From 5754f280b42d7a8f013e7950425ef255b974e465 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 15:11:25 +0000 Subject: [PATCH 05/30] typo --- .../test/redshift-query/redshift-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index a4ba3275fb825..e8c9fa4dc73d9 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -3,7 +3,7 @@ import * as events from '../../../aws-events'; import { Stack } from '../../../core'; import * as targets from '../../lib'; -describe('KinesisFirehoseStream event target', () => { +describe('RedshiftQuery event target', () => { let stack: Stack; let clusterArn: string; let streamArn: any; From ceb2b1002c21bb30345afc02c11f8ad743e5abe4 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 15:22:17 +0000 Subject: [PATCH 06/30] adding default parameter --- packages/aws-cdk-lib/aws-events/lib/target.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/aws-cdk-lib/aws-events/lib/target.ts b/packages/aws-cdk-lib/aws-events/lib/target.ts index 991c78dc52133..acc2974791770 100644 --- a/packages/aws-cdk-lib/aws-events/lib/target.ts +++ b/packages/aws-cdk-lib/aws-events/lib/target.ts @@ -94,6 +94,8 @@ export interface RuleTargetConfig { /** * Parameters used when the rule invokes Amazon Redshift Queries + * + * @default - no parameters set */ readonly redshiftDataParameters?: CfnRule.RedshiftDataParametersProperty; From 5fb6eb3b5d5bde140f7eddff8a68bafdcba4fd5f Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 16:11:49 +0000 Subject: [PATCH 07/30] using default props --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 45f6fa5c8538a..5a79cb335e7c4 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -1,4 +1,4 @@ -import { singletonEventRole } from './util'; +import { bindBaseTargetConfig, singletonEventRole } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as secretsmanager from '../../aws-secretsmanager'; @@ -95,8 +95,8 @@ export class RedshiftQuery implements events.IRuleTarget { } return { + ...bindBaseTargetConfig(this.props), arn: this.clusterArn, - deadLetterConfig: this.props.deadLetterQueue ? { arn: this.props.deadLetterQueue?.queueArn } : undefined, role, redshiftDataParameters: { database: this.props.database ?? 'dev', From 2978d8bd1fc60b1f79e7c43e788e7fc6e95864a0 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 12 Mar 2024 16:11:59 +0000 Subject: [PATCH 08/30] adding console.log for testing purposes --- packages/aws-cdk-lib/aws-events/test/rule.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/aws-cdk-lib/aws-events/test/rule.test.ts b/packages/aws-cdk-lib/aws-events/test/rule.test.ts index 8eda0bd66be87..86dae6397f32e 100644 --- a/packages/aws-cdk-lib/aws-events/test/rule.test.ts +++ b/packages/aws-cdk-lib/aws-events/test/rule.test.ts @@ -689,6 +689,9 @@ describe('rule', () => { targets: [t1], }); + // eslint-disable-next-line no-console + console.log(Template.fromStack(stack).toJSON()); + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { From c29a807324f58310201c77dfd6542b4f43917db9 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 13 Mar 2024 16:46:50 +0000 Subject: [PATCH 09/30] adding to push in array --- .../integ.redshift-query.js.snapshot/manifest.json | 2 +- .../redshift-query-events.assets.json | 4 ++-- .../redshift-query-events.template.json | 11 ++++++----- .../integ.redshift-query.js.snapshot/tree.json | 11 ++++++----- .../test/redshift-query/integ.redshift-query.ts | 6 +----- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json index 79d856b103c2a..86e414040fa7d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json index 161274fde1edd..a2770f6410787 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162": { + "a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda": { "source": { "path": "redshift-query-events.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "8f4b883e19980fead6335bebd2e359abc9cdc89f374e9f99b063539693c40162.json", + "objectKey": "a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json index 0f8c691cc7f0d..cdc462bf23d45 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json @@ -32,7 +32,7 @@ "Timer30894E3BB": { "Type": "AWS::Events::Rule", "Properties": { - "ScheduleExpression": "rate(1 minute)", + "ScheduleExpression": "rate(5 minutes)", "State": "ENABLED", "Targets": [ { @@ -51,6 +51,10 @@ } }, "Id": "Target0", + "RedshiftDataParameters": { + "Database": "dev", + "Sql": "SELECT * FROM baz" + }, "RoleArn": { "Fn::GetAtt": [ "Timer3EventsRole909B99A1", @@ -84,10 +88,7 @@ "PolicyDocument": { "Statement": [ { - "Action": [ - "redshift-data:BatchExecuteStatement", - "redshift-data:ExecuteStatement" - ], + "Action": "redshift-data:ExecuteStatement", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json index db69e906b74db..186267d52ac63 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json @@ -77,7 +77,7 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::Events::Rule", "aws:cdk:cloudformation:props": { - "scheduleExpression": "rate(1 minute)", + "scheduleExpression": "rate(5 minutes)", "state": "ENABLED", "targets": [ { @@ -101,6 +101,10 @@ "Arn" ] } + }, + "redshiftDataParameters": { + "database": "dev", + "sql": "SELECT * FROM baz" } } ] @@ -161,10 +165,7 @@ "policyDocument": { "Statement": [ { - "Action": [ - "redshift-data:BatchExecuteStatement", - "redshift-data:ExecuteStatement" - ], + "Action": "redshift-data:ExecuteStatement", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts index fa50513125b35..53cfd177f5bbb 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -24,15 +24,11 @@ workGroup.addDependency(namespace); const queue = new sqs.Queue(stack, 'dlq'); const timer3 = new events.Rule(stack, 'Timer3', { - schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + schedule: events.Schedule.rate(cdk.Duration.minutes(5)), }); timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { database: 'dev', deadLetterQueue: queue, - batchSQL: [ - 'SELECT * FROM foo', - 'SELECT * FROM bar', - ], sql: 'SELECT * FROM baz', })); From 5faf78676557313fae9ef00f73731229fcca1856 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 13 Mar 2024 16:47:36 +0000 Subject: [PATCH 10/30] adding targetprops --- .../aws-events-targets/lib/redshift-query.ts | 14 ++- .../redshift-query/redshift-query.test.ts | 97 ++++++++++++++----- packages/aws-cdk-lib/aws-events/lib/rule.ts | 1 + .../aws-cdk-lib/aws-events/test/rule.test.ts | 2 +- 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 5a79cb335e7c4..2f510aaec8830 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -73,6 +73,13 @@ export interface RedshiftQueryProps { * @default - a new role will be created. */ readonly role?: iam.IRole; + + /** + * The input to the state machine execution + * + * @default the entire EventBridge event + */ + readonly input?: events.RuleTargetInput; } /** @@ -80,9 +87,9 @@ export interface RedshiftQueryProps { * It is recommended to append a `QS2-` prefix to both `statementName` and `ruleName`, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. */ export class RedshiftQuery implements events.IRuleTarget { - constructor(private readonly clusterArn: string, private readonly props: RedshiftQueryProps = { - database: 'dev', - }) { + constructor( + private readonly clusterArn: string, + private readonly props: RedshiftQueryProps = {}) { } bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { @@ -98,6 +105,7 @@ export class RedshiftQuery implements events.IRuleTarget { ...bindBaseTargetConfig(this.props), arn: this.clusterArn, role, + input: this.props.input, redshiftDataParameters: { database: this.props.database ?? 'dev', dbUser: this.props.dbUser, diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index e8c9fa4dc73d9..e28123ab85d0e 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -1,17 +1,16 @@ import { Template } from '../../../assertions'; import * as events from '../../../aws-events'; +import * as redshiftserverless from '../../../aws-redshiftserverless'; import { Stack } from '../../../core'; import * as targets from '../../lib'; describe('RedshiftQuery event target', () => { let stack: Stack; let clusterArn: string; - let streamArn: any; beforeEach(() => { stack = new Stack(); clusterArn = 'arn:aws:redshift:us-west-2:123456789012:cluster:my-cluster'; - streamArn = { 'Fn::GetAtt': ['MyStream', 'Arn'] }; }); describe('when added to an event rule as a target', () => { @@ -34,27 +33,12 @@ describe('RedshiftQuery event target', () => { { Arn: clusterArn, Id: 'Target0', - RoleArn: { 'Fn::GetAtt': ['MyClusterEventsRole5B6CC6AF', 'Arn'] }, + RoleArn: { 'Fn::GetAtt': ['ruleEventsRole7F0DD2EE', 'Arn'] }, }, ], }); }); - test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { - Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: ['redshift:ExecuteStatement'], - Effect: 'Allow', - Resource: clusterArn, - }, - ], - Version: '2012-10-17', - }, - }); - }); - test('assigns the `dev` database to the RedshiftQuery', () => { Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ @@ -69,18 +53,18 @@ describe('RedshiftQuery event target', () => { }); describe('with explicity SQL statements', () => { - beforeEach(() => { + test('sets the SQL statements', () => { + // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { sql: 'SELECT * FROM foo', batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); - }); - test('sets the SQL statements', () => { + // THEN Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { - Arn: streamArn, + Arn: clusterArn, Id: 'Target0', RedshiftDataParameters: { Database: 'dev', @@ -91,6 +75,75 @@ describe('RedshiftQuery event target', () => { ], }); }); + + test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + sql: 'SELECT * FROM foo', + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'redshift-data:ExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('creates a policy that has BatchExecuteStatement permission on the clusters ARN', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'redshift-data:BatchExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('creates a policy that has both ExecuteStatement and BatchExecuteStatement permissions on the clusters ARN', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + sql: 'SELECT * FROM foo', + batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'redshift-data:ExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + { + Action: 'redshift-data:BatchExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); }); }); }); diff --git a/packages/aws-cdk-lib/aws-events/lib/rule.ts b/packages/aws-cdk-lib/aws-events/lib/rule.ts index faa7ea7c488f6..dccffc9af38af 100644 --- a/packages/aws-cdk-lib/aws-events/lib/rule.ts +++ b/packages/aws-cdk-lib/aws-events/lib/rule.ts @@ -238,6 +238,7 @@ export class Rule extends Resource implements IRule { deadLetterConfig: targetProps.deadLetterConfig, retryPolicy: targetProps.retryPolicy, sqsParameters: targetProps.sqsParameters, + redshiftDataParameters: targetProps.redshiftDataParameters, input: inputProps && inputProps.input, inputPath: inputProps && inputProps.inputPath, inputTransformer: inputProps?.inputTemplate !== undefined ? { diff --git a/packages/aws-cdk-lib/aws-events/test/rule.test.ts b/packages/aws-cdk-lib/aws-events/test/rule.test.ts index 86dae6397f32e..911a120bb13a3 100644 --- a/packages/aws-cdk-lib/aws-events/test/rule.test.ts +++ b/packages/aws-cdk-lib/aws-events/test/rule.test.ts @@ -690,7 +690,7 @@ describe('rule', () => { }); // eslint-disable-next-line no-console - console.log(Template.fromStack(stack).toJSON()); + console.log(Template.fromStack(stack).toJSON().Resources.EventRule5A491D2C.Properties.Targets[0]); Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ From 9d1f85c76387e24d2bb54c96cb4db77045f569a8 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 13 Mar 2024 17:42:19 +0000 Subject: [PATCH 11/30] Update README.md --- packages/aws-cdk-lib/aws-events-targets/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 3a9f4c89e9b5e..cdf912fc788ec 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -483,7 +483,7 @@ const rule = new events.Rule(this, 'Rule', { const dlq = new sqs.Queue(this, 'DeadLetterQueue'); -rule.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn{ +rule.addTarget(new targets.RedshiftQuery(workgroup.attrWorkgroupWorkgroupArn{ database: 'dev', deadLetterQueue: dlq, batchSQL: [ From 647b75a13705b6442806f7e51a4e4125edc4cf62 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Thu, 14 Mar 2024 10:18:42 +0000 Subject: [PATCH 12/30] further typos --- packages/aws-cdk-lib/aws-events-targets/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 3a9f4c89e9b5e..112c54d1e6e1b 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -474,7 +474,8 @@ Use the `RedshiftQuery` target to schedule an Amazon Redshift Query. The code snippet below creates the scheduled event rule that route events to an Amazon Redshift Query ```ts -import * as redshiftserverless from 'aws-cdk-lib/aws-redshift-serverless' +import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless' + declare const workgroup: redshiftserverless.CfnWorkgroup; const rule = new events.Rule(this, 'Rule', { @@ -483,7 +484,7 @@ const rule = new events.Rule(this, 'Rule', { const dlq = new sqs.Queue(this, 'DeadLetterQueue'); -rule.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn{ +rule.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { database: 'dev', deadLetterQueue: dlq, batchSQL: [ From 663001a2bc72e8f46352faf9e2ca2b1e1c9f8e9d Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Thu, 14 Mar 2024 10:22:44 +0000 Subject: [PATCH 13/30] updating markdown --- packages/aws-cdk-lib/aws-events-targets/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 03d9d4dcbaf44..b42ed785964f7 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -23,7 +23,7 @@ Currently supported are: - [Launch type for ECS Task](#launch-type-for-ecs-task) - [Assign public IP addresses to tasks](#assign-public-ip-addresses-to-tasks) - [Enable Amazon ECS Exec for ECS Task](#enable-amazon-ecs-exec-for-ecs-task) - - [Run a Redshift query](#schedule-a-redshift-query-(serverless-or-cluster)) + - [Run a Redshift query](#schedule-a-redshift-query-serverless-or-cluster) See the README of the `aws-cdk-lib/aws-events` library for more information on EventBridge. From 4455786a490d7b80763451f5de620deccd91f92c Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Thu, 14 Mar 2024 11:27:17 +0000 Subject: [PATCH 14/30] integ without --no-clean --- .../integ.redshift-query.js.snapshot/integ.json | 1 - .../integ.redshift-query.js.snapshot/manifest.json | 2 +- .../redshift-query-events.assets.json | 4 ++-- .../redshift-query-events.template.json | 2 +- .../redshift-query/integ.redshift-query.js.snapshot/tree.json | 2 +- .../test/redshift-query/integ.redshift-query.ts | 3 +-- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json index 7d0ae285b7b56..10dbab9e91ac9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json @@ -5,7 +5,6 @@ "stacks": [ "redshift-query-events" ], - "diffAssets": true, "assertionStack": "LogGroup/DefaultTest/DeployAssert", "assertionStackName": "LogGroupDefaultTestDeployAssert353EE07A" } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json index 86e414040fa7d..2d126f7d110cf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json index a2770f6410787..03dbead2ba6c2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda": { + "7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea": { "source": { "path": "redshift-query-events.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "a54da7fb960a9a5397aa171b2165eaac102e3027f4f19eaef1dcc62ef4b63dda.json", + "objectKey": "7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json index cdc462bf23d45..cffc787300c0a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json @@ -32,7 +32,7 @@ "Timer30894E3BB": { "Type": "AWS::Events::Rule", "Properties": { - "ScheduleExpression": "rate(5 minutes)", + "ScheduleExpression": "rate(1 minute)", "State": "ENABLED", "Targets": [ { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json index 186267d52ac63..3639cc694673f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json @@ -77,7 +77,7 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::Events::Rule", "aws:cdk:cloudformation:props": { - "scheduleExpression": "rate(5 minutes)", + "scheduleExpression": "rate(1 minute)", "state": "ENABLED", "targets": [ { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts index 53cfd177f5bbb..62f2cd70b7f43 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -24,7 +24,7 @@ workGroup.addDependency(namespace); const queue = new sqs.Queue(stack, 'dlq'); const timer3 = new events.Rule(stack, 'Timer3', { - schedule: events.Schedule.rate(cdk.Duration.minutes(5)), + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { database: 'dev', @@ -34,7 +34,6 @@ timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, new IntegTest(app, 'LogGroup', { testCases: [stack], - diffAssets: true, }); app.synth(); From 5705063cc543ebc66c352b40e3f888ba081c7720 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 19 Mar 2024 16:48:48 +0000 Subject: [PATCH 15/30] removing mandatory assignment, and using checks for validation --- .../aws-events-targets/lib/redshift-query.ts | 11 ++- .../redshift-query/redshift-query.test.ts | 80 +++++++++++++------ 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 2f510aaec8830..ac96024ca5c7d 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -100,6 +100,15 @@ export class RedshiftQuery implements events.IRuleTarget { if (this.props.batchSQL) { role.addToPrincipalPolicy(this.putBatchEventStatement()); } + if (!(this.props.database)) { + throw new Error('A database must be specified.'); + } + if (this.props.sql && this.props.batchSQL) { + throw new Error('Only one of `sql` or `batchSQL` can be specified, not both.'); + } + if (!this.props.sql && !this.props.batchSQL) { + throw new Error('One of `sql` or `batchSQL` must be specified.'); + } return { ...bindBaseTargetConfig(this.props), @@ -107,7 +116,7 @@ export class RedshiftQuery implements events.IRuleTarget { role, input: this.props.input, redshiftDataParameters: { - database: this.props.database ?? 'dev', + database: this.props.database, dbUser: this.props.dbUser, secretManagerArn: this.props.secret?.secretFullArn ?? this.props.secret?.secretName, sql: this.props.sql, diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index e28123ab85d0e..b17805e7614d0 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -24,7 +24,10 @@ describe('RedshiftQuery event target', () => { describe('with default settings', () => { beforeEach(() => { - rule.addTarget(new targets.RedshiftQuery(clusterArn)); + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: 'SELECT * FROM foo', + })); }); test('adds the clusters ARN and role to the targets of the rule', () => { @@ -53,11 +56,11 @@ describe('RedshiftQuery event target', () => { }); describe('with explicity SQL statements', () => { - test('sets the SQL statements', () => { + test('sets the SQL statement', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', sql: 'SELECT * FROM foo', - batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); // THEN @@ -69,38 +72,39 @@ describe('RedshiftQuery event target', () => { RedshiftDataParameters: { Database: 'dev', Sql: 'SELECT * FROM foo', - Sqls: ['SELECT * FROM foo', 'SELECT * FROM bar'], }, }, ], }); }); - test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { + test('sets the batch SQL statements', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { - sql: 'SELECT * FROM foo', + database: 'dev', + batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); // THEN - Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: 'redshift-data:ExecuteStatement', - Effect: 'Allow', - Resource: clusterArn, + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sqls: ['SELECT * FROM foo', 'SELECT * FROM bar'], }, - ], - Version: '2012-10-17', - }, + }, + ], }); }); - test('creates a policy that has BatchExecuteStatement permission on the clusters ARN', () => { + test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { - batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + database: 'dev', + sql: 'SELECT * FROM foo', })); // THEN @@ -108,7 +112,7 @@ describe('RedshiftQuery event target', () => { PolicyDocument: { Statement: [ { - Action: 'redshift-data:BatchExecuteStatement', + Action: 'redshift-data:ExecuteStatement', Effect: 'Allow', Resource: clusterArn, }, @@ -118,10 +122,10 @@ describe('RedshiftQuery event target', () => { }); }); - test('creates a policy that has both ExecuteStatement and BatchExecuteStatement permissions on the clusters ARN', () => { + test('creates a policy that has BatchExecuteStatement permission on the clusters ARN', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { - sql: 'SELECT * FROM foo', + database: 'dev', batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); @@ -129,11 +133,6 @@ describe('RedshiftQuery event target', () => { Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ - { - Action: 'redshift-data:ExecuteStatement', - Effect: 'Allow', - Resource: clusterArn, - }, { Action: 'redshift-data:BatchExecuteStatement', Effect: 'Allow', @@ -145,6 +144,35 @@ describe('RedshiftQuery event target', () => { }); }); }); + + describe('failures', () => { + test('throws an error if neither sql nor batchSQL is specified', () => { + expect(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + })); + }).toThrow(/One of `sql` or `batchSQL` must be specified./); + }); + + test('throws an error if both sql and batchSQL are specified', () => { + // THEN + expect(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: 'SELECT * FROM foo', + batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + }).toThrow(/Only one of `sql` or `batchSQL` can be specified, not both./); + }); + + test('throws an error if a database is not specified', () => { + expect(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + sql: 'SELECT * FROM foo', + })); + }).toThrow(/A database must be specified./); + }); + }); }); }); From 9740d482339a607af905ad3c10788ee522f23099 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 19 Mar 2024 16:51:09 +0000 Subject: [PATCH 16/30] removing mandatory assignment, and using checks for validation --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index ac96024ca5c7d..81e99b47f8985 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -12,7 +12,7 @@ export interface RedshiftQueryProps { /** * The Amazon Redshift database to run the query against. * - * @default - dev + * @default - no database is specified */ readonly database?: string; From 8ffed39ffed58de6bd2dc6c6c8c5c66e5d6bf370 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Tue, 19 Mar 2024 16:51:27 +0000 Subject: [PATCH 17/30] removing trailing space --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 81e99b47f8985..c1a563dd2f9ae 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -12,7 +12,7 @@ export interface RedshiftQueryProps { /** * The Amazon Redshift database to run the query against. * - * @default - no database is specified + * @default - no database is specified */ readonly database?: string; From 291b86b0f793933ccca718da1aa08440dcd4eed9 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 15 May 2024 09:56:15 +0000 Subject: [PATCH 18/30] undoing prettier --- packages/aws-cdk-lib/aws-events/lib/rule.ts | 261 ++++++-------------- 1 file changed, 75 insertions(+), 186 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events/lib/rule.ts b/packages/aws-cdk-lib/aws-events/lib/rule.ts index 08f899da41b9a..bdaacbb2009a2 100644 --- a/packages/aws-cdk-lib/aws-events/lib/rule.ts +++ b/packages/aws-cdk-lib/aws-events/lib/rule.ts @@ -1,26 +1,14 @@ -import { Node, Construct } from "constructs"; -import { IEventBus } from "./event-bus"; -import { EventPattern } from "./event-pattern"; -import { CfnEventBusPolicy, CfnRule } from "./events.generated"; -import { EventCommonOptions } from "./on-event-options"; -import { IRule } from "./rule-ref"; -import { Schedule } from "./schedule"; -import { IRuleTarget } from "./target"; -import { mergeEventPattern, renderEventPattern } from "./util"; -import { IRole, PolicyStatement, Role, ServicePrincipal } from "../../aws-iam"; -import { - App, - IResource, - Lazy, - Names, - Resource, - Stack, - Token, - TokenComparison, - PhysicalName, - ArnFormat, - Annotations, -} from "../../core"; +import { Node, Construct } from 'constructs'; +import { IEventBus } from './event-bus'; +import { EventPattern } from './event-pattern'; +import { CfnEventBusPolicy, CfnRule } from './events.generated'; +import { EventCommonOptions } from './on-event-options'; +import { IRule } from './rule-ref'; +import { Schedule } from './schedule'; +import { IRuleTarget } from './target'; +import { mergeEventPattern, renderEventPattern } from './util'; +import { IRole, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam'; +import { App, IResource, Lazy, Names, Resource, Stack, Token, TokenComparison, PhysicalName, ArnFormat, Annotations } from '../../core'; /** * Properties for defining an EventBridge Rule @@ -72,6 +60,7 @@ export interface RuleProps extends EventCommonOptions { * @resource AWS::Events::Rule */ export class Rule extends Resource implements IRule { + /** * Import an existing EventBridge Rule provided an ARN * @@ -79,19 +68,12 @@ export class Rule extends Resource implements IRule { * @param id The construct's name. * @param eventRuleArn Event Rule ARN (i.e. arn:aws:events:::rule/MyScheduledRule). */ - public static fromEventRuleArn( - scope: Construct, - id: string, - eventRuleArn: string - ): IRule { - const parts = Stack.of(scope).splitArn( - eventRuleArn, - ArnFormat.SLASH_RESOURCE_NAME - ); + public static fromEventRuleArn(scope: Construct, id: string, eventRuleArn: string): IRule { + const parts = Stack.of(scope).splitArn(eventRuleArn, ArnFormat.SLASH_RESOURCE_NAME); class Import extends Resource implements IRule { public ruleArn = eventRuleArn; - public ruleName = parts.resourceName || ""; + public ruleName = parts.resourceName || ''; } return new Import(scope, id, { environmentFromArn: eventRuleArn, @@ -102,22 +84,20 @@ export class Rule extends Resource implements IRule { public readonly ruleName: string; private readonly targets = new Array(); - private readonly eventPattern: EventPattern = {}; + private readonly eventPattern: EventPattern = { }; private readonly scheduleExpression?: string; private readonly description?: string; /** Set to keep track of what target accounts and regions we've already created event buses for */ private readonly _xEnvTargetsAdded = new Set(); - constructor(scope: Construct, id: string, props: RuleProps = {}) { + constructor(scope: Construct, id: string, props: RuleProps = { }) { super(determineRuleScope(scope, props), id, { physicalName: props.ruleName, }); if (props.eventBus && props.schedule) { - throw new Error( - "Cannot associate rule with 'eventBus' when using 'schedule'" - ); + throw new Error('Cannot associate rule with \'eventBus\' when using \'schedule\''); } this.description = props.description; @@ -126,15 +106,10 @@ export class Rule extends Resource implements IRule { // add a warning on synth when minute is not defined in a cron schedule props.schedule?._bind(this); - const resource = new CfnRule(this, "Resource", { + const resource = new CfnRule(this, 'Resource', { name: this.physicalName, description: this.description, - state: - props.enabled == null - ? "ENABLED" - : props.enabled - ? "ENABLED" - : "DISABLED", + state: props.enabled == null ? 'ENABLED' : (props.enabled ? 'ENABLED' : 'DISABLED'), scheduleExpression: this.scheduleExpression, eventPattern: Lazy.any({ produce: () => this._renderEventPattern() }), targets: Lazy.any({ produce: () => this.renderTargets() }), @@ -142,8 +117,8 @@ export class Rule extends Resource implements IRule { }); this.ruleArn = this.getResourceArnAttribute(resource.attrArn, { - service: "events", - resource: "rule", + service: 'events', + resource: 'rule', resourceName: this.physicalName, }); this.ruleName = this.getResourceNameAttribute(resource.ref); @@ -164,9 +139,7 @@ export class Rule extends Resource implements IRule { * No-op if target is undefined. */ public addTarget(target?: IRuleTarget): void { - if (!target) { - return; - } + if (!target) { return; } // Simply increment id for each `addTarget` call. This is guaranteed to be unique. const autoGeneratedId = `Target${this.targets.length}`; @@ -180,12 +153,8 @@ export class Rule extends Resource implements IRule { if (targetProps.targetResource) { const targetStack = Stack.of(targetProps.targetResource); - const targetAccount = - (targetProps.targetResource as IResource).env?.account || - targetStack.account; - const targetRegion = - (targetProps.targetResource as IResource).env?.region || - targetStack.region; + const targetAccount = (targetProps.targetResource as IResource).env?.account || targetStack.account; + const targetRegion = (targetProps.targetResource as IResource).env?.region || targetStack.region; const sourceStack = Stack.of(this); const sourceAccount = sourceStack.account; @@ -196,58 +165,38 @@ export class Rule extends Resource implements IRule { // - forwarding rule in the source stack (target: default event bus of the receiver region) // - eventbus permissions policy (creating an extra stack) // - receiver rule in the target stack (target: the actual target) - if ( - !this.sameEnvDimension(sourceAccount, targetAccount) || - !this.sameEnvDimension(sourceRegion, targetRegion) - ) { + if (!this.sameEnvDimension(sourceAccount, targetAccount) || !this.sameEnvDimension(sourceRegion, targetRegion)) { // cross-account and/or cross-region event - strap in, this works differently than regular events! // based on: // https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cross-account.html // for cross-account or cross-region events, we require a concrete target account and region if (!targetAccount || Token.isUnresolved(targetAccount)) { - throw new Error( - "You need to provide a concrete account for the target stack when using cross-account or cross-region events" - ); + throw new Error('You need to provide a concrete account for the target stack when using cross-account or cross-region events'); } if (!targetRegion || Token.isUnresolved(targetRegion)) { - throw new Error( - "You need to provide a concrete region for the target stack when using cross-account or cross-region events" - ); + throw new Error('You need to provide a concrete region for the target stack when using cross-account or cross-region events'); } if (Token.isUnresolved(sourceAccount)) { - throw new Error( - "You need to provide a concrete account for the source stack when using cross-account or cross-region events" - ); + throw new Error('You need to provide a concrete account for the source stack when using cross-account or cross-region events'); } // Don't exactly understand why this code was here (seems unlikely this rule would be violated), but // let's leave it in nonetheless. const sourceApp = this.node.root; if (!sourceApp || !App.isApp(sourceApp)) { - throw new Error( - "Event stack which uses cross-account or cross-region targets must be part of a CDK app" - ); + throw new Error('Event stack which uses cross-account or cross-region targets must be part of a CDK app'); } const targetApp = Node.of(targetProps.targetResource).root; if (!targetApp || !App.isApp(targetApp)) { - throw new Error( - "Target stack which uses cross-account or cross-region event targets must be part of a CDK app" - ); + throw new Error('Target stack which uses cross-account or cross-region event targets must be part of a CDK app'); } if (sourceApp !== targetApp) { - throw new Error( - "Event stack and target stack must belong to the same CDK app" - ); + throw new Error('Event stack and target stack must belong to the same CDK app'); } // The target of this Rule will be the default event bus of the target environment - this.ensureXEnvTargetEventBus( - targetStack, - targetAccount, - targetRegion, - id - ); + this.ensureXEnvTargetEventBus(targetStack, targetAccount, targetRegion, id); // The actual rule lives in the target stack. Other than the account, it's identical to this one, // but only evaluated at render time (via a special subclass). @@ -261,24 +210,13 @@ export class Rule extends Resource implements IRule { // happens in the source env; and activating, which happens in the target env). // // Don't have time to do that right now. - const mirrorRuleScope = this.obtainMirrorRuleScope( - targetStack, - targetAccount, - targetRegion - ); - new MirrorRule( - mirrorRuleScope, - `${Names.uniqueId(this)}-${id}`, - { - targets: [target], - eventPattern: this.eventPattern, - schedule: this.scheduleExpression - ? Schedule.expression(this.scheduleExpression) - : undefined, - description: this.description, - }, - this - ); + const mirrorRuleScope = this.obtainMirrorRuleScope(targetStack, targetAccount, targetRegion); + new MirrorRule(mirrorRuleScope, `${Names.uniqueId(this)}-${id}`, { + targets: [target], + eventPattern: this.eventPattern, + schedule: this.scheduleExpression ? Schedule.expression(this.scheduleExpression) : undefined, + description: this.description, + }, this); return; } @@ -304,13 +242,10 @@ export class Rule extends Resource implements IRule { appSyncParameters: targetProps.appSyncParameters, input: inputProps && inputProps.input, inputPath: inputProps && inputProps.inputPath, - inputTransformer: - inputProps?.inputTemplate !== undefined - ? { - inputTemplate: inputProps.inputTemplate, - inputPathsMap: inputProps.inputPathsMap, - } - : undefined, + inputTransformer: inputProps?.inputTemplate !== undefined ? { + inputTemplate: inputProps.inputTemplate, + inputPathsMap: inputProps.inputPathsMap, + } : undefined, }); } @@ -369,22 +304,15 @@ export class Rule extends Resource implements IRule { const name = this.physicalName; if (name !== undefined && !Token.isUnresolved(name)) { if (name.length < 1 || name.length > 64) { - errors.push( - `Event rule name must be between 1 and 64 characters. Received: ${name}` - ); + errors.push(`Event rule name must be between 1 and 64 characters. Received: ${name}`); } if (!/^[\.\-_A-Za-z0-9]+$/.test(name)) { - errors.push( - `Event rule name ${name} can contain only letters, numbers, periods, hyphens, or underscores with no spaces.` - ); + errors.push(`Event rule name ${name} can contain only letters, numbers, periods, hyphens, or underscores with no spaces.`); } } - if ( - Object.keys(this.eventPattern).length === 0 && - !this.scheduleExpression - ) { - errors.push("Either 'eventPattern' or 'schedule' must be defined"); + if (Object.keys(this.eventPattern).length === 0 && !this.scheduleExpression) { + errors.push('Either \'eventPattern\' or \'schedule\' must be defined'); } return errors; @@ -403,24 +331,17 @@ export class Rule extends Resource implements IRule { * * For cross-account rules, uses a support stack to set up a policy on the target event bus. */ - private ensureXEnvTargetEventBus( - targetStack: Stack, - targetAccount: string, - targetRegion: string, - id: string - ) { + private ensureXEnvTargetEventBus(targetStack: Stack, targetAccount: string, targetRegion: string, id: string) { // the _actual_ target is just the event bus of the target's account // make sure we only add it once per account per region const key = `${targetAccount}:${targetRegion}`; - if (this._xEnvTargetsAdded.has(key)) { - return; - } + if (this._xEnvTargetsAdded.has(key)) { return; } this._xEnvTargetsAdded.add(key); const eventBusArn = targetStack.formatArn({ - service: "events", - resource: "event-bus", - resourceName: "default", + service: 'events', + resource: 'event-bus', + resourceName: 'default', region: targetRegion, account: targetAccount, }); @@ -452,9 +373,7 @@ export class Rule extends Resource implements IRule { // for a role). if (!this.sameEnvDimension(sourceAccount, targetAccount)) { const stackId = `EventBusPolicy-${sourceAccount}-${targetRegion}-${targetAccount}`; - let eventBusPolicyStack: Stack = sourceApp.node.tryFindChild( - stackId - ) as Stack; + let eventBusPolicyStack: Stack = sourceApp.node.tryFindChild(stackId) as Stack; if (!eventBusPolicyStack) { eventBusPolicyStack = new Stack(sourceApp, stackId, { env: { @@ -466,13 +385,11 @@ export class Rule extends Resource implements IRule { stackName: `${targetStack.stackName}-EventBusPolicy-support-${targetRegion}-${sourceAccount}`, }); const statementPrefix = `Allow-account-${sourceAccount}-`; - new CfnEventBusPolicy(eventBusPolicyStack, "GivePermToOtherAccount", { - action: "events:PutEvents", - statementId: - statementPrefix + - Names.uniqueResourceName(this, { - maxLength: 64 - statementPrefix.length, - }), + new CfnEventBusPolicy(eventBusPolicyStack, 'GivePermToOtherAccount', { + action: 'events:PutEvents', + statementId: statementPrefix + Names.uniqueResourceName(this, { + maxLength: 64 - statementPrefix.length, + }), principal: sourceAccount, }); } @@ -490,24 +407,15 @@ export class Rule extends Resource implements IRule { * We don't implement the second yet, as I have to think long and hard on whether we * can reuse the existing support stack or not, and I don't have time for that right now. */ - private obtainMirrorRuleScope( - targetStack: Stack, - targetAccount: string, - targetRegion: string - ): Construct { + private obtainMirrorRuleScope(targetStack: Stack, targetAccount: string, targetRegion: string): Construct { // for cross-account or cross-region events, we cannot create new components for an imported resource // because we don't have the target stack - if ( - this.sameEnvDimension(targetStack.account, targetAccount) && - this.sameEnvDimension(targetStack.region, targetRegion) - ) { + if (this.sameEnvDimension(targetStack.account, targetAccount) && this.sameEnvDimension(targetStack.region, targetRegion)) { return targetStack; } // For now, we don't do the work for the support stack yet - throw new Error( - "Cannot create a cross-account or cross-region rule for an imported resource (create a stack with the right environment for the imported resource)" - ); + throw new Error('Cannot create a cross-account or cross-region rule for an imported resource (create a stack with the right environment for the imported resource)'); } /** @@ -518,21 +426,19 @@ export class Rule extends Resource implements IRule { * @internal */ private crossRegionPutEventsRole(eventBusArn: string): IRole { - const id = "EventsRole"; + const id = 'EventsRole'; let role = this.node.tryFindChild(id) as IRole; if (!role) { role = new Role(this, id, { roleName: PhysicalName.GENERATE_IF_NEEDED, - assumedBy: new ServicePrincipal("events.amazonaws.com"), + assumedBy: new ServicePrincipal('events.amazonaws.com'), }); } - role.addToPrincipalPolicy( - new PolicyStatement({ - actions: ["events:PutEvents"], - resources: [eventBusArn], - }) - ); + role.addToPrincipalPolicy(new PolicyStatement({ + actions: ['events:PutEvents'], + resources: [eventBusArn], + })); return role; } @@ -546,11 +452,8 @@ export class Rule extends Resource implements IRule { private sameEnvDimension(dim1: string, dim2: string) { switch (Token.compareStrings(dim1, dim2)) { case TokenComparison.ONE_UNRESOLVED: - Annotations.of(this).addWarningV2( - "@aws-cdk/aws-events:ruleUnresolvedEnvironment", - "Either the Event Rule or target has an unresolved environment. \n \ - If they are being used in a cross-environment setup you need to specify the environment for both." - ); + Annotations.of(this).addWarningV2('@aws-cdk/aws-events:ruleUnresolvedEnvironment', 'Either the Event Rule or target has an unresolved environment. \n \ + If they are being used in a cross-environment setup you need to specify the environment for both.'); return true; case TokenComparison.BOTH_UNRESOLVED: case TokenComparison.SAME: @@ -572,19 +475,10 @@ function determineRuleScope(scope: Construct, props: RuleProps): Construct { } // cross-region/account Events require their own setup, // so we use the base scope in that case - const regionComparison = Token.compareStrings( - scopeStack.region, - targetStack.region - ); - const accountComparison = Token.compareStrings( - scopeStack.account, - targetStack.account - ); - const stacksInSameAccountAndRegion = - (regionComparison === TokenComparison.SAME || - regionComparison === TokenComparison.BOTH_UNRESOLVED) && - (accountComparison === TokenComparison.SAME || - accountComparison === TokenComparison.BOTH_UNRESOLVED); + const regionComparison = Token.compareStrings(scopeStack.region, targetStack.region); + const accountComparison = Token.compareStrings(scopeStack.account, targetStack.account); + const stacksInSameAccountAndRegion = (regionComparison === TokenComparison.SAME || regionComparison === TokenComparison.BOTH_UNRESOLVED) && + (accountComparison === TokenComparison.SAME || accountComparison === TokenComparison.BOTH_UNRESOLVED); return stacksInSameAccountAndRegion ? props.crossStackScope : scope; } @@ -592,12 +486,7 @@ function determineRuleScope(scope: Construct, props: RuleProps): Construct { * A rule that mirrors another rule */ class MirrorRule extends Rule { - constructor( - scope: Construct, - id: string, - props: RuleProps, - private readonly source: Rule - ) { + constructor(scope: Construct, id: string, props: RuleProps, private readonly source: Rule) { super(scope, id, props); } From 72c4dd278434d302457a83b6c032e7e6c1603242 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 15 May 2024 09:59:21 +0000 Subject: [PATCH 19/30] adding line break --- packages/aws-cdk-lib/aws-events-targets/lib/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts index 7c78e0f45e5ac..af88ee14bc5d3 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts @@ -16,4 +16,4 @@ export * from './api-gateway'; export * from './api-destination'; export * from './appsync'; export * from './util'; -export * from './redshift-query'; \ No newline at end of file +export * from './redshift-query'; From 16cf4f3b1eccb83aa6e8703b425189e472ae7c36 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Sun, 4 Aug 2024 14:45:55 +0000 Subject: [PATCH 20/30] making the database required --- .../aws-events-targets/lib/redshift-query.ts | 11 +++-------- .../test/redshift-query/redshift-query.test.ts | 9 --------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index c1a563dd2f9ae..3160a31d3ae6c 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -1,8 +1,8 @@ -import { bindBaseTargetConfig, singletonEventRole } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as secretsmanager from '../../aws-secretsmanager'; import * as sqs from '../../aws-sqs'; +import { bindBaseTargetConfig, singletonEventRole } from './util'; /** * Configuration properties of an Amazon Redshift Query event. @@ -11,10 +11,8 @@ export interface RedshiftQueryProps { /** * The Amazon Redshift database to run the query against. - * - * @default - no database is specified */ - readonly database?: string; + readonly database: string; /** * The Amazon Redshift database user to run the query as. This is required when authenticating via temporary credentials. @@ -89,7 +87,7 @@ export interface RedshiftQueryProps { export class RedshiftQuery implements events.IRuleTarget { constructor( private readonly clusterArn: string, - private readonly props: RedshiftQueryProps = {}) { + private readonly props: RedshiftQueryProps) { } bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { @@ -100,9 +98,6 @@ export class RedshiftQuery implements events.IRuleTarget { if (this.props.batchSQL) { role.addToPrincipalPolicy(this.putBatchEventStatement()); } - if (!(this.props.database)) { - throw new Error('A database must be specified.'); - } if (this.props.sql && this.props.batchSQL) { throw new Error('Only one of `sql` or `batchSQL` can be specified, not both.'); } diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index b17805e7614d0..5bbc68c15078a 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -1,6 +1,5 @@ import { Template } from '../../../assertions'; import * as events from '../../../aws-events'; -import * as redshiftserverless from '../../../aws-redshiftserverless'; import { Stack } from '../../../core'; import * as targets from '../../lib'; @@ -164,14 +163,6 @@ describe('RedshiftQuery event target', () => { })); }).toThrow(/Only one of `sql` or `batchSQL` can be specified, not both./); }); - - test('throws an error if a database is not specified', () => { - expect(() => { - rule.addTarget(new targets.RedshiftQuery(clusterArn, { - sql: 'SELECT * FROM foo', - })); - }).toThrow(/A database must be specified./); - }); }); }); }); From 5493d2c177c496334f2d316bfc590ff8bb7ae2d1 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Sun, 4 Aug 2024 15:04:29 +0000 Subject: [PATCH 21/30] feature flag update, and import order --- .../aws-events-targets/lib/redshift-query.ts | 2 +- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 19 +------------------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 3160a31d3ae6c..82ce48494e159 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -1,8 +1,8 @@ +import { bindBaseTargetConfig, singletonEventRole } from './util'; import * as events from '../../aws-events'; import * as iam from '../../aws-iam'; import * as secretsmanager from '../../aws-secretsmanager'; import * as sqs from '../../aws-sqs'; -import { bindBaseTargetConfig, singletonEventRole } from './util'; /** * Configuration properties of an Amazon Redshift Query event. diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index be8cfb6c4d69c..9e980cc71faae 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -71,7 +71,6 @@ Flags come in three types: | [@aws-cdk/pipelines:reduceAssetRoleTrustScope](#aws-cdkpipelinesreduceassetroletrustscope) | Remove the root account principal from PipelineAssetsFileRole trust policy | 2.141.0 | (default) | | [@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm](#aws-cdkaws-ecsremovedefaultdeploymentalarm) | When enabled, remove default deployment alarm settings | 2.143.0 | (default) | | [@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault](#aws-cdkcustom-resourceslogapiresponsedatapropertytruedefault) | When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default | 2.145.0 | (fix) | -| [@aws-cdk/aws-stepfunctions-tasks:ecsReduceRunTaskPermissions](#aws-cdkaws-stepfunctions-tasksecsreduceruntaskpermissions) | When enabled, IAM Policy created to run tasks won't include the task definition ARN, only the revision ARN. | 2.148.0 | (fix) | @@ -132,8 +131,7 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/aws-eks:nodegroupNameAttribute": true, "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, - "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, - "@aws-cdk/aws-stepfunctions-tasks:ecsReduceRunTaskPermissions": true + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false } } ``` @@ -1340,19 +1338,4 @@ property from the event object. | 2.145.0 | `false` | `false` | -### @aws-cdk/aws-stepfunctions-tasks:ecsReduceRunTaskPermissions - -*When enabled, IAM Policy created to run tasks won't include the task definition ARN, only the revision ARN.* (fix) - -When this feature flag is enabled, the IAM Policy created to run tasks won't include the task definition ARN, only the revision ARN. -The revision ARN is more specific than the task definition ARN. See https://docs.aws.amazon.com/step-functions/latest/dg/ecs-iam.html -for more details. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.148.0 | `false` | `true` | - - From 7393b0ffc47d4e4ca91819d23eba67ad771ac362 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Mon, 5 Aug 2024 11:08:42 +0000 Subject: [PATCH 22/30] updating test signature --- .../test/redshift-query/redshift-query.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index 5bbc68c15078a..bb5e428535e58 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -41,7 +41,7 @@ describe('RedshiftQuery event target', () => { }); }); - test('assigns the `dev` database to the RedshiftQuery', () => { + test('assigns the database to the RedshiftQuery', () => { Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { Targets: [ { @@ -54,7 +54,7 @@ describe('RedshiftQuery event target', () => { }); }); - describe('with explicity SQL statements', () => { + describe('with explicity set SQL statements', () => { test('sets the SQL statement', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { From 7b9f21e875eadd51a5836d02f8d19d4df2563dcb Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 28 Aug 2024 09:29:18 +0000 Subject: [PATCH 23/30] changing doc --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 82ce48494e159..2e6ead7fa5d95 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -37,7 +37,7 @@ export interface RedshiftQueryProps { readonly sql?: string; /** - * The SQL queries to run. All statements are executed as a single transaction. They each run serially, as appeared in the array; the next sql statement will not run until the previous statement completes. + * The SQL queries to be executed. Each query is run sequentially within a single transaction; the next query in the array will only execute after the previous one has successfully completed. * If any statement fails, the entire transaction is rolled back. This will use the `batchExecuteStatement` API. * * @default - No SQL queries are specified From 4e98da8d89d3391ffe9c12d9a614280bc1045716 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 28 Aug 2024 09:30:52 +0000 Subject: [PATCH 24/30] adjusting docstring --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 2e6ead7fa5d95..29ea33d2d4e5a 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -45,7 +45,7 @@ export interface RedshiftQueryProps { readonly batchSQL?: string[]; /** - * The name of the SQL statement. You can name the SQL statement for identitfication purposes. It is recommended to append a `QS2-` prefix to the statement name, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. + * The name of the SQL statement. You can name the SQL statement for identitfication purposes. If you would like Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console, append a `QS2-` prefix to the statement name. * * @default - No statement name is specified */ From 3312d3e47f925c5751bb4c91355cf90c3dc7f2b8 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 28 Aug 2024 09:33:43 +0000 Subject: [PATCH 25/30] changing parameter to be more meaningful --- .../aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index 29ea33d2d4e5a..f5bede8ea219a 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -54,9 +54,9 @@ export interface RedshiftQueryProps { /** * Should an event be sent back to Event Bridge when the SQL statement is executed. * - * @default - false + * @default false */ - readonly withEvent?: boolean; + readonly sendEventBridgeEvent?: boolean; /** * The queue to be used as dead letter queue. @@ -117,7 +117,7 @@ export class RedshiftQuery implements events.IRuleTarget { sql: this.props.sql, sqls: this.props.batchSQL, statementName: this.props.statementName, - withEvent: this.props.withEvent, + withEvent: this.props.sendEventBridgeEvent, }, }; } From 966047cd4f8a29f7c208a27ebb0d9588fc34be88 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 28 Aug 2024 09:34:59 +0000 Subject: [PATCH 26/30] adding docstring specifying restrictions --- .../aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index f5bede8ea219a..ba256e0bbdd15 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -32,6 +32,8 @@ export interface RedshiftQueryProps { /** * The SQL query to run. This will use the `executeStatement` API. * + * Either this or `batchSQL` must be specified, but not both. + * * @default - No SQL query is specified */ readonly sql?: string; @@ -40,6 +42,8 @@ export interface RedshiftQueryProps { * The SQL queries to be executed. Each query is run sequentially within a single transaction; the next query in the array will only execute after the previous one has successfully completed. * If any statement fails, the entire transaction is rolled back. This will use the `batchExecuteStatement` API. * + * Either this or `sql` must be specified, but not both. + * * @default - No SQL queries are specified */ readonly batchSQL?: string[]; @@ -75,7 +79,7 @@ export interface RedshiftQueryProps { /** * The input to the state machine execution * - * @default the entire EventBridge event + * @default - the entire EventBridge event */ readonly input?: events.RuleTargetInput; } From 8b1a9b6430579b5dae5aa08080fe30838075dd19 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Wed, 28 Aug 2024 18:00:57 +0000 Subject: [PATCH 27/30] adding unit tests for secrets manager --- .../redshift-query/redshift-query.test.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index bb5e428535e58..51363eb8df355 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -1,5 +1,6 @@ import { Template } from '../../../assertions'; import * as events from '../../../aws-events'; +import * as secretsmanager from '../../../aws-secretsmanager'; import { Stack } from '../../../core'; import * as targets from '../../lib'; @@ -144,6 +145,62 @@ describe('RedshiftQuery event target', () => { }); }); + describe('with secrets manager', () => { + test('adding a secrets manager secret to the target', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + + // WHEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: 'SELECT * FROM foo', + secret, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + SecretManagerArn: { Ref: 'SecretA720EF05' }, + }, + }, + ], + }); + }); + + test('adding an imported secrets manager secret to the target, that does not have `secretFullArn` set', () => { + // GIVEN + const secret = secretsmanager.Secret.fromSecretNameV2(stack, 'Secret', 'my-secret'); + + // WHEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: 'SELECT * FROM foo', + secret, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + SecretManagerArn: 'my-secret', + }, + }, + ], + }); + }); + }); + describe('failures', () => { test('throws an error if neither sql nor batchSQL is specified', () => { expect(() => { From bb442d682d1560d7f1c1bdddbcc5fac721f55fe3 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Thu, 29 Aug 2024 20:29:04 +0000 Subject: [PATCH 28/30] a single sql prop --- ...efaultTestDeployAssert353EE07A.assets.json | 2 +- .../integ.redshift-query.js.snapshot/cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 10 ++- .../redshift-query-events.assets.json | 6 +- .../redshift-query-events.template.json | 49 +++++++++++++- .../tree.json | 65 ++++++++++++++++++- .../redshift-query/integ.redshift-query.ts | 13 +++- .../aws-events-targets/lib/redshift-query.ts | 48 +++++++------- .../redshift-query/redshift-query.test.ts | 30 ++++----- 10 files changed, 173 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json index 469352596e9b7..2aa2b214acb69 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "36.0.5", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out index 1f0068d32659a..bd5311dc372de 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"36.0.0"} \ No newline at end of file +{"version":"36.0.5"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json index 10dbab9e91ac9..027efdcf5d2be 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "36.0.5", "testCases": { "LogGroup/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json index 2d126f7d110cf..02a985ee171ac 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "36.0.5", "artifacts": { "redshift-query-events.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -52,6 +52,12 @@ "data": "dlq09C78ACC" } ], + "/redshift-query-events/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecretA720EF05" + } + ], "/redshift-query-events/Timer3/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json index 03dbead2ba6c2..6d1c133d923ae 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json @@ -1,7 +1,7 @@ { - "version": "36.0.0", + "version": "36.0.5", "files": { - "7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea": { + "97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9": { "source": { "path": "redshift-query-events.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "7ae2ee445e978867abfce3409f39ab0c59a7a93ed20d17d3d83884fd26092dea.json", + "objectKey": "97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json index cffc787300c0a..92de97c1b462c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json @@ -29,6 +29,14 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, + "SecretA720EF05": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "Timer30894E3BB": { "Type": "AWS::Events::Rule", "Properties": { @@ -53,6 +61,9 @@ "Id": "Target0", "RedshiftDataParameters": { "Database": "dev", + "SecretManagerArn": { + "Ref": "SecretA720EF05" + }, "Sql": "SELECT * FROM baz" }, "RoleArn": { @@ -61,6 +72,39 @@ "Arn" ] } + }, + { + "Arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "Id": "Target1", + "RedshiftDataParameters": { + "Database": "dev", + "SecretManagerArn": { + "Ref": "SecretA720EF05" + }, + "Sqls": [ + "SELECT * FROM foo", + "SELECT * FROM bar" + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + } } ] } @@ -88,7 +132,10 @@ "PolicyDocument": { "Statement": [ { - "Action": "redshift-data:ExecuteStatement", + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], "Effect": "Allow", "Resource": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json index 3639cc694673f..979b6c8cc3a6c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json @@ -67,6 +67,30 @@ "version": "0.0.0" } }, + "Secret": { + "id": "Secret", + "path": "redshift-query-events/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "generateSecretString": {} + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", + "version": "0.0.0" + } + }, "Timer3": { "id": "Timer3", "path": "redshift-query-events/Timer3", @@ -104,8 +128,44 @@ }, "redshiftDataParameters": { "database": "dev", + "secretManagerArn": { + "Ref": "SecretA720EF05" + }, "sql": "SELECT * FROM baz" } + }, + { + "id": "Target1", + "arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + }, + "deadLetterConfig": { + "arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "redshiftDataParameters": { + "database": "dev", + "secretManagerArn": { + "Ref": "SecretA720EF05" + }, + "sqls": [ + "SELECT * FROM foo", + "SELECT * FROM bar" + ] + } } ] } @@ -165,7 +225,10 @@ "policyDocument": { "Statement": [ { - "Action": "redshift-data:ExecuteStatement", + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], "Effect": "Allow", "Resource": { "Fn::GetAtt": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts index 62f2cd70b7f43..72d2c1b1ddc79 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -4,6 +4,7 @@ import * as cdk from 'aws-cdk-lib'; import { IntegTest } from '@aws-cdk/integ-tests-alpha'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless'; +import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; const app = new cdk.App(); @@ -23,13 +24,23 @@ workGroup.addDependency(namespace); const queue = new sqs.Queue(stack, 'dlq'); +const secret = new secretsmanager.Secret(stack, 'Secret'); + const timer3 = new events.Rule(stack, 'Timer3', { schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { database: 'dev', deadLetterQueue: queue, - sql: 'SELECT * FROM baz', + sql: ['SELECT * FROM baz'], + secret, +})); + +timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { + database: 'dev', + deadLetterQueue: queue, + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], + secret, })); new IntegTest(app, 'LogGroup', { diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index ba256e0bbdd15..ed746165124f1 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -30,23 +30,16 @@ export interface RedshiftQueryProps { readonly secret?: secretsmanager.ISecret; /** - * The SQL query to run. This will use the `executeStatement` API. + * The SQL queries to be executed. Each query is run sequentially within a single transaction; the next query in the array will only execute after the previous one has successfully completed. + * + * - When multiple sql queries are included, this will use the `batchExecuteStatement` API. Therefore, if any statement fails, the entire transaction is rolled back. + * - If a single SQL statement is to be executed, this will use the `executeStatement` API. * * Either this or `batchSQL` must be specified, but not both. * * @default - No SQL query is specified */ - readonly sql?: string; - - /** - * The SQL queries to be executed. Each query is run sequentially within a single transaction; the next query in the array will only execute after the previous one has successfully completed. - * If any statement fails, the entire transaction is rolled back. This will use the `batchExecuteStatement` API. - * - * Either this or `sql` must be specified, but not both. - * - * @default - No SQL queries are specified - */ - readonly batchSQL?: string[]; + readonly sql: string[]; /** * The name of the SQL statement. You can name the SQL statement for identitfication purposes. If you would like Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console, append a `QS2-` prefix to the statement name. @@ -86,28 +79,33 @@ export interface RedshiftQueryProps { /** * Schedule an Amazon Redshift Query to be run, using the Redshift Data API. - * It is recommended to append a `QS2-` prefix to both `statementName` and `ruleName`, to allow Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console. + * + * If you would like Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console, append a `QS2-` prefix to both `statementName` and `ruleName`. */ export class RedshiftQuery implements events.IRuleTarget { constructor( + /** + * The ARN of the Amazon Redshift cluster + */ private readonly clusterArn: string, - private readonly props: RedshiftQueryProps) { - } + + /** + * The properties of the Redshift Query event + */ + private readonly props: RedshiftQueryProps, + ) {} bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { const role = this.props.role ?? singletonEventRole(rule); - if (this.props.sql) { + if (this.props.sql.length < 1) { + throw new Error('At least one SQL statement must be specified.'); + } + if (this.props.sql.length === 1) { role.addToPrincipalPolicy(this.putEventStatement()); } - if (this.props.batchSQL) { + if (this.props.sql.length > 1) { role.addToPrincipalPolicy(this.putBatchEventStatement()); } - if (this.props.sql && this.props.batchSQL) { - throw new Error('Only one of `sql` or `batchSQL` can be specified, not both.'); - } - if (!this.props.sql && !this.props.batchSQL) { - throw new Error('One of `sql` or `batchSQL` must be specified.'); - } return { ...bindBaseTargetConfig(this.props), @@ -118,8 +116,8 @@ export class RedshiftQuery implements events.IRuleTarget { database: this.props.database, dbUser: this.props.dbUser, secretManagerArn: this.props.secret?.secretFullArn ?? this.props.secret?.secretName, - sql: this.props.sql, - sqls: this.props.batchSQL, + sql: this.props.sql.length === 1 ? this.props.sql[0] : undefined, + sqls: this.props.sql.length > 1 ? this.props.sql : undefined, statementName: this.props.statementName, withEvent: this.props.sendEventBridgeEvent, }, diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts index 51363eb8df355..be51299ffd6f6 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -26,7 +26,7 @@ describe('RedshiftQuery event target', () => { beforeEach(() => { rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - sql: 'SELECT * FROM foo', + sql: ['SELECT * FROM foo'], })); }); @@ -60,7 +60,7 @@ describe('RedshiftQuery event target', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - sql: 'SELECT * FROM foo', + sql: ['SELECT * FROM foo'], })); // THEN @@ -82,7 +82,7 @@ describe('RedshiftQuery event target', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); // THEN @@ -104,7 +104,7 @@ describe('RedshiftQuery event target', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - sql: 'SELECT * FROM foo', + sql: ['SELECT * FROM foo'], })); // THEN @@ -126,7 +126,7 @@ describe('RedshiftQuery event target', () => { // GIVEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], })); // THEN @@ -153,7 +153,7 @@ describe('RedshiftQuery event target', () => { // WHEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - sql: 'SELECT * FROM foo', + sql: ['SELECT * FROM foo'], secret, })); @@ -180,7 +180,7 @@ describe('RedshiftQuery event target', () => { // WHEN rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', - sql: 'SELECT * FROM foo', + sql: ['SELECT * FROM foo'], secret, })); @@ -202,23 +202,17 @@ describe('RedshiftQuery event target', () => { }); describe('failures', () => { - test('throws an error if neither sql nor batchSQL is specified', () => { + test('throws an error if there are no elements in the sql array', () => { + // WHEN expect(() => { rule.addTarget(new targets.RedshiftQuery(clusterArn, { database: 'dev', + sql: [], })); - }).toThrow(/One of `sql` or `batchSQL` must be specified./); - }); + }) - test('throws an error if both sql and batchSQL are specified', () => { // THEN - expect(() => { - rule.addTarget(new targets.RedshiftQuery(clusterArn, { - database: 'dev', - sql: 'SELECT * FROM foo', - batchSQL: ['SELECT * FROM foo', 'SELECT * FROM bar'], - })); - }).toThrow(/Only one of `sql` or `batchSQL` can be specified, not both./); + .toThrow(/At least one SQL statement must be specified./); }); }); }); From 9f0e352759e2d40487bcef52ec06ac5939462042 Mon Sep 17 00:00:00 2001 From: Rizxcviii Date: Thu, 29 Aug 2024 20:57:29 +0000 Subject: [PATCH 29/30] updating README --- packages/aws-cdk-lib/aws-events-targets/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index a6b50ac5d81f2..73c052ac889a2 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -584,10 +584,6 @@ const dlq = new sqs.Queue(this, 'DeadLetterQueue'); rule.addTarget(new targets.RedshiftQuery(workgroup.attrWorkgroupWorkgroupArn, { database: 'dev', deadLetterQueue: dlq, - batchSQL: [ - 'SELECT * FROM foo', - 'SELECT * FROM bar', - ], - sql: 'SELECT * FROM baz', + sql: ['SELECT * FROM foo','SELECT * FROM baz'], })); ``` From 5e71a18270a26bf272883292c0965b9d92b3c21e Mon Sep 17 00:00:00 2001 From: paulhcsun <47882901+paulhcsun@users.noreply.github.com> Date: Thu, 29 Aug 2024 14:54:13 -0700 Subject: [PATCH 30/30] Update packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts --- packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts index ed746165124f1..6decb307ba881 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -35,8 +35,6 @@ export interface RedshiftQueryProps { * - When multiple sql queries are included, this will use the `batchExecuteStatement` API. Therefore, if any statement fails, the entire transaction is rolled back. * - If a single SQL statement is to be executed, this will use the `executeStatement` API. * - * Either this or `batchSQL` must be specified, but not both. - * * @default - No SQL query is specified */ readonly sql: string[];