Skip to content
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
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,56 @@ You can input an value as json in request body, the value is passed as the input
```
$ curl -XPOST https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/posts/create -d '{"foo":"bar"}'
```
### Schedule
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.

```yaml
stepFunctions:
stateMachines:
crawl:
events:
- schedule: rate(2 hours)
- schedule: cron(0 12 * * ? *)
definition:
```

## Enabling / Disabling

**Note:** `schedule` events are enabled by default.

This will create and attach a schedule event for the `aggregate` stateMachine which is disabled. If enabled it will call
the `aggregate` stateMachine every 10 minutes.

```yaml
stepFunctions:
stateMachines:
aggregate:
events:
- schedule:
rate: rate(10 minutes)
enabled: false
input:
key1: value1
key2: value2
stageParams:
stage: dev
- schedule:
rate: cron(0 12 * * ? *)
enabled: false
inputPath: '$.stageVariables'
```

## Specify Name and Description

Name and Description can be specified for a schedule event. These are not required properties.

```yaml
events:
- schedule:
name: your-scheduled-rate-event-name
description: 'your scheduled rate event description'
rate: rate(2 hours)
```

## Command
### deploy
Expand Down
164 changes: 164 additions & 0 deletions lib/deploy/events/schedule/compileScheduledEvents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
'use strict';

const _ = require('lodash');
const BbPromise = require('bluebird');

module.exports = {
compileScheduledEvents() {
_.forEach(this.getAllStateMachines(), (stateMachineName) => {
const stateMachineObj = this.getStateMachine(stateMachineName);
let scheduleNumberInFunction = 0;

if (stateMachineObj.events) {
_.forEach(stateMachineObj.events, (event) => {
if (event.schedule) {
scheduleNumberInFunction++;
let ScheduleExpression;
let State;
let Input;
let InputPath;
let Name;
let Description;

// TODO validate rate syntax
if (typeof event.schedule === 'object') {
if (!event.schedule.rate) {
const errorMessage = [
`Missing "rate" property for schedule event in stateMachine ${stateMachineName}`,
' The correct syntax is: schedule: rate(10 minutes)',
' OR an object with "rate" property.',
' Please check the README for more info.',
].join('');
throw new this.serverless.classes
.Error(errorMessage);
}
ScheduleExpression = event.schedule.rate;
State = 'ENABLED';
if (event.schedule.enabled === false) {
State = 'DISABLED';
}
Input = event.schedule.input;
InputPath = event.schedule.inputPath;
Name = event.schedule.name;
Description = event.schedule.description;

if (Input && InputPath) {
const errorMessage = [
'You can\'t set both input & inputPath properties at the',
'same time for schedule events.',
'Please check the AWS docs for more info',
].join('');
throw new this.serverless.classes
.Error(errorMessage);
}

if (Input && typeof Input === 'object') {
Input = JSON.stringify(Input);
}
if (Input && typeof Input === 'string') {
// escape quotes to favor JSON.parse
Input = Input.replace(/\"/g, '\\"'); // eslint-disable-line
}
} else if (typeof event.schedule === 'string') {
ScheduleExpression = event.schedule;
State = 'ENABLED';
} else {
const errorMessage = [
`Schedule event of stateMachine ${stateMachineName} is not an object nor a string`,
' The correct syntax is: schedule: rate(10 minutes)',
' OR an object with "rate" property.',
' Please check the README for more info.',
].join('');
throw new this.serverless.classes
.Error(errorMessage);
}

const stateMachineLogicalId = this
.getStateMachineLogicalId(stateMachineName, stateMachineObj.name);
const scheduleLogicalId = this
.getScheduleLogicalId(stateMachineName, scheduleNumberInFunction);
const scheduleIamRoleLogicalId = this
.getScheduleToStepFunctionsIamRoleLogicalId(stateMachineName);
const scheduleId = this.getScheduleId(stateMachineName);
const policyName = this.getSchedulePolicyName(stateMachineName);

const scheduleTemplate = `
{
"Type": "AWS::Events::Rule",
"Properties": {
"ScheduleExpression": "${ScheduleExpression}",
"State": "${State}",
${Name ? `"Name": "${Name}",` : ''}
${Description ? `"Description": "${Description}",` : ''}
"Targets": [{
${Input ? `"Input": "${Input}",` : ''}
${InputPath ? `"InputPath": "${InputPath}",` : ''}
"Arn": { "Ref": "${stateMachineLogicalId}" },
"Id": "${scheduleId}",
"RoleArn": {
"Fn::GetAtt": [
"${scheduleIamRoleLogicalId}",
"Arn"
]
}
}]
}
}
`;

const iamRoleTemplate = `
{
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "${policyName}",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"states:StartExecution"
],
"Resource": {
"Ref": "${stateMachineLogicalId}"
}
}
]
}
}
]
}
}
`;

const newScheduleObject = {
[scheduleLogicalId]: JSON.parse(scheduleTemplate),
};

const newPermissionObject = {
[scheduleIamRoleLogicalId]: JSON.parse(iamRoleTemplate),
};

_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
newScheduleObject, newPermissionObject);
}
});
}
});
return BbPromise.resolve();
},
};
Loading