@milaboratories/pl-client 2.16.1 → 2.16.3
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/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.cjs +61 -0
- package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.cjs.map +1 -0
- package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.js +58 -0
- package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.js.map +1 -0
- package/dist/core/PromiseTracker.cjs +39 -0
- package/dist/core/PromiseTracker.cjs.map +1 -0
- package/dist/core/PromiseTracker.d.ts +14 -0
- package/dist/core/PromiseTracker.d.ts.map +1 -0
- package/dist/core/PromiseTracker.js +37 -0
- package/dist/core/PromiseTracker.js.map +1 -0
- package/dist/core/StatefulPromise.cjs +65 -0
- package/dist/core/StatefulPromise.cjs.map +1 -0
- package/dist/core/StatefulPromise.d.ts +39 -0
- package/dist/core/StatefulPromise.d.ts.map +1 -0
- package/dist/core/StatefulPromise.js +63 -0
- package/dist/core/StatefulPromise.js.map +1 -0
- package/dist/core/ll_transaction.cjs +3 -2
- package/dist/core/ll_transaction.cjs.map +1 -1
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/ll_transaction.js +3 -2
- package/dist/core/ll_transaction.js.map +1 -1
- package/dist/core/transaction.cjs +577 -515
- package/dist/core/transaction.cjs.map +1 -1
- package/dist/core/transaction.d.ts +6 -4
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +578 -516
- package/dist/core/transaction.js.map +1 -1
- package/dist/test/tcp-proxy.cjs +129 -0
- package/dist/test/tcp-proxy.cjs.map +1 -0
- package/dist/test/tcp-proxy.d.ts +17 -0
- package/dist/test/tcp-proxy.d.ts.map +1 -0
- package/dist/test/tcp-proxy.js +107 -0
- package/dist/test/tcp-proxy.js.map +1 -0
- package/dist/test/test_config.cjs +54 -7
- package/dist/test/test_config.cjs.map +1 -1
- package/dist/test/test_config.d.ts +18 -2
- package/dist/test/test_config.d.ts.map +1 -1
- package/dist/test/test_config.js +54 -7
- package/dist/test/test_config.js.map +1 -1
- package/package.json +7 -8
- package/src/core/PromiseTracker.ts +39 -0
- package/src/core/StatefulPromise.ts +92 -0
- package/src/core/client.test.ts +1 -1
- package/src/core/config.test.ts +1 -1
- package/src/core/connectivity.test.ts +70 -0
- package/src/core/error.test.ts +1 -1
- package/src/core/ll_client.test.ts +1 -1
- package/src/core/ll_transaction.test.ts +1 -1
- package/src/core/ll_transaction.ts +3 -2
- package/src/core/transaction.test.ts +6 -1
- package/src/core/transaction.ts +91 -48
- package/src/core/types.test.ts +1 -1
- package/src/core/unauth_client.test.ts +1 -1
- package/src/helpers/rich_resource_types.test.ts +1 -1
- package/src/test/tcp-proxy.ts +126 -0
- package/src/test/test_config.test.ts +1 -1
- package/src/test/test_config.ts +82 -7
- package/src/util/util.test.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var tslib_es6 = require('../__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.cjs');
|
|
3
4
|
var types = require('./types.cjs');
|
|
4
5
|
var api = require('../proto/github.com/milaboratory/pl/plapi/plapiproto/api.cjs');
|
|
5
6
|
var util = require('../util/util.cjs');
|
|
@@ -9,6 +10,7 @@ var errors = require('./errors.cjs');
|
|
|
9
10
|
var stat = require('./stat.cjs');
|
|
10
11
|
var error_resource = require('./error_resource.cjs');
|
|
11
12
|
var pl = require('../helpers/pl.cjs');
|
|
13
|
+
var PromiseTracker = require('./PromiseTracker.cjs');
|
|
12
14
|
|
|
13
15
|
function isField(ref) {
|
|
14
16
|
return ref.hasOwnProperty('resourceId') && ref.hasOwnProperty('fieldName');
|
|
@@ -66,6 +68,15 @@ async function notFoundToUndefined(cb) {
|
|
|
66
68
|
throw e;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Decorator that wraps the method's returned promise with this.track()
|
|
73
|
+
* This ensures that the promise will be awaited before the transaction is completed.
|
|
74
|
+
*/
|
|
75
|
+
function tracked(value, _context) {
|
|
76
|
+
return function (...args) {
|
|
77
|
+
return this.track(value.apply(this, args));
|
|
78
|
+
};
|
|
79
|
+
}
|
|
69
80
|
/**
|
|
70
81
|
* Each platform transaction has 3 stages:
|
|
71
82
|
* - initialization (txOpen message -> txInfo response)
|
|
@@ -74,542 +85,593 @@ async function notFoundToUndefined(cb) {
|
|
|
74
85
|
*
|
|
75
86
|
* This class encapsulates finalization stage and provides ready-to-communication transaction object.
|
|
76
87
|
* */
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
88
|
+
let PlTransaction = (() => {
|
|
89
|
+
let _instanceExtraInitializers = [];
|
|
90
|
+
let _getResourceData_decorators;
|
|
91
|
+
let _getResourceDataIfExists_decorators;
|
|
92
|
+
let _getFieldIfExists_decorators;
|
|
93
|
+
let _listKeyValues_decorators;
|
|
94
|
+
let _listKeyValuesString_decorators;
|
|
95
|
+
let _listKeyValuesIfResourceExists_decorators;
|
|
96
|
+
let _listKeyValuesStringIfResourceExists_decorators;
|
|
97
|
+
let _getKValue_decorators;
|
|
98
|
+
let _getKValueString_decorators;
|
|
99
|
+
let _getKValueJson_decorators;
|
|
100
|
+
let _getKValueIfExists_decorators;
|
|
101
|
+
let _getKValueStringIfExists_decorators;
|
|
102
|
+
let _getKValueJsonIfExists_decorators;
|
|
103
|
+
return class PlTransaction {
|
|
104
|
+
static {
|
|
105
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
106
|
+
_getResourceData_decorators = [tracked];
|
|
107
|
+
_getResourceDataIfExists_decorators = [tracked];
|
|
108
|
+
_getFieldIfExists_decorators = [tracked];
|
|
109
|
+
_listKeyValues_decorators = [tracked];
|
|
110
|
+
_listKeyValuesString_decorators = [tracked];
|
|
111
|
+
_listKeyValuesIfResourceExists_decorators = [tracked];
|
|
112
|
+
_listKeyValuesStringIfResourceExists_decorators = [tracked];
|
|
113
|
+
_getKValue_decorators = [tracked];
|
|
114
|
+
_getKValueString_decorators = [tracked];
|
|
115
|
+
_getKValueJson_decorators = [tracked];
|
|
116
|
+
_getKValueIfExists_decorators = [tracked];
|
|
117
|
+
_getKValueStringIfExists_decorators = [tracked];
|
|
118
|
+
_getKValueJsonIfExists_decorators = [tracked];
|
|
119
|
+
tslib_es6.__esDecorate(this, null, _getResourceData_decorators, { kind: "method", name: "getResourceData", static: false, private: false, access: { has: obj => "getResourceData" in obj, get: obj => obj.getResourceData }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
120
|
+
tslib_es6.__esDecorate(this, null, _getResourceDataIfExists_decorators, { kind: "method", name: "getResourceDataIfExists", static: false, private: false, access: { has: obj => "getResourceDataIfExists" in obj, get: obj => obj.getResourceDataIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
121
|
+
tslib_es6.__esDecorate(this, null, _getFieldIfExists_decorators, { kind: "method", name: "getFieldIfExists", static: false, private: false, access: { has: obj => "getFieldIfExists" in obj, get: obj => obj.getFieldIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
122
|
+
tslib_es6.__esDecorate(this, null, _listKeyValues_decorators, { kind: "method", name: "listKeyValues", static: false, private: false, access: { has: obj => "listKeyValues" in obj, get: obj => obj.listKeyValues }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
123
|
+
tslib_es6.__esDecorate(this, null, _listKeyValuesString_decorators, { kind: "method", name: "listKeyValuesString", static: false, private: false, access: { has: obj => "listKeyValuesString" in obj, get: obj => obj.listKeyValuesString }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
124
|
+
tslib_es6.__esDecorate(this, null, _listKeyValuesIfResourceExists_decorators, { kind: "method", name: "listKeyValuesIfResourceExists", static: false, private: false, access: { has: obj => "listKeyValuesIfResourceExists" in obj, get: obj => obj.listKeyValuesIfResourceExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
125
|
+
tslib_es6.__esDecorate(this, null, _listKeyValuesStringIfResourceExists_decorators, { kind: "method", name: "listKeyValuesStringIfResourceExists", static: false, private: false, access: { has: obj => "listKeyValuesStringIfResourceExists" in obj, get: obj => obj.listKeyValuesStringIfResourceExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
126
|
+
tslib_es6.__esDecorate(this, null, _getKValue_decorators, { kind: "method", name: "getKValue", static: false, private: false, access: { has: obj => "getKValue" in obj, get: obj => obj.getKValue }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
127
|
+
tslib_es6.__esDecorate(this, null, _getKValueString_decorators, { kind: "method", name: "getKValueString", static: false, private: false, access: { has: obj => "getKValueString" in obj, get: obj => obj.getKValueString }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
128
|
+
tslib_es6.__esDecorate(this, null, _getKValueJson_decorators, { kind: "method", name: "getKValueJson", static: false, private: false, access: { has: obj => "getKValueJson" in obj, get: obj => obj.getKValueJson }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
129
|
+
tslib_es6.__esDecorate(this, null, _getKValueIfExists_decorators, { kind: "method", name: "getKValueIfExists", static: false, private: false, access: { has: obj => "getKValueIfExists" in obj, get: obj => obj.getKValueIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
130
|
+
tslib_es6.__esDecorate(this, null, _getKValueStringIfExists_decorators, { kind: "method", name: "getKValueStringIfExists", static: false, private: false, access: { has: obj => "getKValueStringIfExists" in obj, get: obj => obj.getKValueStringIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
131
|
+
tslib_es6.__esDecorate(this, null, _getKValueJsonIfExists_decorators, { kind: "method", name: "getKValueJsonIfExists", static: false, private: false, access: { has: obj => "getKValueJsonIfExists" in obj, get: obj => obj.getKValueJsonIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
132
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
133
|
+
}
|
|
134
|
+
ll = tslib_es6.__runInitializers(this, _instanceExtraInitializers);
|
|
135
|
+
name;
|
|
136
|
+
writable;
|
|
137
|
+
_clientRoot;
|
|
138
|
+
finalPredicate;
|
|
139
|
+
sharedResourceDataCache;
|
|
140
|
+
enableFormattedErrors;
|
|
141
|
+
globalTxId;
|
|
142
|
+
localTxId = PlTransaction.nextLocalTxId();
|
|
143
|
+
/** Used in caching */
|
|
144
|
+
txOpenTimestamp = Date.now();
|
|
145
|
+
localResourceIdCounter = 0;
|
|
146
|
+
/** Store logical tx open / closed state to prevent invalid sequence of requests.
|
|
147
|
+
* True means output stream was completed.
|
|
148
|
+
* Contract: there must be no async operations between setting this field to true and sending complete signal to stream. */
|
|
149
|
+
_completed = false;
|
|
150
|
+
globalTxIdWasAwaited = false;
|
|
151
|
+
pending = new PromiseTracker.PromiseTracker();
|
|
152
|
+
_startTime = Date.now();
|
|
153
|
+
_stat = stat.initialTxStatWithoutTime();
|
|
154
|
+
get stat() {
|
|
155
|
+
return {
|
|
156
|
+
...this._stat,
|
|
157
|
+
timeMs: Date.now() - this._startTime,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
constructor(ll, name, writable, _clientRoot, finalPredicate, sharedResourceDataCache, enableFormattedErrors = false) {
|
|
161
|
+
this.ll = ll;
|
|
162
|
+
this.name = name;
|
|
163
|
+
this.writable = writable;
|
|
164
|
+
this._clientRoot = _clientRoot;
|
|
165
|
+
this.finalPredicate = finalPredicate;
|
|
166
|
+
this.sharedResourceDataCache = sharedResourceDataCache;
|
|
167
|
+
this.enableFormattedErrors = enableFormattedErrors;
|
|
168
|
+
// initiating transaction
|
|
169
|
+
this.globalTxId = this.sendSingleAndParse({
|
|
170
|
+
oneofKind: 'txOpen',
|
|
171
|
+
txOpen: {
|
|
172
|
+
name,
|
|
173
|
+
enableFormattedErrors,
|
|
174
|
+
writable: writable
|
|
175
|
+
? api.TxAPI_Open_Request_WritableTx.WRITABLE
|
|
176
|
+
: api.TxAPI_Open_Request_WritableTx.NOT_WRITABLE,
|
|
177
|
+
},
|
|
178
|
+
}, (r) => tsHelpers.notEmpty(r.txOpen.tx?.id));
|
|
179
|
+
void this.track(this.globalTxId);
|
|
180
|
+
// To avoid floating promise
|
|
181
|
+
this.globalTxId.catch((err) => {
|
|
182
|
+
if (!this.globalTxIdWasAwaited) {
|
|
183
|
+
console.warn(err);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Adding stats
|
|
187
|
+
this._stat.txCount++;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Collect all pending promises for the transaction finalization.
|
|
191
|
+
*/
|
|
192
|
+
track(promiseOrCallback) {
|
|
193
|
+
return this.pending.track(promiseOrCallback);
|
|
194
|
+
}
|
|
195
|
+
async drainAndAwaitPendingOps() {
|
|
196
|
+
// awaiting these pending operations first, to catch any errors
|
|
197
|
+
await this.pending.awaitAll();
|
|
198
|
+
}
|
|
199
|
+
sendSingleAndParse(r, parser) {
|
|
200
|
+
return this.pending.track(async () => {
|
|
201
|
+
const rawResponsePromise = this.ll.send(r, false);
|
|
202
|
+
void this.pending.track(rawResponsePromise);
|
|
203
|
+
await this.drainAndAwaitPendingOps();
|
|
204
|
+
// awaiting our result, and parsing the response
|
|
205
|
+
return parser(await rawResponsePromise);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
sendMultiAndParse(r, parser) {
|
|
209
|
+
return this.pending.track(async () => {
|
|
210
|
+
const rawResponsePromise = this.ll.send(r, true);
|
|
211
|
+
void this.pending.track(rawResponsePromise);
|
|
212
|
+
await this.drainAndAwaitPendingOps();
|
|
213
|
+
// awaiting our result, and parsing the response
|
|
214
|
+
return parser(await rawResponsePromise);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async sendVoidSync(r) {
|
|
218
|
+
await this.ll.send(r, false);
|
|
219
|
+
}
|
|
220
|
+
/** Requests sent with this method should never produce recoverable errors */
|
|
221
|
+
sendVoidAsync(r) {
|
|
222
|
+
void this.track(this.sendVoidSync(r));
|
|
223
|
+
}
|
|
224
|
+
checkTxOpen() {
|
|
225
|
+
if (this._completed)
|
|
226
|
+
throw new Error('Transaction already closed');
|
|
227
|
+
}
|
|
228
|
+
get completed() {
|
|
229
|
+
return this._completed;
|
|
230
|
+
}
|
|
231
|
+
/** Commit & closes transaction. {@link TxCommitConflict} is thrown on
|
|
232
|
+
* commit conflicts. */
|
|
233
|
+
async commit() {
|
|
234
|
+
this.checkTxOpen();
|
|
235
|
+
// tx will accept no requests after this one
|
|
236
|
+
this._completed = true;
|
|
237
|
+
if (!this.writable) {
|
|
238
|
+
// no need to explicitly commit or reject read-only tx
|
|
239
|
+
const completeResult = this.track(this.ll.complete());
|
|
240
|
+
await this.drainAndAwaitPendingOps();
|
|
241
|
+
await completeResult;
|
|
242
|
+
await this.ll.await();
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const commitResponse = this.track(this.sendSingleAndParse({ oneofKind: 'txCommit', txCommit: {} }, (r) => r.txCommit.success));
|
|
246
|
+
// send closing frame right after commit to save some time on round-trips
|
|
247
|
+
const completeResult = this.track(this.ll.complete());
|
|
248
|
+
// now when we pushed all packets into the stream, we should wait for any
|
|
249
|
+
// pending void operations from before, to catch any errors
|
|
250
|
+
await this.drainAndAwaitPendingOps();
|
|
251
|
+
if (!(await commitResponse))
|
|
252
|
+
throw new TxCommitConflict();
|
|
253
|
+
await completeResult;
|
|
254
|
+
// await event-loop completion
|
|
255
|
+
await this.ll.await();
|
|
130
256
|
}
|
|
131
|
-
});
|
|
132
|
-
// Adding stats
|
|
133
|
-
this._stat.txCount++;
|
|
134
|
-
}
|
|
135
|
-
async drainAndAwaitPendingOps() {
|
|
136
|
-
if (this.pendingVoidOps.length === 0)
|
|
137
|
-
return;
|
|
138
|
-
// drain pending operations into temp array
|
|
139
|
-
const pending = this.pendingVoidOps;
|
|
140
|
-
this.pendingVoidOps = [];
|
|
141
|
-
// awaiting these pending operations first, to catch any errors
|
|
142
|
-
await Promise.all(pending);
|
|
143
|
-
}
|
|
144
|
-
async sendSingleAndParse(r, parser) {
|
|
145
|
-
// pushing operation packet to server
|
|
146
|
-
const rawResponsePromise = this.ll.send(r, false);
|
|
147
|
-
await this.drainAndAwaitPendingOps();
|
|
148
|
-
// awaiting our result, and parsing the response
|
|
149
|
-
return parser(await rawResponsePromise);
|
|
150
|
-
}
|
|
151
|
-
async sendMultiAndParse(r, parser) {
|
|
152
|
-
// pushing operation packet to server
|
|
153
|
-
const rawResponsePromise = this.ll.send(r, true);
|
|
154
|
-
await this.drainAndAwaitPendingOps();
|
|
155
|
-
// awaiting our result, and parsing the response
|
|
156
|
-
return parser(await rawResponsePromise);
|
|
157
|
-
}
|
|
158
|
-
async sendVoidSync(r) {
|
|
159
|
-
await this.ll.send(r, false);
|
|
160
|
-
}
|
|
161
|
-
/** Requests sent with this method should never produce recoverable errors */
|
|
162
|
-
sendVoidAsync(r) {
|
|
163
|
-
this.pendingVoidOps.push(this.sendVoidSync(r));
|
|
164
|
-
}
|
|
165
|
-
checkTxOpen() {
|
|
166
|
-
if (this._completed)
|
|
167
|
-
throw new Error('Transaction already closed');
|
|
168
|
-
}
|
|
169
|
-
get completed() {
|
|
170
|
-
return this._completed;
|
|
171
|
-
}
|
|
172
|
-
/** Commit & closes transaction. {@link TxCommitConflict} is thrown on
|
|
173
|
-
* commit conflicts. */
|
|
174
|
-
async commit() {
|
|
175
|
-
this.checkTxOpen();
|
|
176
|
-
// tx will accept no requests after this one
|
|
177
|
-
this._completed = true;
|
|
178
|
-
if (!this.writable) {
|
|
179
|
-
// no need to explicitly commit or reject read-only tx
|
|
180
|
-
const completeResult = this.ll.complete();
|
|
181
|
-
await this.drainAndAwaitPendingOps();
|
|
182
|
-
await completeResult;
|
|
183
|
-
await this.ll.await();
|
|
184
257
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
258
|
+
async discard() {
|
|
259
|
+
this.checkTxOpen();
|
|
260
|
+
// tx will accept no requests after this one
|
|
261
|
+
this._completed = true;
|
|
262
|
+
const discardResponse = this.sendVoidSync({ oneofKind: 'txDiscard', txDiscard: {} });
|
|
263
|
+
void this.track(discardResponse);
|
|
188
264
|
// send closing frame right after commit to save some time on round-trips
|
|
189
|
-
const completeResult = this.ll.complete();
|
|
265
|
+
const completeResult = this.track(this.ll.complete());
|
|
190
266
|
// now when we pushed all packets into the stream, we should wait for any
|
|
191
267
|
// pending void operations from before, to catch any errors
|
|
192
268
|
await this.drainAndAwaitPendingOps();
|
|
193
|
-
|
|
194
|
-
throw new TxCommitConflict();
|
|
269
|
+
await discardResponse;
|
|
195
270
|
await completeResult;
|
|
196
|
-
// await event-loop completion
|
|
197
271
|
await this.ll.await();
|
|
198
272
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
},
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
deleteResourceName(name) {
|
|
309
|
-
this.sendVoidAsync({ oneofKind: 'resourceNameDelete', resourceNameDelete: { name } });
|
|
310
|
-
}
|
|
311
|
-
async getResourceByName(name) {
|
|
312
|
-
return await this.sendSingleAndParse({ oneofKind: 'resourceNameGet', resourceNameGet: { name } }, (r) => types.ensureResourceIdNotNull(r.resourceNameGet.resourceId));
|
|
313
|
-
}
|
|
314
|
-
async checkResourceNameExists(name) {
|
|
315
|
-
return await this.sendSingleAndParse({ oneofKind: 'resourceNameExists', resourceNameExists: { name } }, (r) => r.resourceNameExists.exists);
|
|
316
|
-
}
|
|
317
|
-
removeResource(rId) {
|
|
318
|
-
this.sendVoidAsync({ oneofKind: 'resourceRemove', resourceRemove: { id: rId } });
|
|
319
|
-
}
|
|
320
|
-
async resourceExists(rId) {
|
|
321
|
-
return await this.sendSingleAndParse({ oneofKind: 'resourceExists', resourceExists: { resourceId: rId } }, (r) => r.resourceExists.exists);
|
|
322
|
-
}
|
|
323
|
-
async getResourceData(rId, loadFields = true, ignoreCache = false) {
|
|
324
|
-
if (!ignoreCache && !isResourceRef(rId) && !types.isLocalResourceId(rId)) {
|
|
325
|
-
// checking if we can return result from cache
|
|
326
|
-
const fromCache = this.sharedResourceDataCache.get(rId);
|
|
327
|
-
if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
|
|
328
|
-
if (!loadFields) {
|
|
329
|
-
this._stat.rGetDataCacheHits++;
|
|
330
|
-
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
331
|
-
return fromCache.basicData;
|
|
332
|
-
}
|
|
333
|
-
else if (fromCache.data) {
|
|
334
|
-
this._stat.rGetDataCacheHits++;
|
|
335
|
-
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
336
|
-
this._stat.rGetDataCacheFields += fromCache.data.fields.length;
|
|
337
|
-
return fromCache.data;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
273
|
+
//
|
|
274
|
+
// Main tx methods
|
|
275
|
+
//
|
|
276
|
+
get clientRoot() {
|
|
277
|
+
return types.ensureResourceIdNotNull(this._clientRoot);
|
|
278
|
+
}
|
|
279
|
+
//
|
|
280
|
+
// Resources
|
|
281
|
+
//
|
|
282
|
+
createSingleton(name, type, errorIfExists = false) {
|
|
283
|
+
const localId = this.nextLocalResourceId(false);
|
|
284
|
+
const globalId = this.sendSingleAndParse({
|
|
285
|
+
oneofKind: 'resourceCreateSingleton',
|
|
286
|
+
resourceCreateSingleton: {
|
|
287
|
+
type,
|
|
288
|
+
id: localId,
|
|
289
|
+
data: Buffer.from(name),
|
|
290
|
+
errorIfExists,
|
|
291
|
+
},
|
|
292
|
+
}, (r) => r.resourceCreateSingleton.resourceId);
|
|
293
|
+
void this.track(globalId);
|
|
294
|
+
return { globalId, localId };
|
|
295
|
+
}
|
|
296
|
+
getSingleton(name, loadFields = true) {
|
|
297
|
+
return this.sendSingleAndParse({
|
|
298
|
+
oneofKind: 'resourceGetSingleton',
|
|
299
|
+
resourceGetSingleton: {
|
|
300
|
+
data: Buffer.from(name),
|
|
301
|
+
loadFields,
|
|
302
|
+
},
|
|
303
|
+
}, (r) => type_conversion.protoToResource(tsHelpers.notEmpty(r.resourceGetSingleton.resource)));
|
|
304
|
+
}
|
|
305
|
+
createResource(root, req, parser) {
|
|
306
|
+
const localId = this.nextLocalResourceId(root);
|
|
307
|
+
const globalId = this.sendSingleAndParse(req(localId), (r) => parser(r));
|
|
308
|
+
void this.track(globalId);
|
|
309
|
+
return { globalId, localId };
|
|
310
|
+
}
|
|
311
|
+
createRoot(type) {
|
|
312
|
+
this._stat.rootsCreated++;
|
|
313
|
+
return this.createResource(true, (localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }), (r) => r.resourceCreateRoot.resourceId);
|
|
314
|
+
}
|
|
315
|
+
createStruct(type, data) {
|
|
316
|
+
this._stat.structsCreated++;
|
|
317
|
+
this._stat.structsCreatedDataBytes += data?.length ?? 0;
|
|
318
|
+
return this.createResource(false, (localId) => ({
|
|
319
|
+
oneofKind: 'resourceCreateStruct',
|
|
320
|
+
resourceCreateStruct: {
|
|
321
|
+
type,
|
|
322
|
+
id: localId,
|
|
323
|
+
data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
|
|
324
|
+
},
|
|
325
|
+
}), (r) => r.resourceCreateStruct.resourceId);
|
|
326
|
+
}
|
|
327
|
+
createEphemeral(type, data) {
|
|
328
|
+
this._stat.ephemeralsCreated++;
|
|
329
|
+
this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
|
|
330
|
+
return this.createResource(false, (localId) => ({
|
|
331
|
+
oneofKind: 'resourceCreateEphemeral',
|
|
332
|
+
resourceCreateEphemeral: {
|
|
333
|
+
type,
|
|
334
|
+
id: localId,
|
|
335
|
+
data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
|
|
336
|
+
},
|
|
337
|
+
}), (r) => r.resourceCreateEphemeral.resourceId);
|
|
338
|
+
}
|
|
339
|
+
createValue(type, data, errorIfExists = false) {
|
|
340
|
+
this._stat.valuesCreated++;
|
|
341
|
+
this._stat.valuesCreatedDataBytes += data?.length ?? 0;
|
|
342
|
+
return this.createResource(false, (localId) => ({
|
|
343
|
+
oneofKind: 'resourceCreateValue',
|
|
344
|
+
resourceCreateValue: {
|
|
345
|
+
type,
|
|
346
|
+
id: localId,
|
|
347
|
+
data: typeof data === 'string' ? Buffer.from(data) : data,
|
|
348
|
+
errorIfExists,
|
|
349
|
+
},
|
|
350
|
+
}), (r) => r.resourceCreateValue.resourceId);
|
|
351
|
+
}
|
|
352
|
+
createJsonValue(data) {
|
|
353
|
+
const jsonData = tsHelpers.canonicalJsonBytes(data);
|
|
354
|
+
return this.createValue(pl.JsonObject, jsonData, false);
|
|
355
|
+
}
|
|
356
|
+
createJsonGzValue(data, minSizeToGzip = 16_384) {
|
|
357
|
+
const { data: jsonData, isGzipped } = tsHelpers.canonicalJsonGzBytes(data, minSizeToGzip);
|
|
358
|
+
return this.createValue(isGzipped ? pl.JsonGzObject : pl.JsonObject, jsonData, false);
|
|
359
|
+
}
|
|
360
|
+
createError(message) {
|
|
361
|
+
return this.createValue(error_resource.ErrorResourceType, JSON.stringify({ message }));
|
|
362
|
+
}
|
|
363
|
+
setResourceName(name, rId) {
|
|
364
|
+
this.sendVoidAsync({
|
|
365
|
+
oneofKind: 'resourceNameSet',
|
|
366
|
+
resourceNameSet: { resourceId: toResourceId(rId), name },
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
deleteResourceName(name) {
|
|
370
|
+
this.sendVoidAsync({ oneofKind: 'resourceNameDelete', resourceNameDelete: { name } });
|
|
371
|
+
}
|
|
372
|
+
getResourceByName(name) {
|
|
373
|
+
return this.sendSingleAndParse({ oneofKind: 'resourceNameGet', resourceNameGet: { name } }, (r) => types.ensureResourceIdNotNull(r.resourceNameGet.resourceId));
|
|
374
|
+
}
|
|
375
|
+
checkResourceNameExists(name) {
|
|
376
|
+
return this.sendSingleAndParse({ oneofKind: 'resourceNameExists', resourceNameExists: { name } }, (r) => r.resourceNameExists.exists);
|
|
377
|
+
}
|
|
378
|
+
removeResource(rId) {
|
|
379
|
+
this.sendVoidAsync({ oneofKind: 'resourceRemove', resourceRemove: { id: rId } });
|
|
340
380
|
}
|
|
341
|
-
|
|
342
|
-
oneofKind: '
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
381
|
+
resourceExists(rId) {
|
|
382
|
+
return this.sendSingleAndParse({ oneofKind: 'resourceExists', resourceExists: { resourceId: rId } }, (r) => r.resourceExists.exists);
|
|
383
|
+
}
|
|
384
|
+
async getResourceData(rId, loadFields = true, ignoreCache = false) {
|
|
385
|
+
if (!ignoreCache && !isResourceRef(rId) && !types.isLocalResourceId(rId)) {
|
|
386
|
+
// checking if we can return result from cache
|
|
387
|
+
const fromCache = this.sharedResourceDataCache.get(rId);
|
|
388
|
+
if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
|
|
389
|
+
if (!loadFields) {
|
|
390
|
+
this._stat.rGetDataCacheHits++;
|
|
391
|
+
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
392
|
+
return fromCache.basicData;
|
|
393
|
+
}
|
|
394
|
+
else if (fromCache.data) {
|
|
395
|
+
this._stat.rGetDataCacheHits++;
|
|
396
|
+
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
397
|
+
this._stat.rGetDataCacheFields += fromCache.data.fields.length;
|
|
398
|
+
return fromCache.data;
|
|
399
|
+
}
|
|
358
400
|
}
|
|
359
401
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
402
|
+
const result = await this.sendSingleAndParse({
|
|
403
|
+
oneofKind: 'resourceGet',
|
|
404
|
+
resourceGet: { resourceId: toResourceId(rId), loadFields: loadFields },
|
|
405
|
+
}, (r) => type_conversion.protoToResource(tsHelpers.notEmpty(r.resourceGet.resource)));
|
|
406
|
+
this._stat.rGetDataNetRequests++;
|
|
407
|
+
this._stat.rGetDataNetBytes += result.data?.length ?? 0;
|
|
408
|
+
this._stat.rGetDataNetFields += result.fields.length;
|
|
409
|
+
// we will cache only final resource data states
|
|
410
|
+
// caching result even if we were ignore the cache
|
|
411
|
+
if (!isResourceRef(rId) && !types.isLocalResourceId(rId) && this.finalPredicate(result)) {
|
|
412
|
+
tsHelpers.deepFreeze(result);
|
|
413
|
+
const fromCache = this.sharedResourceDataCache.get(rId);
|
|
414
|
+
if (fromCache) {
|
|
415
|
+
if (loadFields && !fromCache.data) {
|
|
416
|
+
fromCache.data = result;
|
|
417
|
+
// updating timestamp because we updated the record
|
|
418
|
+
fromCache.cacheTxOpenTimestamp = this.txOpenTimestamp;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
const basicData = types.extractBasicResourceData(result);
|
|
423
|
+
tsHelpers.deepFreeze(basicData);
|
|
424
|
+
if (loadFields)
|
|
425
|
+
this.sharedResourceDataCache.set(rId, {
|
|
426
|
+
basicData,
|
|
427
|
+
data: result,
|
|
428
|
+
cacheTxOpenTimestamp: this.txOpenTimestamp,
|
|
429
|
+
});
|
|
430
|
+
else
|
|
431
|
+
this.sharedResourceDataCache.set(rId, {
|
|
432
|
+
basicData,
|
|
433
|
+
data: undefined,
|
|
434
|
+
cacheTxOpenTimestamp: this.txOpenTimestamp,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
375
437
|
}
|
|
438
|
+
return result;
|
|
376
439
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
lockOutputs(rId) {
|
|
407
|
-
this._stat.outputsLocked++;
|
|
408
|
-
this.sendVoidAsync({
|
|
409
|
-
oneofKind: 'resourceLockOutputs',
|
|
410
|
-
resourceLockOutputs: { resourceId: toResourceId(rId) },
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
lock(rID) {
|
|
414
|
-
this.lockInputs(rID);
|
|
415
|
-
this.lockOutputs(rID);
|
|
416
|
-
}
|
|
417
|
-
setResourceError(rId, ref) {
|
|
418
|
-
this.sendVoidAsync({
|
|
419
|
-
oneofKind: 'resourceSetError',
|
|
420
|
-
resourceSetError: { resourceId: toResourceId(rId), errorResourceId: toResourceId(ref) },
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
//
|
|
424
|
-
// Fields
|
|
425
|
-
//
|
|
426
|
-
createField(fId, fieldType, value) {
|
|
427
|
-
this._stat.fieldsCreated++;
|
|
428
|
-
this.sendVoidAsync({
|
|
429
|
-
oneofKind: 'fieldCreate',
|
|
430
|
-
fieldCreate: { type: type_conversion.fieldTypeToProto(fieldType), id: toFieldId(fId) },
|
|
431
|
-
});
|
|
432
|
-
if (value !== undefined)
|
|
433
|
-
this.setField(fId, value);
|
|
434
|
-
}
|
|
435
|
-
async fieldExists(fId) {
|
|
436
|
-
return await this.sendSingleAndParse({
|
|
437
|
-
oneofKind: 'fieldExists',
|
|
438
|
-
fieldExists: { field: toFieldId(fId) },
|
|
439
|
-
}, (r) => r.fieldExists.exists);
|
|
440
|
-
}
|
|
441
|
-
setField(fId, ref) {
|
|
442
|
-
this._stat.fieldsSet++;
|
|
443
|
-
if (isResource(ref))
|
|
440
|
+
async getResourceDataIfExists(rId, loadFields = true) {
|
|
441
|
+
// calling this method will ignore cache, because user intention is to detect resource absence
|
|
442
|
+
// which cache will prevent
|
|
443
|
+
const result = await notFoundToUndefined(async () => await this.getResourceData(rId, loadFields, true));
|
|
444
|
+
// cleaning cache record if resource was removed from the db
|
|
445
|
+
if (result === undefined && !isResourceRef(rId) && !types.isLocalResourceId(rId))
|
|
446
|
+
this.sharedResourceDataCache.delete(rId);
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Inform platform that resource will not get any new input fields.
|
|
451
|
+
* This is required, when client creates resource without schema and wants
|
|
452
|
+
* controller to start calculations.
|
|
453
|
+
* Most controllers will not start calculations even when all inputs
|
|
454
|
+
* have their values, if inputs list is not locked.
|
|
455
|
+
*/
|
|
456
|
+
lockInputs(rId) {
|
|
457
|
+
this._stat.inputsLocked++;
|
|
458
|
+
this.sendVoidAsync({
|
|
459
|
+
oneofKind: 'resourceLockInputs',
|
|
460
|
+
resourceLockInputs: { resourceId: toResourceId(rId) },
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Inform platform that resource will not get any new output fields.
|
|
465
|
+
* This is required for resource to pass deduplication.
|
|
466
|
+
*/
|
|
467
|
+
lockOutputs(rId) {
|
|
468
|
+
this._stat.outputsLocked++;
|
|
444
469
|
this.sendVoidAsync({
|
|
445
|
-
oneofKind: '
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
470
|
+
oneofKind: 'resourceLockOutputs',
|
|
471
|
+
resourceLockOutputs: { resourceId: toResourceId(rId) },
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
lock(rID) {
|
|
475
|
+
this.lockInputs(rID);
|
|
476
|
+
this.lockOutputs(rID);
|
|
477
|
+
}
|
|
478
|
+
setResourceError(rId, ref) {
|
|
479
|
+
this.sendVoidAsync({
|
|
480
|
+
oneofKind: 'resourceSetError',
|
|
481
|
+
resourceSetError: { resourceId: toResourceId(rId), errorResourceId: toResourceId(ref) },
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
//
|
|
485
|
+
// Fields
|
|
486
|
+
//
|
|
487
|
+
createField(fId, fieldType, value) {
|
|
488
|
+
this._stat.fieldsCreated++;
|
|
489
|
+
this.sendVoidAsync({
|
|
490
|
+
oneofKind: 'fieldCreate',
|
|
491
|
+
fieldCreate: { type: type_conversion.fieldTypeToProto(fieldType), id: toFieldId(fId) },
|
|
492
|
+
});
|
|
493
|
+
if (value !== undefined)
|
|
494
|
+
this.setField(fId, value);
|
|
495
|
+
}
|
|
496
|
+
fieldExists(fId) {
|
|
497
|
+
return this.sendSingleAndParse({
|
|
498
|
+
oneofKind: 'fieldExists',
|
|
499
|
+
fieldExists: { field: toFieldId(fId) },
|
|
500
|
+
}, (r) => r.fieldExists.exists);
|
|
501
|
+
}
|
|
502
|
+
setField(fId, ref) {
|
|
503
|
+
this._stat.fieldsSet++;
|
|
504
|
+
if (isResource(ref))
|
|
505
|
+
this.sendVoidAsync({
|
|
506
|
+
oneofKind: 'fieldSet',
|
|
507
|
+
fieldSet: {
|
|
508
|
+
field: toFieldId(fId),
|
|
509
|
+
value: {
|
|
510
|
+
resourceId: toResourceId(ref),
|
|
511
|
+
fieldName: '', // default value, read as undefined
|
|
512
|
+
},
|
|
451
513
|
},
|
|
514
|
+
});
|
|
515
|
+
else
|
|
516
|
+
this.sendVoidAsync({
|
|
517
|
+
oneofKind: 'fieldSet',
|
|
518
|
+
fieldSet: {
|
|
519
|
+
field: toFieldId(fId),
|
|
520
|
+
value: toFieldId(ref),
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
setFieldError(fId, ref) {
|
|
525
|
+
this._stat.fieldsSet++;
|
|
526
|
+
this.sendVoidAsync({
|
|
527
|
+
oneofKind: 'fieldSetError',
|
|
528
|
+
fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
getField(fId) {
|
|
532
|
+
this._stat.fieldsGet++;
|
|
533
|
+
return this.sendSingleAndParse({ oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } }, (r) => type_conversion.protoToField(tsHelpers.notEmpty(r.fieldGet.field)));
|
|
534
|
+
}
|
|
535
|
+
async getFieldIfExists(fId) {
|
|
536
|
+
return notFoundToUndefined(async () => await this.getField(fId));
|
|
537
|
+
}
|
|
538
|
+
resetField(fId) {
|
|
539
|
+
this.sendVoidAsync({ oneofKind: 'fieldReset', fieldReset: { field: toFieldId(fId) } });
|
|
540
|
+
}
|
|
541
|
+
removeField(fId) {
|
|
542
|
+
this.sendVoidAsync({ oneofKind: 'fieldRemove', fieldRemove: { field: toFieldId(fId) } });
|
|
543
|
+
}
|
|
544
|
+
//
|
|
545
|
+
// KV
|
|
546
|
+
//
|
|
547
|
+
async listKeyValues(rId) {
|
|
548
|
+
const result = await this.sendMultiAndParse({
|
|
549
|
+
oneofKind: 'resourceKeyValueList',
|
|
550
|
+
resourceKeyValueList: { resourceId: toResourceId(rId), startFrom: '', limit: 0 },
|
|
551
|
+
}, (r) => r.map((e) => e.resourceKeyValueList.record));
|
|
552
|
+
this._stat.kvListRequests++;
|
|
553
|
+
this._stat.kvListEntries += result.length;
|
|
554
|
+
for (const kv of result)
|
|
555
|
+
this._stat.kvListBytes += kv.key.length + kv.value.length;
|
|
556
|
+
return result;
|
|
557
|
+
}
|
|
558
|
+
async listKeyValuesString(rId) {
|
|
559
|
+
return (await this.listKeyValues(rId)).map(({ key, value }) => ({
|
|
560
|
+
key,
|
|
561
|
+
value: Buffer.from(value).toString(),
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
564
|
+
async listKeyValuesIfResourceExists(rId) {
|
|
565
|
+
return notFoundToUndefined(async () => await this.listKeyValues(rId));
|
|
566
|
+
}
|
|
567
|
+
async listKeyValuesStringIfResourceExists(rId) {
|
|
568
|
+
return notFoundToUndefined(async () => await this.listKeyValuesString(rId));
|
|
569
|
+
}
|
|
570
|
+
setKValue(rId, key, value) {
|
|
571
|
+
this._stat.kvSetRequests++;
|
|
572
|
+
this._stat.kvSetBytes++;
|
|
573
|
+
this.sendVoidAsync({
|
|
574
|
+
oneofKind: 'resourceKeyValueSet',
|
|
575
|
+
resourceKeyValueSet: {
|
|
576
|
+
resourceId: toResourceId(rId),
|
|
577
|
+
key,
|
|
578
|
+
value: util.toBytes(value),
|
|
452
579
|
},
|
|
453
580
|
});
|
|
454
|
-
|
|
581
|
+
}
|
|
582
|
+
deleteKValue(rId, key) {
|
|
455
583
|
this.sendVoidAsync({
|
|
456
|
-
oneofKind: '
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
584
|
+
oneofKind: 'resourceKeyValueDelete',
|
|
585
|
+
resourceKeyValueDelete: {
|
|
586
|
+
resourceId: toResourceId(rId),
|
|
587
|
+
key,
|
|
460
588
|
},
|
|
461
589
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
oneofKind: 'resourceKeyValueGetIfExists',
|
|
548
|
-
resourceKeyValueGetIfExists: { resourceId: toResourceId(rId), key },
|
|
549
|
-
}, (r) => r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined);
|
|
550
|
-
this._stat.kvGetRequests++;
|
|
551
|
-
this._stat.kvGetBytes += result?.length ?? 0;
|
|
552
|
-
return result;
|
|
553
|
-
}
|
|
554
|
-
async getKValueStringIfExists(rId, key) {
|
|
555
|
-
const data = await this.getKValueIfExists(rId, key);
|
|
556
|
-
return data === undefined ? undefined : Buffer.from(data).toString();
|
|
557
|
-
}
|
|
558
|
-
async getKValueJsonIfExists(rId, key) {
|
|
559
|
-
const str = await this.getKValueString(rId, key);
|
|
560
|
-
if (str === undefined)
|
|
561
|
-
return undefined;
|
|
562
|
-
return JSON.parse(str);
|
|
563
|
-
}
|
|
564
|
-
//
|
|
565
|
-
// Cache
|
|
566
|
-
//
|
|
567
|
-
// TODO
|
|
568
|
-
//
|
|
569
|
-
// High level ops
|
|
570
|
-
//
|
|
571
|
-
/** Resolves existing or create first level resource from */
|
|
572
|
-
getFutureFieldValue(rId, fieldName, fieldType) {
|
|
573
|
-
const data = Buffer.from(JSON.stringify({ fieldName, fieldType }));
|
|
574
|
-
const getFieldResource = this.createEphemeral({ name: 'json/getField', version: '1' }, data);
|
|
575
|
-
this.setField({ resourceId: getFieldResource, fieldName: 'resource' }, rId);
|
|
576
|
-
return { resourceId: getFieldResource, fieldName: 'result' };
|
|
577
|
-
}
|
|
578
|
-
//
|
|
579
|
-
// Technical
|
|
580
|
-
//
|
|
581
|
-
async getGlobalTxId() {
|
|
582
|
-
this.globalTxIdWasAwaited = true;
|
|
583
|
-
return await this.globalTxId;
|
|
584
|
-
}
|
|
585
|
-
/** Closes output event stream */
|
|
586
|
-
async complete() {
|
|
587
|
-
if (this._completed)
|
|
588
|
-
return;
|
|
589
|
-
this._completed = true;
|
|
590
|
-
const completeResult = this.ll.complete();
|
|
591
|
-
await this.drainAndAwaitPendingOps();
|
|
592
|
-
await completeResult;
|
|
593
|
-
}
|
|
594
|
-
/** Await incoming message loop termination and throw
|
|
595
|
-
* any leftover errors if it was unsuccessful */
|
|
596
|
-
async await() {
|
|
597
|
-
await this.ll.await();
|
|
598
|
-
}
|
|
599
|
-
//
|
|
600
|
-
// Helpers
|
|
601
|
-
//
|
|
602
|
-
nextLocalResourceId(root) {
|
|
603
|
-
return types.createLocalResourceId(root, ++this.localResourceIdCounter, this.localTxId);
|
|
604
|
-
}
|
|
605
|
-
static localTxIdCounter = 0;
|
|
606
|
-
static nextLocalTxId() {
|
|
607
|
-
PlTransaction.localTxIdCounter++;
|
|
608
|
-
if (PlTransaction.localTxIdCounter === types.MaxTxId)
|
|
609
|
-
PlTransaction.localTxIdCounter = 1;
|
|
610
|
-
return PlTransaction.localTxIdCounter;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
590
|
+
}
|
|
591
|
+
async getKValue(rId, key) {
|
|
592
|
+
const result = await this.sendSingleAndParse({
|
|
593
|
+
oneofKind: 'resourceKeyValueGet',
|
|
594
|
+
resourceKeyValueGet: { resourceId: toResourceId(rId), key },
|
|
595
|
+
}, (r) => r.resourceKeyValueGet.value);
|
|
596
|
+
this._stat.kvGetRequests++;
|
|
597
|
+
this._stat.kvGetBytes += result.length;
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
async getKValueString(rId, key) {
|
|
601
|
+
return Buffer.from(await this.getKValue(rId, key)).toString();
|
|
602
|
+
}
|
|
603
|
+
async getKValueJson(rId, key) {
|
|
604
|
+
return JSON.parse(await this.getKValueString(rId, key));
|
|
605
|
+
}
|
|
606
|
+
async getKValueIfExists(rId, key) {
|
|
607
|
+
const result = await this.sendSingleAndParse({
|
|
608
|
+
oneofKind: 'resourceKeyValueGetIfExists',
|
|
609
|
+
resourceKeyValueGetIfExists: { resourceId: toResourceId(rId), key },
|
|
610
|
+
}, (r) => r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined);
|
|
611
|
+
this._stat.kvGetRequests++;
|
|
612
|
+
this._stat.kvGetBytes += result?.length ?? 0;
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
async getKValueStringIfExists(rId, key) {
|
|
616
|
+
const data = await this.getKValueIfExists(rId, key);
|
|
617
|
+
return data === undefined ? undefined : Buffer.from(data).toString();
|
|
618
|
+
}
|
|
619
|
+
async getKValueJsonIfExists(rId, key) {
|
|
620
|
+
const str = await this.getKValueString(rId, key);
|
|
621
|
+
if (str === undefined)
|
|
622
|
+
return undefined;
|
|
623
|
+
return JSON.parse(str);
|
|
624
|
+
}
|
|
625
|
+
//
|
|
626
|
+
// Cache
|
|
627
|
+
//
|
|
628
|
+
// TODO
|
|
629
|
+
//
|
|
630
|
+
// High level ops
|
|
631
|
+
//
|
|
632
|
+
/** Resolves existing or create first level resource from */
|
|
633
|
+
getFutureFieldValue(rId, fieldName, fieldType) {
|
|
634
|
+
const data = Buffer.from(JSON.stringify({ fieldName, fieldType }));
|
|
635
|
+
const getFieldResource = this.createEphemeral({ name: 'json/getField', version: '1' }, data);
|
|
636
|
+
this.setField({ resourceId: getFieldResource, fieldName: 'resource' }, rId);
|
|
637
|
+
return { resourceId: getFieldResource, fieldName: 'result' };
|
|
638
|
+
}
|
|
639
|
+
//
|
|
640
|
+
// Technical
|
|
641
|
+
//
|
|
642
|
+
async getGlobalTxId() {
|
|
643
|
+
this.globalTxIdWasAwaited = true;
|
|
644
|
+
return await this.globalTxId;
|
|
645
|
+
}
|
|
646
|
+
/** Closes output event stream */
|
|
647
|
+
async complete() {
|
|
648
|
+
if (this._completed)
|
|
649
|
+
return;
|
|
650
|
+
this._completed = true;
|
|
651
|
+
const completeResult = this.track(this.ll.complete());
|
|
652
|
+
await this.drainAndAwaitPendingOps();
|
|
653
|
+
await completeResult;
|
|
654
|
+
}
|
|
655
|
+
/** Await incoming message loop termination and throw
|
|
656
|
+
* any leftover errors if it was unsuccessful */
|
|
657
|
+
async await() {
|
|
658
|
+
await this.ll.await();
|
|
659
|
+
}
|
|
660
|
+
//
|
|
661
|
+
// Helpers
|
|
662
|
+
//
|
|
663
|
+
nextLocalResourceId(root) {
|
|
664
|
+
return types.createLocalResourceId(root, ++this.localResourceIdCounter, this.localTxId);
|
|
665
|
+
}
|
|
666
|
+
static localTxIdCounter = 0;
|
|
667
|
+
static nextLocalTxId() {
|
|
668
|
+
PlTransaction.localTxIdCounter++;
|
|
669
|
+
if (PlTransaction.localTxIdCounter === types.MaxTxId)
|
|
670
|
+
PlTransaction.localTxIdCounter = 1;
|
|
671
|
+
return PlTransaction.localTxIdCounter;
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
})();
|
|
613
675
|
|
|
614
676
|
exports.PlTransaction = PlTransaction;
|
|
615
677
|
exports.TxCommitConflict = TxCommitConflict;
|