Skip to content

Commit 673f26b

Browse files
authored
Merge pull request #77 from horike37/add-scheduled-event
Add scheduled event
2 parents 44697c1 + 1cf2ced commit 673f26b

File tree

7 files changed

+638
-14
lines changed

7 files changed

+638
-14
lines changed

README.md

+50
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,56 @@ You can input an value as json in request body, the value is passed as the input
160160
```
161161
$ curl -XPOST https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/posts/create -d '{"foo":"bar"}'
162162
```
163+
### Schedule
164+
The following config will attach a schedule event and causes the stateMachine `crawl` to be called every 2 hours. The configuration allows you to attach multiple schedules to the same stateMachine. You can either use the `rate` or `cron` syntax. Take a look at the [AWS schedule syntax documentation](http://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html) for more details.
165+
166+
```yaml
167+
stepFunctions:
168+
stateMachines:
169+
crawl:
170+
events:
171+
- schedule: rate(2 hours)
172+
- schedule: cron(0 12 * * ? *)
173+
definition:
174+
```
175+
176+
## Enabling / Disabling
177+
178+
**Note:** `schedule` events are enabled by default.
179+
180+
This will create and attach a schedule event for the `aggregate` stateMachine which is disabled. If enabled it will call
181+
the `aggregate` stateMachine every 10 minutes.
182+
183+
```yaml
184+
stepFunctions:
185+
stateMachines:
186+
aggregate:
187+
events:
188+
- schedule:
189+
rate: rate(10 minutes)
190+
enabled: false
191+
input:
192+
key1: value1
193+
key2: value2
194+
stageParams:
195+
stage: dev
196+
- schedule:
197+
rate: cron(0 12 * * ? *)
198+
enabled: false
199+
inputPath: '$.stageVariables'
200+
```
201+
202+
## Specify Name and Description
203+
204+
Name and Description can be specified for a schedule event. These are not required properties.
205+
206+
```yaml
207+
events:
208+
- schedule:
209+
name: your-scheduled-rate-event-name
210+
description: 'your scheduled rate event description'
211+
rate: rate(2 hours)
212+
```
163213
164214
## Command
165215
### deploy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
'use strict';
2+
3+
const _ = require('lodash');
4+
const BbPromise = require('bluebird');
5+
6+
module.exports = {
7+
compileScheduledEvents() {
8+
_.forEach(this.getAllStateMachines(), (stateMachineName) => {
9+
const stateMachineObj = this.getStateMachine(stateMachineName);
10+
let scheduleNumberInFunction = 0;
11+
12+
if (stateMachineObj.events) {
13+
_.forEach(stateMachineObj.events, (event) => {
14+
if (event.schedule) {
15+
scheduleNumberInFunction++;
16+
let ScheduleExpression;
17+
let State;
18+
let Input;
19+
let InputPath;
20+
let Name;
21+
let Description;
22+
23+
// TODO validate rate syntax
24+
if (typeof event.schedule === 'object') {
25+
if (!event.schedule.rate) {
26+
const errorMessage = [
27+
`Missing "rate" property for schedule event in stateMachine ${stateMachineName}`,
28+
' The correct syntax is: schedule: rate(10 minutes)',
29+
' OR an object with "rate" property.',
30+
' Please check the README for more info.',
31+
].join('');
32+
throw new this.serverless.classes
33+
.Error(errorMessage);
34+
}
35+
ScheduleExpression = event.schedule.rate;
36+
State = 'ENABLED';
37+
if (event.schedule.enabled === false) {
38+
State = 'DISABLED';
39+
}
40+
Input = event.schedule.input;
41+
InputPath = event.schedule.inputPath;
42+
Name = event.schedule.name;
43+
Description = event.schedule.description;
44+
45+
if (Input && InputPath) {
46+
const errorMessage = [
47+
'You can\'t set both input & inputPath properties at the',
48+
'same time for schedule events.',
49+
'Please check the AWS docs for more info',
50+
].join('');
51+
throw new this.serverless.classes
52+
.Error(errorMessage);
53+
}
54+
55+
if (Input && typeof Input === 'object') {
56+
Input = JSON.stringify(Input);
57+
}
58+
if (Input && typeof Input === 'string') {
59+
// escape quotes to favor JSON.parse
60+
Input = Input.replace(/\"/g, '\\"'); // eslint-disable-line
61+
}
62+
} else if (typeof event.schedule === 'string') {
63+
ScheduleExpression = event.schedule;
64+
State = 'ENABLED';
65+
} else {
66+
const errorMessage = [
67+
`Schedule event of stateMachine ${stateMachineName} is not an object nor a string`,
68+
' The correct syntax is: schedule: rate(10 minutes)',
69+
' OR an object with "rate" property.',
70+
' Please check the README for more info.',
71+
].join('');
72+
throw new this.serverless.classes
73+
.Error(errorMessage);
74+
}
75+
76+
const stateMachineLogicalId = this
77+
.getStateMachineLogicalId(stateMachineName, stateMachineObj.name);
78+
const scheduleLogicalId = this
79+
.getScheduleLogicalId(stateMachineName, scheduleNumberInFunction);
80+
const scheduleIamRoleLogicalId = this
81+
.getScheduleToStepFunctionsIamRoleLogicalId(stateMachineName);
82+
const scheduleId = this.getScheduleId(stateMachineName);
83+
const policyName = this.getSchedulePolicyName(stateMachineName);
84+
85+
const scheduleTemplate = `
86+
{
87+
"Type": "AWS::Events::Rule",
88+
"Properties": {
89+
"ScheduleExpression": "${ScheduleExpression}",
90+
"State": "${State}",
91+
${Name ? `"Name": "${Name}",` : ''}
92+
${Description ? `"Description": "${Description}",` : ''}
93+
"Targets": [{
94+
${Input ? `"Input": "${Input}",` : ''}
95+
${InputPath ? `"InputPath": "${InputPath}",` : ''}
96+
"Arn": { "Ref": "${stateMachineLogicalId}" },
97+
"Id": "${scheduleId}",
98+
"RoleArn": {
99+
"Fn::GetAtt": [
100+
"${scheduleIamRoleLogicalId}",
101+
"Arn"
102+
]
103+
}
104+
}]
105+
}
106+
}
107+
`;
108+
109+
const iamRoleTemplate = `
110+
{
111+
"Type": "AWS::IAM::Role",
112+
"Properties": {
113+
"AssumeRolePolicyDocument": {
114+
"Version": "2012-10-17",
115+
"Statement": [
116+
{
117+
"Effect": "Allow",
118+
"Principal": {
119+
"Service": "events.amazonaws.com"
120+
},
121+
"Action": "sts:AssumeRole"
122+
}
123+
]
124+
},
125+
"Policies": [
126+
{
127+
"PolicyName": "${policyName}",
128+
"PolicyDocument": {
129+
"Version": "2012-10-17",
130+
"Statement": [
131+
{
132+
"Effect": "Allow",
133+
"Action": [
134+
"states:StartExecution"
135+
],
136+
"Resource": {
137+
"Ref": "${stateMachineLogicalId}"
138+
}
139+
}
140+
]
141+
}
142+
}
143+
]
144+
}
145+
}
146+
`;
147+
148+
const newScheduleObject = {
149+
[scheduleLogicalId]: JSON.parse(scheduleTemplate),
150+
};
151+
152+
const newPermissionObject = {
153+
[scheduleIamRoleLogicalId]: JSON.parse(iamRoleTemplate),
154+
};
155+
156+
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
157+
newScheduleObject, newPermissionObject);
158+
}
159+
});
160+
}
161+
});
162+
return BbPromise.resolve();
163+
},
164+
};

0 commit comments

Comments
 (0)