Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Test nodejs websocket
- name: Run tests
working-directory: nodejs-connector/nodejs
env:
TDENGINE_CLOUD_URL: ${{ secrets.TDENGINE_CLOUD_URL }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/compatibility.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: nodejs Compatibility
name: Node.js Compatibility

on:
push:
Expand Down
59 changes: 59 additions & 0 deletions .github/workflows/enterprise.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Node.js Enterprise

on:
push:
branches:
- "main"
- "3.0"
pull_request:
branches:
- "main"
- "3.0"

jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [16.x, 20.x]
steps:
- name: Get TDengine
run: wget "${{ secrets.NIGHTLY_TDENGINE_ENTERPRISE_BASE_URL }}/tsdb-nightly-3.0.tar.gz?v=$(date +%s)" -O tsdb-nightly-3.0.tar.gz

- name: Install TDengine
run: |
tar -zxf tsdb-nightly-3.0.tar.gz
rm -rf tsdb-nightly-3.0.tar.gz
cd tsdb-nightly-3.0
sudo ./install.sh

- name: Start TDengine
run: |
sudo mkdir -p /etc/taos
sudo mkdir -p /var/log/taos
nohup sudo taosd &
nohup sudo taosadapter &

- name: Checkout
uses: actions/checkout@v4
with:
path: "nodejs-connector"
clean: true
set-safe-directory: true

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- name: Run tests
working-directory: nodejs-connector/nodejs
env:
TDENGINE_CLOUD_URL: ${{ secrets.TDENGINE_CLOUD_URL }}
TDENGINE_CLOUD_TOKEN: ${{ secrets.TDENGINE_CLOUD_TOKEN }}
TEST_ENTERPRISE: true
run: |
npm install
npm list
npm run example
npm run test
77 changes: 33 additions & 44 deletions nodejs/src/client/wsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { WSVersionResponse, WSQueryResponse } from "./wsResponse";
import { ReqId } from "../common/reqid";
import logger from "../common/log";
import { safeDecodeURIComponent, compareVersions, maskPasswordForLog } from "../common/utils";
import { safeDecodeURIComponent, compareVersions, maskSensitiveForLog, maskUrlForLog } from "../common/utils";
import { w3cwebsocket } from "websocket";
import { TSDB_OPTION_CONNECTION } from "../common/constant";

Expand All @@ -21,16 +21,19 @@ export class WsClient {
private readonly _url: URL;
private static readonly _minVersion = "3.3.2.0";
private _version?: string | undefined | null;
private _bearerToken?: string | undefined | null;

constructor(url: URL, timeout?: number | undefined | null) {
this.checkURL(url);
this._url = url;
this._timeout = timeout;
if (this._url.searchParams.has("timezone")) {
this._timezone =
this._url.searchParams.get("timezone") || undefined;
this._timezone = this._url.searchParams.get("timezone") || undefined;
this._url.searchParams.delete("timezone");
}
if (this._url.searchParams.has("bearer_token")) {
this._bearerToken = this._url.searchParams.get("bearer_token") || undefined;
}
}

async connect(database?: string | undefined | null): Promise<void> {
Expand All @@ -42,11 +45,12 @@ export class WsClient {
password: safeDecodeURIComponent(this._url.password),
db: database,
...(this._timezone && { tz: this._timezone }),
...(this._bearerToken && { bearer_token: this._bearerToken }),
},
};
if (logger.isDebugEnabled()) {
logger.debug("[wsClient.connect.connMsg]===>" + JSONBig.stringify(connMsg, (key, value) =>
key === "password" ? "[REDACTED]" : value
(key === "password" || key === "bearer_token") ? "[REDACTED]" : value
));
}
this._wsConnector = await WebSocketConnectionPool.instance().getConnection(
Expand All @@ -58,34 +62,25 @@ export class WsClient {
}
try {
await this._wsConnector.ready();
let result: any = await this._wsConnector.sendMsg(
JSON.stringify(connMsg)
);
let result: any = await this._wsConnector.sendMsg(JSON.stringify(connMsg));
if (result.msg.code == 0) {
return;
}
await this.close();
throw new WebSocketQueryError(result.msg.code, result.msg.message);
} catch (e: any) {
await this.close();
logger.error(
`connection creation failed, url: ${this._url}, code:${e.code}, msg:${e.message}`
);
const maskedUrl = maskUrlForLog(this._url);
logger.error(`connection creation failed, url: ${maskedUrl}, code:${e.code}, msg:${e.message}`);
throw new TDWebSocketClientError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`connection creation failed, url: ${this._url}, code:${e.code}, msg:${e.message}`
`connection creation failed, url: ${maskedUrl}, code:${e.code}, msg:${e.message}`
);
}
}

async setOptionConnection(
option: TSDB_OPTION_CONNECTION,
value: string | null
): Promise<void> {
logger.debug(
"[wsClient.setOptionConnection]===>" + option + ", " + value
);

async setOptionConnection(option: TSDB_OPTION_CONNECTION, value: string | null): Promise<void> {
logger.debug("[wsClient.setOptionConnection]===>" + option + ", " + value);
let connMsg = {
action: "options_connection",
args: {
Expand Down Expand Up @@ -125,7 +120,7 @@ export class WsClient {
async exec(queryMsg: string, bSqlQuery: boolean = true): Promise<any> {
return new Promise((resolve, reject) => {
if (logger.isDebugEnabled()) {
logger.debug("[wsQueryInterface.query.queryMsg]===>" + maskPasswordForLog(queryMsg));
logger.debug("[wsQueryInterface.query.queryMsg]===>" + maskSensitiveForLog(queryMsg));
}
if (
this._wsConnector &&
Expand Down Expand Up @@ -221,27 +216,25 @@ export class WsClient {

async ready(): Promise<void> {
try {
this._wsConnector =
await WebSocketConnectionPool.instance().getConnection(
this._url,
this._timeout
);
this._wsConnector = await WebSocketConnectionPool.instance().getConnection(
this._url,
this._timeout
);
if (this._wsConnector.readyState() !== w3cwebsocket.OPEN) {
await this._wsConnector.ready();
}
logger.debug(
"ready status ",
this._url,
this._wsConnector.readyState()
);
if (logger.isDebugEnabled()) {
logger.debug("ready status ", maskUrlForLog(this._url), this._wsConnector.readyState());
}
return;
} catch (e: any) {
const maskedUrl = maskUrlForLog(this._url);
logger.error(
`connection creation failed, url: ${this._url}, code: ${e.code}, message: ${e.message}`
`connection creation failed, url: ${maskedUrl}, code: ${e.code}, message: ${e.message}`
);
throw new TDWebSocketClientError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`connection creation failed, url: ${this._url}, code: ${e.code}, message: ${e.message}`
`connection creation failed, url: ${maskedUrl}, code: ${e.code}, message: ${e.message}`
);
}
}
Expand Down Expand Up @@ -321,23 +314,19 @@ export class WsClient {
if (this._wsConnector.readyState() !== w3cwebsocket.OPEN) {
await this._wsConnector.ready();
}
let result: any = await this._wsConnector.sendMsg(
JSONBig.stringify(versionMsg)
);
let result: any = await this._wsConnector.sendMsg(JSONBig.stringify(versionMsg));
if (result.msg.code == 0) {
return new WSVersionResponse(result).version;
}
throw new WebSocketInterfaceError(
result.msg.code,
result.msg.message
);
throw new WebSocketInterfaceError(result.msg.code, result.msg.message);
} catch (e: any) {
const maskedUrl = maskUrlForLog(this._url);
logger.error(
`connection creation failed, url: ${this._url}, code: ${e.code}, message: ${e.message}`
`connection creation failed, url: ${maskedUrl}, code: ${e.code}, message: ${e.message}`
);
throw new TDWebSocketClientError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`connection creation failed, url: ${this._url}, code: ${e.code}, message: ${e.message}`
`connection creation failed, url: ${maskedUrl}, code: ${e.code}, message: ${e.message}`
);
}
}
Expand All @@ -354,12 +343,12 @@ export class WsClient {
}

checkURL(url: URL) {
// Assert is cloud url
if (!url.searchParams.has("token")) {
// Assert token or bearer_token exists, otherwise username and password must exist.
if (!url.searchParams.get("token") && !url.searchParams.get("bearer_token")) {
if (!(url.username || url.password)) {
throw new WebSocketInterfaceError(
ErrorCode.ERR_INVALID_AUTHENTICATION,
"invalid url, password or username needed."
`invalid url, provide non-empty "token" or "bearer_token", or provide username/password`
);
}
}
Expand Down
22 changes: 8 additions & 14 deletions nodejs/src/client/wsConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { OnMessageType, WsEventCallback } from "./wsEventCallback";
import logger from "../common/log";
import { ReqId } from "../common/reqid";
import { maskPasswordForLog } from "../common/utils";
import { maskSensitiveForLog, maskUrlForLog } from "../common/utils";

export class WebSocketConnector {
private _wsConn: w3cwebsocket;
Expand Down Expand Up @@ -35,13 +35,9 @@ export class WebSocketConnector {
}
);
this._wsConn.onerror = function (err: Error) {
logger.error(
`webSocket connection failed, url: ${this.url}, error: ${err.message}`
);
logger.error(`webSocket connection failed, url: ${maskUrlForLog(new URL(this.url))}, error: ${err.message}`);
};

this._wsConn.onclose = this._onclose;

this._wsConn.onmessage = this._onmessage;
this._wsConn._binaryType = "arraybuffer";
} else {
Expand Down Expand Up @@ -87,9 +83,7 @@ export class WebSocketConnector {

private _onmessage(event: any) {
let data = event.data;
logger.debug(
"wsClient._onMessage()====" + Object.prototype.toString.call(data)
);
logger.debug("wsClient._onMessage()====" + Object.prototype.toString.call(data));
if (Object.prototype.toString.call(data) === "[object ArrayBuffer]") {
let id = new DataView(data, 26, 8).getBigUint64(0, true);
WsEventCallback.instance().handleEventCallback(
Expand Down Expand Up @@ -143,7 +137,7 @@ export class WebSocketConnector {
reject(
new WebSocketQueryError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`WebSocket connection is not ready,status :${this._wsConn?.readyState}`
`WebSocket connection is not ready, status: ${this._wsConn?.readyState}`
)
);
}
Expand All @@ -152,7 +146,7 @@ export class WebSocketConnector {

async sendMsg(message: string, register: Boolean = true) {
if (logger.isDebugEnabled()) {
logger.debug("[wsClient.sendMessage()]===>" + maskPasswordForLog(message));
logger.debug("[wsClient.sendMessage()]===>" + maskSensitiveForLog(message));
}
let msg = JSON.parse(message);
if (msg.args.id !== undefined) {
Expand All @@ -174,14 +168,14 @@ export class WebSocketConnector {
);
}
if (logger.isDebugEnabled()) {
logger.debug("[wsClient.sendMessage.msg]===>" + maskPasswordForLog(message));
logger.debug("[wsClient.sendMessage.msg]===>" + maskSensitiveForLog(message));
}
this._wsConn.send(message);
} else {
reject(
new WebSocketQueryError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`WebSocket connection is not ready,status :${this._wsConn?.readyState}`
`WebSocket connection is not ready, status: ${this._wsConn?.readyState}`
)
);
}
Expand Down Expand Up @@ -219,7 +213,7 @@ export class WebSocketConnector {
reject(
new WebSocketQueryError(
ErrorCode.ERR_WEBSOCKET_CONNECTION_FAIL,
`WebSocket connection is not ready,status :${this._wsConn?.readyState}`
`WebSocket connection is not ready, status: ${this._wsConn?.readyState}`
)
);
}
Expand Down
Loading
Loading