Skip to content

Commit

Permalink
chore(integ-tests): add waiterProvider to IApiCall (aws#27844)
Browse files Browse the repository at this point in the history
This PR changes to add the `waiterProvider` property to an `IApiCall` for `awsApiCall` in integ-tests-alpha.

By default `awsApiCall` in integ tests, 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 following example the service is SQS and the api is receiveMessage so it will create a policy with Action: 'sqs:ReceiveMessage'.

```ts
const integ = new IntegTest(app, 'Integ', {
  testCases: [stack],
});
integ.assertions.awsApiCall('SQS', 'receiveMessage', {
  QueueUrl: 'url',
});
```

There are some cases where the permissions do not exactly match the service/api call, for example the S3 listObjectsV2 api. In these cases it is possible to add the correct policy by accessing the `provider` object.

```ts
const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', {
  Bucket: 'mybucket',
});

apiCall.provider.addToRolePolicy({
  Effect: 'Allow',
  Action: ['s3:GetObject', 's3:ListBucket'],
  Resource: ['*'],
});
```

On the other hand, there is the case to use `waitForAssertions` when using `awsApiCall` in integ tests. This causes `apiCall` to have a `waiterProvider` property in addition to `provider`.

```ts
const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', {
  Bucket: 'mybucket',
}).expect(ExpectedResult.objectLike({
  KeyCount: 1,
})).waitForAssertions({
  interval: cdk.Duration.seconds(30),
  totalTimeout: cdk.Duration.minutes(10),
});
```

In the case, `waiterProvider` actually calls to the service/api, so it should have the proper policies.

However a type of a return value of `apiCall` is `IApiCall` interface so that the interface has a `provider` property, `waiterProvider` is not in `IApiCall` but in `AwsApiCall`.

Then it cannot take the policies without casting the following. (`apiCall instanceof AwsApiCall`)

```ts
if (apiCall instanceof AwsApiCall) {
  apiCall.waiterProvider?.addToRolePolicy({
    Effect: 'Allow',
    Action: ['s3:GetObject', 's3:ListBucket'],
    Resource: ['*'],
  });
}
```

So I add `waiterProvider` to `IApiCall`, so that it can take the policies without casting:

```ts
// if (apiCall instanceof AwsApiCall) {
  apiCall.waiterProvider?.addToRolePolicy({
    Effect: 'Allow',
    Action: ['s3:GetObject', 's3:ListBucket'],
    Resource: ['*'],
  });
//}
```

In my opinion, I see no negative impact from this.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
go-to-k authored and paulhcsun committed Jan 5, 2024
1 parent 6792ffd commit 116c0ba
Show file tree
Hide file tree
Showing 12 changed files with 1,235 additions and 2 deletions.
29 changes: 29 additions & 0 deletions packages/@aws-cdk/integ-tests-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,32 @@ const describe = testCase.assertions.awsApiCall('StepFunctions', 'describeExecut
});
```

When `waitForAssertions()` is used for the `awsApiCall`, the actual API call is executed
by the `waiterProvider` assertion provider.

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
`receiveMessage` so it will create a policy with `Action: 'sqs:ReceiveMessage`.

There are some cases where the permissions do not exactly match the service/api call, for
example the S3 `listObjectsV2` api. In these cases it is possible to add the correct policy
by accessing the `waiterProvider` object.

```ts
declare const integ: IntegTest;

const apiCall = integ.assertions.awsApiCall('S3', 'listObjectsV2', {
Bucket: 'mybucket',
}).waitForAssertions({
totalTimeout: Duration.minutes(5),
interval: Duration.seconds(15),
backoffRate: 3,
});

apiCall.waiterProvider?.addToRolePolicy({
Effect: 'Allow',
Action: ['s3:GetObject', 's3:ListBucket'],
Resource: ['*'],
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { WaiterStateMachineOptions } from './waiter-state-machine';
*/
export interface IApiCall extends IConstruct {
/**
* access the AssertionsProvider. This can be used to add additional IAM policies
* the the provider role policy
* Access the AssertionsProvider. This can be used to add additional IAM policies
* to the provider role policy.
*
* @example
* declare const apiCall: AwsApiCall;
Expand All @@ -22,6 +22,21 @@ export interface IApiCall extends IConstruct {
*/
readonly provider: AssertionsProvider;

/**
* Access the AssertionsProvider for the waiter state machine.
* This can be used to add additional IAM policies
* to the provider role policy.
*
* @example
* declare const apiCall: AwsApiCall;
* apiCall.waiterProvider?.addToRolePolicy({
* Effect: 'Allow',
* Action: ['s3:GetObject'],
* Resource: ['*'],
* });
*/
readonly waiterProvider?: AssertionsProvider;

/**
* Returns the value of an attribute of the custom resource of an arbitrary
* type. Attributes are returned from the custom resource provider through the
Expand Down

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"Resources": {
"Bucket83908E77": {
"Type": "AWS::S3::Bucket",
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
}
},
"Outputs": {
"ExportsOutputRefBucket83908E7781C90AC0": {
"Value": {
"Ref": "Bucket83908E77"
},
"Export": {
"Name": "WaiterProviderStack:ExportsOutputRefBucket83908E7781C90AC0"
}
},
"ExportsOutputFnGetAttBucket83908E77Arn063C8555": {
"Value": {
"Fn::GetAtt": [
"Bucket83908E77",
"Arn"
]
},
"Export": {
"Name": "WaiterProviderStack:ExportsOutputFnGetAttBucket83908E77Arn063C8555"
}
}
},
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}

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

Loading

0 comments on commit 116c0ba

Please sign in to comment.