@opentap/runner-client 2.2.4 → 2.3.0-alpha.1.2

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.
@@ -9,6 +9,7 @@ export declare class BaseClient {
9
9
  private _accessToken;
10
10
  private _headers;
11
11
  private _timeout;
12
+ private _chunkSize;
12
13
  /** Get request access token */
13
14
  get accessToken(): string;
14
15
  /** Set request access token */
@@ -27,11 +28,9 @@ export declare class BaseClient {
27
28
  * @param subject The subject to request
28
29
  * @param payload (optional)
29
30
  * @param options (optional)
30
- * @param isFullSubject (optional) If true, use the subject as request subject
31
- * without appending it to the baseSubject
32
31
  * @returns Promise of an object
33
32
  */
34
- protected request<T>(subject: string, payload?: any, options?: RequestOptions): Promise<T>;
33
+ protected request<T>(subject: string, payload?: any, options?: RequestOptions, rawResponse?: boolean, fullSubject?: boolean): Promise<T>;
35
34
  /**
36
35
  * Handle the error
37
36
  * @param error
@@ -51,15 +50,6 @@ export declare class BaseClient {
51
50
  * @returns Subscription object
52
51
  */
53
52
  protected subscribe(subject: string, options: SubscriptionOptions): Subscription;
54
- /**
55
- * Make a request to the runner with a chunked payload. The response will be received in chunks.
56
- * @param subject The request subject.
57
- * @param payload The request body which will be chunked
58
- * @param options Optional request options
59
- * @param rawResponse If true, the response will not be decoded
60
- * @returns
61
- */
62
- protected sendChunked<T>(subject: string, payload: any, options?: RequestOptions, rawResponse?: boolean): Promise<T>;
63
53
  /**
64
54
  * Receive a chunked file specified by a request response
65
55
  * @param requestResponse Contains a reply subject and a file descriptor which can be used to download chunks
@@ -68,16 +58,7 @@ export declare class BaseClient {
68
58
  * @returns
69
59
  */
70
60
  private downloadChunkedRequest;
71
- /**
72
- * Make a request to the runner whose response will be sent in chunked
73
- * @param subject The request subject
74
- * @param payload Optional request body
75
- * @param options Optional request options
76
- * @param rawResponse If true, the return value will not be decoded
77
- * @param fullSubject If true, the base subject will not be prepended
78
- * @returns
79
- */
80
- protected requestChunked<T>(subject: string, payload?: any, options?: RequestOptions, rawResponse?: boolean, fullSubject?: boolean): Promise<T>;
61
+ protected encode(payload: any): Uint8Array;
81
62
  /**
82
63
  * Check if the the response is an error from the server.
83
64
  * @param {any} response
package/lib/BaseClient.js CHANGED
@@ -45,6 +45,15 @@ 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
+ };
48
57
  import { ComponentSettingsBase, ComponentSettingsIdentifier, ComponentSettingsListItem, DataGridControl, ErrorResponse, FileDescriptor, ListItemType, ProfileGroup, } from './DTOs';
49
58
  import { Empty, ErrorCode, JSONCodec, StringCodec, connect, headers, } from 'nats.ws';
50
59
  import { EventEmitter } from 'events';
@@ -57,6 +66,7 @@ var BaseClient = /** @class */ (function () {
57
66
  function BaseClient(baseSubject, options) {
58
67
  this.domainAccess = new Map();
59
68
  this._headers = new Headers();
69
+ this._chunkSize = 512000;
60
70
  this.baseSubject = baseSubject;
61
71
  this.connectionOptions = __assign({}, options) || {};
62
72
  this.connectionOptions.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || DEFAULT_TIMEOUT;
@@ -103,39 +113,60 @@ var BaseClient = /** @class */ (function () {
103
113
  * @param subject The subject to request
104
114
  * @param payload (optional)
105
115
  * @param options (optional)
106
- * @param isFullSubject (optional) If true, use the subject as request subject
107
- * without appending it to the baseSubject
108
116
  * @returns Promise of an object
109
117
  */
110
- BaseClient.prototype.request = function (subject, payload, options) {
118
+ BaseClient.prototype.request = function (subject, payload, options, rawResponse, fullSubject) {
111
119
  return __awaiter(this, void 0, void 0, function () {
112
- var stringCodec, data, headers, timeout, opts;
113
- var _this = this;
120
+ var data, headers, timeout, opts, fileDescriptor, getChunk, requestResponse, dataSubject, i, result, error;
114
121
  return __generator(this, function (_a) {
115
122
  switch (_a.label) {
116
123
  case 0:
117
- subject = "".concat(this.baseSubject, ".Request.").concat(subject);
124
+ // Prepend the base subject if the given subject does not start with that
125
+ if (!fullSubject) {
126
+ subject = "".concat(this.baseSubject, ".Request.").concat(subject);
127
+ }
118
128
  if (!this.connection)
119
129
  return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
120
130
  if (this.connection.isClosed())
121
131
  return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
122
- stringCodec = StringCodec();
123
- data = payload ? stringCodec.encode(JSON.stringify(payload)) : Empty;
132
+ data = this.encode(payload);
124
133
  headers = this.buildHeaders();
134
+ headers.append('ChunkSize', this._chunkSize.toString());
125
135
  timeout = (options === null || options === void 0 ? void 0 : options.timeout) || this.timeout;
126
136
  opts = __assign(__assign({}, options), { timeout: timeout, headers: headers });
127
- return [4 /*yield*/, this.connection
128
- .request(subject, data, opts)
129
- .then(function (message) {
130
- var response;
131
- if ((message === null || message === void 0 ? void 0 : message.data.length) !== 0) {
132
- var jsonCodec = JSONCodec();
133
- response = jsonCodec.decode(message.data);
134
- }
135
- return _this.isErrorResponse(response) ? Promise.reject(ErrorResponse.fromJS(response)) : Promise.resolve(response);
136
- })
137
- .catch(function (error) { return Promise.reject(_this.natsErrorHandler(error, subject)); })];
138
- case 1: return [2 /*return*/, _a.sent()];
137
+ fileDescriptor = new FileDescriptor(data.length, this._chunkSize);
138
+ getChunk = function (chunk) {
139
+ var offset = chunk * fileDescriptor.chunkSize;
140
+ return data.slice(offset, offset + fileDescriptor.chunkSize);
141
+ };
142
+ dataSubject = subject;
143
+ i = 0;
144
+ _a.label = 1;
145
+ case 1:
146
+ if (!(i < fileDescriptor.numberOfChunks)) return [3 /*break*/, 4];
147
+ return [4 /*yield*/, this.connection.request(dataSubject, getChunk(i), opts)];
148
+ case 2:
149
+ requestResponse = _a.sent();
150
+ dataSubject = requestResponse.reply;
151
+ _a.label = 3;
152
+ case 3:
153
+ i++;
154
+ return [3 /*break*/, 1];
155
+ case 4:
156
+ if (!(fileDescriptor.fileSize % fileDescriptor.chunkSize === 0)) return [3 /*break*/, 6];
157
+ return [4 /*yield*/, this.connection.request(dataSubject, Empty, opts)];
158
+ case 5:
159
+ requestResponse = _a.sent();
160
+ _a.label = 6;
161
+ case 6: return [4 /*yield*/, this.downloadChunkedRequest(requestResponse, rawResponse, opts)];
162
+ case 7:
163
+ result = _a.sent();
164
+ if (this.isErrorResponse(result)) {
165
+ error = ErrorResponse.fromJS(result);
166
+ this.eventEmitter.emit(Events.ERROR, error);
167
+ return [2 /*return*/, Promise.reject(error)];
168
+ }
169
+ return [2 /*return*/, result];
139
170
  }
140
171
  });
141
172
  });
@@ -188,82 +219,6 @@ var BaseClient = /** @class */ (function () {
188
219
  var natsSubject = "".concat(this.baseSubject, ".").concat(subject);
189
220
  return this.connection.subscribe(natsSubject, options);
190
221
  };
191
- /**
192
- * Make a request to the runner with a chunked payload. The response will be received in chunks.
193
- * @param subject The request subject.
194
- * @param payload The request body which will be chunked
195
- * @param options Optional request options
196
- * @param rawResponse If true, the response will not be decoded
197
- * @returns
198
- */
199
- BaseClient.prototype.sendChunked = function (subject, payload, options, rawResponse) {
200
- return __awaiter(this, void 0, void 0, function () {
201
- var stringCodec, data, headers, opts, fileDescriptor, requestResponse, jsonCodec, response, error, getChunk, dataSubject, i;
202
- return __generator(this, function (_a) {
203
- switch (_a.label) {
204
- case 0:
205
- if (!this.connection)
206
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
207
- if (this.connection.isClosed())
208
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
209
- subject = "".concat(this.baseSubject, ".Request.").concat(subject);
210
- stringCodec = StringCodec();
211
- if (payload) {
212
- data = payload instanceof Uint8Array ? payload : stringCodec.encode(JSON.stringify(payload));
213
- }
214
- else {
215
- data = Empty;
216
- }
217
- headers = this.buildHeaders();
218
- opts = __assign(__assign({}, options), { timeout: this.timeout, headers: headers });
219
- fileDescriptor = new FileDescriptor(data.length);
220
- if (!fileDescriptor.numberOfChunks)
221
- return [2 /*return*/, Promise.reject("".concat(subject, ": File is empty!"))];
222
- return [4 /*yield*/, this.connection.request(subject, stringCodec.encode(JSON.stringify(fileDescriptor)), opts)];
223
- case 1:
224
- requestResponse = _a.sent();
225
- if (requestResponse.data.length > 0) {
226
- jsonCodec = JSONCodec();
227
- response = jsonCodec.decode(requestResponse.data);
228
- if (this.isErrorResponse(response)) {
229
- error = ErrorResponse.fromJS(response);
230
- this.eventEmitter.emit(Events.ERROR, error);
231
- return [2 /*return*/, Promise.reject(error)];
232
- }
233
- }
234
- // This should never happen. If it does happen, it likely means we are targetting an incompatible Runner version.
235
- if (!requestResponse.reply) {
236
- return [2 /*return*/, Promise.reject('Send chunks: Runner did not return a reply subject.')];
237
- }
238
- getChunk = function (chunk) {
239
- var offset = chunk * fileDescriptor.chunkSize;
240
- return data.slice(offset, offset + fileDescriptor.chunkSize);
241
- };
242
- dataSubject = requestResponse.reply;
243
- i = 0;
244
- _a.label = 2;
245
- case 2:
246
- if (!(i < fileDescriptor.numberOfChunks)) return [3 /*break*/, 5];
247
- return [4 /*yield*/, this.connection.request(dataSubject, getChunk(i), opts)];
248
- case 3:
249
- // The runner must acknowledge that it has received each chunk.
250
- // Likewise, we must wait for the runner to acknowledge each chunk
251
- // before sending the next one, because the acknowledge from the runner
252
- // contains the subject where we should send the next chunk.
253
- requestResponse = _a.sent();
254
- dataSubject = requestResponse.reply;
255
- _a.label = 4;
256
- case 4:
257
- i++;
258
- return [3 /*break*/, 2];
259
- case 5:
260
- // The response to any chunked request is itself a chunked response.
261
- // The last acknowledge from the runner indicates the subject where the response can be downloaded from.
262
- return [2 /*return*/, this.downloadChunkedRequest(requestResponse, rawResponse, opts)];
263
- }
264
- });
265
- });
266
- };
267
222
  /**
268
223
  * Receive a chunked file specified by a request response
269
224
  * @param requestResponse Contains a reply subject and a file descriptor which can be used to download chunks
@@ -273,7 +228,7 @@ var BaseClient = /** @class */ (function () {
273
228
  */
274
229
  BaseClient.prototype.downloadChunkedRequest = function (requestResponse, rawResponse, opts) {
275
230
  return __awaiter(this, void 0, void 0, function () {
276
- var jsonCodec, responseBody, error, fileDescriptor, dataSubject, result, bytesReceived, i, j, resultCodec;
231
+ var result, resultCodec;
277
232
  return __generator(this, function (_a) {
278
233
  switch (_a.label) {
279
234
  case 0:
@@ -281,91 +236,41 @@ var BaseClient = /** @class */ (function () {
281
236
  return [2 /*return*/, Promise.reject('Chunking: Connection is down! Please try again!')];
282
237
  if (this.connection.isClosed())
283
238
  return [2 /*return*/, Promise.reject("Chunking: Connection has been closed! Please reconnect!")];
284
- jsonCodec = JSONCodec();
285
- responseBody = jsonCodec.decode(requestResponse.data);
286
- if (this.isErrorResponse(responseBody)) {
287
- error = ErrorResponse.fromJS(responseBody);
288
- this.eventEmitter.emit(Events.ERROR, error);
289
- return [2 /*return*/, Promise.reject(error)];
290
- }
291
- fileDescriptor = FileDescriptor.fromJS(responseBody);
292
- if (!requestResponse.reply || !fileDescriptor || fileDescriptor.numberOfChunks === 0) {
293
- return [2 /*return*/, Promise.reject('Chunking: Response payload does not indicate a chunked response.')];
294
- }
295
- dataSubject = requestResponse.reply;
296
- result = new Uint8Array(fileDescriptor.fileSize);
297
- bytesReceived = 0;
298
- i = 0;
239
+ result = new Uint8Array([]);
299
240
  _a.label = 1;
300
241
  case 1:
301
- if (!(i < fileDescriptor.numberOfChunks)) return [3 /*break*/, 4];
302
- return [4 /*yield*/, this.connection.request(dataSubject, Empty, opts)];
242
+ result = new Uint8Array(__spreadArray(__spreadArray([], Array.from(result), true), Array.from(requestResponse.data), true));
243
+ // Acknowledge that the final chunk was received
244
+ if (requestResponse.data.length < this._chunkSize) {
245
+ this.connection.publish(requestResponse.reply, Empty);
246
+ return [3 /*break*/, 4];
247
+ }
248
+ return [4 /*yield*/, this.connection.request(requestResponse.reply, Empty, opts)];
303
249
  case 2:
304
- // Send a request with an empty payload to acknowledge that we received the previous chunk
305
- // The response body contains the current chunk.
306
- // The reply subject of the response indicates where we can get the next chunk
307
250
  requestResponse = _a.sent();
308
- dataSubject = requestResponse.reply;
309
- for (j = 0; j < requestResponse.data.length; j++) {
310
- result[bytesReceived++] = requestResponse.data[j];
311
- }
312
251
  _a.label = 3;
313
- case 3:
314
- i++;
315
- return [3 /*break*/, 1];
252
+ case 3: return [3 /*break*/, 1];
316
253
  case 4:
317
- // Send an empty message to acknowledge that the final chunk was received
318
- this.connection.publish(dataSubject, Empty);
319
- if (bytesReceived !== fileDescriptor.fileSize) {
320
- return [2 /*return*/, Promise.reject("Unexpected repsonse size. Got ".concat(bytesReceived, " bytes, but expected ").concat(fileDescriptor.fileSize, "."))];
254
+ if (result.length !== 0) {
255
+ // 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.
256
+ if (rawResponse)
257
+ return [2 /*return*/, Promise.resolve(result)];
258
+ resultCodec = JSONCodec();
259
+ return [2 /*return*/, resultCodec.decode(result)];
321
260
  }
322
- // 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.
323
- if (rawResponse)
324
- return [2 /*return*/, Promise.resolve(result)];
325
- resultCodec = JSONCodec();
326
- return [2 /*return*/, resultCodec.decode(result)];
261
+ return [2 /*return*/];
327
262
  }
328
263
  });
329
264
  });
330
265
  };
331
- /**
332
- * Make a request to the runner whose response will be sent in chunked
333
- * @param subject The request subject
334
- * @param payload Optional request body
335
- * @param options Optional request options
336
- * @param rawResponse If true, the return value will not be decoded
337
- * @param fullSubject If true, the base subject will not be prepended
338
- * @returns
339
- */
340
- BaseClient.prototype.requestChunked = function (subject, payload, options, rawResponse, fullSubject) {
341
- return __awaiter(this, void 0, void 0, function () {
342
- var data, stringCodec, headers, opts, requestResponse;
343
- return __generator(this, function (_a) {
344
- switch (_a.label) {
345
- case 0:
346
- if (!this.connection)
347
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
348
- if (this.connection.isClosed())
349
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
350
- if (!fullSubject) {
351
- subject = "".concat(this.baseSubject, ".Request.").concat(subject);
352
- }
353
- if (payload instanceof Uint8Array) {
354
- data = payload;
355
- }
356
- else {
357
- stringCodec = StringCodec();
358
- data = payload ? stringCodec.encode(JSON.stringify(payload)) : Empty;
359
- }
360
- headers = this.buildHeaders();
361
- opts = __assign(__assign({}, options), { timeout: this.timeout, headers: headers });
362
- return [4 /*yield*/, this.connection.request(subject, data, opts)];
363
- case 1:
364
- requestResponse = _a.sent();
365
- return [2 /*return*/, this.downloadChunkedRequest(requestResponse, rawResponse, opts)];
366
- }
367
- });
368
- });
266
+ BaseClient.prototype.encode = function (payload) {
267
+ if (!payload) {
268
+ return Empty;
269
+ }
270
+ if (payload instanceof Uint8Array) {
271
+ return payload;
272
+ }
273
+ return StringCodec().encode(JSON.stringify(payload));
369
274
  };
370
275
  /**
371
276
  * Check if the the response is an error from the server.
package/lib/DTOs.d.ts CHANGED
@@ -446,8 +446,7 @@ export declare class FileDescriptor implements IFileDescriptor {
446
446
  numberOfChunks: number;
447
447
  fileSize: number;
448
448
  chunkSize: number;
449
- private readonly defaultChunkSize;
450
- constructor(fileSize: number, chunkSize?: number);
449
+ constructor(fileSize: number, chunkSize: number);
451
450
  init(_data?: any): void;
452
451
  static fromJS(data: any): FileDescriptor;
453
452
  toJSON(data?: any): any;
package/lib/DTOs.js CHANGED
@@ -1306,12 +1306,11 @@ export { Resource };
1306
1306
  var FileDescriptor = /** @class */ (function () {
1307
1307
  function FileDescriptor(fileSize, chunkSize) {
1308
1308
  var _a;
1309
- this.defaultChunkSize = 512000;
1310
1309
  if (chunkSize === 0) {
1311
1310
  throw Error('chunkSize cannot set to 0');
1312
1311
  }
1313
1312
  this.fileSize = fileSize;
1314
- this.chunkSize = chunkSize !== null && chunkSize !== void 0 ? chunkSize : this.defaultChunkSize;
1313
+ this.chunkSize = chunkSize;
1315
1314
  this.numberOfChunks = Math.floor((((_a = this.fileSize) !== null && _a !== void 0 ? _a : 0) + this.chunkSize - 1) / this.chunkSize);
1316
1315
  }
1317
1316
  FileDescriptor.prototype.init = function (_data) {
@@ -1323,7 +1322,7 @@ var FileDescriptor = /** @class */ (function () {
1323
1322
  };
1324
1323
  FileDescriptor.fromJS = function (data) {
1325
1324
  data = typeof data === 'object' ? data : {};
1326
- var result = new FileDescriptor(0);
1325
+ var result = new FileDescriptor(0, 512000);
1327
1326
  result.init(data);
1328
1327
  return result;
1329
1328
  };
@@ -197,7 +197,7 @@ var SessionClient = /** @class */ (function (_super) {
197
197
  * @return Test plan loaded. List of load errors is returned.
198
198
  */
199
199
  SessionClient.prototype.setTestPlanXML = function (xml) {
200
- return this.sendChunked('SetTestPlanXML', xml).then(this.success()).catch(this.error());
200
+ return this.request('SetTestPlanXML', xml).then(this.success()).catch(this.error());
201
201
  };
202
202
  /**
203
203
  * Retrieve loaded test plan XML
@@ -205,7 +205,7 @@ var SessionClient = /** @class */ (function (_super) {
205
205
  */
206
206
  SessionClient.prototype.getTestPlanXML = function () {
207
207
  // Generate a unique subject where the Runner will publish the chunks
208
- return this.requestChunked('GetTestPlanXML').then(this.success()).catch(this.error());
208
+ return this.request('GetTestPlanXML').then(this.success()).catch(this.error());
209
209
  };
210
210
  /**
211
211
  * Downloads the resource from the runner and returns it as raw bytes
@@ -218,7 +218,7 @@ 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.requestChunked(subject, undefined, undefined, true, true).then(this.success()).catch(this.error());
221
+ return this.request(subject, undefined, undefined, true, true).then(this.success()).catch(this.error());
222
222
  };
223
223
  /**
224
224
  * Load test plan using a test plan TapPackage from a repository
@@ -241,7 +241,7 @@ var SessionClient = /** @class */ (function (_super) {
241
241
  * @return Test plan resources opened.
242
242
  */
243
243
  SessionClient.prototype.resourcesOpen = function () {
244
- return this.requestChunked('ResourcesOpen')
244
+ return this.request('ResourcesOpen')
245
245
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
246
246
  .then(this.success())
247
247
  .catch(this.error());
@@ -251,7 +251,7 @@ var SessionClient = /** @class */ (function (_super) {
251
251
  * @return Test plan resources closed.
252
252
  */
253
253
  SessionClient.prototype.resourcesClose = function () {
254
- return this.requestChunked('ResourcesClose')
254
+ return this.request('ResourcesClose')
255
255
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
256
256
  .then(this.success())
257
257
  .catch(this.error());
@@ -286,7 +286,7 @@ var SessionClient = /** @class */ (function (_super) {
286
286
  * @return Test plan retrieved
287
287
  */
288
288
  SessionClient.prototype.getTestPlan = function (properties) {
289
- return this.requestChunked('GetTestPlan', properties)
289
+ return this.request('GetTestPlan', properties)
290
290
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
291
291
  .then(this.success())
292
292
  .catch(this.error());
@@ -297,7 +297,7 @@ var SessionClient = /** @class */ (function (_super) {
297
297
  * @return Test plan changed
298
298
  */
299
299
  SessionClient.prototype.setTestPlan = function (plan) {
300
- return this.sendChunked('SetTestPlan', plan)
300
+ return this.request('SetTestPlan', plan)
301
301
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
302
302
  .then(this.success())
303
303
  .catch(this.error());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentap/runner-client",
3
- "version": "2.2.4",
3
+ "version": "2.3.0-alpha.1.2",
4
4
  "description": "This is the web client for the OpenTAP Runner.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",