@tableau/taco-toolkit
Version:
Tableau Connector Toolkit
551 lines • 40.5 kB
JavaScript
"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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