-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pipes-targets): add API destination (#30756)
Add EventBridge API destination as a Pipes target. CloudFormation groups EventBridge API destination with API Gateway REST API as [PipeTargetHttpParameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipetargethttpparameters.html#cfn-pipes-pipe-pipetargethttpparameters-pathparametervalues), but I think separating them here similar to [aws-event-targets](https://github.com/aws/aws-cdk/tree/main/packages/aws-cdk-lib/aws-events-targets/lib) makes more sense, as API Gateway requires `stage`, `path`, and `method` (see [here](https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-events-targets/lib/api-gateway.ts#L11-L32)).
- Loading branch information
Showing
16 changed files
with
34,800 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
packages/@aws-cdk/aws-pipes-targets-alpha/lib/api-destination.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { IInputTransformation, IPipe, ITarget, TargetConfig } from '@aws-cdk/aws-pipes-alpha'; | ||
import { IApiDestination } from 'aws-cdk-lib/aws-events'; | ||
import { IRole, PolicyStatement } from 'aws-cdk-lib/aws-iam'; | ||
|
||
/** | ||
* EventBridge API destination target properties. | ||
*/ | ||
export interface ApiDestinationTargetParameters { | ||
/** | ||
* The input transformation to apply to the message before sending it to the target. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipetargetparameters.html#cfn-pipes-pipe-pipetargetparameters-inputtemplate | ||
* @default - none | ||
*/ | ||
readonly inputTransformation?: IInputTransformation; | ||
|
||
/** | ||
* The headers to send as part of the request invoking the EventBridge API destination. | ||
* | ||
* The headers are merged with the headers from the API destination. | ||
* If there are conflicts, the headers from the API destination take precedence. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipetargethttpparameters.html#cfn-pipes-pipe-pipetargethttpparameters-headerparameters | ||
* @default - none | ||
*/ | ||
readonly headerParameters?: { [key: string]: string }; | ||
|
||
/** | ||
* The path parameter values used to populate the EventBridge API destination path wildcards ("*"). | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipetargethttpparameters.html#cfn-pipes-pipe-pipetargethttpparameters-pathparametervalues | ||
* @default - none | ||
*/ | ||
readonly pathParameterValues?: string[]; | ||
|
||
/** | ||
* The query string keys/values that need to be sent as part of request invoking the EventBridge API destination. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-pipes-pipe-pipetargethttpparameters.html#cfn-pipes-pipe-pipetargethttpparameters-querystringparameters | ||
* @default - none | ||
*/ | ||
readonly queryStringParameters?: Record<string, string>; | ||
} | ||
|
||
/** | ||
* A EventBridge Pipes target that sends messages to an EventBridge API destination. | ||
*/ | ||
export class ApiDestinationTarget implements ITarget { | ||
private destination: IApiDestination; | ||
private apiParameters?: ApiDestinationTargetParameters; | ||
public readonly targetArn: string; | ||
|
||
constructor(destination: IApiDestination, parameters?: ApiDestinationTargetParameters) { | ||
this.destination = destination; | ||
this.apiParameters = parameters; | ||
this.targetArn = destination.apiDestinationArn; | ||
} | ||
|
||
grantPush(grantee: IRole): void { | ||
grantee.addToPrincipalPolicy(new PolicyStatement({ | ||
resources: [this.destination.apiDestinationArn], | ||
actions: ['events:InvokeApiDestination'], | ||
})); | ||
} | ||
|
||
bind(pipe: IPipe): TargetConfig { | ||
if (!this.apiParameters) { | ||
return { | ||
targetParameters: {}, | ||
}; | ||
} | ||
|
||
return { | ||
targetParameters: { | ||
inputTemplate: this.apiParameters.inputTransformation?.bind(pipe).inputTemplate, | ||
httpParameters: this.apiParameters, | ||
}, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './api-destination'; | ||
export * from './lambda'; | ||
export * from './sqs'; | ||
export * from './stepfunctions'; |
2 changes: 2 additions & 0 deletions
2
packages/@aws-cdk/aws-pipes-targets-alpha/rosetta/default.ts-fixture
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
packages/@aws-cdk/aws-pipes-targets-alpha/test/api-destination.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import { InputTransformation, Pipe } from '@aws-cdk/aws-pipes-alpha'; | ||
import { App, Stack, SecretValue } from 'aws-cdk-lib'; | ||
import { Template } from 'aws-cdk-lib/assertions'; | ||
import * as events from 'aws-cdk-lib/aws-events'; | ||
import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; | ||
import { TestSource } from './test-classes'; | ||
import { ApiDestinationTarget } from '../lib'; | ||
|
||
describe('API destination', () => { | ||
let app: App; | ||
let stack: Stack; | ||
let secret: Secret; | ||
let connection: events.Connection; | ||
|
||
beforeEach(() => { | ||
app = new App(); | ||
stack = new Stack(app, 'TestStack'); | ||
secret = new Secret(stack, 'MySecret', { | ||
secretStringValue: SecretValue.unsafePlainText('abc123'), | ||
}); | ||
connection = new events.Connection(stack, 'MyConnection', { | ||
authorization: events.Authorization.apiKey('x-api-key', secret.secretValue), | ||
description: 'Connection with API Key x-api-key', | ||
connectionName: 'MyConnection', | ||
}); | ||
}); | ||
|
||
it('should have only target arn', () => { | ||
// ARRANGE | ||
const destination = new events.ApiDestination(stack, 'MyApiDestination', { | ||
connection, | ||
endpoint: 'https://httpbin.org/headers', | ||
httpMethod: events.HttpMethod.GET, | ||
apiDestinationName: 'MyDestination', | ||
rateLimitPerSecond: 1, | ||
description: 'Calling example.com with API key x-api-key', | ||
}); | ||
const target = new ApiDestinationTarget(destination); | ||
|
||
new Pipe(stack, 'MyPipe', { | ||
source: new TestSource(), | ||
target, | ||
}); | ||
|
||
// ACT | ||
const template = Template.fromStack(stack); | ||
|
||
// ASSERT | ||
template.hasResourceProperties('AWS::Pipes::Pipe', { | ||
Target: { | ||
'Fn::GetAtt': [ | ||
'MyApiDestination07E6A8F9', | ||
'Arn', | ||
], | ||
}, | ||
TargetParameters: {}, | ||
}); | ||
}); | ||
|
||
it('should have target parameters', () => { | ||
// ARRANGE | ||
const destination = new events.ApiDestination(stack, 'MyApiDestination', { | ||
connection, | ||
endpoint: 'https://httpbin.org/headers', | ||
httpMethod: events.HttpMethod.GET, | ||
apiDestinationName: 'MyDestination', | ||
rateLimitPerSecond: 1, | ||
description: 'Calling example.com with API key x-api-key', | ||
}); | ||
const target = new ApiDestinationTarget(destination, { | ||
headerParameters: { headerName: 'headerValue' }, | ||
pathParameterValues: ['pathValue'], | ||
queryStringParameters: { queryName: 'queryValue' }, | ||
}); | ||
|
||
new Pipe(stack, 'MyPipe', { | ||
source: new TestSource(), | ||
target, | ||
}); | ||
|
||
// ACT | ||
const template = Template.fromStack(stack); | ||
|
||
// ASSERT | ||
template.hasResourceProperties('AWS::Pipes::Pipe', { | ||
TargetParameters: { | ||
HttpParameters: { | ||
HeaderParameters: { | ||
headerName: 'headerValue', | ||
}, | ||
PathParameterValues: ['pathValue'], | ||
QueryStringParameters: { | ||
queryName: 'queryValue', | ||
}, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should have input transformation', () => { | ||
// ARRANGE | ||
const destination = new events.ApiDestination(stack, 'MyApiDestination', { | ||
connection, | ||
endpoint: 'https://httpbin.org/headers', | ||
httpMethod: events.HttpMethod.GET, | ||
apiDestinationName: 'MyDestination', | ||
rateLimitPerSecond: 1, | ||
description: 'Calling example.com with API key x-api-key', | ||
}); | ||
|
||
const inputTransformation = InputTransformation.fromObject({ | ||
key: 'value', | ||
}); | ||
|
||
const target = new ApiDestinationTarget(destination, { | ||
inputTransformation, | ||
}); | ||
|
||
new Pipe(stack, 'MyPipe', { | ||
source: new TestSource(), | ||
target, | ||
}); | ||
|
||
// ACT | ||
const template = Template.fromStack(stack); | ||
|
||
// ASSERT | ||
template.hasResourceProperties('AWS::Pipes::Pipe', { | ||
TargetParameters: { | ||
InputTemplate: '{"key":"value"}', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should grant pipe role push access', () => { | ||
// ARRANGE | ||
const destination = new events.ApiDestination(stack, 'MyApiDestination', { | ||
connection, | ||
endpoint: 'https://httpbin.org/headers', | ||
httpMethod: events.HttpMethod.GET, | ||
apiDestinationName: 'MyDestination', | ||
rateLimitPerSecond: 1, | ||
description: 'Calling example.com with API key x-api-key', | ||
}); | ||
const target = new ApiDestinationTarget(destination); | ||
|
||
new Pipe(stack, 'MyPipe', { | ||
source: new TestSource(), | ||
target, | ||
}); | ||
|
||
// ACT | ||
const template = Template.fromStack(stack); | ||
|
||
// ASSERT | ||
template.hasResource('AWS::IAM::Policy', { | ||
Properties: { | ||
Roles: [{ | ||
Ref: 'MyPipeRoleCBC8E9AB', | ||
}], | ||
PolicyDocument: { | ||
Statement: [{ | ||
Action: 'events:InvokeApiDestination', | ||
Resource: { | ||
'Fn::GetAtt': ['MyApiDestination07E6A8F9', 'Arn'], | ||
}, | ||
}], | ||
}, | ||
}, | ||
}); | ||
}); | ||
}); |
1 change: 1 addition & 0 deletions
1
....snapshot/asset.c9e084a249774d97a978bed2e1976874a70517128a904136b8737f0792322c1f/index.js
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.