integrations.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. Integrations = new Mongo.Collection('integrations');
  2. /**
  3. * Integration with third-party applications
  4. */
  5. Integrations.attachSchema(
  6. new SimpleSchema({
  7. enabled: {
  8. /**
  9. * is the integration enabled?
  10. */
  11. type: Boolean,
  12. defaultValue: true,
  13. },
  14. title: {
  15. /**
  16. * name of the integration
  17. */
  18. type: String,
  19. optional: true,
  20. },
  21. type: {
  22. /**
  23. * type of the integratation (Default to 'outgoing-webhooks')
  24. */
  25. type: String,
  26. defaultValue: 'outgoing-webhooks',
  27. },
  28. activities: {
  29. /**
  30. * activities the integration gets triggered (list)
  31. */
  32. type: [String],
  33. defaultValue: ['all'],
  34. },
  35. url: {
  36. // URL validation regex (https://mathiasbynens.be/demo/url-regex)
  37. /**
  38. * URL validation regex (https://mathiasbynens.be/demo/url-regex)
  39. */
  40. type: String,
  41. },
  42. token: {
  43. /**
  44. * token of the integration
  45. */
  46. type: String,
  47. optional: true,
  48. },
  49. boardId: {
  50. /**
  51. * Board ID of the integration
  52. */
  53. type: String,
  54. },
  55. createdAt: {
  56. /**
  57. * Creation date of the integration
  58. */
  59. type: Date,
  60. denyUpdate: false,
  61. // eslint-disable-next-line consistent-return
  62. autoValue() {
  63. if (this.isInsert) {
  64. return new Date();
  65. } else if (this.isUpsert) {
  66. return { $setOnInsert: new Date() };
  67. } else {
  68. this.unset();
  69. }
  70. },
  71. },
  72. modifiedAt: {
  73. type: Date,
  74. denyUpdate: false,
  75. // eslint-disable-next-line consistent-return
  76. autoValue() {
  77. if (this.isInsert || this.isUpsert || this.isUpdate) {
  78. return new Date();
  79. } else {
  80. this.unset();
  81. }
  82. },
  83. },
  84. userId: {
  85. /**
  86. * user ID who created the interation
  87. */
  88. type: String,
  89. },
  90. }),
  91. );
  92. Integrations.Const = {
  93. GLOBAL_WEBHOOK_ID: '_global',
  94. ONEWAY: 'outgoing-webhooks',
  95. TWOWAY: 'bidirectional-webhooks',
  96. get WEBHOOK_TYPES() {
  97. return [this.ONEWAY, this.TWOWAY];
  98. },
  99. };
  100. const permissionHelper = {
  101. allow(userId, doc) {
  102. const user = Users.findOne(userId);
  103. const isAdmin = user && Meteor.user().isAdmin;
  104. return isAdmin || allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
  105. },
  106. };
  107. Integrations.allow({
  108. insert(userId, doc) {
  109. return permissionHelper.allow(userId, doc);
  110. },
  111. update(userId, doc) {
  112. return permissionHelper.allow(userId, doc);
  113. },
  114. remove(userId, doc) {
  115. return permissionHelper.allow(userId, doc);
  116. },
  117. fetch: ['boardId'],
  118. });
  119. //INTEGRATIONS REST API
  120. if (Meteor.isServer) {
  121. Meteor.startup(() => {
  122. Integrations._collection.createIndex({ modifiedAt: -1 });
  123. Integrations._collection.createIndex({ boardId: 1 });
  124. });
  125. /**
  126. * @operation get_all_integrations
  127. * @summary Get all integrations in board
  128. *
  129. * @param {string} boardId the board ID
  130. * @return_type [Integrations]
  131. */
  132. JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
  133. req,
  134. res,
  135. ) {
  136. try {
  137. const paramBoardId = req.params.boardId;
  138. Authentication.checkBoardAccess(req.userId, paramBoardId);
  139. const data = Integrations.find(
  140. { boardId: paramBoardId },
  141. { fields: { token: 0 } },
  142. ).map(function(doc) {
  143. return doc;
  144. });
  145. JsonRoutes.sendResult(res, { code: 200, data });
  146. } catch (error) {
  147. JsonRoutes.sendResult(res, {
  148. code: 200,
  149. data: error,
  150. });
  151. }
  152. });
  153. /**
  154. * @operation get_integration
  155. * @summary Get a single integration in board
  156. *
  157. * @param {string} boardId the board ID
  158. * @param {string} intId the integration ID
  159. * @return_type Integrations
  160. */
  161. JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
  162. req,
  163. res,
  164. ) {
  165. try {
  166. const paramBoardId = req.params.boardId;
  167. const paramIntId = req.params.intId;
  168. Authentication.checkBoardAccess(req.userId, paramBoardId);
  169. JsonRoutes.sendResult(res, {
  170. code: 200,
  171. data: Integrations.findOne(
  172. { _id: paramIntId, boardId: paramBoardId },
  173. { fields: { token: 0 } },
  174. ),
  175. });
  176. } catch (error) {
  177. JsonRoutes.sendResult(res, {
  178. code: 200,
  179. data: error,
  180. });
  181. }
  182. });
  183. /**
  184. * @operation new_integration
  185. * @summary Create a new integration
  186. *
  187. * @param {string} boardId the board ID
  188. * @param {string} url the URL of the integration
  189. * @return_type {_id: string}
  190. */
  191. JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(
  192. req,
  193. res,
  194. ) {
  195. try {
  196. const paramBoardId = req.params.boardId;
  197. Authentication.checkBoardAccess(req.userId, paramBoardId);
  198. const id = Integrations.insert({
  199. userId: req.userId,
  200. boardId: paramBoardId,
  201. url: req.body.url,
  202. });
  203. JsonRoutes.sendResult(res, {
  204. code: 200,
  205. data: {
  206. _id: id,
  207. },
  208. });
  209. } catch (error) {
  210. JsonRoutes.sendResult(res, {
  211. code: 200,
  212. data: error,
  213. });
  214. }
  215. });
  216. /**
  217. * @operation edit_integration
  218. * @summary Edit integration data
  219. *
  220. * @param {string} boardId the board ID
  221. * @param {string} intId the integration ID
  222. * @param {string} [enabled] is the integration enabled?
  223. * @param {string} [title] new name of the integration
  224. * @param {string} [url] new URL of the integration
  225. * @param {string} [token] new token of the integration
  226. * @param {string} [activities] new list of activities of the integration
  227. * @return_type {_id: string}
  228. */
  229. JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function(
  230. req,
  231. res,
  232. ) {
  233. try {
  234. const paramBoardId = req.params.boardId;
  235. const paramIntId = req.params.intId;
  236. Authentication.checkBoardAccess(req.userId, paramBoardId);
  237. if (req.body.hasOwnProperty('enabled')) {
  238. const newEnabled = req.body.enabled;
  239. Integrations.direct.update(
  240. { _id: paramIntId, boardId: paramBoardId },
  241. { $set: { enabled: newEnabled } },
  242. );
  243. }
  244. if (req.body.hasOwnProperty('title')) {
  245. const newTitle = req.body.title;
  246. Integrations.direct.update(
  247. { _id: paramIntId, boardId: paramBoardId },
  248. { $set: { title: newTitle } },
  249. );
  250. }
  251. if (req.body.hasOwnProperty('url')) {
  252. const newUrl = req.body.url;
  253. Integrations.direct.update(
  254. { _id: paramIntId, boardId: paramBoardId },
  255. { $set: { url: newUrl } },
  256. );
  257. }
  258. if (req.body.hasOwnProperty('token')) {
  259. const newToken = req.body.token;
  260. Integrations.direct.update(
  261. { _id: paramIntId, boardId: paramBoardId },
  262. { $set: { token: newToken } },
  263. );
  264. }
  265. if (req.body.hasOwnProperty('activities')) {
  266. const newActivities = req.body.activities;
  267. Integrations.direct.update(
  268. { _id: paramIntId, boardId: paramBoardId },
  269. { $set: { activities: newActivities } },
  270. );
  271. }
  272. JsonRoutes.sendResult(res, {
  273. code: 200,
  274. data: {
  275. _id: paramIntId,
  276. },
  277. });
  278. } catch (error) {
  279. JsonRoutes.sendResult(res, {
  280. code: 200,
  281. data: error,
  282. });
  283. }
  284. });
  285. /**
  286. * @operation delete_integration_activities
  287. * @summary Delete subscribed activities
  288. *
  289. * @param {string} boardId the board ID
  290. * @param {string} intId the integration ID
  291. * @param {string} newActivities the activities to remove from the integration
  292. * @return_type Integrations
  293. */
  294. JsonRoutes.add(
  295. 'DELETE',
  296. '/api/boards/:boardId/integrations/:intId/activities',
  297. function(req, res) {
  298. try {
  299. const paramBoardId = req.params.boardId;
  300. const paramIntId = req.params.intId;
  301. const newActivities = req.body.activities;
  302. Authentication.checkBoardAccess(req.userId, paramBoardId);
  303. Integrations.direct.update(
  304. { _id: paramIntId, boardId: paramBoardId },
  305. { $pullAll: { activities: newActivities } },
  306. );
  307. JsonRoutes.sendResult(res, {
  308. code: 200,
  309. data: Integrations.findOne(
  310. { _id: paramIntId, boardId: paramBoardId },
  311. { fields: { _id: 1, activities: 1 } },
  312. ),
  313. });
  314. } catch (error) {
  315. JsonRoutes.sendResult(res, {
  316. code: 200,
  317. data: error,
  318. });
  319. }
  320. },
  321. );
  322. /**
  323. * @operation new_integration_activities
  324. * @summary Add subscribed activities
  325. *
  326. * @param {string} boardId the board ID
  327. * @param {string} intId the integration ID
  328. * @param {string} newActivities the activities to add to the integration
  329. * @return_type Integrations
  330. */
  331. JsonRoutes.add(
  332. 'POST',
  333. '/api/boards/:boardId/integrations/:intId/activities',
  334. function(req, res) {
  335. try {
  336. const paramBoardId = req.params.boardId;
  337. const paramIntId = req.params.intId;
  338. const newActivities = req.body.activities;
  339. Authentication.checkBoardAccess(req.userId, paramBoardId);
  340. Integrations.direct.update(
  341. { _id: paramIntId, boardId: paramBoardId },
  342. { $addToSet: { activities: { $each: newActivities } } },
  343. );
  344. JsonRoutes.sendResult(res, {
  345. code: 200,
  346. data: Integrations.findOne(
  347. { _id: paramIntId, boardId: paramBoardId },
  348. { fields: { _id: 1, activities: 1 } },
  349. ),
  350. });
  351. } catch (error) {
  352. JsonRoutes.sendResult(res, {
  353. code: 200,
  354. data: error,
  355. });
  356. }
  357. },
  358. );
  359. /**
  360. * @operation delete_integration
  361. * @summary Delete integration
  362. *
  363. * @param {string} boardId the board ID
  364. * @param {string} intId the integration ID
  365. * @return_type {_id: string}
  366. */
  367. JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function(
  368. req,
  369. res,
  370. ) {
  371. try {
  372. const paramBoardId = req.params.boardId;
  373. const paramIntId = req.params.intId;
  374. Authentication.checkBoardAccess(req.userId, paramBoardId);
  375. Integrations.direct.remove({ _id: paramIntId, boardId: paramBoardId });
  376. JsonRoutes.sendResult(res, {
  377. code: 200,
  378. data: {
  379. _id: paramIntId,
  380. },
  381. });
  382. } catch (error) {
  383. JsonRoutes.sendResult(res, {
  384. code: 200,
  385. data: error,
  386. });
  387. }
  388. });
  389. }
  390. export default Integrations;