@opentap/runner-client 2.3.2 → 2.3.3-alpha.1.1
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/lib/BaseClient.d.ts +10 -11
- package/lib/BaseClient.js +105 -117
- package/lib/RunnerClient.js +1 -1
- package/lib/SessionClient.js +3 -1
- package/package.json +1 -1
package/lib/BaseClient.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ComponentSettingsBase, ComponentSettingsIdentifier, ComponentSettingsListItem, DataGridControl, ErrorResponse, FileParameter, FileResponse, ListItemType, ProfileGroup, RepositoryPackageReference, RepositorySettingsPackageDefinition, SettingsTapPackage } from './DTOs';
|
|
2
|
-
import { ConnectionOptions,
|
|
2
|
+
import { ConnectionOptions, Subscription, SubscriptionOptions, PublishOptions } from 'nats.ws';
|
|
3
|
+
interface BaseClientRequestOptions {
|
|
4
|
+
publishOptions?: PublishOptions;
|
|
5
|
+
rawResponse?: boolean;
|
|
6
|
+
fullSubject?: boolean;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
3
9
|
export declare class BaseClient {
|
|
4
10
|
private connection;
|
|
5
11
|
private baseSubject;
|
|
@@ -9,7 +15,6 @@ export declare class BaseClient {
|
|
|
9
15
|
private _accessToken;
|
|
10
16
|
private _headers;
|
|
11
17
|
private _timeout;
|
|
12
|
-
private _chunkSize;
|
|
13
18
|
/** Get request access token */
|
|
14
19
|
get accessToken(): string;
|
|
15
20
|
/** Set request access token */
|
|
@@ -23,6 +28,7 @@ export declare class BaseClient {
|
|
|
23
28
|
/** Set timeout in milliseconds. Default is 40000 milliseconds */
|
|
24
29
|
set timeout(value: number);
|
|
25
30
|
constructor(baseSubject: string, options: ConnectionOptions);
|
|
31
|
+
private withTimeout;
|
|
26
32
|
/**
|
|
27
33
|
* Send a request to the nats server.
|
|
28
34
|
* @param subject The subject to request
|
|
@@ -30,7 +36,7 @@ export declare class BaseClient {
|
|
|
30
36
|
* @param options (optional)
|
|
31
37
|
* @returns Promise of an object
|
|
32
38
|
*/
|
|
33
|
-
protected request<T>(subject: string, payload?: any, options?:
|
|
39
|
+
protected request<T>(subject: string, payload?: any, options?: BaseClientRequestOptions): Promise<T>;
|
|
34
40
|
/**
|
|
35
41
|
* Handle the error
|
|
36
42
|
* @param error
|
|
@@ -50,14 +56,6 @@ export declare class BaseClient {
|
|
|
50
56
|
* @returns Subscription object
|
|
51
57
|
*/
|
|
52
58
|
protected subscribe(subject: string, options: SubscriptionOptions): Subscription;
|
|
53
|
-
/**
|
|
54
|
-
* Receive a chunked file specified by a request response
|
|
55
|
-
* @param requestResponse Contains a reply subject and a file descriptor which can be used to download chunks
|
|
56
|
-
* @param rawResponse If true, the response should not be decoded
|
|
57
|
-
* @param opts Request options
|
|
58
|
-
* @returns
|
|
59
|
-
*/
|
|
60
|
-
private downloadChunkedRequest;
|
|
61
59
|
protected encode(payload: any): Uint8Array;
|
|
62
60
|
/**
|
|
63
61
|
* Check if the the response is an error from the server.
|
|
@@ -246,3 +244,4 @@ export declare class BaseClient {
|
|
|
246
244
|
*/
|
|
247
245
|
downloadSettingsPackage(settingsTapPackage: SettingsTapPackage): Promise<FileResponse>;
|
|
248
246
|
}
|
|
247
|
+
export {};
|
package/lib/BaseClient.js
CHANGED
|
@@ -45,17 +45,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
45
45
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
49
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
50
|
-
if (ar || !(i in from)) {
|
|
51
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
52
|
-
ar[i] = from[i];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
56
|
-
};
|
|
57
48
|
import { ComponentSettingsBase, ComponentSettingsIdentifier, ComponentSettingsListItem, DataGridControl, ErrorResponse, FileDescriptor, ListItemType, ProfileGroup, } from './DTOs';
|
|
58
|
-
import { Empty, ErrorCode, JSONCodec,
|
|
49
|
+
import { Empty, ErrorCode, JSONCodec, StringCodec, connect, headers, } from 'nats.ws';
|
|
59
50
|
import { EventEmitter } from 'events';
|
|
60
51
|
var DEFAULT_TIMEOUT = 40000; // default timeout of 40 seconds
|
|
61
52
|
var Events;
|
|
@@ -66,7 +57,6 @@ var BaseClient = /** @class */ (function () {
|
|
|
66
57
|
function BaseClient(baseSubject, options) {
|
|
67
58
|
this.domainAccess = new Map();
|
|
68
59
|
this._headers = new Headers();
|
|
69
|
-
this._chunkSize = 512000;
|
|
70
60
|
this.baseSubject = baseSubject;
|
|
71
61
|
this.connectionOptions = __assign({}, options) || {};
|
|
72
62
|
this.connectionOptions.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || DEFAULT_TIMEOUT;
|
|
@@ -108,6 +98,9 @@ var BaseClient = /** @class */ (function () {
|
|
|
108
98
|
enumerable: false,
|
|
109
99
|
configurable: true
|
|
110
100
|
});
|
|
101
|
+
BaseClient.prototype.withTimeout = function (promise, timeout) {
|
|
102
|
+
return Promise.race([promise, new Promise(function (_, reject) { return setTimeout(function () { return reject(new Error(ErrorCode.Timeout)); }, timeout); })]);
|
|
103
|
+
};
|
|
111
104
|
/**
|
|
112
105
|
* Send a request to the nats server.
|
|
113
106
|
* @param subject The subject to request
|
|
@@ -115,74 +108,113 @@ var BaseClient = /** @class */ (function () {
|
|
|
115
108
|
* @param options (optional)
|
|
116
109
|
* @returns Promise of an object
|
|
117
110
|
*/
|
|
118
|
-
BaseClient.prototype.request = function (subject, payload, options
|
|
111
|
+
BaseClient.prototype.request = function (subject, payload, options) {
|
|
112
|
+
var _a, _b, _c;
|
|
119
113
|
return __awaiter(this, void 0, void 0, function () {
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
var data, headers, timeout, replySubject, chunkSize, requestId, opts, fileDescriptor, chunkNumber, getChunk, subscription, responsePromise, chunk, i;
|
|
115
|
+
var _this = this;
|
|
116
|
+
return __generator(this, function (_d) {
|
|
117
|
+
// Prepend the base subject if the given subject does not start with that
|
|
118
|
+
if (!(options === null || options === void 0 ? void 0 : options.fullSubject)) {
|
|
119
|
+
subject = "".concat(this.baseSubject, ".Request.").concat(subject);
|
|
120
|
+
}
|
|
121
|
+
if (!this.connection)
|
|
122
|
+
return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
|
|
123
|
+
if (this.connection.isClosed())
|
|
124
|
+
return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
|
|
125
|
+
data = this.encode(payload);
|
|
126
|
+
headers = this.buildHeaders();
|
|
127
|
+
timeout = (options === null || options === void 0 ? void 0 : options.timeout) || this.timeout;
|
|
128
|
+
replySubject = crypto.randomUUID();
|
|
129
|
+
chunkSize = ((_c = (_b = (_a = this.connection) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.max_payload) !== null && _c !== void 0 ? _c : 512000) - 2000;
|
|
130
|
+
// The Session and the Client need to agree on the chunk size. Put it in a header.
|
|
131
|
+
headers.append('ChunkSize', chunkSize.toString());
|
|
132
|
+
requestId = crypto.randomUUID();
|
|
133
|
+
headers.append('RequestId', requestId);
|
|
134
|
+
opts = __assign(__assign({}, options === null || options === void 0 ? void 0 : options.publishOptions), { headers: headers, reply: replySubject });
|
|
135
|
+
fileDescriptor = new FileDescriptor(data.length, chunkSize);
|
|
136
|
+
chunkNumber = 1;
|
|
137
|
+
headers.set('ChunkNumber', chunkNumber.toString());
|
|
138
|
+
getChunk = function (chunk) {
|
|
139
|
+
var offset = chunk * fileDescriptor.chunkSize;
|
|
140
|
+
return data.slice(offset, offset + fileDescriptor.chunkSize);
|
|
141
|
+
};
|
|
142
|
+
subscription = this.connection.subscribe(replySubject);
|
|
143
|
+
responsePromise = new Promise(function (resolve, reject) {
|
|
144
|
+
var messages = [];
|
|
145
|
+
subscription.callback = function (error, message) {
|
|
146
|
+
var _a;
|
|
147
|
+
if (error) {
|
|
148
|
+
reject(error);
|
|
127
149
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
133
|
-
data = this.encode(payload);
|
|
134
|
-
headers = this.buildHeaders();
|
|
135
|
-
headers.append('ChunkSize', this._chunkSize.toString());
|
|
136
|
-
timeout = (options === null || options === void 0 ? void 0 : options.timeout) || this.timeout;
|
|
137
|
-
opts = __assign(__assign({}, options), { timeout: timeout, headers: headers });
|
|
138
|
-
fileDescriptor = new FileDescriptor(data.length, this._chunkSize);
|
|
139
|
-
getChunk = function (chunk) {
|
|
140
|
-
var offset = chunk * fileDescriptor.chunkSize;
|
|
141
|
-
return data.slice(offset, offset + fileDescriptor.chunkSize);
|
|
142
|
-
};
|
|
143
|
-
_a.label = 1;
|
|
144
|
-
case 1:
|
|
145
|
-
_a.trys.push([1, 9, , 10]);
|
|
146
|
-
requestResponse = void 0;
|
|
147
|
-
dataSubject = subject;
|
|
148
|
-
i = 0;
|
|
149
|
-
_a.label = 2;
|
|
150
|
-
case 2:
|
|
151
|
-
if (!(i < fileDescriptor.numberOfChunks)) return [3 /*break*/, 5];
|
|
152
|
-
return [4 /*yield*/, this.connection.request(dataSubject, getChunk(i), opts)];
|
|
153
|
-
case 3:
|
|
154
|
-
requestResponse = _a.sent();
|
|
155
|
-
dataSubject = requestResponse.reply;
|
|
156
|
-
_a.label = 4;
|
|
157
|
-
case 4:
|
|
158
|
-
i++;
|
|
159
|
-
return [3 /*break*/, 2];
|
|
160
|
-
case 5:
|
|
161
|
-
if (!(fileDescriptor.fileSize % fileDescriptor.chunkSize === 0)) return [3 /*break*/, 7];
|
|
162
|
-
return [4 /*yield*/, this.connection.request(dataSubject, Empty, opts)];
|
|
163
|
-
case 6:
|
|
164
|
-
requestResponse = _a.sent();
|
|
165
|
-
_a.label = 7;
|
|
166
|
-
case 7: return [4 /*yield*/, this.downloadChunkedRequest(requestResponse, rawResponse, opts)];
|
|
167
|
-
case 8:
|
|
168
|
-
result = _a.sent();
|
|
169
|
-
if (this.isErrorResponse(result)) {
|
|
170
|
-
error = ErrorResponse.fromJS(result);
|
|
171
|
-
this.eventEmitter.emit(Events.ERROR, error);
|
|
172
|
-
return [2 /*return*/, Promise.reject(error)];
|
|
150
|
+
// Put all the response chunks in an array in the order they are received
|
|
151
|
+
messages.push(message);
|
|
152
|
+
// If the chunk has a size equal to the chunkSize, we should expect another message
|
|
153
|
+
if (message.data.length === chunkSize) {
|
|
154
|
+
return;
|
|
173
155
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if
|
|
178
|
-
|
|
156
|
+
// If the chunk has a length smaller than the chunkSize, the message is complete
|
|
157
|
+
// If the final message was received, we can safely unsubscribe
|
|
158
|
+
subscription.unsubscribe();
|
|
159
|
+
// Check if the number of the final message is equal to the number of
|
|
160
|
+
// messages we received. If this is not the case, we dropped a chunk,
|
|
161
|
+
// likely due to a network error. In this case, the entire message is invalid.
|
|
162
|
+
var finalMessage = message;
|
|
163
|
+
var finalMessageNumber = (_a = finalMessage.headers) === null || _a === void 0 ? void 0 : _a.get('ChunkNumber');
|
|
164
|
+
if (!finalMessageNumber) {
|
|
165
|
+
return reject('Response is not a valid chunk.');
|
|
179
166
|
}
|
|
180
|
-
|
|
181
|
-
return
|
|
167
|
+
if (parseInt(finalMessageNumber) !== messages.length) {
|
|
168
|
+
return reject("Expected {finalMessageNumber} chunks, but received ".concat(messages.length, ". ") +
|
|
169
|
+
"The connection may have been interrupted.");
|
|
182
170
|
}
|
|
183
|
-
|
|
184
|
-
|
|
171
|
+
// Concatenate the payloads
|
|
172
|
+
// When there are many chunks, doing a single allocation
|
|
173
|
+
// is significantly faster than concatenating arrays in sequence
|
|
174
|
+
var dataArrays = messages.map(function (m) { return m.data; });
|
|
175
|
+
var flattenedSize = dataArrays.reduce(function (sum, array) { return sum + array.length; }, 0);
|
|
176
|
+
var flattenedArray = new Uint8Array(flattenedSize);
|
|
177
|
+
var k = 0;
|
|
178
|
+
dataArrays.map(function (m) {
|
|
179
|
+
for (var i = 0; i < m.length; i++) {
|
|
180
|
+
flattenedArray[k++] = m[i];
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return resolve(flattenedArray);
|
|
184
|
+
};
|
|
185
|
+
})
|
|
186
|
+
.then(function (byteArray) {
|
|
187
|
+
if (byteArray.length === 0) {
|
|
188
|
+
return Promise.resolve(undefined);
|
|
189
|
+
}
|
|
190
|
+
var jsonCodec = JSONCodec();
|
|
191
|
+
// If a raw response is requested, we should avoid decoding the bytes.
|
|
192
|
+
var response = (options === null || options === void 0 ? void 0 : options.rawResponse) ? byteArray : jsonCodec.decode(byteArray);
|
|
193
|
+
return _this.isErrorResponse(response) ? Promise.reject(ErrorResponse.fromJS(response)) : Promise.resolve(response);
|
|
194
|
+
})
|
|
195
|
+
.catch(function (err) {
|
|
196
|
+
return Promise.reject(_this.natsErrorHandler(err, subject));
|
|
197
|
+
});
|
|
198
|
+
chunk = getChunk(0);
|
|
199
|
+
this.connection.publish(subject, chunk, opts);
|
|
200
|
+
chunkNumber += 1;
|
|
201
|
+
for (i = 1; i < fileDescriptor.numberOfChunks; i++) {
|
|
202
|
+
headers.set('ChunkNumber', chunkNumber.toString());
|
|
203
|
+
chunk = getChunk(i);
|
|
204
|
+
this.connection.publish(subject, chunk, opts);
|
|
205
|
+
chunkNumber += 1;
|
|
206
|
+
}
|
|
207
|
+
// In the special case where the last published chunk was full, we need to publish
|
|
208
|
+
// an empty message
|
|
209
|
+
if (data.length > 0 && data.length % fileDescriptor.chunkSize === 0) {
|
|
210
|
+
headers.set('ChunkNumber', chunkNumber.toString());
|
|
211
|
+
this.connection.publish(subject, Empty, opts);
|
|
185
212
|
}
|
|
213
|
+
// Now that we have sent the terminating chunk, the result should arrive on our promise.
|
|
214
|
+
return [2 /*return*/, this.withTimeout(responsePromise, timeout).catch(function (err) {
|
|
215
|
+
subscription.unsubscribe();
|
|
216
|
+
return Promise.reject(_this.natsErrorHandler(err, subject));
|
|
217
|
+
})];
|
|
186
218
|
});
|
|
187
219
|
});
|
|
188
220
|
};
|
|
@@ -234,50 +266,6 @@ var BaseClient = /** @class */ (function () {
|
|
|
234
266
|
var natsSubject = "".concat(this.baseSubject, ".").concat(subject);
|
|
235
267
|
return this.connection.subscribe(natsSubject, options);
|
|
236
268
|
};
|
|
237
|
-
/**
|
|
238
|
-
* Receive a chunked file specified by a request response
|
|
239
|
-
* @param requestResponse Contains a reply subject and a file descriptor which can be used to download chunks
|
|
240
|
-
* @param rawResponse If true, the response should not be decoded
|
|
241
|
-
* @param opts Request options
|
|
242
|
-
* @returns
|
|
243
|
-
*/
|
|
244
|
-
BaseClient.prototype.downloadChunkedRequest = function (requestResponse, rawResponse, opts) {
|
|
245
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
246
|
-
var result, resultCodec;
|
|
247
|
-
return __generator(this, function (_a) {
|
|
248
|
-
switch (_a.label) {
|
|
249
|
-
case 0:
|
|
250
|
-
if (!this.connection)
|
|
251
|
-
return [2 /*return*/, Promise.reject('Chunking: Connection is down! Please try again!')];
|
|
252
|
-
if (this.connection.isClosed())
|
|
253
|
-
return [2 /*return*/, Promise.reject("Chunking: Connection has been closed! Please reconnect!")];
|
|
254
|
-
result = new Uint8Array([]);
|
|
255
|
-
_a.label = 1;
|
|
256
|
-
case 1:
|
|
257
|
-
result = new Uint8Array(__spreadArray(__spreadArray([], Array.from(result), true), Array.from(requestResponse.data), true));
|
|
258
|
-
// Acknowledge that the final chunk was received
|
|
259
|
-
if (requestResponse.data.length < this._chunkSize) {
|
|
260
|
-
this.connection.publish(requestResponse.reply, Empty, opts);
|
|
261
|
-
return [3 /*break*/, 4];
|
|
262
|
-
}
|
|
263
|
-
return [4 /*yield*/, this.connection.request(requestResponse.reply, Empty, opts)];
|
|
264
|
-
case 2:
|
|
265
|
-
requestResponse = _a.sent();
|
|
266
|
-
_a.label = 3;
|
|
267
|
-
case 3: return [3 /*break*/, 1];
|
|
268
|
-
case 4:
|
|
269
|
-
if (result.length !== 0) {
|
|
270
|
-
// The runner skips the encoding step if the return type is a byte array, so we must also skip the decoding step in this case.
|
|
271
|
-
if (rawResponse)
|
|
272
|
-
return [2 /*return*/, Promise.resolve(result)];
|
|
273
|
-
resultCodec = JSONCodec();
|
|
274
|
-
return [2 /*return*/, resultCodec.decode(result)];
|
|
275
|
-
}
|
|
276
|
-
return [2 /*return*/];
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
};
|
|
281
269
|
BaseClient.prototype.encode = function (payload) {
|
|
282
270
|
if (!payload) {
|
|
283
271
|
return Empty;
|
package/lib/RunnerClient.js
CHANGED
|
@@ -51,7 +51,7 @@ var RunnerClient = /** @class */ (function (_super) {
|
|
|
51
51
|
*/
|
|
52
52
|
RunnerClient.prototype.resolveImage = function (images, timeout) {
|
|
53
53
|
return (images === null || images === void 0 ? void 0 : images.length) > 0
|
|
54
|
-
? this.request('ResolveImage', images, { timeout: timeout
|
|
54
|
+
? this.request('ResolveImage', images, { timeout: timeout })
|
|
55
55
|
.then(function (imageJs) { return Image.fromJS(imageJs); })
|
|
56
56
|
.then(this.success())
|
|
57
57
|
.catch(this.error())
|
package/lib/SessionClient.js
CHANGED
|
@@ -218,7 +218,9 @@ var SessionClient = /** @class */ (function (_super) {
|
|
|
218
218
|
return Promise.reject('The source of the provided resource is not a nats subject.');
|
|
219
219
|
}
|
|
220
220
|
var subject = resource.source.slice(runnerResourcePrefix.length);
|
|
221
|
-
return this.request(subject, undefined,
|
|
221
|
+
return this.request(subject, undefined, { rawResponse: true, fullSubject: true })
|
|
222
|
+
.then(this.success())
|
|
223
|
+
.catch(this.error());
|
|
222
224
|
};
|
|
223
225
|
/**
|
|
224
226
|
* Load test plan using a test plan TapPackage from a repository
|