8000 Improve integrations by andresmanelli · Pull Request #1199 · wekan/wekan · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Improve integrations #1199

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

Merged
merged 3 commits into from
Sep 1, 2017
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
8000
Diff view
Diff view
20 changes: 15 additions & 5 deletions client/components/boards/boardHeader.jade
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,21 @@ template(name="archiveBoardPopup")
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}

template(name="outgoingWebhooksPopup")
form
each integrations
form.integration-form
if title
h4 {{title}}
else
h4 {{_ 'no-name'}}
label
| URL
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
input(type="hidden" value=_id name="id")
input.primary.wide(type="submit" value="{{_ 'save'}}")
form.integration-form
h4
| {{_ 'new-integration'}}
label
| URL
if integration.enabled
input.js-outgoing-webhooks-url(type="text" value=integration.url autofocus)
else
input.js-outgoing-webhooks-url(type="text" autofocus)
input.js-outgoing-webhooks-url(type="text" name="url" autofocus)
input.primary.wide(type="submit" value="{{_ 'save'}}")
27 changes: 16 additions & 11 deletions client/components/boards/boardHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,39 +241,44 @@ BlazeComponent.extendComponent({
}).register('boardChangeWatchPopup');

BlazeComponent.extendComponent({
integration() {
integrations() {
const boardId = Session.get('currentBoard');
return Integrations.findOne({ boardId: `${boardId}` });
return Integrations.find({ boardId: `${boardId}` }).fetch();
},

integration(id) {
const boardId = Session.get('currentBoard');
return Integrations.findOne({ _id: id, boardId: `${boardId}` });
},

events() {
return [{
'submit'(evt) {
evt.preventDefault();
const url = this.find('.js-outgoing-webhooks-url').value.trim();
const url = evt.target.url.value;
const boardId = Session.get('currentBoard');
const integration = this.integration();
if (integration) {
let id = null;
let integration = null;
if (evt.target.id) {
id = evt.target.id.value;
integration = this.integration(id);
if (url) {
Integrations.update(integration._id, {
$set: {
enabled: true,
url: `${url}`,
},
});
} else {
Integrations.update(integration._id, {
$set: {
enabled: false,
},
});
Integrations.remove(integration._id);
}
} else if (url) {
Integrations.insert({
userId: Meteor.userId(),
enabled: true,
type: 'outgoing-webhooks',
url: `${url}`,
boardId: `${boardId}`,
activities: ['all'],
});
}
Popup.close();
Expand Down
3 changes: 3 additions & 0 deletions client/components/boards/boardHeader.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.integration-form
padding: 5px
border-bottom: 1px solid #ccc
2 changes: 2 additions & 0 deletions i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@
"error-notAuthorized": "You are not authorized to view this page.",
"outgoing-webhooks": "Outgoing Webhooks",
"outgoingWebhooksPopup-title": "Outgoing Webhooks",
"new-integration": "New integration",
"no-name": "(Unknown)",
"Wekan_version": "Wekan version",
"Node_version": "Node version",
"OS_Arch": "OS Arch",
Expand Down
6 changes: 3 additions & 3 deletions models/activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ if (Meteor.isServer) {
Notifications.notify(user, title, description, params);
});

const integration = Integrations.findOne({ boardId: board._id, type: 'outgoing-webhooks', enabled: true });
if (integration) {
Meteor.call('outgoingWebhooks', integration, description, params);
const integrations = Integrations.find({ boardId: board._id, type: 'outgoing-webhooks', enabled: true, activities: { '$in': [description, 'all'] } }).fetch();
if (integrations.length > 0) {
Meteor.call('outgoingWebhooks', integrations, description, params);
}
});
}
146 changes: 141 additions & 5 deletions models/integrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Integrations.attachSchema(new SimpleSchema({
},
type: {
type: String,
defaultValue: 'outgoing-webhooks',
},
activities: {
type: [String],
defaultValue: ['all'],
},
url: { // URL validation regex (https://mathiasbynens.be/demo/url-regex)
type: String,
Expand All @@ -35,11 +40,6 @@ Integrations.attachSchema(new SimpleSchema({
},
userId: {
type: String,
autoValue() { // eslint-disable-line consistent-return
if (this.isInsert || this.isUpdate) {
return this.userId;
}
},
},
}));

Expand All @@ -50,5 +50,141 @@ Integrations.allow({
update(userId, doc) {
return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});

//INTEGRATIONS REST API
if (Meteor.isServer) {
// Get all integrations in board
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(req, res, next) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);

const data = Integrations.find({ boardId: paramBoardId }, { fields: { token: 0 } }).map(function(doc) {
return doc;
});

JsonRoutes.sendResult(res, {code: 200, data});
});

// Get a single integration in board
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(req, res, next) {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
Authentication.checkBoardAccess(req.userId, paramBoardId);

JsonRoutes.sendResult(res, {
code: 200,
data: Integrations.findOne({ _id: paramIntId, boardId: paramBoardId }, { fields: { token: 0 } }),
});
});

// Create a new integration
JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(req, res, next) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);

const id = Integrations.insert({
userId: req.userId,
boardId: paramBoardId,
url: req.body.url,
});

JsonRoutes.sendResult(res, {
code: 200,
data: {
_id: id,
},
});
});

// Edit integration data
JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function (req, res, next) {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
Authentication.checkBoardAccess(req.userId, paramBoardId);

if (req.body.hasOwnProperty('enabled')) {
const newEnabled = req.body.enabled;
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$set: {enabled: newEnabled}});
}
if (req.body.hasOwnProperty('title')) {
const newTitle = req.body.title;
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$set: {title: newTitle}});
}
if (req.body.hasOwnProperty('url')) {
const newUrl = req.body.url;
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$set: {url: newUrl}});
}
if (req.body.hasOwnProperty('token')) {
const newToken = req.body.token;
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$set: {token: newToken}});
}
if (req.body.hasOwnProperty('activities')) {
const newActivities = req.body.activities;
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$set: {activities: newActivities}});
}

JsonRoutes.sendResult(res, {
code: 200,
data: {
_id: paramIntId,
},
});
});

// Delete subscribed activities
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId/activities', function (req, res, next) {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
const newActivities = req.body.activities;
Authentication.checkBoardAccess(req.userId, paramBoardId);

Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$pullAll: {activities: newActivities}});

JsonRoutes.sendResult(res, {
code: 200,
data: Integrations.findOne({_id: paramIntId, boardId: paramBoardId}, { fields: {_id: 1, activities: 1}}),
});
});

// Add subscribed activities
JsonRoutes.add('POST', '/api/boards/:boardId/integrations/:intId/activities', function (req, res, next) {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
const newActivities = req.body.activities;
Authentication.checkBoardAccess(req.userId, paramBoardId);

Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
{$addToSet: {activities: { $each: newActivities}}});

JsonRoutes.sendResult(res, {
code: 200,
data: Integrations.findOne({_id: paramIntId, boardId: paramBoardId}, { fields: {_id: 1, activities: 1}}),
});
});

// Delete integration
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function (req, res, next) {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
Authentication.checkBoardAccess(req.userId, paramBoardId);

Integrations.direct.remove({_id: paramIntId, boardId: paramBoardId});
JsonRoutes.sendResult(res, {
code: 200,
data: {
_id: paramIntId,
},
});
});
}
23 changes: 13 additions & 10 deletions server/notifications/outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const postCatchError = Meteor.wrapAsync((url, options, resolve) => {
});

Meteor.methods({
outgoingWebhooks(integration, description, params) {
check(integration, Object);
outgoingWebhooks(integrations, description, params) {
check(integrations, Array);
check(description, String);
check(params, Object);

Expand All @@ -19,7 +19,8 @@ Meteor.methods({
if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
});

const user = Users.findOne(integration.userId);
const userId = (params.userId)?params.userId:integrations[0].userId;
const user = Users.findOne(userId);
const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;

if (text.length === 0) return;
Expand All @@ -31,7 +32,7 @@ Meteor.methods({
['cardId', 'listId', 'oldListId', 'boardId'].forEach((key) => {
if (params[key]) value[key] = params[key];
});
value.$description = description;
value.description = description;

const options = {
headers: {
Expand All @@ -41,12 +42,14 @@ Meteor.methods({
data: value,
};

const response = postCatchError(integration.url, options);
integrations.forEach((integration) => {
const response = postCatchError(integration.url, options);

if (response && response.statusCode && response.statusCode === 200) {
return true; // eslint-disable-line consistent-return
} else {
throw new Meteor.Error('error-invalid-webhook-response');
}
if (response && response.statusCode && response.statusCode === 200) {
return true; // eslint-disable-line consistent-return
} else {
throw new Meteor.Error('error-invalid-webhook-response');
}
});
},
});
0