@tableau/taco-toolkit
Version:
Tableau Connector Toolkit
298 lines • 24.5 kB
JavaScript
;
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startConnector = void 0;
var child_process_1 = __importDefault(require("child_process"));
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
var superagent_1 = __importDefault(require("superagent"));
var chalk_1 = __importDefault(require("chalk"));
var status_code_1 = require("../../../shared/errors/status-code");
var error_source_1 = require("../../../shared/errors/error-source");
var error_codes_1 = require("../../../shared/errors/error-codes");
var command_1 = require("../../taco-commands/utils/command");
var constants_1 = require("../../taco-commands/utils/constants");
var toolkit_paths_1 = require("../../taco-commands/utils/toolkit-paths");
var connector_1 = require("../../taco-commands/utils/connector");
var connector_json_1 = require("../../taco-commands/utils/connector-json");
var logging_1 = require("../../taco-commands/utils/logging");
var PORT_FILE_CHECK_INTERVAL = 500;
var PORT_FILE_WAIT_TIMEOUT = 30000;
var CALLER_ID = 'taco-start';
var CONNECTION_ID = 'simulator';
var PORT_FILE_NAME = '.eps';
var epsProcess;
/**
* Starting EPS with loading a connector at current path.
*
* TODO: this code path is only for connector authors developments.
* We should make this as a dev server.
*/
function startConnector(config, state) {
return __awaiter(this, void 0, void 0, function () {
var spinner, debugMode, tacoRootPath, portFilePath, logPath, error_1, connectionState, error_2, connectionInfo;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
spinner = state.spinner;
debugMode = config.debugMode;
tacoRootPath = (0, command_1.resolvePath)(config.tacoRootPath);
(0, connector_1.exitIfNotTacoRoot)(tacoRootPath);
spinner.start('Starting EPS...');
portFilePath = getEPSPortFilePath(tacoRootPath);
logPath = (0, logging_1.getLogPath)(tacoRootPath);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, startEps(toolkit_paths_1.EPS_MAIN_PATH, portFilePath, true, logPath)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
error_1 = _a.sent();
spinner.fail('Failed to start EPS');
(0, logging_1.logFatalError)({
statusCode: status_code_1.StatusCode.Cancelled,
errorSource: error_source_1.ErrorSources.Client,
errorCode: error_codes_1.ErrorCodes.Q1XA447W,
message: error_1.toString(),
error: error_1,
});
terminateEPSAndExit();
return [3 /*break*/, 4];
case 4:
spinner.success('EPS started');
spinner.start("Loading connector: ".concat(tacoRootPath));
_a.label = 5;
case 5:
_a.trys.push([5, 7, , 8]);
return [4 /*yield*/, launchConnector(tacoRootPath, debugMode)];
case 6:
connectionState = _a.sent();
return [3 /*break*/, 8];
case 7:
error_2 = _a.sent();
spinner.fail('Error occurred while loading the connector');
(0, logging_1.logFatalError)({
statusCode: status_code_1.StatusCode.Cancelled,
errorSource: error_source_1.ErrorSources.Client,
errorCode: error_codes_1.ErrorCodes.OD3MMIGV,
message: error_2.toString(),
error: error_2,
});
terminateEPSAndExit();
return [3 /*break*/, 8];
case 8:
if (isCanceledConnector(connectionState)) {
spinner.warn('Connector loading is canceled. Terminating EPS...');
terminateEPSAndExit();
}
connectionInfo = connectionState.info;
spinner.success("Successfully loaded connector '".concat(connectionInfo.connectorName, "'"));
displayConnectionInfo(connectionInfo, logPath);
return [2 /*return*/];
}
});
});
}
exports.startConnector = startConnector;
function startEps(epsExecutablePath, epsPortFilePath, debug, logDir) {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var forkParams;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!fs_1.default.existsSync(epsExecutablePath)) {
throw new Error("Failed to find EPS executable file: ".concat(epsExecutablePath));
}
// Delete the existing port file, so we won't read the wrong eps info from last time starting eps
if (fs_1.default.existsSync(epsPortFilePath)) {
logging_1.logger.debug("Found stale EPS port file. Deleting ".concat(epsPortFilePath));
fs_1.default.unlinkSync(epsPortFilePath);
}
forkParams = [
"--caller-pid=".concat(process.pid),
"--caller-id=".concat(CALLER_ID),
"--port-file=".concat(epsPortFilePath),
"--log-dir=".concat(logDir),
'--feature-flags=EPSSupportgRPC:off,EPSStaticTacoUnpack:off',
];
// TODO make the simulator log level configurable
// if(logLevel){
// forkParams.push(`--log-level ${logLevel}`)
// }
if (debug) {
forkParams.push('--inspect-brk');
}
logging_1.logger.debug("Launching EPS with ".concat(forkParams));
epsProcess = child_process_1.default.fork(epsExecutablePath, forkParams, {
detached: false,
silent: false,
stdio: 'pipe',
});
(_a = epsProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (msg) {
logging_1.logger.debug(Buffer.from(msg).toString());
});
(_b = epsProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (msg) {
logging_1.logger.error("Error occured in EPS.\n".concat(Buffer.from(msg).toString()));
});
return [4 /*yield*/, waitForEPSStart(epsPortFilePath, PORT_FILE_CHECK_INTERVAL, PORT_FILE_WAIT_TIMEOUT)];
case 1: return [2 /*return*/, _c.sent()];
}
});
});
}
function waitForEPSStart(epsPortFilePath, interval, timeout) {
return new Promise(function (resolve, reject) {
var startTime = Date.now();
var intervalId = setInterval(function () {
if (fs_1.default.existsSync(epsPortFilePath)) {
clearInterval(intervalId);
logging_1.logger.debug("Found EPS port file '".concat(epsPortFilePath, "'"));
resolve();
}
else if (Date.now() - startTime > timeout) {
clearInterval(intervalId);
reject(new Error("Timed out! Failed to locate EPS port file after ".concat(timeout, " ms at path: ").concat(epsPortFilePath)));
}
}, interval);
});
}
function launchConnector(tacoRootPath, debug) {
return __awaiter(this, void 0, void 0, function () {
var connectorJsonPath, connectorJson, connectorName, _a, epsInstancePort, epsInstanceId, url, response, _b, extractorId, extractorPort;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
connectorJsonPath = (0, connector_1.getConnectorJsonPath)(tacoRootPath);
connectorJson = (0, connector_json_1.getConnectorJson)(connectorJsonPath);
connectorName = (0, connector_json_1.getConnectorClassName)(connectorJson);
_a = getEPSInstanceInfo(tacoRootPath), epsInstancePort = _a.epsInstancePort, epsInstanceId = _a.epsInstanceId;
url = "http://localhost:".concat(epsInstancePort, "/launch/").concat(connectorName, "?debug=").concat(debug);
logging_1.logger.debug("Loading connector with url: ".concat(url));
return [4 /*yield*/, superagent_1.default
.post(url)
.set('connection-id', CONNECTION_ID)
.set('caller-id', CALLER_ID)
.set('eps-instance-id', epsInstanceId)
.set('plugin-path', tacoRootPath)];
case 1:
response = _c.sent();
if (isCanceledResponse(response)) {
return [2 /*return*/, {
state: 'canceled',
}];
}
_b = response.body, extractorId = _b.extractorId, extractorPort = _b.port;
if (!extractorId || !extractorPort) {
throw new Error("Received invalid extractor info from ".concat(url, "\n") + // prettier
"Response body: ".concat(JSON.stringify(response.body)));
}
return [2 /*return*/, {
state: 'success',
info: {
extractorId: extractorId,
extractorPort: extractorPort,
epsInstanceId: epsInstanceId,
epsInstancePort: epsInstancePort,
connectorName: connectorName,
},
}];
}
});
});
}
function isCanceledResponse(response) {
var body = response.body;
if (typeof body === 'object' && body !== null) {
return body.status === 'canceled';
}
return false;
}
function terminateEPSAndExit() {
logging_1.logger.debug('Shutting down EPS');
if (epsProcess !== undefined) {
try {
epsProcess.kill('SIGKILL');
}
catch (error) {
(0, logging_1.logFatalError)({
statusCode: status_code_1.StatusCode.Cancelled,
errorSource: error_source_1.ErrorSources.Client,
errorCode: error_codes_1.ErrorCodes.KY5XRFYV,
message: "Unable to terminate EPS process: ".concat(error),
error: error,
});
process.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE);
}
}
logging_1.logger.info('EPS terminated');
return process.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE);
}
function getEPSPortFilePath(tacoRootPath) {
return path_1.default.join(tacoRootPath, PORT_FILE_NAME);
}
function getEPSInstanceInfo(tacoRootPath) {
var epsFilePath = getEPSPortFilePath(tacoRootPath);
var lines = fs_1.default.readFileSync(epsFilePath, 'utf-8').split(/\r?\n/);
return {
epsInstancePort: lines[0],
epsInstanceId: lines[1],
};
}
function isCanceledConnector(connectorState) {
return connectorState.state === 'canceled';
}
function displayConnectionInfo(connectionInfo, serverLogPath) {
var extractorPort = connectionInfo.extractorPort, extractorId = connectionInfo.extractorId, epsInstanceId = connectionInfo.epsInstanceId;
console.info("\nYou may now fetch metadata and data from the following urls with the headers:\n" +
" Tables: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables")), "\n") +
" Metadata: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables/<TABLE_NAME>/metadata")), "\n") +
" Data: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables/<TABLE_NAME>/rows"))));
console.info("\n".concat(chalk_1.default.green('Request Headers:'), "\n") +
" extractor-id: ".concat(extractorId, "\n") +
" eps-instance-id: ".concat(epsInstanceId, "\n") +
" caller-id: ".concat(CALLER_ID, "\n") +
" connection-id: ".concat(CONNECTION_ID));
console.info("\nServer logs are located at: ".concat(serverLogPath, "\n"));
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY2xpL3RhY28tc2NyaXB0cy9zdGFydC9zdGFydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnRUFBMkQ7QUFDM0QsMENBQW1CO0FBQ25CLDhDQUF1QjtBQUN2QiwwREFBbUM7QUFDbkMsZ0RBQXlCO0FBRXpCLGtFQUErRDtBQUMvRCxvRUFBa0U7QUFDbEUsa0VBQStEO0FBQy9ELDZEQUErRTtBQUMvRSxpRUFBa0Y7QUFDbEYseUVBQXVFO0FBQ3ZFLGlFQUE2RjtBQUM3RiwyRUFBa0c7QUFDbEcsNkRBQXFGO0FBRXJGLElBQU0sd0JBQXdCLEdBQUcsR0FBRyxDQUFBO0FBQ3BDLElBQU0sc0JBQXNCLEdBQUcsS0FBTSxDQUFBO0FBQ3JDLElBQU0sU0FBUyxHQUFHLFlBQVksQ0FBQTtBQUM5QixJQUFNLGFBQWEsR0FBRyxXQUFXLENBQUE7QUFDakMsSUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFBO0FBRTdCLElBQUksVUFBb0MsQ0FBQTtBQStCeEM7Ozs7O0dBS0c7QUFDSCxTQUFzQixjQUFjLENBQUMsTUFBbUIsRUFBRSxLQUFxQjs7Ozs7O29CQUNyRSxPQUFPLEdBQUssS0FBSyxRQUFWLENBQVU7b0JBQ2pCLFNBQVMsR0FBSyxNQUFNLFVBQVgsQ0FBVztvQkFFdEIsWUFBWSxHQUFHLElBQUEscUJBQVcsRUFBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7b0JBQ3JELElBQUEsNkJBQWlCLEVBQUMsWUFBWSxDQUFDLENBQUE7b0JBRS9CLE9BQU8sQ0FBQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQTtvQkFDMUIsWUFBWSxHQUFHLGtCQUFrQixDQUFDLFlBQVksQ0FBQyxDQUFBO29CQUMvQyxPQUFPLEdBQUcsSUFBQSxvQkFBVSxFQUFDLFlBQVksQ0FBQyxDQUFBOzs7O29CQUd0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