UNPKG

@vulcan-sql/extension-driver-ksqldb

Version:

ksqlDB driver for VulcanSQL

173 lines 6.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RestfulClient = void 0; const tslib_1 = require("tslib"); const http2 = require("http2"); const RESTFUL_API = { INFO: '/info', KSQL: '/ksql', QUERY: '/query', }; const DEFAULT_OPTIONS = { host: 'http://localhost:8088', timeout: 25000, }; class RestfulClient { constructor(options) { this.connected = false; this.startSession = () => null; this.options = options; this.connect(); } /** * The connect method will create a promise "startSession" method, not really to connect http2 immediately. * To let users establish a "startSession" promise request only when they need to query or exec a statement. */ connect() { this.startSession = () => new Promise((resolve, reject) => { this.client = http2.connect(this.options.host || DEFAULT_OPTIONS.host, { timeout: this.options.timeout || DEFAULT_OPTIONS.timeout, }); this.client.setTimeout(this.options.timeout || DEFAULT_OPTIONS.timeout, () => { var _a; if (this.connected === false) { const timeoutError = new Error("Connection timeout."); reject(timeoutError); (_a = this.client) === null || _a === void 0 ? void 0 : _a.destroy(timeoutError); } }); this.client.on('connect', () => { this.connected = true; resolve(); }); this.client.on('error', (error) => { reject(error); }); }); } closeSession() { return new Promise((resolve, reject) => { if (this.client) { !this.client.destroyed && this.client.destroy(); this.connected = false; resolve(); } else { reject(new Error('Client is not initialized')); } }); } checkConnection() { return tslib_1.__awaiter(this, void 0, void 0, function* () { try { const res = yield this.request(RESTFUL_API.INFO, 'GET'); return res.KsqlServerInfo['serverStatus']; } catch (e) { if (e.error_code) { throw new Error(JSON.stringify(e)); } else { throw new Error('KsqlDb server is not ready'); } } }); } checkConnectionRunning() { return tslib_1.__awaiter(this, void 0, void 0, function* () { const status = yield this.checkConnection(); const isRunning = status === 'RUNNING'; return isRunning; }); } /** * According to ksqldb restful API: https://docs.ksqldb.io/en/latest/developer-guide/ksqldb-rest-api/query-endpoint * To run a SELECT statement and stream back the results. * SELECT statement: https://docs.ksqldb.io/en/latest/developer-guide/ksqldb-reference/select-pull-query */ query({ query, query_params = {}, }) { return tslib_1.__awaiter(this, void 0, void 0, function* () { // bind query parameters const ksql = this.bindParams(query, query_params); const buffer = Buffer.from(JSON.stringify({ ksql })); const res = yield this.request(RESTFUL_API.QUERY, 'POST', buffer); return res; }); } /** * According to ksqldb restful API: https://docs.ksqldb.io/en/latest/developer-guide/ksqldb-rest-api/ksql-endpoint * All statements, except those starting with SELECT and PRINT, can be run on this exec method. * To run SELECT and PRINT statements use the "query" method instead. */ exec({ query, query_params = {}, }) { return tslib_1.__awaiter(this, void 0, void 0, function* () { // bind query parameters const ksql = this.bindParams(query, query_params); const buffer = Buffer.from(JSON.stringify({ ksql })); const res = yield this.request(RESTFUL_API.KSQL, 'POST', buffer); return res; }); } bindParams(query, query_params) { const values = Object.values(query_params); // replace the parameterized placeholder to values const ksql = query.replace(/\$(\d+)/g, (_, index) => { const valueIndex = parseInt(index) - 1; const paramValue = values[valueIndex]; // Because the ksqldb queries are expressed using a strict subset of ANSI SQL. // It didn't support the string auto conversion, so we need to add the single quote for string value. return typeof paramValue === 'string' ? `'${paramValue}'` : paramValue; }); return ksql; } request(path, method, buffer) { return tslib_1.__awaiter(this, void 0, void 0, function* () { this.startSession && (yield this.startSession()); return new Promise((resolve, reject) => { const config = { 'content-type': 'application/vnd.ksql.v1+json', ':method': method, ':path': path, }; // add authorization if username and password is provided if (this.options.username && this.options.password) { config['authorization'] = `Basic ${Buffer.from(`${this.options.username}:${this.options.password}`).toString('base64')}`; } const req = this.client.request(config); req.setEncoding('utf-8'); let status = null; req.on('response', (headers) => { status = headers[':status']; }); let data = ''; req.on('data', (chunk) => { data += chunk; }); req.on('end', () => { let responseData = data; try { responseData = JSON.parse(data); } catch (e) { responseData = data; } if (status === 200) { resolve(responseData); } else { reject(responseData); } this.closeSession(); }); req.on('error', (error) => { reject(error); this.closeSession(); }); buffer && req.write(buffer); req.end(); }); }); } } exports.RestfulClient = RestfulClient; //# sourceMappingURL=restfulClient.js.map