diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/api-call-base.ts b/packages/@aws-cdk/integ-tests/lib/assertions/api-call-base.ts index 88ac967545307..12a4f9f614877 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/api-call-base.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/api-call-base.ts @@ -60,6 +60,8 @@ export interface IApiCall extends IConstruct { * Assert that the ExpectedResult is equal * to the result of the AwsApiCall at the given path. * + * Providing a path will filter the output of the initial API call. + * * For example the SQS.receiveMessage api response would look * like: * diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/private/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/private/deploy-assert.ts index 3cce887cc27b0..0d15f14daa35c 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/private/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/private/deploy-assert.ts @@ -58,11 +58,12 @@ export class DeployAssert extends Construct implements IDeployAssert { Object.defineProperty(this, DEPLOY_ASSERT_SYMBOL, { value: true }); } - public awsApiCall(service: string, api: string, parameters?: any): IApiCall { + public awsApiCall(service: string, api: string, parameters?: any, outputPaths?: string[]): IApiCall { return new AwsApiCall(this.scope, `AwsApiCall${service}${api}`, { api, service, parameters, + outputPaths, }); } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts index 51d677b81084c..23e47ff34f3a9 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts @@ -52,12 +52,28 @@ export class AwsApiCallHandler extends CustomResourceHandler { + for (const searchString of searchStrings) { + if (key.startsWith(`apiCallResponse.${searchString}`)) { + filteredObject[key] = value; + } + } + return filteredObject; + }, {}); +} + function isJsonString(value: string): any { try { return JSON.parse(value); diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts index 1fa58ebe998dd..096731c804f23 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts @@ -42,6 +42,16 @@ export interface AwsApiCallRequest { * @default 'false' */ readonly flattenResponse?: string; + + /** + * Restrict the data returned by the API call to specific paths in + * the API response. Use this to limit the data returned by the custom + * resource if working with API calls that could potentially result in custom + * response objects exceeding the hard limit of 4096 bytes. + * + * @default - return all data + */ + readonly outputPaths?: string[]; } /** diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts index 96a994a8fc0a8..1f4084670062c 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts @@ -25,6 +25,16 @@ export interface AwsApiCallOptions { * @default - no parameters */ readonly parameters?: any; + + /** + * Restrict the data returned by the API call to specific paths in + * the API response. Use this to limit the data returned by the custom + * resource if working with API calls that could potentially result in custom + * response objects exceeding the hard limit of 4096 bytes. + * + * @default - return all data + */ + readonly outputPaths?: string[]; } /** @@ -59,6 +69,7 @@ export class AwsApiCall extends ApiCallBase { private readonly name: string; private _assertAtPath?: string; + private _outputPaths?: string[]; private readonly api: string; private readonly service: string; @@ -70,6 +81,7 @@ export class AwsApiCall extends ApiCallBase { this.name = `${props.service}${props.api}`; this.api = props.api; this.service = props.service; + this._outputPaths = props.outputPaths; this.apiCallResource = new CustomResource(this, 'Default', { serviceToken: this.provider.serviceToken, @@ -81,6 +93,7 @@ export class AwsApiCall extends ApiCallBase { stateMachineArn: Lazy.string({ produce: () => this.stateMachineArn }), parameters: this.provider.encode(props.parameters), flattenResponse: Lazy.string({ produce: () => this.flattenResponse }), + outputPaths: Lazy.list({ produce: () => this._outputPaths }), salt: Date.now().toString(), }, resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${this.name}`.substring(0, 60), @@ -107,6 +120,7 @@ export class AwsApiCall extends ApiCallBase { public assertAtPath(path: string, expected: ExpectedResult): IApiCall { this._assertAtPath = path; + this._outputPaths = [path]; this.expectedResult = expected.result; this.flattenResponse = 'true'; return this; diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/types.ts b/packages/@aws-cdk/integ-tests/lib/assertions/types.ts index 468eaf54b28ec..81b3f1ec37250 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/types.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/types.ts @@ -27,7 +27,7 @@ export interface IDeployAssert { * Messages: [{ Body: 'hello' }], * })); */ - awsApiCall(service: string, api: string, parameters?: any): IApiCall; + awsApiCall(service: string, api: string, parameters?: any, outputPaths?: string[]): IApiCall; /** * Invoke a lambda function and return the response which can be asserted diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts index c9d9c606d38d9..c6208cd4ca466 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts @@ -104,4 +104,40 @@ describe('SdkHandler', () => { sinon.assert.calledWith(fake, { DryRun: false }); }); }); + + test('restrict output path', async () => { + // GIVEN + const responseFake = { + Name: 'bucket-name', + Contents: [ + { + Key: 'first-key', + ETag: 'first-key-etag', + }, + { + Key: 'second-key', + ETag: 'second-key-etag', + }, + ], + } as SDK.S3.ListObjectsOutput; + AWS.mock('S3', 'listObjects', sinon.fake.resolves(responseFake)); + const handler = sdkHandler() as any; + const request: AwsApiCallRequest = { + service: 'S3', + api: 'listObjects', + parameters: { + Bucket: 'myBucket', + }, + outputPaths: ['Name', 'Contents.0.Key'], + }; + + // WHEN + const response: AwsApiCallResult = await handler.processEvent(request); + + // THEN + expect(response).toEqual({ + 'apiCallResponse.Name': 'bucket-name', + 'apiCallResponse.Contents.0.Key': 'first-key', + }); + }); }); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts index 8a0123ca8e96b..97a9d87cd9b2f 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts @@ -47,6 +47,63 @@ describe('AwsApiCall', () => { }); + test('restrict output paths', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + deplossert.awsApiCall('MyService', 'MyApi', { + param1: 'val1', + param2: 2, + }, ['path1', 'path2']); + + // THEN + const template = Template.fromStack(deplossert.scope); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { + service: 'MyService', + api: 'MyApi', + parameters: { + param1: 'val1', + param2: 2, + }, + outputPaths: [ + 'path1', + 'path2', + ], + }); + }); + + test('assert at path', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + deplossert.awsApiCall('MyService', 'MyApi', { + param1: 'val1', + param2: 2, + }).assertAtPath('Messages.0.Key', ExpectedResult.exact('first-key')); + + // THEN + const template = Template.fromStack(deplossert.scope); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { + service: 'MyService', + api: 'MyApi', + parameters: { + param1: 'val1', + param2: 2, + }, + flattenResponse: 'true', + outputPaths: [ + 'Messages.0.Key', + ], + expected: JSON.stringify({ $Exact: 'first-key' }), + }); + }); + test('add policy to provider', () => { // GIVEN const app = new App();