NotificationService.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * Licensed Materials - Property of IBM
  3. *
  4. * IBM Cognos Products: SHARE
  5. *
  6. * (C) Copyright IBM Corp. 2015, 2019
  7. *
  8. * US Government Users Restricted Rights - Use, duplication or disclosure
  9. * restricted by GSA ADP Schedule Contract with IBM Corp.
  10. */
  11. define([
  12. 'underscore',
  13. 'bi/glass/core/Events',
  14. 'jquery',
  15. 'bi/sharecommon/utils/translator'
  16. ], function(_,Events, $, t) {
  17. var SERVER_URI = 'v1/reports/';
  18. var SERVICE_URL = 'v1/notifications';
  19. var DISPLAY_DEFAULT = 20;
  20. var OFFSET_DEFAULT = 0;
  21. var POLLING_INTERVAL = 60000; // 1 minute
  22. var NOTIFICATION_STATE_ENDPOINT = '/notification_state';
  23. var NotificationService = Events.extend({
  24. /**
  25. * @classdesc Notification service class giving access to events related
  26. * to notifications
  27. * @constructs
  28. * @public
  29. * @param {Object} options - set of initial properties
  30. */
  31. init: function(options) {
  32. NotificationService.inherited('init', this, arguments);
  33. $.extend(true, this, options);
  34. this.unreadIds = [];
  35. // These are for the 'ajax-hijack polling'
  36. this.canRun = true;
  37. this.excludeUrlsRegEx = /login/;
  38. },
  39. /**
  40. * Called by glass when service is registered
  41. */
  42. initialize: function(glassContext) {
  43. this.glassContext = glassContext;
  44. },
  45. /**
  46. * This function is used to know whether or not the ajax service can do
  47. * a poll for new messages. If the (canRun) flag is true, set it to
  48. * false, create a timer that will automatically set it to true again
  49. * and return true. Otherwise (the flag is false) return false
  50. *
  51. * @returns boolean
  52. */
  53. allowedToRun: function() {
  54. if (this.canRun) {
  55. this.canRun = false;
  56. this._resetCanRun();
  57. return true;
  58. }
  59. return false;
  60. },
  61. _resetCanRun: function() {
  62. setTimeout(function() {
  63. this.canRun = true;
  64. }.bind(this), POLLING_INTERVAL);
  65. },
  66. pollForNewNotifications: function(options) {
  67. if (typeof options !== 'undefined') {
  68. var noNotificationPoll = options.noNotificationPoll;
  69. var url = options.url;
  70. var foundRegEx = this.excludeUrlsRegEx.exec(url);
  71. if (foundRegEx === null && this.allowedToRun() && (typeof noNotificationPoll == 'undefined' || (typeof noNotificationPoll != 'undefined' && noNotificationPoll !== true))) {
  72. return this.getNewNotifications();
  73. }
  74. }
  75. },
  76. /**
  77. * Get a list of unread notifications starting from the newest for a
  78. * maximum amount
  79. *
  80. * @param maxFetch max number of notifications to get
  81. * @returns promise
  82. */
  83. getNewNotifications: function(maxFetch) {
  84. var max = maxFetch || DISPLAY_DEFAULT;
  85. var url = SERVICE_URL + '?unread-only=true&max=' + max + '&offset=0';
  86. return this.glassContext.getCoreSvc('.Ajax').ajax({
  87. type: 'GET',
  88. url: url,
  89. contentType: 'application/json; charset=utf-8',
  90. dataType: 'json',
  91. noNotificationPoll: true
  92. }).then(function(response) {
  93. var data = response.data;
  94. this._updateUnreadIds(data);
  95. this.trigger('notifications:new', data);
  96. return data;
  97. }.bind(this)).catch(function(err) {
  98. var error = {
  99. message: this.retrieveErrorMessage({
  100. result: err.error,
  101. defaultMessage: t.translate('error_get_notifications')
  102. })
  103. };
  104. throw error;
  105. }.bind(this));
  106. },
  107. /**
  108. * Get a list of notifications starting from an offset amount for a
  109. * maximum amount
  110. *
  111. * @param maxFetch The maximum number of notifications to be returned
  112. * @param offsetFetch The offset number to have a new starting point
  113. * instead of at zero (0)
  114. * @returns promise
  115. */
  116. getNotifications: function(maxFetch, offsetFetch) {
  117. var max = maxFetch || DISPLAY_DEFAULT;
  118. var offset = offsetFetch || OFFSET_DEFAULT;
  119. var url = SERVICE_URL + '?max=' + max + '&offset=' + offset;
  120. return this.glassContext.getCoreSvc('.Ajax').ajax({
  121. type: 'GET',
  122. url: url,
  123. contentType: 'application/json; charset=utf-8',
  124. dataType: 'json',
  125. noNotificationPoll: true
  126. }).then(function(response) {
  127. var data = response.data;
  128. this._updateUnreadIds(data);
  129. this.trigger('notifications:received', data);
  130. return data;
  131. }.bind(this)).catch(function(err) {
  132. var error = {
  133. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_notifications') })
  134. };
  135. this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  136. throw error;
  137. }.bind(this));
  138. },
  139. /**
  140. * Mark a notification as read
  141. *
  142. * @param messageId The id of the message being marked as read
  143. * @returns promise
  144. */
  145. markRead: function(messageId) {
  146. var url = SERVICE_URL + '/' + messageId;
  147. return this.glassContext.getCoreSvc('.Ajax').ajax({
  148. type: 'PUT',
  149. url: url,
  150. contentType: 'application/json; charset=utf-8',
  151. dataType: 'json',
  152. noNotificationPoll: true
  153. }).then(function(response) {
  154. var data = response.data;
  155. this._updateUnreadIds([data]);
  156. this.trigger('notifications:read', data);
  157. return data;
  158. }.bind(this))
  159. .catch(function(err) {
  160. var error = {
  161. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_put_notification') })
  162. };
  163. this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  164. throw error;
  165. }.bind(this));
  166. },
  167. /**
  168. * Retrieves information for the specified notification
  169. *
  170. * @param messageId The id of the message
  171. * @returns promise
  172. */
  173. getSpecificNotification: function(messageId) {
  174. var url = SERVICE_URL + '/' + messageId;
  175. return this.glassContext.getCoreSvc('.Ajax').ajax({
  176. type: 'GET',
  177. url: url,
  178. contentType: 'application/json; charset=utf-8',
  179. dataType: 'json'
  180. }).then(function(response) {
  181. return response.data;
  182. }).catch(function(err) {
  183. var error = {
  184. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_notification') })
  185. };
  186. this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  187. throw error;
  188. }.bind(this));
  189. },
  190. /**
  191. * Deletes the specified notification
  192. *
  193. * @param context The id of the message to delete
  194. * @returns promise
  195. */
  196. deleteNotification: function(messageId) {
  197. var url = SERVICE_URL + '/' + messageId;
  198. return this.glassContext.getCoreSvc('.Ajax').ajax({
  199. type: 'DELETE',
  200. url: url,
  201. contentType: 'application/json; charset=utf-8',
  202. dataType: 'json',
  203. noNotificationPoll: true
  204. }).then(function(response) {
  205. var data = response.data;
  206. var deleted = {
  207. id: messageId,
  208. result: data
  209. };
  210. this._updateUnreadIds([deleted], true);
  211. this.trigger('notifications:deleted', deleted);
  212. return data;
  213. }.bind(this)).catch(function(err) {
  214. var error = {
  215. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_delete_notification') })
  216. };
  217. this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  218. throw error;
  219. }.bind(this));
  220. },
  221. retrieveErrorMessage: function(input) {
  222. input = input || {};
  223. var message = input.defaultMessage;
  224. try {
  225. var text = $.parseJSON(input.result.responseText);
  226. message = text.error || text.message;
  227. } catch (e) { /*continue*/ }
  228. return message;
  229. },
  230. getNotificationState: function(reportId) {
  231. var server_URL = SERVER_URI + reportId + NOTIFICATION_STATE_ENDPOINT;
  232. return this.glassContext.getCoreSvc('.Ajax').ajax({
  233. type: 'GET',
  234. url: server_URL,
  235. contentType: 'application/json; charset=utf-8',
  236. dataType: 'json'
  237. }).then(function(response) {
  238. return response.data;
  239. }).catch(function(err) {
  240. var error = {
  241. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_report_notification_state') })
  242. };
  243. //NotificationService should not be presenting an error message. It throws an error, which should be handled appropriately by a calling View component.
  244. //this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  245. throw error;
  246. }.bind(this));
  247. },
  248. updateNotificationState: function(descriptor) {
  249. var server_URL = SERVER_URI + descriptor.reportId + NOTIFICATION_STATE_ENDPOINT;
  250. var jsonData = {
  251. 'state': descriptor.state
  252. };
  253. return this.glassContext.getCoreSvc('.Ajax').ajax({
  254. url: server_URL,
  255. type: 'PUT',
  256. headers: {
  257. Accept: 'application/vnd.ibm.bi.notification-state+json',
  258. 'Content-Type': 'application/vnd.ibm.bi.notification-state+json'
  259. },
  260. dataType: 'json',
  261. data: JSON.stringify(jsonData)
  262. }).then(function(response) {
  263. return response.data;
  264. }).catch(function(err) {
  265. var error = {
  266. message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_update_report_notification_state') })
  267. };
  268. this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share'));
  269. throw err;
  270. }.bind(this));
  271. },
  272. /**
  273. * Function to update the unreadIds array and then trigger an update
  274. * event to update the notification button badge
  275. *
  276. * @param data an array of data objects to use to update the array of
  277. * unreadIds
  278. * @param remove flag to force the removal of an id in the unreadIds
  279. * array
  280. */
  281. _updateUnreadIds: function(data, remove) {
  282. if (Array.isArray(data)) {
  283. var dataLength = data.length;
  284. var forceRemove = remove || false;
  285. for (var i = 0; i < dataLength; i++) {
  286. if (forceRemove || (!data[i].unread && _.contains(this.unreadIds, data[i].id))) {
  287. this._removeUnreadId(data[i].id);
  288. } else if (data[i].unread && !_.contains(this.unreadIds, data[i].id)) {
  289. this.unreadIds.push(data[i].id);
  290. }
  291. }
  292. this.trigger('notifications:newCount', this.unreadIds.length);
  293. }
  294. },
  295. _removeUnreadId: function(messageId) {
  296. var index = this.unreadIds.indexOf(messageId);
  297. if (index >= 0) {
  298. this.unreadIds.splice(index, 1);
  299. }
  300. }
  301. });
  302. return NotificationService;
  303. });