Skip to content

Commit

Permalink
fix(integ-tests): cannot use v3 package name in an awsApiCall (#28895)
Browse files Browse the repository at this point in the history
When using the `awsApiCall` of integ-tests, several possible ways exist to specify the `service` and `api`.
This is made possible by the following PR.
https://github.com/aws/aws-cdk/pull/27313/files#diff-3ab65cbf843775673ff370c9c90deceba5f0ead8a3e016e0c2f243d27bf84609

However, currently, when specifying the package name or client name in SDK V3, the resource type in custom resource or the logical id in CloudFormation Output contains non-alphanumeric (`@`, `/`, `-`), which results in an error.

For custom resources, the resource type can include alphanumeric characters and the following characters: `_@-`
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html#aws-resource-cloudformation-customresource--remarks

For `CfnOutput`, the logical id can include only alphanumeric.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html#outputs-section-syntax

This PR fixes to remove these strings that cannot be included and allows users to specify the SDK v3 package name and client name when using `awsApiCall`.


Closes #28844

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
sakurai-ryo authored and SankyRed committed Feb 8, 2024
1 parent 265ac2d commit b0f2240
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 24 deletions.
14 changes: 14 additions & 0 deletions packages/@aws-cdk/integ-tests-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,20 @@ integ.assertions.awsApiCall('SQS', 'receiveMessage', {
});
```

You must specify the `service` and the `api` when using The `AwsApiCall` construct.
The `service` is the name of an AWS service, in one of the following forms:

- An AWS SDK for JavaScript v3 package name (`@aws-sdk/client-api-gateway`)
- An AWS SDK for JavaScript v3 client name (`api-gateway`)
- An AWS SDK for JavaScript v2 constructor name (`APIGateway`)
- A lowercase AWS SDK for JavaScript v2 constructor name (`apigateway`)

The `api` is the name of an AWS API call, in one of the following forms:

- An API call name as found in the API Reference documentation (`GetObject`)
- The API call name starting with a lowercase letter (`getObject`)
- The AWS SDK for JavaScript v3 command class name (`GetObjectCommand`)

By default, the `AwsApiCall` construct will automatically add the correct IAM policies
to allow the Lambda function to make the API call. It does this based on the `service`
and `api` that is provided. In the above example the service is `SQS` and the api is
Expand Down
10 changes: 8 additions & 2 deletions packages/@aws-cdk/integ-tests-alpha/lib/assertions/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ export class AwsApiCall extends ApiCallBase {
outputPaths: Lazy.list({ produce: () => this.outputPaths }),
salt: Date.now().toString(),
},
resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${this.name}`.substring(0, 60),
// Remove the slash from the resource type because when using the v3 package name as the service name,
// the `service` props includes the slash, but the resource type name cannot contain the slash
// See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html#aws-resource-cloudformation-customresource--remarks
resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${this.name}`.substring(0, 60).replace(/[\/]/g, ''),
});
// Needed so that all the policies set up by the provider should be available before the custom resource is provisioned.
this.apiCallResource.node.addDependency(this.provider);
Expand All @@ -112,7 +115,10 @@ export class AwsApiCall extends ApiCallBase {

new CfnOutput(node, 'AssertionResults', {
value: result,
}).overrideLogicalId(`AssertionResults${id}`);
// Remove the at sign, slash, and hyphen because when using the v3 package name or client name as the service name,
// the `id` includes them, but they are not allowed in the `CfnOutput` logical id
// See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html#outputs-section-syntax
}).overrideLogicalId(`AssertionResults${id}`.replace(/[\@\/\-]/g, ''));
}
}
},
Expand Down
13 changes: 12 additions & 1 deletion packages/@aws-cdk/integ-tests-alpha/lib/assertions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ import { LambdaInvokeFunctionProps } from './sdk';
*/
export interface IDeployAssert {
/**
* Query AWS using JavaScript SDK V2 API calls. This can be used to either
* Query AWS using JavaScript SDK API calls. This can be used to either
* trigger an action or to return a result that can then be asserted against
* an expected value
*
* The `service` is the name of an AWS service, in one of the following forms:
* - An AWS SDK for JavaScript v3 package name (`@aws-sdk/client-api-gateway`)
* - An AWS SDK for JavaScript v3 client name (`api-gateway`)
* - An AWS SDK for JavaScript v2 constructor name (`APIGateway`)
* - A lowercase AWS SDK for JavaScript v2 constructor name (`apigateway`)
*
* The `api` is the name of an AWS API call, in one of the following forms:
* - An API call name as found in the API Reference documentation (`GetObject`)
* - The API call name starting with a lowercase letter (`getObject`)
* - The AWS SDK for JavaScript v3 command class name (`GetObjectCommand`)
*
* @example
* declare const app: App;
* declare const integ: IntegTest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,57 @@ describe('DeployAssert', () => {
template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourceCountIs(truncatedType, 1);
});

test('can use v3 package name and command class name', () => {
// GIVEN
const app = new App();

// WHEN
const deplossert = new DeployAssert(app);
deplossert.awsApiCall('@aws-sdk/client-ssm', 'GetParameterCommand');

// THEN
const template = Template.fromStack(deplossert.scope);

template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourcePropertiesCountIs(
'Custom::DeployAssert@SdkCall@aws-sdkclient-ssmGetParameterC',
{
service: '@aws-sdk/client-ssm',
api: 'GetParameterCommand',
},
1,
);
});

test('can use v3 package name and command class name with assertions', () => {
// GIVEN
const app = new App();

// WHEN
const deplossert = new DeployAssert(app);
deplossert.awsApiCall('@aws-sdk/client-ssm', 'GetParameterCommand').expect(
ExpectedResult.objectLike({}),
);;

// THEN
const template = Template.fromStack(deplossert.scope);

template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourcePropertiesCountIs(
'Custom::DeployAssert@SdkCall@aws-sdkclient-ssmGetParameterC',
{
service: '@aws-sdk/client-ssm',
api: 'GetParameterCommand',
},
1,
);
template.hasOutput('AssertionResultsAwsApiCallawssdkclientssmGetParameterCommand', {
Value: {
'Fn::GetAtt': ['AwsApiCallawssdkclientssmGetParameterCommand', 'assertion'],
},
});
});
});

describe('httpApiCall', () => {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b0f2240

Please sign in to comment.