Source: messaging/connectors/slack/SlackClient.js

/**
 * Licensed Materials - Property of IBM
 * IBM Cognos Products: Collaboration
 * (C) Copyright IBM Corp. 2017, 2019
 *
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp.
 */
define([
	'../../../lib/@waca/core-client/js/core-client/ui/core/Class',
	'../../utils/CustomStatus',
	'./SlackAuth',
	'../../../nls/StringResources',
	'../../utils/ImageUtils'
], function (Class, CustomStatus, SlackAuth, StringResources, ImageUtils) {
	'use strict';

	var SCOPE_BASIC = 'identity.basic';

	var SlackClient = Class.extend( /** @lends SlackClient */ {

		/**
		 * @desc A common object that calls backend REST APIs to handle Slack relevant operations
		 * @constructs SlackClient
		 * @extends Class
		 * @public
		 * @param {Object} glassContext - the Glass context
		 * @returns an object
		 *
		 * @example var slackClient = new SlackClient(glassContext);
		 */
		init: function (glassContext) {
			SlackClient.inherited('init', this, arguments);
			this.glassContext = glassContext;
			this.slackAuth = new SlackAuth(glassContext);
		},

		/**
		 * Signs in to Slack.
		 * @instance
		 * @returns {promise} a JSON object
		 */
		signIn: function () {
			return this.slackAuth.authWithSlack(SCOPE_BASIC);
		},

		/**
		 * Signs out of Slack
		 * @instance
		 * @param {string} uri the URI for signing out
		 * @returns {promise} a JSON object
		 */
		signOut: function (uri) {
			return this.glassContext.getCoreSvc('fetch').get(uri, {}).then(function (response) {
				return response.data;
			});
		},

		_getWindowLocation: function() {
			return window.location;
		},

		/**
		 * Returns the redirect URI that needs to be added to the Slack application
		 * @instance
		 * @returns {string} the redirect URI
		 */
		getRedirectUrl: function () {
			return this.slackAuth.getRedirectUrl(this._getWindowLocation());
		},

		/**
		 * Revoke access token
		 * @instance
		 * @param {string} uri the URI for revoking
		 * @returns {promise} a JSON object
		 */
		revoke: function (uri) {
			return this.glassContext.getCoreSvc('fetch').get(uri, {}).then(function (response) {
				return response.data;
			});
		},

		/**
		 * Sends messages to Slack. For message formatting, check out {@link https://api.slack.com/docs/message-formatting|message-formatting}.
		 * For <code>options.data.message</code> content, see also {@link https://api.slack.com/methods/chat.postMessage|chat.postMessage}
		 * @instance
		 * @param {object} options
		 * @param {string} options.url the url for sending screenshot
		 * @param {object} options.data contains {@code channel}, {@code message} and {@code dataUrl}
		 * @param {string} options.data.dataUrl screenshot data url
		 * @param {string} options.data.comment initial comment to add to screenshot
		 * @param {string} options.data.message a JSON object represents a message to send
		 * @returns {promise} a JSON object
		 */
		sendMessage: function (options) {
			var data = options.data;

			var payload = {
				'channel': data.channel,
				'comment': data.comment,
				'message': data.message,
				'filename': data.filename
			};

			var formData = new FormData();
			formData.append('payload', JSON.stringify(payload));

			var image = data['dataUrl'];
			if (image) {
				var base64ImageContent = image.replace(/^data:image\/(png|jpg);base64,/, '');
				var blob = ImageUtils.base64ToBlob(base64ImageContent, 'image/png');
				formData.append('file', blob);
			} else {
				formData.append('file', '');
			}

			return this.doMultipart({
				url: options.url,
				data: formData
			});
		},

		/**
		 * Sends a multipart POST request
		 * @instance
		 * @param {object} options
		 * @param {string} options.url URL for sending requests
		 * @param {object} options.data form data
		 * @returns {promise} a JSON object
		 */
		doMultipart: function (options) {
			var ajaxOptions = {
				type: 'POST',
				url: options.url,
				cache: false,
				contentType: false,
				processData: false,
				data: options.data
			};
			return this._doAjax(ajaxOptions);
		},

		/**
		 * Sends a GET request
		 * @instance
		 * @param {object} options
		 * @param {string} options.url URL for sending requests
		 * @param {object} options.data JSON object of params
		 * @returns {promise} a JSON object
		 */
		doGet: function (options) {
			var ajaxOptions = {
				type: 'GET',
				url: options.url,
				dataType: 'json',
				data: {
					'payload': JSON.stringify(options.data)
				}
			};
			return this._doAjax(ajaxOptions);
		},

		/**
		 * Sends a POST request
		 * @instance
		 * @param {object} options
		 * @param {string} options.url URL for sending requests
		 * @param {object} options.data JSON object of POST body
		 * @returns {promise} a JSON object
		 */
		doPost: function (options) {
			var ajaxOptions = {
				type: 'POST',
				url: options.url,
				data: JSON.stringify(options.data),
				contentType: 'application/json; charset=utf-8',
				dataType: 'json'
			};
			return this._doAjax(ajaxOptions);
		},

		/**
		 * Translates Slack OAuth errors into text.
		 * @param data OAuth error response data
		 * @return {Error}
		 */
		createAuthError: function (data) {
			var msg;
			var showContactAdmin = false;
			if (!data.error) {
				return new Error(StringResources.get('error_auth_failure_generic'));
			}
			switch (data.error) {
				case 'auth_cancelled':
					msg = StringResources.get('error_auth_cancelled');
					break;
				case 'certificate_invalid':
					msg = StringResources.get('error_certificate_invalid');
					showContactAdmin = true;
					break;
				case 'internal_server_error':
					msg = StringResources.get('error_server_internal');
					showContactAdmin = true;
					break;
				case 'bad_client_secret':
					msg = StringResources.get('error_bad_client_secret');
					showContactAdmin = true;
					break;
				default:
					msg = data.error;
					break;
			}

			var error = new Error(StringResources.get('error_auth_failure', { error: msg }));
			error.showContactAdmin = showContactAdmin;
			return error;
		},

		/**
		 * Sends an HTTP request with given options. It will handle re-auth when access token is invalid.
		 * @instance
		 * @private
		 * @param {object} options AJAX options
		 * @returns {promise} a JSON response
		 */
		_doAjax: function (options) {
			return this.glassContext.getCoreSvc('.Ajax')
				.ajax(options)
				.catch(function (error) {
					if (error.jqXHR) {
						var data = error.jqXHR.responseJSON;
						switch (error.code) {
							case CustomStatus.TOKEN_INVALID:
								return this.slackAuth.authWithSlack(data)
									.then(function (auth) {
										if (auth.ok) {
											return this._doAjax(options);
										} else {
											var error = this.createAuthError(auth);
											error.code = auth.error;
											return Promise.reject(error);
										}
									}.bind(this));

							case CustomStatus.NOT_FOUND_ERROR:
								error.message = StringResources.get('error_platform_not_found');
								error.showContactAdmin = true;
								break;

							case CustomStatus.PROVIDER_CONFIG_ERROR:
								error.message = StringResources.get('error_platform_config');
								error.showContactAdmin = true;
								break;

							case CustomStatus.SERVICE_ERROR:
								// translate slack provider errors into text
								switch (data.error) {
									case 'certificate_invalid':
										error.message = StringResources.get('error_certificate_invalid');
										error.showContactAdmin = true;
										break;
									case 'workspace_not_found':
										error.message = StringResources.get('error_workspace_not_found');
										error.showContactAdmin = true;
										break;
									case 'channel_not_found':
										error.message = StringResources.get('error_channel_not_found');
										break;
									case 'not_in_channel':
										error.message = StringResources.get('error_not_in_channel');
										break;
									case 'invalid_channel':
										error.message = StringResources.get('error_invalid_channel');
										break;
									case 'is_archived':
										error.message = StringResources.get('error_is_archived');
										break;
									case 'msg_too_long':
										error.message = StringResources.get('error_msg_too_long');
										break;
									case 'cant_join':
										error.message = StringResources.get('error_cant_join');
										break;
									case 'not_enough_users':
										error.message = StringResources.get('error_not_enough_users');
										break;
									case 'fatal_error':
										error.message = StringResources.get('error_service_down');
										break;
									default:
										if (data.error && data.error.indexOf('ASSocket: timed out') === 0) {
											error.message = StringResources.get('error_slack_timeout');
											error.showContactAdmin = true;
										} else {
											error.message = StringResources.get('error_slack', data);
											error.showContactAdmin = true;
										}
										break;
								}
								break;
						}
					}
					throw error;
				}.bind(this));
		}
	});

	return SlackClient;
});