未验证 提交 c323744d 编写于 作者: X xiaolei li 提交者: GitHub

[TD-13707]<feature>:TypeScript RESTful (#11171)

* [TD-14356]<fix>:csharp remove stream_open() && stream_close()

* [TD-13707]<feature>:TypeScript RESTful 1st

* [TD-13707]<feature>:TypeScript RESTful 2st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 2st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 3st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 4st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 5st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 6st

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 7th

* [TD-13707]<feature>:TypeScript RESTful 2st, add CI test script 8th

* [TD-13707]<feature>:TypeScript RESTful 3rd

* [TD-13707]<feature>:TypeScript RESTful 4th

* [TD-13707]<feature>:TypeScript RESTful 5th

* [TD-13707]<feature>:TypeScript RESTful 6th
上级 03302941
package-lock.json
dist/main
dist/module
dist/types.d.ts
node_modules/
tsc/
\ No newline at end of file
import { options, connect } from '../tdengine_rest'
options.path = '/rest/sqlt'
const db = 'rest_ts_db';
const table = 'rest'
const createDB = `create database if not exists ${db} keep 3650`;
const dropDB = `drop database if exists ${db}`;
const createTB = `create table if not exists ${db}.${table}(ts timestamp,i8 tinyint,i16 smallint,i32 int,i64 bigint,bnr binary(40),nchr nchar(40))`;
const addColumn = `alter table ${db}.${table} add column new_column nchar(40) `;
const dropColumn = `alter table ${db}.${table} drop column new_column`;
const insertSql = `insert into ${db}.${table} values('2022-03-30 18:30:51.567',1,2,3,4,'binary1','nchar1')` +
`('2022-03-30 18:30:51.568',5,6,7,8,'binary2','nchar2')` +
`('2022-03-30 18:30:51.569',9,0,1,2,'binary3','nchar3')`;
const querySql = `select * from ${db}.${table}`;
const errorSql = 'show database';
let conn = connect(options);
let cursor = conn.cursor();
async function execute(sql: string, pure = false) {
let result = await cursor.query(sql, pure);
// print query result as taos shell
result.toString();
// Get Result object, return Result object.
console.log("result.getResult()",result.getResult());
// Get status, return 'succ'|'error'.
console.log("result.getStatus()",result.getStatus());
// Get head,return response head (Array<any>|undefined,when execute failed this is undefined).
console.log("result.getHead()",result.getHead());
// Get Meta data, return Meta[]|undefined(when execute failed this is undefined).
console.log("result.getMeta()",result.getMeta());
// Get data,return Array<Array<any>>|undefined(when execute failed this is undefined).
console.log("result.getData()",result.getData());
// Get affect rows,return number|undefined(when execute failed this is undefined).
console.log("result.getAffectRows()",result.getAffectRows());
// Get command,return SQL send to server(need to `query(sql,false)`,set 'pure=false',default true).
console.log("result.getCommand()",result.getCommand());
// Get error code ,return number|undefined(when execute failed this is undefined).
console.log("result.getErrCode()",result.getErrCode());
// Get error string,return string|undefined(when execute failed this is undefined).
console.log("result.getErrStr()",result.getErrStr());
}
(async () => {
let start = new Date().getTime(); // 开始时间
await execute(createDB);
await execute(createTB);
await execute(addColumn);
await execute(dropColumn);
await execute(insertSql);
await execute(querySql);
await execute(errorSql);
await execute(dropDB);
let end = new Date().getTime(); // 结束时间
console.log("total spend time:%d ms",end - start);
})()
import {TDConnect,Options} from './src/connect';
let options:Options = {
host : '127.0.0.1',
port : 6041,
path : '/rest/sql',
user : 'root',
passwd : 'taosdata'
}
let connect = function connect(option:Options){
return new TDConnect(option);
}
export {options,connect}
\ No newline at end of file
{
"name": "td2.0-rest-connector",
"version": "1.0.0",
"description": "A REST connector for TDengine",
"source": "tdengine_rest.ts",
"main": "dist/main/rest.js",
"module": "dist/module/rest.mjs",
"types": "dist/types.d.ts",
"directories": {
"example": "example",
"test": "test"
},
"scripts": {
"test": "parcel build --no-source-maps && node ./dist/test/test.main.js && node ./dist/test/test.module.mjs",
"example": "tsc && node ./tsc/example/example.js",
"build": "parcel build --no-source-maps",
"watch": "parcel watch"
},
"keywords": [
"REST",
"Node.js",
"Typescript",
"TDengine",
"taos",
"IOT",
"node-fetch"
],
"author": "",
"license": "MIT",
"dependencies": {
"node-fetch": "^2.6.7"
},
"devDependencies": {
"@parcel/packager-ts": "^2.4.1",
"@parcel/transformer-typescript-types": "^2.4.1",
"@types/node": "^17.0.23",
"@types/node-fetch": "^2.6.1",
"parcel": "^2.4.0",
"typescript": "^4.6.3"
}
}
# TDengine RESTful
This is a TDengine's RESTful connector in TypeScript. It's depend on [node-fetch v2](https://github.com/node-fetch/node-fetch/tree/2.x). Using `fetch(url,options)` to send sql statement and receive response.
# Usage
```TypeScript
import { options, connect } from '../tdengine_rest'
options.path='/rest/sqlt';
let conn = connect(options);
let cursor = conn.cursor();
(async()=>{
let result = cursor.execute("show database");
// print query result as taos shell
result.toString();
// Get Result object, return Result object.
console.log(result.getResult());
// Get status, return 'succ'|'error'.
console.log(result.getStatus());
// Get head,return response head (Array<any>|undefined,when execute failed this is undefined).
console.log(result.getHead());
// Get Meta data, return Meta[]|undefined(when execute failed this is undefined).
console.log(result.getMeta());
// Get data,return Array<Array<any>>|undefined(when execute failed this is undefined).
console.log(result.getData());
// Get affect rows,return number|undefined(when execute failed this is undefined).
console.log(result.getAffectRows());
// Get command,return SQL send to server(need to `query(sql,false)`,set 'pure=false',default true).
console.log(result.getCommand());
// Get error code ,return number|undefined(when execute failed this is undefined).
console.log(result.getErrCode());
// Get error string,return string|undefined(when execute failed this is undefined).
console.log(result.getErrStr());
})()
```
import { TDengineCursor } from './cursor'
import { Uri, User } from './options'
/**
* Options used to connect with REST(taosAdapter)
* Need to set options with 'host','path','port','user','passwd'.
* connWith is optional attribute for further use.
*/
export interface Options extends Uri, User {
connWith?: 'rest' | 'taosc'
}
/**
* Create connect with TDengine,actually pass options
* to `Cursor` which send and receive HTTP request.
*/
export class TDConnect {
_connOption: Options;
constructor(connOption: Options) {
this._connOption = connOption
}
cursor(): TDengineCursor {
return new TDengineCursor(this._connOption);
}
}
import { Uri, User } from './options'
import { TDResRequest } from './request'
import { Result } from './result'
export class TDengineCursor {
field: Array<any>;
data: Array<any>
_rowCount: number;
_uri: Uri;
_user: User;
constructor(options: any) {
this._uri = {
host: options.host,
path: options.path,
port: options.port,
}
this._user = {
user: options.user,
passwd: options.passwd,
}
this._rowCount = 0;
this.field = [];
this.data = [];
}
async query(sql: string, pure = true): Promise<Result> {
let req = new TDResRequest(this._uri, this._user);
let response = await req.request(sql);
let res_json = await response.json();
if (pure == false) {
return new Result(res_json, sql);
} else {
return new Result(res_json);
}
}
}
\ No newline at end of file
export interface FetchOptions {
method: 'POST';
body: string;
headers: Record<string, string>
}
export interface User {
user: string;
passwd: string;
}
export interface Uri {
host: '127.0.0.1';
path: "/rest/sqlt" | '/rest/sqlutc' | '/rest/sql';
port: 6041;
}
\ No newline at end of file
import { Uri,User,FetchOptions } from "./options";
import fetch from 'node-fetch';
export class TDResRequest {
uri: Uri;
options: FetchOptions;
user:User;
constructor(url: Uri, user:User) {
this.uri = url;
this.user = user;
this.options = {
method: 'POST',
body:'',
headers:{'Authorization':this._token()}
}
}
_makeUrl(): string {
return `http://${this.uri.host}:${this.uri.port}${this.uri.path}`;
}
_token(): string {
return`Basic ${Buffer.from(`${this.user.user}:${this.user.passwd}`).toString('base64')}`
}
_body(command:string):void{
this.options.body = command;
}
request(command:string): Promise<any> {
this._body(command);
return fetch(this._makeUrl(), this.options);
}
}
interface IResult {
status: string;
head?: Array<string>;
column_meta?: Array<Array<any>>;
data?: Array<Array<any>>;
rows?: number;
command?: string;
//for error
code?: number;
desc?: string;
}
interface meta {
columnName: string;
code: number;
size: number;
typeName?: string;
}
export class Result {
private _status: string;
private _head?: string[];
private _column_meta?: Array<meta>;
private _data?: Array<Array<any>>;
private _rows?: number;
private _command?: string;
//for error
private _code?: number;
private _desc?: string;
constructor(res: IResult, commands?: string) {
let meta_list_length = res.column_meta == undefined ? 0 : res.column_meta.length
if (res.status === 'succ') {
this._status = res.status;
this._head = res.head;
this._column_meta = new Array(meta_list_length);
this._data = res.data;
this._rows = res.rows;
this._command = commands;
this._initMeta(res);
this._code = undefined;
this._desc = undefined;
} else {
this._status = res.status;
this._head = undefined;
this._column_meta = undefined;
this._data = undefined;
this._rows = undefined;
this._command = commands;
this._code = res.code;
this._desc = res.desc;
}
}
private _initMeta(res: IResult): void {
if (res.column_meta != undefined) {
res.column_meta.forEach((item, index) => {
if (this._column_meta != undefined)
this._column_meta[index] = {
columnName: item[0],
code: item[1],
size: item[2],
typeName: typeNameMap[item[1]]
}
})
}
}
getResult(): Result {
return this;
}
getStatus(): string {
return this._status;
}
getHead(): Array<any> | undefined {
return this._head;
}
getMeta(): Array<meta> | undefined {
return this._column_meta;
}
getData(): Array<Array<any>> | undefined {
return this._data;
}
getAffectRows(): number | undefined {
return this._rows;
}
getCommand(): string | undefined {
return this._command;
}
getErrCode(): number | undefined {
return this._code;
}
getErrStr(): string | undefined {
return this._desc;
}
toString(): void {
let str = '';
if(this._command != undefined){
console.log(this._command);
}
if (this._status === 'succ' && this._column_meta != undefined && this._data != undefined) {
str = this._prettyStr(this._column_meta, this._data)
} else {
str = `Execute ${this._status},reason:${this._desc}. error_no:${this._code} `;
console.log(str)
}
}
private _prettyStr(fields: Array<meta>, data: Array<Array<any>>): string {
let colName = new Array<string>();
let colType = new Array<string | undefined>();
let colSize = new Array<number>();
let colStr = '';
for (let i = 0; i < fields.length; i++) {
colName.push(fields[i].columnName)
colType.push(fields[i].typeName);
if ((fields[i].code) == 8 || (fields[i].code) == 10) {
colSize.push(Math.max(fields[i].columnName.length, fields[i].size)); //max(column_name.length,column_type_precision)
} else {
colSize.push(Math.max(fields[i].columnName.length, suggestedMinWidths[fields[i].size]));// 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
}
private _fillEmpty(n:number) {
let str = "";
for (let i = 0; i < n; i++) {
str += " ";
}
return str;
}
private _printN(s:string, n:number) {
let f = "";
for (let i = 0; i < n; i++) {
f += s;
}
return f;
}
}
interface indexableString {
[index: number]: string
}
/**
* this file record TDengine's data type and code.
*/
const typeNameMap: indexableString = {
0: 'null',
1: 'bool',
2: 'tinyint',
3: 'smallint',
4: 'int',
5: 'bigint',
6: 'float',
7: 'double',
8: 'binary',
9: 'timestamp',
10: 'nchar',
11: 'unsigned tinyint',
12: 'unsigned smallint',
13: 'unsigned int',
14: 'unsigned bigint',
15: 'json'
}
interface indexableNumber {
[index: number]: number
}
const suggestedMinWidths: indexableNumber = {
0: 4,
1: 4,
2: 4,
3: 6,
4: 11,
5: 12,
6: 24,
7: 24,
8: 10,
9: 25,
10: 10,
}
import {TDConnect,Options} from './src/connect';
let options:Options = {
host : '127.0.0.1',
port : 6041,
path : '/rest/sql',
user : 'root',
passwd : 'taosdata'
}
let connect = function connect(option:Options){
return new TDConnect(option);
}
export {options,connect}
\ No newline at end of file
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
"outDir": "./tsc/", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
#!/bin/bash
function stopProcess {
echo "Stop $1"
sudo systemctl stop $1 || echo 'no sudo or systemctl or stop fail'
PID=`ps -ef|grep -w $1 | grep -v grep | awk '{print $2}'`
while [ -n "$PID" ]
do
pkill -TERM -x $1
sleep 1
PID=`ps -ef|grep -w $1 | grep -v grep | awk '{print $2}'`
done
}
stopProcess taosadapter
stopProcess taosd
rm -rf /var/lib/taos/*
rm -rf /var/log/taos/*
nohup taosd -c ${taosdConfig} > /dev/null 2>&1 &
nohup taosadapter -c ${adapterConfig} > /dev/null 2>&1 &
sleep 10
# echo `pwd`
cd ../../
WKC=`pwd`
echo ${WKC}
cd ${WKC}/src/connector/TypeScript-REST
npm install
npm run example
# npm run test
...@@ -311,6 +311,7 @@ ...@@ -311,6 +311,7 @@
20,,develop-test,python3 test.py -f 2-query/ltrim_func.py 20,,develop-test,python3 test.py -f 2-query/ltrim_func.py
20,,develop-test,python3 test.py -f 2-query/rtrim_func.py 20,,develop-test,python3 test.py -f 2-query/rtrim_func.py
20,,develop-test,python3 test.py -f 2-query/substr_func.py 20,,develop-test,python3 test.py -f 2-query/substr_func.py
20,,develop-test,bash 3-connectors/TypeScript-REST/test.sh
20,,pytest,python3 test.py -f query/query.py 20,,pytest,python3 test.py -f query/query.py
20,,pytest,python3 test.py -f import_merge/importLastTO.py 20,,pytest,python3 test.py -f import_merge/importLastTO.py
20,,pytest,python3 test.py -f import_merge/importDataSub.py 20,,pytest,python3 test.py -f import_merge/importDataSub.py
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册