Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iotevents): add IoT Events input L2 Construct #17847

Merged
merged 7 commits into from
Dec 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions packages/@aws-cdk/aws-iotevents/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# AWS::IoTEvents Construct Library

<!--BEGIN STABILITY BANNER-->

---
Expand All @@ -9,23 +10,45 @@
>
> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib

![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)

> The APIs of higher level constructs in this module are experimental and under active development.
> They are subject to non-backward compatible changes or removal in any future version. These are
> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be
> announced in the release notes. This means that while you may use them, you may need to update
> your source code when upgrading to a newer version of this package.

---

<!--END STABILITY BANNER-->

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
AWS IoT Events enables you to monitor your equipment or device fleets for
failures or changes in operation, and to trigger actions when such events
occur.

## Installation

Install the module:

```console
$ npm i @aws-cdk/aws-iotevents
```

Import it into your code:

```ts nofixture
import * as iotevents from '@aws-cdk/aws-iotevents';
```

<!--BEGIN CFNONLY DISCLAIMER-->

There are no hand-written ([L2](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) constructs for this service yet.
However, you can still use the automatically generated [L1](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_l1_using) constructs, and use this service exactly as you would using CloudFormation directly.
## `Input`

For more information on the resources and properties available for this service, see the [CloudFormation documentation for AWS::IoTEvents](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_IoTEvents.html).
Add an AWS IoT Events input to your stack:

(Read the [CDK Contributing Guide](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) if you are interested in contributing to this construct library.)
```ts
import * as iotevents from '@aws-cdk/aws-iotevents';

<!--END CFNONLY DISCLAIMER-->
new iotevents.Input(this, 'MyInput', {
inputName: 'my_input',
attributeJsonPaths: ['payload.temperature'],
});
```
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './input';

// AWS::IoTEvents CloudFormation Resources:
export * from './iotevents.generated';
71 changes: 71 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Resource, IResource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnInput } from './iotevents.generated';

/**
* Represents an AWS IoT Events input
*/
export interface IInput extends IResource {
/**
* The name of the input
* @attribute
*/
readonly inputName: string;
}

/**
* Properties for defining an AWS IoT Events input
*/
export interface InputProps {
/**
* The name of the input
*
* @default - CloudFormation will generate a unique name of the input
*/
readonly inputName?: string,

/**
* An expression that specifies an attribute-value pair in a JSON structure.
* Use this to specify an attribute from the JSON payload that is made available
* by the input. Inputs are derived from messages sent to AWS IoT Events (BatchPutMessage).
* Each such message contains a JSON payload. The attribute (and its paired value)
* specified here are available for use in the condition expressions used by detectors.
*/
readonly attributeJsonPaths: string[];
}

/**
* Defines an AWS IoT Events input in this stack.
*/
export class Input extends Resource implements IInput {
/**
* Import an existing input
*/
public static fromInputName(scope: Construct, id: string, inputName: string): IInput {
class Import extends Resource implements IInput {
public readonly inputName = inputName;
}
return new Import(scope, id);
}

public readonly inputName: string;

constructor(scope: Construct, id: string, props: InputProps) {
super(scope, id, {
physicalName: props.inputName,
});

if (props.attributeJsonPaths.length === 0) {
throw new Error('attributeJsonPaths property cannot be empty');
}

const resource = new CfnInput(this, 'Resource', {
inputName: this.physicalName,
inputDefinition: {
attributes: props.attributeJsonPaths.map(path => ({ jsonPath: path })),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can attributeJsonPaths be empty? If not, can we validate that it's not? If it can be empty, can we make it optional, and default to []?

Copy link
Contributor Author

@yamatatsu yamatatsu Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you said, attributeJsonPaths can not be empty. So I've been thinking that it is better to validate it too!
On the other hand, I thought I had to make my initial L2 PR smaller so as not to repeat my previous mistakes😅 .

I never mind which ways, implement in this PR or after! 😉
I've fixed it!

},
});

this.inputName = this.getResourceNameAttribute(resource.ref);
}
}
6 changes: 4 additions & 2 deletions packages/@aws-cdk/aws-iotevents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@
"devDependencies": {
"@aws-cdk/assertions": "0.0.0",
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/cdk-integ-tools": "0.0.0",
"@aws-cdk/cfn2ts": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@types/jest": "^27.0.3"
"@types/jest": "^27.0.3",
"jest": "^27.3.1"
},
"dependencies": {
"@aws-cdk/core": "0.0.0",
Expand All @@ -92,7 +94,7 @@
"node": ">= 10.13.0 <13 || >=13.7.0"
},
"stability": "experimental",
"maturity": "cfn-only",
"maturity": "experimental",
"awscdkio": {
"announce": false
},
Expand Down
78 changes: 78 additions & 0 deletions packages/@aws-cdk/aws-iotevents/test/input.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Template } from '@aws-cdk/assertions';
import * as cdk from '@aws-cdk/core';
import * as iotevents from '../lib';

test('Default property', () => {
const stack = new cdk.Stack();

// WHEN
new iotevents.Input(stack, 'MyInput', {
attributeJsonPaths: ['payload.temperature'],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::Input', {
InputDefinition: {
Attributes: [{ JsonPath: 'payload.temperature' }],
},
});
});

test('can get input name', () => {
const stack = new cdk.Stack();
// GIVEN
const input = new iotevents.Input(stack, 'MyInput', {
attributeJsonPaths: ['payload.temperature'],
});

// WHEN
new cdk.CfnResource(stack, 'Res', {
type: 'Test::Resource',
properties: {
InputName: input.inputName,
},
});

// THEN
Template.fromStack(stack).hasResourceProperties('Test::Resource', {
InputName: { Ref: 'MyInput08947B23' },
});
});

test('can set physical name', () => {
const stack = new cdk.Stack();

// WHEN
new iotevents.Input(stack, 'MyInput', {
inputName: 'test_input',
attributeJsonPaths: ['payload.temperature'],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::Input', {
InputName: 'test_input',
});
});

test('can import a Input by inputName', () => {
const stack = new cdk.Stack();

// WHEN
const inputName = 'test-input-name';
const topicRule = iotevents.Input.fromInputName(stack, 'InputFromInputName', inputName);

// THEN
expect(topicRule).toMatchObject({
inputName,
});
});

test('cannot be created with an empty array of attributeJsonPaths', () => {
const stack = new cdk.Stack();

expect(() => {
new iotevents.Input(stack, 'MyInput', {
attributeJsonPaths: [],
});
}).toThrow('attributeJsonPaths property cannot be empty');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Resources": {
"MyInput08947B23": {
"Type": "AWS::IoTEvents::Input",
"Properties": {
"InputDefinition": {
"Attributes": [
{
"JsonPath": "payload.temperature"
}
]
},
"InputName": "test_input"
}
}
}
}
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as cdk from '@aws-cdk/core';
import * as iotevents from '../lib';

const app = new cdk.App();

class TestStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);

new iotevents.Input(this, 'MyInput', {
inputName: 'test_input',
attributeJsonPaths: ['payload.temperature'],
});
}
}

new TestStack(app, 'test-stack');
app.synth();
6 changes: 0 additions & 6 deletions packages/@aws-cdk/aws-iotevents/test/iotevents.test.ts

This file was deleted.