Skip to content

Commit

Permalink
Merge branch 'master' into add_provisioning_rules
Browse files Browse the repository at this point in the history
  • Loading branch information
arcrank authored Jul 27, 2021
2 parents cd721bd + 2cefe57 commit bffb25f
Showing 82 changed files with 5,893 additions and 353 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/yarn-upgrade.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v2

- name: Set up Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.3.0
with:
node-version: 10

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -251,7 +251,7 @@ The steps here are usually AWS CLI commands but they need not be.

Examples:
* [integ.destinations.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-lambda-destinations/test/integ.destinations.ts#L7)
* [integ.token-authorizer.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.ts#L6)
* [integ.token-authorizer.lit.ts](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-apigateway/test/authorizers/integ.token-authorizer.lit.ts#L7-L12)

#### yarn watch (Optional)

6 changes: 5 additions & 1 deletion packages/@aws-cdk/assertions/README.md
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ in a template.
assert.resourceCountIs('Foo::Bar', 2);
```

## Resource Matching
## Resource Matching & Retrieval

Beyond resource counting, the module also allows asserting that a resource with
specific properties are present.
@@ -88,6 +88,10 @@ assert.hasResource('Foo::Bar', {
});
```

Beyond assertions, the module provides APIs to retrieve matching resources.
The `findResources()` API is complementary to the `hasResource()` API, except,
instead of asserting its presence, it returns the set of matching resources.

By default, the `hasResource()` and `hasResourceProperties()` APIs perform deep
partial object matching. This behavior can be configured using matchers.
See subsequent section on [special matchers](#special-matchers).
2 changes: 1 addition & 1 deletion packages/@aws-cdk/assertions/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './assertions';
export * from './template';
export * from './match';
export * from './matcher';
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
import { Match } from './match';
import { Matcher, MatchResult } from './matcher';
import { StackInspector } from './vendored/assert';
import { Match } from '../match';
import { Matcher, MatchResult } from '../matcher';
import { StackInspector } from '../vendored/assert';

export function findResources(inspector: StackInspector, type: string, props: any = {}): { [key: string]: any }[] {
const matcher = Matcher.isMatcher(props) ? props : Match.objectLike(props);
let results: { [key: string]: any }[] = [];

eachResourceWithType(inspector, type, (resource) => {
const result = matcher.test(resource);
if (!result.hasFailed()) {
results.push(resource);
}
});

return results;
}

export function hasResource(inspector: StackInspector, type: string, props: any): string | void {
const matcher = Matcher.isMatcher(props) ? props : Match.objectLike(props);
let closestResult: MatchResult | undefined = undefined;
let closestResource: { [key: string]: any } | undefined = undefined;
let count: number = 0;

for (const logicalId of Object.keys(inspector.value.Resources ?? {})) {
const resource: { [key: string]: any } = inspector.value.Resources[logicalId];
if (resource.Type === type) {
count++;
const result = matcher.test(resource);
if (!result.hasFailed()) {
return;
}
if (closestResult === undefined || closestResult.failCount > result.failCount) {
closestResult = result;
closestResource = resource;
}
let match = false;
eachResourceWithType(inspector, type, (resource) => {
if (match) { return; }
count++;
const result = matcher.test(resource);
if (!result.hasFailed()) {
match = true;
}
if (closestResult === undefined || closestResult.failCount > result.failCount) {
closestResult = result;
closestResource = resource;
}
});

if (match) {
return;
}

if (closestResult === undefined) {
@@ -37,6 +54,19 @@ export function hasResource(inspector: StackInspector, type: string, props: any)
].join('\n');
}

function eachResourceWithType(
inspector: StackInspector,
type: string,
cb: (resource: {[key: string]: any}) => void): void {

for (const logicalId of Object.keys(inspector.value.Resources ?? {})) {
const resource: { [key: string]: any } = inspector.value.Resources[logicalId];
if (resource.Type === type) {
cb(resource);
}
}
}

function formatMessage(closestResult: MatchResult, closestResource: {}): string {
return [
'The closest result is:',
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Stack, Stage } from '@aws-cdk/core';
import { hasResource } from './has-resource';
import { Match } from './match';
import { Matcher } from './matcher';
import { findResources, hasResource } from './private/resource';
import * as assert from './vendored/assert';

/**
@@ -82,6 +82,17 @@ export class TemplateAssertions {
}
}

/**
* Get the set of matching resources of a given type and properties in the CloudFormation template.
* @param type the type to match in the CloudFormation template
* @param props by default, matches all resources with the given type.
* When a literal is provided, performs a partial match via `Match.objectLike()`.
* Use the `Match` APIs to configure a different behaviour.
*/
public findResources(type: string, props: any = {}): { [key: string]: any }[] {
return findResources(this.inspector, type, props);
}

/**
* Assert that the CloudFormation template matches the given value
* @param expected the expected CloudFormation template as key-value pairs.
Original file line number Diff line number Diff line change
@@ -221,6 +221,68 @@ describe('TemplateAssertions', () => {
})).toThrow(/No resource/);
});
});

describe('getResources', () => {
test('matching resource type', () => {
const stack = new Stack();
new CfnResource(stack, 'Foo', {
type: 'Foo::Bar',
properties: { baz: 'qux', fred: 'waldo' },
});

const inspect = TemplateAssertions.fromStack(stack);
expect(inspect.findResources('Foo::Bar')).toEqual([{
Type: 'Foo::Bar',
Properties: { baz: 'qux', fred: 'waldo' },
}]);
});

test('no matching resource type', () => {
const stack = new Stack();
new CfnResource(stack, 'Foo', {
type: 'Foo::Bar',
properties: { baz: 'qux', fred: 'waldo' },
});

const inspect = TemplateAssertions.fromStack(stack);
expect(inspect.findResources('Foo::Baz')).toEqual([]);
});

test('matching resource props', () => {
const stack = new Stack();
new CfnResource(stack, 'Foo', {
type: 'Foo::Bar',
properties: { baz: 'qux', fred: 'waldo' },
});

const inspect = TemplateAssertions.fromStack(stack);
expect(inspect.findResources('Foo::Bar', {
Properties: { baz: 'qux' },
}).length).toEqual(1);
});

test('no matching resource props', () => {
const stack = new Stack();
new CfnResource(stack, 'Foo', {
type: 'Foo::Bar',
properties: { baz: 'qux', fred: 'waldo' },
});

const inspect = TemplateAssertions.fromStack(stack);
expect(inspect.findResources('Foo::Bar', {
Properties: { baz: 'waldo' },
})).toEqual([]);
});

test('multiple matching resources', () => {
const stack = new Stack();
new CfnResource(stack, 'Foo', { type: 'Foo::Bar' });
new CfnResource(stack, 'Bar', { type: 'Foo::Bar' });

const inspect = TemplateAssertions.fromStack(stack);
expect(inspect.findResources('Foo::Bar').length).toEqual(2);
});
});
});

function expectToThrow(fn: () => void, msgs: (RegExp | string)[], done: jest.DoneCallback): void {
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-cloudwatch/package.json
Original file line number Diff line number Diff line change
@@ -73,13 +73,13 @@
},
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assertions": "0.0.0",
"@types/jest": "^26.0.24",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"cfn2ts": "0.0.0",
"jest": "^26.6.3",
"pkglint": "0.0.0",
"@aws-cdk/assert-internal": "0.0.0"
"pkglint": "0.0.0"
},
"dependencies": {
"@aws-cdk/aws-iam": "0.0.0",
27 changes: 13 additions & 14 deletions packages/@aws-cdk/aws-cloudwatch/test/alarm.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import '@aws-cdk/assert-internal/jest';
import { ABSENT } from '@aws-cdk/assert-internal';
import { Match, TemplateAssertions } from '@aws-cdk/assertions';
import { Duration, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { Alarm, IAlarm, IAlarmAction, Metric, MathExpression, IMetric } from '../lib';
@@ -68,7 +67,7 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 3,
MetricName: 'Metric',
@@ -94,7 +93,7 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 3,
MetricName: 'Metric',
@@ -120,14 +119,14 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 3,
MetricName: 'Metric',
Namespace: 'CDK/Test',
Period: 300,
Statistic: 'Maximum',
ExtendedStatistic: ABSENT,
ExtendedStatistic: Match.absentProperty(),
Threshold: 1000,
});

@@ -147,13 +146,13 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 3,
MetricName: 'Metric',
Namespace: 'CDK/Test',
Period: 300,
Statistic: ABSENT,
Statistic: Match.absentProperty(),
ExtendedStatistic: 'p99',
Threshold: 1000,
});
@@ -174,7 +173,7 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 3,
DatapointsToAlarm: 2,
@@ -204,7 +203,7 @@ describe('Alarm', () => {
alarm.addOkAction(new TestAlarmAction('C'));

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
AlarmActions: ['A'],
InsufficientDataActions: ['B'],
OKActions: ['C'],
@@ -226,7 +225,7 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
EvaluationPeriods: 2,
MetricName: 'Metric',
@@ -251,7 +250,7 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
ExtendedStatistic: 'p99.9',
});

@@ -270,8 +269,8 @@ describe('Alarm', () => {
});

// THEN
expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
Statistic: ABSENT,
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
Statistic: Match.absentProperty(),
ExtendedStatistic: 'tm99.9999999999',
});

4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-cloudwatch/test/composite-alarm.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '@aws-cdk/assert-internal/jest';
import { TemplateAssertions } from '@aws-cdk/assertions';
import { Stack } from '@aws-cdk/core';
import { Alarm, AlarmRule, AlarmState, CompositeAlarm, Metric } from '../lib';

@@ -59,7 +59,7 @@ describe('CompositeAlarm', () => {
alarmRule,
});

expect(stack).toHaveResource('AWS::CloudWatch::CompositeAlarm', {
TemplateAssertions.fromStack(stack).hasResourceProperties('AWS::CloudWatch::CompositeAlarm', {
AlarmName: 'CompositeAlarm',
AlarmRule: {
'Fn::Join': [
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '@aws-cdk/assert-internal/jest';
import { TemplateAssertions } from '@aws-cdk/assertions';
import { Stack } from '@aws-cdk/core';
import { Alarm, GraphWidget, IWidget, Metric } from '../lib';

@@ -89,7 +89,7 @@ describe('cross environment', () => {
});

// THEN
expect(stack1).toHaveResourceLike('AWS::CloudWatch::Alarm', {
TemplateAssertions.fromStack(stack1).hasResourceProperties('AWS::CloudWatch::Alarm', {
MetricName: 'ACount',
Namespace: 'Test',
Period: 300,
Loading

0 comments on commit bffb25f

Please sign in to comment.