mirror of https://github.com/wekan/wekan
The Open Source kanban (built with Meteor). Keep variable/table/field names camelCase. For translations, only add Pull Request changes to wekan/i18n/en.i18n.json , other translations are done at https://transifex.com/wekan/wekan only.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
7.2 KiB
200 lines
7.2 KiB
import { Meteor } from 'meteor/meteor';
|
|
import { WebApp } from 'meteor/webapp';
|
|
import { Authentication } from '/server/authentication';
|
|
import { sendJsonResult } from '/server/apiMiddleware';
|
|
import { ReactiveCache } from '/imports/reactiveCache';
|
|
import Rules from '/models/rules';
|
|
import Triggers from '/models/triggers';
|
|
import Actions from '/models/actions';
|
|
|
|
// REST API for board automation Rules (add / edit / remove).
|
|
//
|
|
// A rule links a trigger (the event/schedule) to an action (what happens). The
|
|
// API embeds the full trigger and action inline so a rule is self-contained:
|
|
//
|
|
// POST /api/boards/:boardId/rules
|
|
// body: { "title": "...", "trigger": { "activityType": "...", ... },
|
|
// "action": { "actionType": "...", ... } }
|
|
// GET /api/boards/:boardId/rules
|
|
// GET /api/boards/:boardId/rules/:ruleId
|
|
// PUT /api/boards/:boardId/rules/:ruleId
|
|
// body: { "title"?, "trigger"?: {...}, "action"?: {...} }
|
|
// DELETE /api/boards/:boardId/rules/:ruleId
|
|
|
|
const STRIP = ['_id', 'boardId', 'createdAt', 'modifiedAt', 'updatedAt'];
|
|
|
|
function strip(doc) {
|
|
const out = {};
|
|
Object.keys(doc || {}).forEach(k => {
|
|
if (!STRIP.includes(k)) out[k] = doc[k];
|
|
});
|
|
return out;
|
|
}
|
|
|
|
async function serializeRule(rule) {
|
|
const trigger = await ReactiveCache.getTrigger(rule.triggerId);
|
|
const action = await ReactiveCache.getAction(rule.actionId);
|
|
return {
|
|
_id: rule._id,
|
|
title: rule.title,
|
|
trigger: trigger ? strip(trigger) : null,
|
|
action: action ? strip(action) : null,
|
|
};
|
|
}
|
|
|
|
if (Meteor.isServer) {
|
|
/**
|
|
* @operation get_board_rules
|
|
* @tag Rules
|
|
* @summary Get the list of automation rules of a board
|
|
*
|
|
* @param {string} boardId the board ID
|
|
* @return_type [{_id: string, title: string, trigger: object, action: object}]
|
|
*/
|
|
WebApp.handlers.get('/api/boards/:boardId/rules', async function(req, res) {
|
|
try {
|
|
const paramBoardId = req.params.boardId;
|
|
await Authentication.checkBoardAccess(req.userId, paramBoardId);
|
|
const rules = await ReactiveCache.getRules({ boardId: paramBoardId });
|
|
const data = [];
|
|
for (const rule of rules) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
data.push(await serializeRule(rule));
|
|
}
|
|
sendJsonResult(res, { code: 200, data });
|
|
} catch (error) {
|
|
sendJsonResult(res, { code: error.statusCode || 500, data: error });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @operation get_board_rule
|
|
* @tag Rules
|
|
* @summary Get a single automation rule
|
|
*
|
|
* @param {string} boardId the board ID
|
|
* @param {string} ruleId the rule ID
|
|
* @return_type {_id: string, title: string, trigger: object, action: object}
|
|
*/
|
|
WebApp.handlers.get('/api/boards/:boardId/rules/:ruleId', async function(req, res) {
|
|
try {
|
|
const paramBoardId = req.params.boardId;
|
|
await Authentication.checkBoardAccess(req.userId, paramBoardId);
|
|
const rule = await ReactiveCache.getRule({
|
|
_id: req.params.ruleId,
|
|
boardId: paramBoardId,
|
|
});
|
|
if (!rule) throw new Meteor.Error('not-found', 'Rule not found');
|
|
sendJsonResult(res, { code: 200, data: await serializeRule(rule) });
|
|
} catch (error) {
|
|
sendJsonResult(res, { code: error.statusCode || 500, data: error });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @operation new_board_rule
|
|
* @tag Rules
|
|
* @summary Add an automation rule to a board
|
|
*
|
|
* @description The trigger and action are embedded inline. See the Rules
|
|
* documentation for the available trigger activityTypes and action actionTypes.
|
|
*
|
|
* @param {string} boardId the board ID
|
|
* @param {string} title the rule title
|
|
* @param {object} trigger the trigger document (must include activityType)
|
|
* @param {object} action the action document (must include actionType)
|
|
* @return_type {_id: string, triggerId: string, actionId: string}
|
|
*/
|
|
WebApp.handlers.post('/api/boards/:boardId/rules', async function(req, res) {
|
|
try {
|
|
const paramBoardId = req.params.boardId;
|
|
await Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
|
|
const { title, trigger, action } = req.body;
|
|
if (!trigger || !action) {
|
|
throw new Meteor.Error('bad-request', 'trigger and action are required');
|
|
}
|
|
const triggerId = await Triggers.insertAsync({ ...strip(trigger), boardId: paramBoardId });
|
|
const actionId = await Actions.insertAsync({ ...strip(action), boardId: paramBoardId });
|
|
const ruleId = await Rules.insertAsync({
|
|
title: title || 'API rule',
|
|
triggerId,
|
|
actionId,
|
|
boardId: paramBoardId,
|
|
});
|
|
sendJsonResult(res, { code: 200, data: { _id: ruleId, triggerId, actionId } });
|
|
} catch (error) {
|
|
sendJsonResult(res, { code: error.statusCode || 500, data: error });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @operation edit_board_rule
|
|
* @tag Rules
|
|
* @summary Edit an automation rule
|
|
*
|
|
* @description Any of title, trigger and action may be supplied; the trigger
|
|
* and action documents are replaced ($set) when present.
|
|
*
|
|
* @param {string} boardId the board ID
|
|
* @param {string} ruleId the rule ID
|
|
* @param {string} [title] the new rule title
|
|
* @param {object} [trigger] the new trigger document
|
|
* @param {object} [action] the new action document
|
|
* @return_type {_id: string}
|
|
*/
|
|
WebApp.handlers.put('/api/boards/:boardId/rules/:ruleId', async function(req, res) {
|
|
try {
|
|
const paramBoardId = req.params.boardId;
|
|
await Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
|
|
const rule = await ReactiveCache.getRule({
|
|
_id: req.params.ruleId,
|
|
boardId: paramBoardId,
|
|
});
|
|
if (!rule) throw new Meteor.Error('not-found', 'Rule not found');
|
|
|
|
if (typeof req.body.title === 'string') {
|
|
await Rules.updateAsync(rule._id, { $set: { title: req.body.title } });
|
|
}
|
|
if (req.body.trigger) {
|
|
await Triggers.updateAsync(rule.triggerId, {
|
|
$set: { ...strip(req.body.trigger), boardId: paramBoardId },
|
|
});
|
|
}
|
|
if (req.body.action) {
|
|
await Actions.updateAsync(rule.actionId, {
|
|
$set: { ...strip(req.body.action), boardId: paramBoardId },
|
|
});
|
|
}
|
|
sendJsonResult(res, { code: 200, data: { _id: rule._id } });
|
|
} catch (error) {
|
|
sendJsonResult(res, { code: error.statusCode || 500, data: error });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @operation delete_board_rule
|
|
* @tag Rules
|
|
* @summary Remove an automation rule (and its trigger and action)
|
|
*
|
|
* @param {string} boardId the board ID
|
|
* @param {string} ruleId the rule ID
|
|
* @return_type {_id: string}
|
|
*/
|
|
WebApp.handlers.delete('/api/boards/:boardId/rules/:ruleId', async function(req, res) {
|
|
try {
|
|
const paramBoardId = req.params.boardId;
|
|
await Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
|
|
const rule = await ReactiveCache.getRule({
|
|
_id: req.params.ruleId,
|
|
boardId: paramBoardId,
|
|
});
|
|
if (!rule) throw new Meteor.Error('not-found', 'Rule not found');
|
|
await Rules.removeAsync(rule._id);
|
|
await Triggers.removeAsync(rule.triggerId);
|
|
await Actions.removeAsync(rule.actionId);
|
|
sendJsonResult(res, { code: 200, data: { _id: rule._id } });
|
|
} catch (error) {
|
|
sendJsonResult(res, { code: error.statusCode || 500, data: error });
|
|
}
|
|
});
|
|
}
|
|
|