Skip to content

Commit 437c21f

Browse files
authored
fix(integ-test): limit api response to avoid 4k limit (#23102)
fixes #22059 follows precedent set in #14041 also added filtering to assertAtPath to reduce duplication and reduce the chance of hitting the error for developers using the library also added test for assertAtPath ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 85fe047 commit 437c21f

File tree

8 files changed

+139
-3
lines changed

8 files changed

+139
-3
lines changed

packages/@aws-cdk/integ-tests/lib/assertions/api-call-base.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export interface IApiCall extends IConstruct {
6060
* Assert that the ExpectedResult is equal
6161
* to the result of the AwsApiCall at the given path.
6262
*
63+
* Providing a path will filter the output of the initial API call.
64+
*
6365
* For example the SQS.receiveMessage api response would look
6466
* like:
6567
*

packages/@aws-cdk/integ-tests/lib/assertions/private/deploy-assert.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ export class DeployAssert extends Construct implements IDeployAssert {
5858
Object.defineProperty(this, DEPLOY_ASSERT_SYMBOL, { value: true });
5959
}
6060

61-
public awsApiCall(service: string, api: string, parameters?: any): IApiCall {
61+
public awsApiCall(service: string, api: string, parameters?: any, outputPaths?: string[]): IApiCall {
6262
return new AwsApiCall(this.scope, `AwsApiCall${service}${api}`, {
6363
api,
6464
service,
6565
parameters,
66+
outputPaths,
6667
});
6768
}
6869

packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,28 @@ export class AwsApiCallHandler extends CustomResourceHandler<AwsApiCallRequest,
5252
...flatten(respond),
5353
};
5454

55-
const resp = request.flattenResponse === 'true' ? flatData : respond;
55+
let resp: AwsApiCallResult | { [key: string]: string } = respond;
56+
if (request.outputPaths) {
57+
resp = filterKeys(flatData, request.outputPaths!);
58+
} else if (request.flattenResponse === 'true') {
59+
resp = flatData;
60+
}
5661
console.log(`Returning result ${JSON.stringify(resp)}`);
5762
return resp;
5863
}
5964
}
6065

66+
function filterKeys(object: object, searchStrings: string[]): { [key: string]: string } {
67+
return Object.entries(object).reduce((filteredObject: { [key: string]: string }, [key, value]) => {
68+
for (const searchString of searchStrings) {
69+
if (key.startsWith(`apiCallResponse.${searchString}`)) {
70+
filteredObject[key] = value;
71+
}
72+
}
73+
return filteredObject;
74+
}, {});
75+
}
76+
6177
function isJsonString(value: string): any {
6278
try {
6379
return JSON.parse(value);

packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ export interface AwsApiCallRequest {
4242
* @default 'false'
4343
*/
4444
readonly flattenResponse?: string;
45+
46+
/**
47+
* Restrict the data returned by the API call to specific paths in
48+
* the API response. Use this to limit the data returned by the custom
49+
* resource if working with API calls that could potentially result in custom
50+
* response objects exceeding the hard limit of 4096 bytes.
51+
*
52+
* @default - return all data
53+
*/
54+
readonly outputPaths?: string[];
4555
}
4656

4757
/**

packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ export interface AwsApiCallOptions {
2525
* @default - no parameters
2626
*/
2727
readonly parameters?: any;
28+
29+
/**
30+
* Restrict the data returned by the API call to specific paths in
31+
* the API response. Use this to limit the data returned by the custom
32+
* resource if working with API calls that could potentially result in custom
33+
* response objects exceeding the hard limit of 4096 bytes.
34+
*
35+
* @default - return all data
36+
*/
37+
readonly outputPaths?: string[];
2838
}
2939

3040
/**
@@ -59,6 +69,7 @@ export class AwsApiCall extends ApiCallBase {
5969
private readonly name: string;
6070

6171
private _assertAtPath?: string;
72+
private _outputPaths?: string[];
6273
private readonly api: string;
6374
private readonly service: string;
6475

@@ -70,6 +81,7 @@ export class AwsApiCall extends ApiCallBase {
7081
this.name = `${props.service}${props.api}`;
7182
this.api = props.api;
7283
this.service = props.service;
84+
this._outputPaths = props.outputPaths;
7385

7486
this.apiCallResource = new CustomResource(this, 'Default', {
7587
serviceToken: this.provider.serviceToken,
@@ -81,6 +93,7 @@ export class AwsApiCall extends ApiCallBase {
8193
stateMachineArn: Lazy.string({ produce: () => this.stateMachineArn }),
8294
parameters: this.provider.encode(props.parameters),
8395
flattenResponse: Lazy.string({ produce: () => this.flattenResponse }),
96+
outputPaths: Lazy.list({ produce: () => this._outputPaths }),
8497
salt: Date.now().toString(),
8598
},
8699
resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${this.name}`.substring(0, 60),
@@ -107,6 +120,7 @@ export class AwsApiCall extends ApiCallBase {
107120

108121
public assertAtPath(path: string, expected: ExpectedResult): IApiCall {
109122
this._assertAtPath = path;
123+
this._outputPaths = [path];
110124
this.expectedResult = expected.result;
111125
this.flattenResponse = 'true';
112126
return this;

packages/@aws-cdk/integ-tests/lib/assertions/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface IDeployAssert {
2727
* Messages: [{ Body: 'hello' }],
2828
* }));
2929
*/
30-
awsApiCall(service: string, api: string, parameters?: any): IApiCall;
30+
awsApiCall(service: string, api: string, parameters?: any, outputPaths?: string[]): IApiCall;
3131

3232
/**
3333
* Invoke a lambda function and return the response which can be asserted

packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,40 @@ describe('SdkHandler', () => {
104104
sinon.assert.calledWith(fake, { DryRun: false });
105105
});
106106
});
107+
108+
test('restrict output path', async () => {
109+
// GIVEN
110+
const responseFake = {
111+
Name: 'bucket-name',
112+
Contents: [
113+
{
114+
Key: 'first-key',
115+
ETag: 'first-key-etag',
116+
},
117+
{
118+
Key: 'second-key',
119+
ETag: 'second-key-etag',
120+
},
121+
],
122+
} as SDK.S3.ListObjectsOutput;
123+
AWS.mock('S3', 'listObjects', sinon.fake.resolves(responseFake));
124+
const handler = sdkHandler() as any;
125+
const request: AwsApiCallRequest = {
126+
service: 'S3',
127+
api: 'listObjects',
128+
parameters: {
129+
Bucket: 'myBucket',
130+
},
131+
outputPaths: ['Name', 'Contents.0.Key'],
132+
};
133+
134+
// WHEN
135+
const response: AwsApiCallResult = await handler.processEvent(request);
136+
137+
// THEN
138+
expect(response).toEqual({
139+
'apiCallResponse.Name': 'bucket-name',
140+
'apiCallResponse.Contents.0.Key': 'first-key',
141+
});
142+
});
107143
});

packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,63 @@ describe('AwsApiCall', () => {
4747

4848
});
4949

50+
test('restrict output paths', () => {
51+
// GIVEN
52+
const app = new App();
53+
const deplossert = new DeployAssert(app);
54+
55+
// WHEN
56+
deplossert.awsApiCall('MyService', 'MyApi', {
57+
param1: 'val1',
58+
param2: 2,
59+
}, ['path1', 'path2']);
60+
61+
// THEN
62+
const template = Template.fromStack(deplossert.scope);
63+
template.resourceCountIs('AWS::Lambda::Function', 1);
64+
template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', {
65+
service: 'MyService',
66+
api: 'MyApi',
67+
parameters: {
68+
param1: 'val1',
69+
param2: 2,
70+
},
71+
outputPaths: [
72+
'path1',
73+
'path2',
74+
],
75+
});
76+
});
77+
78+
test('assert at path', () => {
79+
// GIVEN
80+
const app = new App();
81+
const deplossert = new DeployAssert(app);
82+
83+
// WHEN
84+
deplossert.awsApiCall('MyService', 'MyApi', {
85+
param1: 'val1',
86+
param2: 2,
87+
}).assertAtPath('Messages.0.Key', ExpectedResult.exact('first-key'));
88+
89+
// THEN
90+
const template = Template.fromStack(deplossert.scope);
91+
template.resourceCountIs('AWS::Lambda::Function', 1);
92+
template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', {
93+
service: 'MyService',
94+
api: 'MyApi',
95+
parameters: {
96+
param1: 'val1',
97+
param2: 2,
98+
},
99+
flattenResponse: 'true',
100+
outputPaths: [
101+
'Messages.0.Key',
102+
],
103+
expected: JSON.stringify({ $Exact: 'first-key' }),
104+
});
105+
});
106+
50107
test('add policy to provider', () => {
51108
// GIVEN
52109
const app = new App();

0 commit comments

Comments
 (0)