@aicore/cocodb-ws-client 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -23
- package/package.json +11 -11
- package/src/index.js +46 -0
- package/src/utils/client.js +256 -54
package/README.md
CHANGED
|
@@ -2,33 +2,74 @@
|
|
|
2
2
|
WebSocket client for cocoDb. This will client can pipeline the requests to increase through put connection
|
|
3
3
|
|
|
4
4
|
## Code Guardian
|
|
5
|
-
[](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml)
|
|
6
|
+
|
|
7
|
+
<a href="https://sonarcloud.io/summary/new_code?id=aicore_cocoDbWsClient">
|
|
8
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=alert_status" alt="Sonar code quality check" />
|
|
9
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=security_rating" alt="Security rating" />
|
|
10
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=vulnerabilities" alt="vulnerabilities" />
|
|
11
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=coverage" alt="Code Coverage" />
|
|
12
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=bugs" alt="Code Bugs" />
|
|
13
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=reliability_rating" alt="Reliability Rating" />
|
|
14
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=sqale_rating" alt="Maintainability Rating" />
|
|
15
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=ncloc" alt="Lines of Code" />
|
|
16
|
+
<img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=sqale_index" alt="Technical debt" />
|
|
17
17
|
</a>
|
|
18
18
|
|
|
19
|
+
## Installing the library
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @aicore/cocodb-ws-client
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## importing in your js file
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import * as coco from "@aicore/cocodb-ws-client"; // to import all functions
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Initializing the client
|
|
32
|
+
|
|
33
|
+
Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
|
|
34
|
+
be maintained and it will try to automatically re-establish broken connections if there are network issues.
|
|
35
|
+
You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
|
|
36
|
+
not fully setup will throw an error.
|
|
37
|
+
|
|
38
|
+
### Parameters
|
|
39
|
+
|
|
40
|
+
* `cocoDbServiceEndPoint` **\[string]\[1]** The URL of the coco-db service.
|
|
41
|
+
* `authKey` **\[string]\[1]** The authKey is a base64 encoded string of the username and password.
|
|
42
|
+
|
|
43
|
+
Returns **\[Promise]\[2]\<null>** Resolves when the cocodb client is ready to send/receive requests for the first time.
|
|
44
|
+
Rejects only if the user calls `close` API before any connection is established.
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
await db.init("ws://endpoint.coco", "your_auth_key");
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Detailed API Docs
|
|
51
|
+
|
|
52
|
+
See this wiki for detailed API docs
|
|
53
|
+
|
|
54
|
+
* [https://github.com/aicore/cocoDbWsClient/wiki/api-API][1]
|
|
55
|
+
* More coco lib level detailed docs can be found at [https://github.com/aicore/libmysql/wiki/db-API][2]
|
|
56
|
+
|
|
57
|
+
## close the client
|
|
58
|
+
|
|
59
|
+
Closes the connection to the server. You need to await on this function before you can call init again.
|
|
60
|
+
|
|
61
|
+
Returns **\[Promise]\[2]\<null>** Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
await db.close();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
[1]: https://github.com/aicore/cocoDbWsClient/wiki/api-API
|
|
68
|
+
|
|
69
|
+
[2]: https://github.com/aicore/libmysql/wiki/db-API
|
|
19
70
|
|
|
20
|
-
# TODOs after template use
|
|
21
|
-
## !!!Please see all issues in the generated repository as an issue will be generated tracking the fix of each of the below items.
|
|
22
|
-
1. Update package.json with your app defaults
|
|
23
|
-
2. Check Build actions on pull requests.
|
|
24
|
-
3. create a home page in wiki by going to wiki link https://github.com/<your_org>/<your_repo>/wiki
|
|
25
|
-
4. Goto github `repository` > `settings`> and uncheck `Allow merge commits`. this is usually automatically done by code guardian bots in core.ai org. so you may just need to verify it.
|
|
26
|
-
5. In sonar cloud, enable Automatic analysis from `Administration
|
|
27
|
-
Analysis Method` for the first time before a pull request is raised: 
|
|
28
|
-
6. Check codacy runs on pull requests, set codacy defaults. You may remove codacy if sonar cloud is only needed.
|
|
29
|
-
7. Update the above Code Guardian badges; change all `id=aicore_template-nodejs-ts` to the sonar id of your project fields. see this PR: https://github.com/aicore/libcache/pull/13
|
|
30
71
|
|
|
31
|
-
#
|
|
72
|
+
# Development notes
|
|
32
73
|
|
|
33
74
|
## Building
|
|
34
75
|
Since this is a pure JS template project, build command just runs test with coverage.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicore/cocodb-ws-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Websocket client for cocoDb",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -47,23 +47,23 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/aicore/cocoDbWsClient#readme",
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@commitlint/cli": "17.
|
|
51
|
-
"@commitlint/config-conventional": "17.
|
|
50
|
+
"@commitlint/cli": "17.4.2",
|
|
51
|
+
"@commitlint/config-conventional": "17.4.2",
|
|
52
52
|
"c8": "7.12.0",
|
|
53
|
-
"chai": "4.3.
|
|
53
|
+
"chai": "4.3.7",
|
|
54
54
|
"cli-color": "2.0.3",
|
|
55
|
-
"documentation": "14.0.
|
|
56
|
-
"eslint": "8.
|
|
57
|
-
"glob": "8.0
|
|
58
|
-
"husky": "8.0.
|
|
59
|
-
"mocha": "10.
|
|
55
|
+
"documentation": "14.0.1",
|
|
56
|
+
"eslint": "8.33.0",
|
|
57
|
+
"glob": "8.1.0",
|
|
58
|
+
"husky": "8.0.3",
|
|
59
|
+
"mocha": "10.2.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@aicore/libcommonutils": "1.0.19",
|
|
63
|
-
"ws": "8.
|
|
63
|
+
"ws": "8.12.0"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
66
|
"bufferutil": "4.0.7",
|
|
67
|
-
"utf-8-validate": "
|
|
67
|
+
"utf-8-validate": "6.0.2"
|
|
68
68
|
}
|
|
69
69
|
}
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
// @INCLUDE_IN_API_DOCS
|
|
20
|
+
|
|
19
21
|
export {
|
|
20
22
|
get,
|
|
21
23
|
init,
|
|
@@ -35,3 +37,47 @@ export {
|
|
|
35
37
|
query
|
|
36
38
|
|
|
37
39
|
} from './utils/api.js';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ## Installing the library
|
|
43
|
+
* ```bash
|
|
44
|
+
* npm install @aicore/cocodb-ws-client
|
|
45
|
+
* ```
|
|
46
|
+
* ## importing in your js file
|
|
47
|
+
* ```js
|
|
48
|
+
* import * as coco from "@aicore/cocodb-ws-client"; // to import all functions
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* ## Initializing the client
|
|
52
|
+
*
|
|
53
|
+
* Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
|
|
54
|
+
* be maintained and it will try to automatically re-establish broken connections if there are network issues.
|
|
55
|
+
* You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
|
|
56
|
+
* not fully setup will throw an error.
|
|
57
|
+
*
|
|
58
|
+
* ### Parameters
|
|
59
|
+
*
|
|
60
|
+
* * `cocoDbServiceEndPoint` **[string][1]** The URL of the coco-db service.
|
|
61
|
+
* * `authKey` **[string][1]** The authKey is a base64 encoded string of the username and password.
|
|
62
|
+
*
|
|
63
|
+
* Returns **[Promise][2]\<null>** Resolves when the cocodb client is ready to send/receive requests for the first time.
|
|
64
|
+
* Rejects only if the user calls `close` API before any connection is established.
|
|
65
|
+
* ```js
|
|
66
|
+
* await db.init("ws://endpoint.coco", "your_auth_key");
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* ## Detailed API Docs
|
|
70
|
+
* See this wiki for detailed API docs
|
|
71
|
+
* * https://github.com/aicore/cocoDbWsClient/wiki/api-API
|
|
72
|
+
* * More coco lib level detailed docs can be found at https://github.com/aicore/libmysql/wiki/db-API
|
|
73
|
+
*
|
|
74
|
+
* ## close the client
|
|
75
|
+
*
|
|
76
|
+
* Closes the connection to the server. You need to await on this function before you can call init again.
|
|
77
|
+
*
|
|
78
|
+
* Returns **[Promise][2]\<null>** Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
|
|
79
|
+
* ```js
|
|
80
|
+
* await db.close();
|
|
81
|
+
* ```
|
|
82
|
+
* @module @aicore/cocodb-ws-client
|
|
83
|
+
*/
|
package/src/utils/client.js
CHANGED
|
@@ -1,19 +1,191 @@
|
|
|
1
1
|
import {WS} from "./WebSocket.js";
|
|
2
2
|
import {isString, isObject, isStringEmpty, COCO_DB_FUNCTIONS} from "@aicore/libcommonutils";
|
|
3
3
|
|
|
4
|
-
let client = null
|
|
4
|
+
let client = null,
|
|
5
|
+
cocoDBEndPointURL = null,
|
|
6
|
+
cocoAuthKey = null,
|
|
7
|
+
hibernateTimer = null,
|
|
8
|
+
bufferRequests = false,
|
|
9
|
+
pendingSendMessages = [];
|
|
10
|
+
const MAX_PENDING_SEND_BUFFER_SIZE = 2000;
|
|
5
11
|
const WEBSOCKET_ENDPOINT_COCO_DB = '/ws/';
|
|
6
|
-
const ID_TO_RESOLVE_REJECT_MAP =
|
|
7
|
-
|
|
12
|
+
const ID_TO_RESOLVE_REJECT_MAP = new Map();
|
|
13
|
+
const CONNECT_BACKOFF_TIME_MS = [1, 500, 1000, 3000, 5000, 10000, 20000];
|
|
14
|
+
const INACTIVITY_TIME_FOR_HIBERNATE = 8000;
|
|
15
|
+
let id = 0, activityInHibernateInterval = 0;
|
|
16
|
+
|
|
17
|
+
let currentBackoffIndex = 0;
|
|
18
|
+
function _resetBackoffTime() {
|
|
19
|
+
currentBackoffIndex = 0;
|
|
20
|
+
}
|
|
21
|
+
function _getBackoffTime() {
|
|
22
|
+
if(currentBackoffIndex >= CONNECT_BACKOFF_TIME_MS.length){
|
|
23
|
+
currentBackoffIndex = CONNECT_BACKOFF_TIME_MS.length - 1;
|
|
24
|
+
}
|
|
25
|
+
return CONNECT_BACKOFF_TIME_MS[currentBackoffIndex++];
|
|
26
|
+
}
|
|
8
27
|
|
|
9
28
|
// @INCLUDE_IN_API_DOCS
|
|
10
29
|
|
|
30
|
+
|
|
31
|
+
function _checkActivityForHibernation() {
|
|
32
|
+
if(activityInHibernateInterval > 0){
|
|
33
|
+
activityInHibernateInterval = 0;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if(!client || client.hibernating
|
|
37
|
+
|| !client.connectionEstablished // cant hibernate if connection isnt already established/ is being establised
|
|
38
|
+
|| ID_TO_RESOLVE_REJECT_MAP.size > 0){ // if there are any pending responses, we cant hibernate
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// hibernate
|
|
42
|
+
client.hibernating = true;
|
|
43
|
+
client.hibernatingPromise = new Promise((resolve=>{
|
|
44
|
+
client.hibernatingPromiseResolve = resolve;
|
|
45
|
+
}));
|
|
46
|
+
bufferRequests = true;
|
|
47
|
+
client.terminate();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* returns a promise that resolves when hibernation ends.
|
|
52
|
+
* @return {Promise<unknown>}
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
function _toAwakeFromHibernate() {
|
|
56
|
+
return client.hibernatingPromise;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _wakeupHibernatingClient() {
|
|
60
|
+
if(client.hibernatingPromiseResolved) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
client.hibernatingPromiseResolve();
|
|
64
|
+
client.hibernatingPromiseResolved = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sets up the websocket client and returns a promise that will be resolved when the connection is closed or broken.
|
|
69
|
+
*
|
|
70
|
+
* @param {function} connectedCb a callbak that will be executed when a connection is established to db
|
|
71
|
+
* @return {Promise<null>} promise that will be resolved when the connection is closed/broken/error.
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
function _setupClientAndWaitForClose(connectedCb) {
|
|
75
|
+
return new Promise(resolve =>{
|
|
76
|
+
client = new WS.WebSocket(cocoDBEndPointURL.trim() + WEBSOCKET_ENDPOINT_COCO_DB, {
|
|
77
|
+
perMessageDeflate: false,
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: `Basic ${cocoAuthKey}`
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
client.on('open', function open() {
|
|
83
|
+
console.log('connected to server');
|
|
84
|
+
client.connectionEstablished = true;
|
|
85
|
+
bufferRequests = false;
|
|
86
|
+
connectedCb && connectedCb();
|
|
87
|
+
_sendPendingMessages();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
client.on('message', function message(data) {
|
|
91
|
+
__receiveMessage(data);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
function _connectionTerminated(reason) {
|
|
95
|
+
console.log(reason);
|
|
96
|
+
client.connectionEstablished = false;
|
|
97
|
+
for (let [sequenceNumber, handler] of ID_TO_RESOLVE_REJECT_MAP) {
|
|
98
|
+
handler.reject(reason);
|
|
99
|
+
ID_TO_RESOLVE_REJECT_MAP.delete(sequenceNumber);
|
|
100
|
+
}
|
|
101
|
+
resolve();
|
|
102
|
+
}
|
|
103
|
+
client.on('close', function () {
|
|
104
|
+
// https://websockets.spec.whatwg.org/#eventdef-websocket-error
|
|
105
|
+
// https://stackoverflow.com/questions/40084398/is-onclose-always-called-after-onerror-for-websocket
|
|
106
|
+
// we do not need to listen for error event as an error event is immediately followed by a close event.
|
|
107
|
+
_connectionTerminated('connection closed');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let backoffTimer = null, backoffResolveFn = null;
|
|
113
|
+
function _backoffTimer(timeInMilliSec) {
|
|
114
|
+
return new Promise(resolve => {
|
|
115
|
+
backoffResolveFn = resolve;
|
|
116
|
+
backoffTimer = setTimeout(()=>{
|
|
117
|
+
backoffTimer = null;
|
|
118
|
+
backoffResolveFn = null;
|
|
119
|
+
resolve();
|
|
120
|
+
}, timeInMilliSec);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _cancelBackoffTimer() {
|
|
125
|
+
_resetBackoffTime();
|
|
126
|
+
if(backoffTimer){
|
|
127
|
+
clearTimeout(backoffTimer);
|
|
128
|
+
backoffTimer = null;
|
|
129
|
+
backoffResolveFn();
|
|
130
|
+
backoffResolveFn = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB) {
|
|
135
|
+
backoffTimer = null;
|
|
136
|
+
function connected() {
|
|
137
|
+
_resetBackoffTime();
|
|
138
|
+
if(firstConnectionCb){
|
|
139
|
+
firstConnectionCb("connected");
|
|
140
|
+
firstConnectionCb = null;
|
|
141
|
+
// setup hibernate timer on first connection
|
|
142
|
+
activityInHibernateInterval = 1;
|
|
143
|
+
hibernateTimer = setInterval(_checkActivityForHibernation, INACTIVITY_TIME_FOR_HIBERNATE);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
while(!client || !client.userClosedConnection){
|
|
147
|
+
await _setupClientAndWaitForClose(connected);
|
|
148
|
+
if(client && client.hibernating && !client.userClosedConnection){
|
|
149
|
+
await _toAwakeFromHibernate();
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if(!client || !client.userClosedConnection){
|
|
153
|
+
await _backoffTimer(_getBackoffTime());
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if(hibernateTimer){
|
|
157
|
+
clearInterval(hibernateTimer);
|
|
158
|
+
hibernateTimer = null;
|
|
159
|
+
}
|
|
160
|
+
client && client.userClosedConnectionCB && client.userClosedConnectionCB();
|
|
161
|
+
client = cocoDBEndPointURL = cocoAuthKey = null;
|
|
162
|
+
id = 0;
|
|
163
|
+
if(neverConnectedCB){
|
|
164
|
+
neverConnectedCB(new Error("user Cancelled"));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
11
168
|
/**
|
|
12
|
-
*
|
|
169
|
+
* Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
|
|
170
|
+
* be maintained and, it will try to automatically re-establish broken connections if there are network issues.
|
|
171
|
+
* You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
|
|
172
|
+
* not fully setup will throw an error.
|
|
173
|
+
*
|
|
174
|
+
* ## Hibernation after inactivity
|
|
175
|
+
* After around 10 seconds of no send activity and if there are no outstanding requests, the db connection will be
|
|
176
|
+
* dropped and the client move into a hibernation state. The connection will be immediately re-established on any
|
|
177
|
+
* db activity transparently, though a slight jitter may be observed during the connection establishment time. This
|
|
178
|
+
* auto start-stop will save database resources as servers can be on for months on end.
|
|
179
|
+
*
|
|
13
180
|
* @param {string} cocoDbServiceEndPoint - The URL of the coco-db service.
|
|
14
181
|
* @param {string} authKey - The authKey is a base64 encoded string of the username and password.
|
|
182
|
+
* @return {Promise<null>} Resolves when the cocodb client is ready to send/receive requests for the first time.
|
|
183
|
+
* Rejects only if the user calls `close` API before any connection is established.
|
|
15
184
|
*/
|
|
16
185
|
export function init(cocoDbServiceEndPoint, authKey) {
|
|
186
|
+
if(client) {
|
|
187
|
+
throw new Error('Please close the existing connection before calling init again');
|
|
188
|
+
}
|
|
17
189
|
if (isStringEmpty(cocoDbServiceEndPoint) || !(cocoDbServiceEndPoint.startsWith('ws://')
|
|
18
190
|
|| cocoDbServiceEndPoint.startsWith('wss://'))) {
|
|
19
191
|
throw new Error('Please provide valid cocoDbServiceEndPoint');
|
|
@@ -21,40 +193,45 @@ export function init(cocoDbServiceEndPoint, authKey) {
|
|
|
21
193
|
if (isStringEmpty(authKey)) {
|
|
22
194
|
throw new Error('Please provide valid authKey');
|
|
23
195
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
client.on('open', function open() {
|
|
31
|
-
console.log('connected to server');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
client.on('message', function message(data) {
|
|
35
|
-
__receiveMessage(data);
|
|
36
|
-
});
|
|
37
|
-
client.on('close', function terminate() {
|
|
38
|
-
console.log('closing connection');
|
|
39
|
-
for (let sequenceNumber in ID_TO_RESOLVE_REJECT_MAP) {
|
|
40
|
-
let reject = ID_TO_RESOLVE_REJECT_MAP[sequenceNumber].reject;
|
|
41
|
-
reject('connection closed');
|
|
42
|
-
delete ID_TO_RESOLVE_REJECT_MAP[sequenceNumber];
|
|
43
|
-
}
|
|
44
|
-
client = null;
|
|
45
|
-
id = 0;
|
|
196
|
+
cocoDBEndPointURL = cocoDbServiceEndPoint;
|
|
197
|
+
cocoAuthKey = authKey;
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
_setupAndMaintainConnection(resolve, reject);
|
|
46
200
|
});
|
|
47
201
|
}
|
|
48
202
|
|
|
49
203
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
204
|
+
* Closes the connection to the server. You need to await on this function before you can call init again.
|
|
205
|
+
*
|
|
206
|
+
* @return {Promise<null>} Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
|
|
52
207
|
*/
|
|
53
208
|
export function close() {
|
|
54
|
-
|
|
55
|
-
|
|
209
|
+
// we need to save the current client for the callback function hooks below. the global client may change
|
|
210
|
+
// as init and close gets called by the user.
|
|
211
|
+
let currentClient = client;
|
|
212
|
+
if (!currentClient) {
|
|
213
|
+
return Promise.resolve();
|
|
56
214
|
}
|
|
57
|
-
|
|
215
|
+
if(currentClient.closePromise){
|
|
216
|
+
return currentClient.closePromise;
|
|
217
|
+
}
|
|
218
|
+
currentClient.closePromise = new Promise((resolve)=>{
|
|
219
|
+
currentClient.userClosedConnection = true;
|
|
220
|
+
currentClient.userClosedConnectionCB = function () {
|
|
221
|
+
for(let entry of pendingSendMessages){
|
|
222
|
+
entry.reject();
|
|
223
|
+
}
|
|
224
|
+
pendingSendMessages = [];
|
|
225
|
+
resolve();
|
|
226
|
+
};
|
|
227
|
+
_cancelBackoffTimer(); // this is for if the connection is broken and, we are retrying the connection
|
|
228
|
+
if(currentClient.hibernating){
|
|
229
|
+
_wakeupHibernatingClient();
|
|
230
|
+
} else {
|
|
231
|
+
currentClient.terminate();
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return currentClient.closePromise;
|
|
58
235
|
}
|
|
59
236
|
|
|
60
237
|
/**
|
|
@@ -66,6 +243,39 @@ function getId() {
|
|
|
66
243
|
return id.toString(16);
|
|
67
244
|
}
|
|
68
245
|
|
|
246
|
+
function _sendMessage(message, resolve, reject) {
|
|
247
|
+
if (!client) {
|
|
248
|
+
reject('Please call init before sending message');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (!client.connectionEstablished) {
|
|
252
|
+
reject('Db connection is not ready, please retry in some time');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (!isObject(message)) {
|
|
256
|
+
reject('Please provide valid Object');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (!isString(message.fn) || !(message.fn in COCO_DB_FUNCTIONS)) {
|
|
260
|
+
reject('please provide valid function name');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const sequenceNumber = getId();
|
|
264
|
+
message.id = sequenceNumber;
|
|
265
|
+
ID_TO_RESOLVE_REJECT_MAP.set(sequenceNumber, {
|
|
266
|
+
resolve: resolve,
|
|
267
|
+
reject: reject
|
|
268
|
+
});
|
|
269
|
+
activityInHibernateInterval++;
|
|
270
|
+
client.send(JSON.stringify(message));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function _sendPendingMessages() {
|
|
274
|
+
for(let entry of pendingSendMessages){
|
|
275
|
+
_sendMessage(entry.message, entry.resolve, entry.reject);
|
|
276
|
+
}
|
|
277
|
+
pendingSendMessages = [];
|
|
278
|
+
}
|
|
69
279
|
|
|
70
280
|
/**
|
|
71
281
|
* It takes a message object, sends it to the server, and returns a promise that resolves when the server responds
|
|
@@ -74,25 +284,18 @@ function getId() {
|
|
|
74
284
|
*/
|
|
75
285
|
export function sendMessage(message) {
|
|
76
286
|
return new Promise(function (resolve, reject) {
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
287
|
+
if(bufferRequests){
|
|
288
|
+
if(pendingSendMessages.length > MAX_PENDING_SEND_BUFFER_SIZE){
|
|
289
|
+
reject('Too many requests sent while waking up from hibernation');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
pendingSendMessages.push({message, resolve, reject});
|
|
293
|
+
if(client&& client.hibernating){
|
|
294
|
+
_wakeupHibernatingClient();
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
_sendMessage(message, resolve, reject);
|
|
84
298
|
}
|
|
85
|
-
if (!isString(message.fn) || !(message.fn in COCO_DB_FUNCTIONS)) {
|
|
86
|
-
reject('please provide valid function name');
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const sequenceNumber = getId();
|
|
90
|
-
message.id = sequenceNumber;
|
|
91
|
-
ID_TO_RESOLVE_REJECT_MAP[sequenceNumber] = {
|
|
92
|
-
resolve: resolve,
|
|
93
|
-
reject: reject
|
|
94
|
-
};
|
|
95
|
-
client.send(JSON.stringify(message));
|
|
96
299
|
});
|
|
97
300
|
}
|
|
98
301
|
|
|
@@ -111,15 +314,14 @@ export function __receiveMessage(rawData) {
|
|
|
111
314
|
console.error('Server message does not have an Id');
|
|
112
315
|
return false;
|
|
113
316
|
}
|
|
114
|
-
const
|
|
115
|
-
if (!isObject(
|
|
317
|
+
const requestHandler = ID_TO_RESOLVE_REJECT_MAP.get(message.id);
|
|
318
|
+
if (!isObject(requestHandler)) {
|
|
116
319
|
//TODO: Emit metrics
|
|
117
320
|
|
|
118
321
|
console.error(`Client did not send message with Id ${message.id} to server`);
|
|
119
322
|
return false;
|
|
120
323
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
delete ID_TO_RESOLVE_REJECT_MAP[message.id];
|
|
324
|
+
requestHandler.resolve(message.response);
|
|
325
|
+
ID_TO_RESOLVE_REJECT_MAP.delete(message.id);
|
|
124
326
|
return true;
|
|
125
327
|
}
|