UNPKG

@tableau/taco-toolkit

Version:
551 lines 40.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Connector = void 0; var toastify_js_1 = __importDefault(require("toastify-js")); var ajax = __importStar(require("../../shared/app-transport/ajax")); var auth_credentials_1 = require("../../shared/auth-credentials"); var caller_type_1 = require("../../shared/enums/caller-type"); var log_level_1 = require("../../shared/logger/log-level"); var handler_input_1 = require("../../shared/types/handler-input"); var app_logger_1 = require("./app-logger"); var permission_check_1 = require("../../shared/utils/permission-check"); var onInitSuccessDefault = function (_connector) { // do nothing }; var onInitFailureDefault = function (_connector, err) { console.error(err); (0, toastify_js_1.default)({ text: "".concat(err), duration: -1, newWindow: true, close: true, gravity: 'top', position: 'right', stopOnFocus: true, style: { color: 'white', background: 'red', }, }).showToast(); }; /** * The Connector class is a facade class used to talk with the eps main server. */ var Connector = /** @class */ (function () { function Connector(onInitSuccess, onInitFailure) { if (onInitSuccess === void 0) { onInitSuccess = onInitSuccessDefault; } if (onInitFailure === void 0) { onInitFailure = onInitFailureDefault; } var _this = this; this.onInitSuccess = onInitSuccess; this.onInitFailure = onInitFailure; this.isInsideIframe = false; this.isInitialized = false; this.permission = { api: {} }; this.initTimeout = setTimeout(function () { if (!_this.isInitialized) { _this.onInitFailure(_this, new Error('There was a timeout waiting for the Connector object to initialize.')); } }, 15000); var queryString = window.location.search; if (queryString.length === 0) { this.isInsideIframe = true; var initConnector_1 = function (event) { console.log('event.data', event.data); var connectorInput = event.data; // when connector UI gets rendered in an iframe, it may receive other // messages from the container if (!isVizQLServerConnectorInput(connectorInput)) { return; } // after receiving the connectorInput, remove the listener window.removeEventListener('message', initConnector_1); _this.initialize(connectorInput); }; window.addEventListener('message', initConnector_1); } else { // TODO: the function should type narrow into ConnectorInput var connectorInput = urlParamsToObject(queryString); this.initialize(connectorInput); } } Connector.prototype.initialize = function (connectorInput) { return __awaiter(this, void 0, void 0, function () { var serverUrl, connectionData, secrets, epsHeaders, parsedData, permissionResponse, permission; return __generator(this, function (_a) { switch (_a.label) { case 0: validateConnectorInput(connectorInput); this.options = getOptionsFromInput(connectorInput); serverUrl = connectorInput.serverUrl, connectionData = connectorInput.connectionData, secrets = connectorInput.secrets; epsHeaders = getEpsHeaders(this.options); app_logger_1.AppLogger.init({ level: log_level_1.LogLevel.INFO, endpoint: "".concat(serverUrl, "/log"), headers: epsHeaders, }); if (secrets) { this.secrets = parseSecrets(secrets); } if (connectionData) { parsedData = parseConnectionData(connectionData); if (parsedData) { this.handlerInputs = parsedData.handlerInputs; this.appSnapshot = parsedData.appSnapshot; } } if (!this.isInsideIframe) { openWebSocket(connectorInput); } return [4 /*yield*/, ajax.get("".concat(serverUrl, "/permission"), { headers: epsHeaders })]; case 1: permissionResponse = _a.sent(); permission = permissionResponse.body; console.log("Got permission from server API: ".concat(JSON.stringify(permission))); this.permission = permission; this.initComplete(); return [2 /*return*/]; } }); }); }; Connector.prototype.initComplete = function () { clearTimeout(this.initTimeout); this.isInitialized = true; this.onInitSuccess(this); }; /** * Send a GET request. * * By default, it sends the request directly to the endpoint. * When requestOptions.bypassCorsPolicy is set, the request will be forwarded * to EPS. This can be used when a server has certain CORS policy. When EPS * receives the forwarded request, it will validate the connector's permission * settings defined in the connector.json. * * The function also validates the connector's permission settings on the * client. */ Connector.prototype.get = function (url, requestOptions) { return __awaiter(this, void 0, void 0, function () { var headers, bypassCorsPolicy; return __generator(this, function (_a) { switch (_a.label) { case 0: assertConnectorOptions(this.options); this.validateApiPermission(url, 'GET'); headers = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers; bypassCorsPolicy = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.bypassCorsPolicy; if (!bypassCorsPolicy) return [3 /*break*/, 2]; return [4 /*yield*/, this.forwardRequest(this.options.serverUrl, { method: 'GET', url: url, headers: headers, })]; case 1: return [2 /*return*/, _a.sent()]; case 2: return [4 /*yield*/, ajax.get(url, { headers: headers })]; case 3: return [2 /*return*/, _a.sent()]; } }); }); }; /** * Send a POST request. * * By default, it sends the request directly to the endpoint. * When requestOptions.bypassCorsPolicy is set, the request will be forwarded * to EPS. This can be used when a server has certain CORS policy. When EPS * receives the forwarded request, it will validate the connector's permission * settings defined in the connector.json. * * The function also validates the connector's permission settings on the * client. */ Connector.prototype.post = function (url, body, requestOptions) { return __awaiter(this, void 0, void 0, function () { var headers, bypassCorsPolicy; return __generator(this, function (_a) { switch (_a.label) { case 0: assertConnectorOptions(this.options); this.validateApiPermission(url, 'POST'); headers = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers; bypassCorsPolicy = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.bypassCorsPolicy; if (!bypassCorsPolicy) return [3 /*break*/, 2]; return [4 /*yield*/, this.forwardRequest(this.options.serverUrl, { method: 'POST', url: url, headers: headers, body: body, })]; case 1: return [2 /*return*/, _a.sent()]; case 2: return [4 /*yield*/, ajax.post(url, body, { headers: headers })]; case 3: return [2 /*return*/, _a.sent()]; } }); }); }; Connector.prototype.getIframeMessage = function () { assertConnectorOptions(this.options); assertHandlerInputs(this.handlerInputs); return { type: 'eps-submit', connectionData: getConnectionData(this.appSnapshot, this.handlerInputs), // currently, monolith expects all secret related infos are sent with // this property for logging encryption secrets: this.secrets, headers: getEpsHeaders(this.options), submissionUrl: getSubmissionUrl(this.options), }; }; Connector.prototype.submit = function () { return __awaiter(this, void 0, void 0, function () { var message, headers, submissionUrl, body; return __generator(this, function (_a) { switch (_a.label) { case 0: assertConnectorOptions(this.options); assertHandlerInputs(this.handlerInputs); app_logger_1.AppLogger.flush(); if (!this.isInsideIframe) return [3 /*break*/, 1]; message = this.getIframeMessage(); window.parent.postMessage(message, '*'); //http://localhost:8080 return [3 /*break*/, 3]; case 1: headers = getRequestHeaders(this.options, this.secrets); submissionUrl = getSubmissionUrl(this.options); body = getConnectionData(this.appSnapshot, this.handlerInputs); return [4 /*yield*/, ajax.post(submissionUrl, body, { headers: headers })]; case 2: _a.sent(); closeWindow(this.options.callerType); _a.label = 3; case 3: return [2 /*return*/]; } }); }); }; Connector.prototype.validateApiPermission = function (url, method) { if (!(0, permission_check_1.checkApiPermission)(this.permission.api, url, method)) { throw new Error("This app does not have permission to make ".concat(method, " request to ").concat(url)); } }; Connector.prototype.forwardRequest = function (serverUrl, requestOptions) { return __awaiter(this, void 0, void 0, function () { var method, headers, url, forwardedRequest, response; return __generator(this, function (_a) { switch (_a.label) { case 0: assertConnectorOptions(this.options); method = requestOptions.method, headers = requestOptions.headers, url = requestOptions.url; forwardedRequest = { method: method, url: url, headers: headers, }; if (forwardedRequest.method === 'POST') { forwardedRequest.body = requestOptions.body; } return [4 /*yield*/, ajax.post(serverUrl, forwardedRequest, { headers: getEpsHeaders(this.options), }) // on the eps side, body is a plain object typed as AjaxResponse ]; case 1: response = _a.sent(); // on the eps side, body is a plain object typed as AjaxResponse return [2 /*return*/, response.body]; } }); }); }; Connector.prototype.cancel = function () { assertConnectorOptions(this.options); app_logger_1.AppLogger.flush(); if (this.isInsideIframe) { var message = { type: 'cancel' }; window.parent.postMessage(message, '*'); //http://localhost:8080 } else { closeWindow(this.options.callerType); } }; Object.defineProperty(Connector.prototype, "oAuthCredentials", { get: function () { return new auth_credentials_1.OAuthCredentials(this.secrets || {}); }, enumerable: false, configurable: true }); return Connector; }()); exports.Connector = Connector; function openWebSocket(connectorInput) { var callerType = connectorInput.callerType, browserWatcherUrl = connectorInput.browserWatcherUrl, connectionId = connectorInput.connectionId; if (callerType === caller_type_1.CallerType.appdev) { return; } var browserWatcherSocket = new WebSocket(browserWatcherUrl); browserWatcherSocket.onopen = function () { console.log('WebSocket Client Connected'); browserWatcherSocket.send(JSON.stringify({ connectionId: connectionId })); }; } function closeWindow(callerType) { if (callerType === caller_type_1.CallerType.appdev) { return; } // This will cause the web socket connection to disconnect and the // server will clean up the process, etc. window.close(); } function getOptionsFromInput(input) { var connectionId = input.connectionId, serverUrl = input.serverUrl, callerId = input.callerId, epsInstanceId = input.epsInstanceId, connectorClass = input.connectorClass, tacoPath = input.tacoPath, callerType = input.callerType; return { connectionId: connectionId, serverUrl: serverUrl, callerId: callerId, epsInstanceId: epsInstanceId, connectorClass: connectorClass, tacoPath: tacoPath, callerType: callerType, }; } function assertConnectorOptions(options) { if (!options) { throw new Error('The connector.options are not initialized.'); } } function assertHandlerInputs(handlerInputs) { var e_1, _a; if (!Array.isArray(handlerInputs) || handlerInputs.length === 0) { throw new Error('Cannot call submit without any handlerInputs.'); } var nameSet = new Set(); try { for (var handlerInputs_1 = __values(handlerInputs), handlerInputs_1_1 = handlerInputs_1.next(); !handlerInputs_1_1.done; handlerInputs_1_1 = handlerInputs_1.next()) { var handlerInput = handlerInputs_1_1.value; // Validate handler input is of valid shape if (!(0, handler_input_1.isValidHandlerInput)(handlerInput)) { throw new Error("Handler input is not valid. ".concat(JSON.stringify(handlerInput))); } // for filebased handler input validate that names are unique if ((0, handler_input_1.isFileBasedHandlerInput)(handlerInput)) { var name_1 = handlerInput.name; if (nameSet.has(name_1)) { throw new Error("Handler input name property value \"".concat(name_1, "\" is not unique.")); } else { nameSet.add(name_1); } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (handlerInputs_1_1 && !handlerInputs_1_1.done && (_a = handlerInputs_1.return)) _a.call(handlerInputs_1); } finally { if (e_1) throw e_1.error; } } } function getSubmissionUrl(options) { var connectorClass = options.connectorClass, serverUrl = options.serverUrl; return "".concat(serverUrl, "/extract/").concat(connectorClass); } function getEpsHeaders(options) { var _a; return { 'caller-id': options.callerId, 'connection-id': options.connectionId, 'eps-instance-id': options.epsInstanceId, 'plugin-path': options.tacoPath, 'connector-class': options.connectorClass, 'Global-Session-Header': ((_a = window === null || window === void 0 ? void 0 : window.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem('vizqlRoutingKey')) || '', }; } function getConnectionData(appSnapshot, handlerInputs) { return { handlerInputs: handlerInputs, appSnapshot: appSnapshot, }; } function parseSecrets(secretsInput) { if (!secretsInput || typeof secretsInput !== 'string') { console.warn("Invalid input type for secrets: ".concat(typeof secretsInput, ". Skip parsing secrets.")); return; } try { var secrets = JSON.parse(secretsInput); if (Object.keys(secrets).length > 0) { return secrets; } } catch (error) { console.error('Secrets exist, but could not be parsed.'); // Note: don't log error } } function parseConnectionData(connectionDataInput) { var _a; if (typeof connectionDataInput !== 'string' || connectionDataInput.length === 0) { console.warn("Invalid input type for connection data: ".concat(typeof connectionDataInput, ". Skip parsing connection data.")); return; } try { var connectionData = JSON.parse(connectionDataInput); var appSnapshot = (_a = connectionData.appSnapshot) !== null && _a !== void 0 ? _a : null; var handlerInputs = connectionData.handlerInputs; if (!Array.isArray(handlerInputs)) { if (handlerInputs !== undefined) { console.warn("handlerInputs must be an array, but the found type is ".concat(typeof handlerInputs, ". Ignore the handlerInputs.")); } return { handlerInputs: [], appSnapshot: appSnapshot, }; } return { handlerInputs: handlerInputs, appSnapshot: appSnapshot, }; } catch (error) { console.error("Could not parse connectionData: ".concat(error, " with input ").concat(connectionDataInput)); } } // TODO: we should do type narrowing with this function function validateConnectorInput(input) { // Just log for now. Do not throw error. var requiredKeys; if (caller_type_1.CallerType.vizQLServer === input.callerType) { requiredKeys = [ // Other field are provided to eps requests, outside and after the iframe. 'callerType', 'connectionId', 'secrets', 'serverUrl', ]; } else { requiredKeys = [ 'browserWatcherUrl', 'callerId', 'connectorClass', 'connectionId', 'epsInstanceId', 'serverUrl', 'tacoPath', ]; } var missingKeys = requiredKeys.filter(function (k) { return input[k] === undefined; }); if (missingKeys.length > 0) { console.error("These keys are missing from connector input: ".concat(missingKeys.join(', '))); return false; } return true; } function urlParamsToObject(serializedParams) { var entries = new URLSearchParams(serializedParams); var result = {}; entries.forEach(function (value, key) { result[key] = value; }); return result; } function getRequestHeaders(options, secrets) { var headers = getEpsHeaders(options); if (secrets) { headers.secrets = JSON.stringify(secrets); } // in case of oAuth monolith will also send access-token and refresh token in the request headers directly, // the /extract api will expect oauth access and refresh token in the headers var credential = new auth_credentials_1.OAuthCredentials(secrets); if (typeof credential.accessToken === 'string') { headers['access-token'] = credential.accessToken; } //TODO (W-14199268): for quickbooks need realm id in secrets, will be removed once we support arbitrary oauth tokens if (secrets && typeof secrets.realmid === 'string') { headers['realmid'] = secrets.realmid; } return headers; } function isVizQLServerConnectorInput(input) { if (input === null || typeof input !== 'object') { return false; } return ['callerType', 'connectionId', 'serverUrl'].every(function (prop) { return prop in input; }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3Nkay9hcHAvY29ubmVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSw0REFBa0M7QUFFbEMsb0VBQXVEO0FBQ3ZELGtFQUFnRTtBQUNoRSw4REFBMkQ7QUFDM0QsMkRBQXdEO0FBR3hELGtFQUE2RztBQUk3RywyQ0FBd0M7QUFFeEMsd0VBQXdFO0FBRXhFLElBQU0sb0JBQW9CLEdBQUcsVUFBQyxVQUFxQjtJQUNqRCxhQUFhO0FBQ2YsQ0FBQyxDQUFBO0FBRUQsSUFBTSxvQkFBb0IsR0FBRyxVQUFDLFVBQXFCLEVBQUUsR0FBVTtJQUM3RCxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ2xCLElBQUEscUJBQVEsRUFBQztRQUNQLElBQUksRUFBRSxVQUFHLEdBQUcsQ0FBRTtRQUNkLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDWixTQUFTLEVBQUUsSUFBSTtRQUNmLEtBQUssRUFBRSxJQUFJO1FBQ1gsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsT0FBTztRQUNqQixXQUFXLEVBQUUsSUFBSTtRQUNqQixLQUFLLEVBQUU7WUFDTCxLQUFLLEVBQUUsT0FBTztZQUNkLFVBQVUsRUFBRSxLQUFLO1NBQ2xCO0tBQ0YsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFBO0FBQ2hCLENBQUMsQ0FBQTtBQUVEOztHQUVHO0FBQ0g7SUF1QkUsbUJBQ1UsYUFBb0UsRUFDcEUsYUFBZ0Y7UUFEaEYsOEJBQUEsRUFBQSxvQ0FBb0U7UUFDcEUsOEJBQUEsRUFBQSxvQ0FBZ0Y7UUFGMUYsaUJBbUNDO1FBbENTLGtCQUFhLEdBQWIsYUFBYSxDQUF1RDtRQUNwRSxrQkFBYSxHQUFiLGFBQWEsQ0FBbUU7UUFQbEYs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