blob: b5b5bde1bfef186d6beea7924c58cf17e584b6a4 [file] [log] [blame]
var CronJob = require('cron').CronJob;
var moment = require('moment');
const common = require('./lib/common');
const Database = require('./lib/Database');
function main(params) {
if (!params.authKey) {
return common.sendError(400, 'no authKey parameter was provided');
}
if (!params.triggerName) {
return common.sendError(400, 'no trigger name parameter was provided');
}
var triggerParts = common.parseQName(params.triggerName);
var triggerID = `${params.authKey}/${triggerParts.namespace}/${triggerParts.name}`;
var triggerURL = `https://${params.apihost}/api/v1/namespaces/${triggerParts.namespace}/triggers/${triggerParts.name}`;
var workers = params.workers instanceof Array ? params.workers : [];
var db;
if (params.__ow_method === "post") {
if (typeof params.trigger_payload === 'string') {
params.trigger_payload = {payload: params.trigger_payload};
}
var newTrigger = {
apikey: params.authKey,
name: triggerParts.name,
namespace: triggerParts.namespace,
payload: params.trigger_payload || {},
maxTriggers: params.maxTriggers || -1,
status: {
'active': true,
'dateChanged': Date.now()
}
};
if (params.fireOnce) {
if (!params.date) {
return common.sendError(400, 'alarms once trigger feed is missing the date parameter');
}
var date = validateDate(params.date, 'date');
if (date !== params.date) {
return common.sendError(400, date);
}
newTrigger.date = date;
}
else {
var cronHandle;
if (params.isInterval) {
if (!params.minutes) {
return common.sendError(400, 'interval trigger feed is missing the minutes parameter');
}
if (+params.minutes !== parseInt(params.minutes)) {
return common.sendError(400, 'the minutes parameter must be an integer');
}
var minutesParam = parseInt(params.minutes);
if (minutesParam <= 0) {
return common.sendError(400, 'the minutes parameter must be an integer greater than zero');
}
newTrigger.minutes = minutesParam;
}
else {
if (!params.cron) {
return common.sendError(400, 'alarms trigger feed is missing the cron parameter');
}
try {
cronHandle = new CronJob(params.cron, function() {});
//validate cron granularity if 5 fields are allowed instead of 6
if (params.limitCronFields && hasSecondsGranularity(params.cron)) {
return common.sendError(400, 'cron pattern is limited to 5 fields with 1 minute as the finest granularity');
}
newTrigger.cron = params.cron;
} catch(ex) {
return common.sendError(400, `cron pattern '${params.cron}' is not valid`);
}
}
if (params.startDate) {
var startDate = validateDate(params.startDate, 'startDate');
if (startDate !== params.startDate) {
return common.sendError(400, startDate);
}
newTrigger.startDate = startDate;
}
else if (params.isInterval) {
//if startDate was not given we will start it 30 seconds
//from now since startDate must be in the future
newTrigger.startDate = Date.now() + (1000 * 30);
}
if (params.maxTriggers && params.stopDate) {
if (params.isInterval) {
return common.sendError(400, 'maxTriggers is not supported for the interval trigger feed');
}
else {
return common.sendError(400, 'maxTriggers is not allowed when the stopDate parameter is specified');
}
}
else if (params.stopDate) {
var stopDate = validateDate(params.stopDate, 'stopDate', newTrigger.startDate);
if (stopDate !== params.stopDate) {
return common.sendError(400, stopDate);
}
newTrigger.stopDate = stopDate;
//verify that the first scheduled trigger fire will occur before the stop date
if (cronHandle && cronHandle.nextDate().isAfter(new Date(params.stopDate))) {
return common.sendError(400, 'the first scheduled trigger fire is not until after the stop date');
}
}
}
return new Promise(function (resolve, reject) {
common.verifyTriggerAuth(triggerURL, params.authKey, false)
.then(() => {
db = new Database(params.DB_URL, params.DB_NAME);
return db.getWorkerID(workers);
})
.then((worker) => {
console.log('trigger will be assigned to worker ' + worker);
newTrigger.worker = worker;
return db.createTrigger(triggerID, newTrigger);
})
.then(() => {
resolve({
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: new Buffer(JSON.stringify({'status': 'success'})).toString('base64')
});
})
.catch(err => {
reject(err);
});
});
}
else if (params.__ow_method === "get") {
return new Promise(function (resolve, reject) {
common.verifyTriggerAuth(triggerURL, params.authKey, false)
.then(() => {
db = new Database(params.DB_URL, params.DB_NAME);
return db.getTrigger(triggerID);
})
.then(doc => {
var body = {
config: {
name: doc.name,
namespace: doc.namespace,
payload: doc.payload
},
status: {
active: doc.status.active,
dateChanged: moment(doc.status.dateChanged).utc().valueOf(),
dateChangedISO: moment(doc.status.dateChanged).utc().format(),
reason: doc.status.reason
}
};
if (doc.date) {
body.config.date = doc.date;
}
else {
body.config.startDate = doc.startDate;
body.config.stopDate = doc.stopDate;
if (doc.minutes) {
body.config.minutes = doc.minutes;
}
else {
body.config.cron = doc.cron;
}
}
resolve({
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: new Buffer(JSON.stringify(body)).toString('base64')
});
})
.catch(err => {
reject(err);
});
});
}
else if (params.__ow_method === "put") {
return new Promise(function (resolve, reject) {
var updatedParams = {};
common.verifyTriggerAuth(triggerURL, params.authKey, false)
.then(() => {
db = new Database(params.DB_URL, params.DB_NAME);
return db.getTrigger(triggerID);
})
.then(trigger => {
if (trigger.status && trigger.status.reason && trigger.status.reason.kind === 'ADMIN') {
return reject(common.sendError(400, `${params.triggerName} cannot be updated because it was disabled by an admin. Please contact support for further assistance`));
}
if (params.trigger_payload) {
updatedParams.payload = common.constructPayload(params.trigger_payload);
}
if (trigger.date) {
if (params.date) {
var date = validateDate(params.date, 'date');
if (date !== params.date) {
return reject(common.sendError(400, date));
}
updatedParams.date = date;
}
}
else {
if (trigger.minutes) {
if (params.minutes) {
if (+params.minutes !== parseInt(params.minutes)) {
return reject(common.sendError(400, 'the minutes parameter must be an integer'));
}
var minutesParam = parseInt(params.minutes);
if (minutesParam <= 0) {
return reject(common.sendError(400, 'the minutes parameter must be an integer greater than zero'));
}
updatedParams.minutes = minutesParam;
}
}
else {
if (params.cron) {
try {
new CronJob(params.cron, function() {});
//validate cron granularity if 5 fields are allowed instead of 6
if (params.limitCronFields && hasSecondsGranularity(params.cron)) {
return common.sendError(400, 'cron pattern is limited to 5 fields with 1 minute as the finest granularity');
}
} catch (ex) {
return reject(common.sendError(400, `cron pattern '${params.cron}' is not valid`));
}
updatedParams.cron = params.cron;
}
}
if (params.startDate) {
var startDate = validateDate(params.startDate, 'startDate');
if (startDate !== params.startDate) {
return reject(common.sendError(400, startDate));
}
updatedParams.startDate = startDate;
}
if (params.stopDate) {
var stopDate = validateDate(params.stopDate, 'stopDate', params.startDate || trigger.startDate);
if (stopDate !== params.stopDate) {
return reject(common.sendError(400, stopDate));
}
updatedParams.stopDate = stopDate;
}
else if (params.startDate && trigger.stopDate) {
//need to verify that new start date is before existing stop date
if (new Date(params.startDate).getTime() >= new Date(trigger.stopDate).getTime()) {
return reject(common.sendError(400, `startDate parameter '${params.startDate}' must be less than the stopDate parameter '${trigger.stopDate}'`));
}
}
}
if (Object.keys(updatedParams).length === 0) {
return reject(common.sendError(400, 'no updatable parameters were specified'));
}
return db.disableTrigger(trigger._id, trigger, 0, 'updating');
})
.then(triggerID => {
return db.getTrigger(triggerID, false);
})
.then(trigger => {
return db.updateTrigger(trigger._id, trigger, updatedParams, 0);
})
.then(() => {
resolve({
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: new Buffer(JSON.stringify({'status': 'success'})).toString('base64')
});
})
.catch(err => {
reject(err);
});
});
}
else if (params.__ow_method === "delete") {
return new Promise(function (resolve, reject) {
common.verifyTriggerAuth(triggerURL, params.authKey, true)
.then(() => {
db = new Database(params.DB_URL, params.DB_NAME);
return db.getTrigger(triggerID);
})
.then(trigger => {
return db.disableTrigger(trigger._id, trigger, 0, 'deleting');
})
.then(triggerID => {
return db.deleteTrigger(triggerID, 0);
})
.then(() => {
resolve({
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: new Buffer(JSON.stringify({'status': 'success'})).toString('base64')
});
})
.catch(err => {
reject(err);
});
});
}
else {
return common.sendError(400, 'unsupported lifecycleEvent');
}
}
function validateDate(date, paramName, startDate) {
var dateObject = new Date(date);
if (isNaN(dateObject.getTime())) {
return `${paramName} parameter '${date}' is not a valid Date`;
}
else if (Date.now() >= dateObject.getTime()) {
return `${paramName} parameter '${date}' must be in the future`;
}
else if (startDate && dateObject <= new Date(startDate).getTime()) {
return `${paramName} parameter '${date}' must be greater than the startDate parameter '${startDate}'`;
}
else {
return date;
}
}
function hasSecondsGranularity(cron) {
var fields = (cron + '').trim().split(/\s+/);
if (fields.length > 5 && fields[fields.length - 6] !== '0') {
return true;
}
return false;
}
exports.main = main;