/* * Licensed Materials - Property of IBM * * IBM Cognos Products: SHARE * * (C) Copyright IBM Corp. 2015, 2019 * * US Government Users Restricted Rights - Use, duplication or disclosure * restricted by GSA ADP Schedule Contract with IBM Corp. */ define([ 'underscore', 'bi/glass/core/Events', 'jquery', 'bi/sharecommon/utils/translator' ], function(_,Events, $, t) { var SERVER_URI = 'v1/reports/'; var SERVICE_URL = 'v1/notifications'; var DISPLAY_DEFAULT = 20; var OFFSET_DEFAULT = 0; var POLLING_INTERVAL = 60000; // 1 minute var NOTIFICATION_STATE_ENDPOINT = '/notification_state'; var NotificationService = Events.extend({ /** * @classdesc Notification service class giving access to events related * to notifications * @constructs * @public * @param {Object} options - set of initial properties */ init: function(options) { NotificationService.inherited('init', this, arguments); $.extend(true, this, options); this.unreadIds = []; // These are for the 'ajax-hijack polling' this.canRun = true; this.excludeUrlsRegEx = /login/; }, /** * Called by glass when service is registered */ initialize: function(glassContext) { this.glassContext = glassContext; }, /** * This function is used to know whether or not the ajax service can do * a poll for new messages. If the (canRun) flag is true, set it to * false, create a timer that will automatically set it to true again * and return true. Otherwise (the flag is false) return false * * @returns boolean */ allowedToRun: function() { if (this.canRun) { this.canRun = false; this._resetCanRun(); return true; } return false; }, _resetCanRun: function() { setTimeout(function() { this.canRun = true; }.bind(this), POLLING_INTERVAL); }, pollForNewNotifications: function(options) { if (typeof options !== 'undefined') { var noNotificationPoll = options.noNotificationPoll; var url = options.url; var foundRegEx = this.excludeUrlsRegEx.exec(url); if (foundRegEx === null && this.allowedToRun() && (typeof noNotificationPoll == 'undefined' || (typeof noNotificationPoll != 'undefined' && noNotificationPoll !== true))) { return this.getNewNotifications(); } } }, /** * Get a list of unread notifications starting from the newest for a * maximum amount * * @param maxFetch max number of notifications to get * @returns promise */ getNewNotifications: function(maxFetch) { var max = maxFetch || DISPLAY_DEFAULT; var url = SERVICE_URL + '?unread-only=true&max=' + max + '&offset=0'; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'GET', url: url, contentType: 'application/json; charset=utf-8', dataType: 'json', noNotificationPoll: true }).then(function(response) { var data = response.data; this._updateUnreadIds(data); this.trigger('notifications:new', data); return data; }.bind(this)).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err.error, defaultMessage: t.translate('error_get_notifications') }) }; throw error; }.bind(this)); }, /** * Get a list of notifications starting from an offset amount for a * maximum amount * * @param maxFetch The maximum number of notifications to be returned * @param offsetFetch The offset number to have a new starting point * instead of at zero (0) * @returns promise */ getNotifications: function(maxFetch, offsetFetch) { var max = maxFetch || DISPLAY_DEFAULT; var offset = offsetFetch || OFFSET_DEFAULT; var url = SERVICE_URL + '?max=' + max + '&offset=' + offset; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'GET', url: url, contentType: 'application/json; charset=utf-8', dataType: 'json', noNotificationPoll: true }).then(function(response) { var data = response.data; this._updateUnreadIds(data); this.trigger('notifications:received', data); return data; }.bind(this)).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_notifications') }) }; this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw error; }.bind(this)); }, /** * Mark a notification as read * * @param messageId The id of the message being marked as read * @returns promise */ markRead: function(messageId) { var url = SERVICE_URL + '/' + messageId; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'PUT', url: url, contentType: 'application/json; charset=utf-8', dataType: 'json', noNotificationPoll: true }).then(function(response) { var data = response.data; this._updateUnreadIds([data]); this.trigger('notifications:read', data); return data; }.bind(this)) .catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_put_notification') }) }; this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw error; }.bind(this)); }, /** * Retrieves information for the specified notification * * @param messageId The id of the message * @returns promise */ getSpecificNotification: function(messageId) { var url = SERVICE_URL + '/' + messageId; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'GET', url: url, contentType: 'application/json; charset=utf-8', dataType: 'json' }).then(function(response) { return response.data; }).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_notification') }) }; this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw error; }.bind(this)); }, /** * Deletes the specified notification * * @param context The id of the message to delete * @returns promise */ deleteNotification: function(messageId) { var url = SERVICE_URL + '/' + messageId; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'DELETE', url: url, contentType: 'application/json; charset=utf-8', dataType: 'json', noNotificationPoll: true }).then(function(response) { var data = response.data; var deleted = { id: messageId, result: data }; this._updateUnreadIds([deleted], true); this.trigger('notifications:deleted', deleted); return data; }.bind(this)).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_delete_notification') }) }; this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw error; }.bind(this)); }, retrieveErrorMessage: function(input) { input = input || {}; var message = input.defaultMessage; try { var text = $.parseJSON(input.result.responseText); message = text.error || text.message; } catch (e) { /*continue*/ } return message; }, getNotificationState: function(reportId) { var server_URL = SERVER_URI + reportId + NOTIFICATION_STATE_ENDPOINT; return this.glassContext.getCoreSvc('.Ajax').ajax({ type: 'GET', url: server_URL, contentType: 'application/json; charset=utf-8', dataType: 'json' }).then(function(response) { return response.data; }).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_get_report_notification_state') }) }; //NotificationService should not be presenting an error message. It throws an error, which should be handled appropriately by a calling View component. //this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw error; }.bind(this)); }, updateNotificationState: function(descriptor) { var server_URL = SERVER_URI + descriptor.reportId + NOTIFICATION_STATE_ENDPOINT; var jsonData = { 'state': descriptor.state }; return this.glassContext.getCoreSvc('.Ajax').ajax({ url: server_URL, type: 'PUT', headers: { Accept: 'application/vnd.ibm.bi.notification-state+json', 'Content-Type': 'application/vnd.ibm.bi.notification-state+json' }, dataType: 'json', data: JSON.stringify(jsonData) }).then(function(response) { return response.data; }).catch(function(err) { var error = { message: this.retrieveErrorMessage({ result: err, defaultMessage: t.translate('error_update_report_notification_state') }) }; this.glassContext.appController.showErrorMessage(error.message, t.translate('error_label_share')); throw err; }.bind(this)); }, /** * Function to update the unreadIds array and then trigger an update * event to update the notification button badge * * @param data an array of data objects to use to update the array of * unreadIds * @param remove flag to force the removal of an id in the unreadIds * array */ _updateUnreadIds: function(data, remove) { if (Array.isArray(data)) { var dataLength = data.length; var forceRemove = remove || false; for (var i = 0; i < dataLength; i++) { if (forceRemove || (!data[i].unread && _.contains(this.unreadIds, data[i].id))) { this._removeUnreadId(data[i].id); } else if (data[i].unread && !_.contains(this.unreadIds, data[i].id)) { this.unreadIds.push(data[i].id); } } this.trigger('notifications:newCount', this.unreadIds.length); } }, _removeUnreadId: function(messageId) { var index = this.unreadIds.indexOf(messageId); if (index >= 0) { this.unreadIds.splice(index, 1); } } }); return NotificationService; });