From 6b09a08f0e2452937aaaf5a8aa04da2f7ed713af Mon Sep 17 00:00:00 2001 From: xialei_li Date: Sat, 11 Sep 2021 19:14:29 +0800 Subject: [PATCH] TD-1860-feature-node-restful --- src/connector/node-rest/TDengineRest.js | 5 + .../node-rest/examples/testRestCursor.js | 12 ++ src/connector/node-rest/package.json | 2 +- src/connector/node-rest/readme.md | 43 +++++ src/connector/node-rest/src/restConnect.js | 49 ++++++ src/connector/node-rest/src/restConstant.js | 17 ++ src/connector/node-rest/src/restCursor.js | 43 +++++ src/connector/node-rest/src/restResult.js | 154 ++++++++++++++++++ src/connector/node-rest/test/testRestConn.js | 32 ++++ 9 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/connector/node-rest/TDengineRest.js create mode 100644 src/connector/node-rest/examples/testRestCursor.js create mode 100644 src/connector/node-rest/readme.md create mode 100644 src/connector/node-rest/src/restConnect.js create mode 100644 src/connector/node-rest/src/restConstant.js create mode 100644 src/connector/node-rest/src/restCursor.js create mode 100644 src/connector/node-rest/src/restResult.js create mode 100644 src/connector/node-rest/test/testRestConn.js diff --git a/src/connector/node-rest/TDengineRest.js b/src/connector/node-rest/TDengineRest.js new file mode 100644 index 0000000000..68ac76019d --- /dev/null +++ b/src/connector/node-rest/TDengineRest.js @@ -0,0 +1,5 @@ +import {TDengineRestConnection} from './src/restConnect' + +export function TDRestConnection(connection = {}) { + return new TDengineRestConnection(connection) +} diff --git a/src/connector/node-rest/examples/testRestCursor.js b/src/connector/node-rest/examples/testRestCursor.js new file mode 100644 index 0000000000..beda3790fa --- /dev/null +++ b/src/connector/node-rest/examples/testRestCursor.js @@ -0,0 +1,12 @@ +import {TDengineRestConnection} from "../src/restConnect"; + +let conn = new TDengineRestConnection({host: 'u195'}) +let cursor = conn.cursor(); + +(async () => { + data = await cursor.query("select * from test.meters limit 10").catch(e => console.log(e)); + data.toString() +})() + + + diff --git a/src/connector/node-rest/package.json b/src/connector/node-rest/package.json index 8cc0bd2991..7c85fa0070 100644 --- a/src/connector/node-rest/package.json +++ b/src/connector/node-rest/package.json @@ -6,7 +6,7 @@ "license": "MIT", "scripts": { "prepare": "npm run build", - "build": "esbuild --bundle --platform=node --outfile=lib/index.js src/index.js", + "build": "esbuild --bundle --platform=node --outfile=lib/index.js src/index.js", "build:dev": "esbuild --bundle --platform=node --outfile=dist/examples/show-database.js examples/show-databases.js --watch" }, "devDependencies": { diff --git a/src/connector/node-rest/readme.md b/src/connector/node-rest/readme.md new file mode 100644 index 0000000000..0317955488 --- /dev/null +++ b/src/connector/node-rest/readme.md @@ -0,0 +1,43 @@ +# TDengine Nodejs Restful + +This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine) though +restful. This restful can help you access the TDengine from different platform. + +## Install + +### On Linux + +### On macOs + +### On Windows + +## Usage + +### Connection + +```javascript +import taoRest from '' +var connRest = taoRest({}) +``` +close a connection +```javascript +connRest.close() +``` +query +```javascript +let data = connRest.Query("sql") +console.log ("data:"+data) +``` + +## Example +An example of using the NodeJS Restful connector to create a table with weather data and create and execute queries can be found [here](https://github.com/taosdata/TDengine/tree/master/tests/examples/node-rest/rest-node-example.js) + +An example of using the NodeJS Restful connector to achieve the same things but without all the object wrappers that wrap around the data returned to achieve higher functionality can be found [here](https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example-raw.js) + +## Contributing to TDengine + +Please follow the [contribution guidelines](https://github.com/taosdata/TDengine/blob/master/CONTRIBUTING.md) to contribute to the project. + +## License + +[GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html) diff --git a/src/connector/node-rest/src/restConnect.js b/src/connector/node-rest/src/restConnect.js new file mode 100644 index 0000000000..0fb9593ed0 --- /dev/null +++ b/src/connector/node-rest/src/restConnect.js @@ -0,0 +1,49 @@ +import {TDengineRestCursor} from '../src/restCursor' + +/** + * + */ +export class TDengineRestConnection { + constructor(options) { + this.host = 'localhost' + this.port = '6041' + this.user = 'root' + this.pass = 'taosdata' + this.path = '/rest/sqlt/' + this._initConnection(options) + return this + } + + /** + * this is a private function that + * @param options + * @private + */ + _initConnection(options) { + if (options['host']) { + this.host = options['host'] + } + if (options['port']) { + this.port = options['port'] + } + if (options['user']) { + this.user = options['user'] + } + if (options['pass']) { + this.pass = options['pass'] + } + if (options['path']) { + this.path = options['path'] + } + } + + cursor() { + return new TDengineRestCursor(this) + console.log("return a cursor object user query sql") + } +} + + + + + diff --git a/src/connector/node-rest/src/restConstant.js b/src/connector/node-rest/src/restConstant.js new file mode 100644 index 0000000000..48994bf59e --- /dev/null +++ b/src/connector/node-rest/src/restConstant.js @@ -0,0 +1,17 @@ +export const typeCodesToName = { + 0: 'Null', + 1: 'Boolean', + 2: 'Tiny Int', + 3: 'Small Int', + 4: 'Int', + 5: 'Big Int', + 6: 'Float', + 7: 'Double', + 8: 'Binary', + 9: 'Timestamp', + 10: 'Nchar', +} + +export function getTaoType(typecode) { + return typeCodesToName[typecode]; +} \ No newline at end of file diff --git a/src/connector/node-rest/src/restCursor.js b/src/connector/node-rest/src/restCursor.js new file mode 100644 index 0000000000..db612528bd --- /dev/null +++ b/src/connector/node-rest/src/restCursor.js @@ -0,0 +1,43 @@ +import fetch from 'node-fetch' +import {TDengineRestResultSet} from '../src/restResult' + +export class TDengineRestCursor { + constructor(connection) { + this._connection = null; + this.data = []; + this.http = false + if (connection != null) { + this._connection = connection + } else { + throw new Error("A TDengineRestConnection object is required to be passed to the TDengineRestCursor") + } + } + + _apiUpl() { + // console.log((this.http ? "https" : "http") + "://" + this._connection.host + ":" + this._connection.port + this._connection.path) + return (this.http ? "https" : "http") + "://" + this._connection.host + ":" + this._connection.port + this._connection.path + } + + _token() { + return 'Basic ' + Buffer.from(this._connection.user + ":" + this._connection.pass).toString('base64') + } + + async query(sql) { + try { + let response = await fetch(this._apiUpl(), { + method: 'POST', + body: sql, + headers: {'Authorization': this._token()} + }) + if (response.status >= 200 && response.status < 300) { + + return new TDengineRestResultSet(await response.json()) + } else { + throw new Error(response.statusText) + } + } catch (e) { + console.log("Request Failed " + e) + } + } + +} \ No newline at end of file diff --git a/src/connector/node-rest/src/restResult.js b/src/connector/node-rest/src/restResult.js new file mode 100644 index 0000000000..fd93654069 --- /dev/null +++ b/src/connector/node-rest/src/restResult.js @@ -0,0 +1,154 @@ +import {getTaoType} from '../src/restConstant' + +export class TDengineRestResultSet { + constructor(jason) { + this.status = '' //succ + this.column_name = {} //head + this.column_type = {} //column_meta + this.data = {} + this.affectRows = null //rows + this.code = null + this.desc = null + this._init(jason) + } + + //initial the resultSet with a jason parameter + /** + * + * @param jason + */ + _init(jason) { + if (jason.status) { + this.status = jason.status + } + if (jason.head) { + this.column_name = jason.head + } + if (jason.column_meta) { + this.column_type = jason.column_meta + } + if (jason.data) { + this.data = jason.data + } + if (jason.rows) { + this.affectRows = jason.rows + } + if (jason.code) { + this.code = jason.code + } + if (jason.desc) { + this.desc = jason.desc + } + } + + getStatus() { + return this.status + } + + getColumn_name() { + return this.column_name + } + + getColumn_type() { + let column_data = [] + this.column_type.forEach(function (column) { + column[1] = getTaoType(column[1]) + column_data.push(column) + }) + return column_data + } + + getData() { + return this.data + } + + getAffectRow() { + return this.affectRows + } + + getCode() { + return this.code + } + + getDesc() { + return this.desc + } + + + toString() { + let fields = this.column_type + let rows = this.data + this._prettyStr(fields, rows) + } + + _prettyStr(fields, data) { + let colName = [] + let colType = [] + let colSize = [] + let colStr = "" + + + for (let i = 0; i < fields.length; i++) { + colName.push(fields[i][0]) + colType.push(fields[i][1]) + + if ((fields[i][1]) == 8 || (fields[i][1]) == 10) { + colSize.push(Math.max(fields[i][0].length, fields[i][2])); //max(column_name.length,column_type_precision) + } else { + colSize.push(Math.max(fields[i][0].length, this._suggestedMinWidths[fields[i][1]]));// max(column_name.length,suggest_column_with_suggestion) + } + // console.log(colSize) + } + colName.forEach((name, i) => { + colStr += this._fillEmpty(Math.floor(colSize[i] / 2 - name.length / 2)) + name.toString() + this._fillEmpty(Math.ceil(colSize[i] / 2 - name.length / 2)) + " | " + }) + + let strSperator = "" + let sizeSum = colSize.reduce((a, b) => a += b, (0)) + colSize.length * 3 + strSperator = this._printN("=", sizeSum) + + console.log("\n" + colStr) + console.log(strSperator) + + data.forEach((row) => { + let rowStr = "" + row.forEach((cell, index) => { + rowStr += cell == null ? 'null' : cell.toString(); + rowStr += this._fillEmpty(colSize[index] - cell.toString().length) + " | " + }) + console.log(rowStr) + }) + + return colStr + } + + _suggestedMinWidths = { + 0: 4, + 1: 4, + 2: 4, + 3: 6, + 4: 11, + 5: 12, + 6: 24, + 7: 24, + 8: 10, + 9: 25, + 10: 10, + } + + _fillEmpty(n) { + let str = ""; + for (let i = 0; i < n; i++) { + str += " "; + } + return str; + } + + _printN(s, n) { + let f = ""; + for (let i = 0; i < n; i++) { + f += s; + } + return f; + } +} \ No newline at end of file diff --git a/src/connector/node-rest/test/testRestConn.js b/src/connector/node-rest/test/testRestConn.js new file mode 100644 index 0000000000..c866d72b5c --- /dev/null +++ b/src/connector/node-rest/test/testRestConn.js @@ -0,0 +1,32 @@ +import {TDRestConnection} from "../TDengineRest"; + +let conn = new TDRestConnection({host: 'u195'}); +let cursor = conn.cursor(); + +(async () => { + result = await cursor.query("drop database if exists node_rest"); + result.toString() +})() + +const createDB = "create database if not exists node_rest"; +const dropDB = "drop database if exists node_rest"; +const createTBL = "CREATE STABLE if not exists node_rest.meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int)"; +const dropTBL = "drop table if exists node_rest.meters "; +const insert = "INSERT INTO node_rest.d1001 USING node_rest.meters TAGS (\"Beijng.Chaoyang\", 2) VALUES (now, 10.2, 219, 0.32) "; +const select = "select * from node_rest.d1001 "; +const selectStbl = "select * from node_rest.meters"; + +async function execute(sql) { + console.log(sql); + result = await cursor.query(sql); + result.toString() +}; + +(async () => { + await execute(createDB); + await execute(createTBL); + await execute(insert); + await execute(select); + await execute(selectStbl); + await execute(dropDB); +})() -- GitLab