Skip to content

Commit

Permalink
Add http:invoke step functions task
Browse files Browse the repository at this point in the history
  • Loading branch information
MathieuGilbert committed Jan 11, 2024
1 parent 10d5a9f commit bc37725
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 0 deletions.
129 changes: 129 additions & 0 deletions packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/http/invoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Construct } from 'constructs';
import * as iam from '../../../aws-iam';
import * as sfn from '../../../aws-stepfunctions';
import { integrationResourceArn } from '../private/task-utils';

export enum URLEncodingArrayFormat {
/**
* Encode arrays using brackets. For example, {"array": ["a","b","c"]} encodes to "array[]=a&array[]=b&array[]=c"
*/
BRACKETS = 'BRACKETS',
/**
* Encode arrays using commas. For example, {"array": ["a","b","c"]} encodes to "array=a,b,c,d"
*/
COMMAS = 'COMMAS',
/**
* Encode arrays using the index value. For example, {"array": ["a","b","c"]} encodes to "array[0]=a&array[1]=b&array[2]=c"
*/
INDICES = 'INDICES',
/**
* Repeat key for each item in the array. For example, {"array": ["a","b","c"]} encodes to "array[]=a&array[]=b&array[]=c"
*/
REPEAT = 'REPEAT',
}

/**
* Properties for calling an external HTTP endpoint with HttpInvoke.
*/
export interface HttpInvokeProps extends sfn.TaskStateBaseProps {
/**
* The API apiEndpoint to call.
*/
readonly apiEndpoint: string;

/**
* The HTTP method to use.
*
*/
readonly method: string;

/**
* The EventBridge Connection ARN to use for authentication.
*
*/
readonly connectionArn: string;

/**
* The body to send to the HTTP endpoint.
*
* @default - No body.
*/
readonly body?: string;

/**
* The headers to send to the HTTP endpoint.
*
* @default - No headers.
*/
readonly headers?: { [key: string]: string };

/**
* The query string parameters to send to the HTTP endpoint.
*
* @default - No query string parameters.
*/
readonly queryStringParameters?: { [key: string]: string };

/**
* Whether to URL-encode the request body.
* If set to true, also sets 'content-type' header to 'application/x-www-form-urlencoded'
*
* @default - No encoding.
*/
readonly urlEncodeBody?: boolean;

/**
* The format of the array encoding if urlEncodeBody is set to true.
*
* @default - ArrayEncodingFormat.INDICES
*/
readonly arrayEncodingFormat?: URLEncodingArrayFormat;
}

export class HttpInvoke extends sfn.TaskStateBase {
protected readonly taskMetrics?: sfn.TaskMetricsConfig;
protected readonly taskPolicies?: iam.PolicyStatement[];

constructor(
scope: Construct,
id: string,
private readonly props: HttpInvokeProps,
) {
super(scope, id, props);

this.taskPolicies = [];
}

/**
* Provides the HTTP Invoke service integration task configuration.
*/
/**
* @internal
*/
protected _renderTask(): any {
return {
Resource: integrationResourceArn('http', 'invoke'),
Parameters: sfn.FieldUtils.renderObject({
Method: this.props.method,
ApiEndpoint: this.props.apiEndpoint,
Authentication: {
ConnectionArn: this.props.connectionArn,
},
RequestBody: this.props.body,
Headers: this.props.headers,
QueryParameters: this.props.queryStringParameters,
Transform:
this.props.urlEncodeBody != null
? {
RequestBodyEncoding: 'URL_ENCODED',
RequestEncodingOptions: {
ArrayFormat:
this.props.arrayEncodingFormat ??
URLEncodingArrayFormat.INDICES,
},
}
: undefined,
}),
};
}
}
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ export * from './apigateway';
export * from './eventbridge/put-events';
export * from './aws-sdk/call-aws-service';
export * from './bedrock/invoke-model';
export * from './http/invoke';
157 changes: 157 additions & 0 deletions packages/aws-cdk-lib/aws-stepfunctions-tasks/test/http/invoke.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { Stack } from '../../../core';
import * as lib from '../../lib';

let stack: Stack;
const connectionArn =
'arn:aws:events:us-test-1:123456789012:connection/connectionName';

const expectTaskWithParameters = (task: lib.HttpInvoke, parameters: any) => {
expect(stack.resolve(task.toStateJson())).toEqual({
Type: 'Task',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{
Ref: 'AWS::Partition',
},
':states:::http:invoke',
],
],
},
End: true,
Parameters: parameters,
});
};

describe('AWS::StepFunctions::Tasks::HttpInvoke', () => {
beforeEach(() => {
stack = new Stack();
});

test('invoke with default props', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
connectionArn,
method: 'POST',
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
});
});

test('invoke with request body', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
body: JSON.stringify({ foo: 'bar' }),
connectionArn,
method: 'POST',
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
RequestBody: JSON.stringify({ foo: 'bar' }),
});
});

test('invoke with headers', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
connectionArn,
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
Headers: {
'Content-Type': 'application/json',
},
});
});

test('invoke with query string parameters', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
connectionArn,
method: 'POST',
queryStringParameters: {
foo: 'bar',
},
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
QueryParameters: {
foo: 'bar',
},
});
});

test('invoke with request body encoding and default arrayEncodingFormat', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
method: 'POST',
connectionArn,
urlEncodeBody: true,
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
Transform: {
RequestBodyEncoding: 'URL_ENCODED',
RequestEncodingOptions: {
ArrayFormat: lib.URLEncodingArrayFormat.INDICES,
},
},
});
});

test('invoke with request body encoding and arrayEncodingFormat', () => {
const task = new lib.HttpInvoke(stack, 'Task', {
apiEndpoint: 'https://api.example.com',
arrayEncodingFormat: lib.URLEncodingArrayFormat.BRACKETS,
connectionArn,
method: 'POST',
urlEncodeBody: true,
});

expectTaskWithParameters(task, {
ApiEndpoint: 'https://api.example.com',
Authentication: {
ConnectionArn: connectionArn,
},
Method: 'POST',
Transform: {
RequestBodyEncoding: 'URL_ENCODED',
RequestEncodingOptions: {
ArrayFormat: lib.URLEncodingArrayFormat.BRACKETS,
},
},
});
});
});

0 comments on commit bc37725

Please sign in to comment.