@opentap/runner-client 2.2.3 → 2.2.4-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.
@@ -1,5 +1,5 @@
1
1
  import { ComponentSettingsBase, ComponentSettingsIdentifier, ComponentSettingsListItem, DataGridControl, ErrorResponse, FileParameter, FileResponse, ListItemType, ProfileGroup, RepositoryPackageReference, RepositorySettingsPackageDefinition, SettingsTapPackage } from './DTOs';
2
- import { ConnectionOptions, RequestOptions, Subscription, SubscriptionOptions } from 'nats.ws';
2
+ import { ConnectionOptions, RequestOptions, Subscription, SubscriptionOptions, Msg } from 'nats.ws';
3
3
  export declare class BaseClient {
4
4
  private connection;
5
5
  private baseSubject;
@@ -31,7 +31,7 @@ export declare class BaseClient {
31
31
  * without appending it to the baseSubject
32
32
  * @returns Promise of an object
33
33
  */
34
- protected request<T>(subject: string, payload?: any, options?: RequestOptions, isFullSubject?: boolean): Promise<T>;
34
+ protected request<T>(subject: string, payload?: any, options?: RequestOptions): Promise<T>;
35
35
  /**
36
36
  * Handle the error
37
37
  * @param error
@@ -58,7 +58,8 @@ export declare class BaseClient {
58
58
  * @param options (optional)
59
59
  * @returns Promise of an object
60
60
  */
61
- protected sendChunked<T>(subject: string, payload?: any, options?: RequestOptions): Promise<T>;
61
+ protected sendChunked<T>(subject: string, payload?: any, options?: RequestOptions, rawResponse?: boolean): Promise<T>;
62
+ receiveFile<T>(msg: Msg, rawResponse?: boolean, opts?: RequestOptions): Promise<T>;
62
63
  /**
63
64
  * Request an object to the nats server with chunks.
64
65
  * @param subject The subject to request
@@ -66,7 +67,7 @@ export declare class BaseClient {
66
67
  * @param options (optional)
67
68
  * @returns Promise of an object
68
69
  */
69
- protected requestChunked<T>(subject: string, replySubject: string, payload: any, options?: RequestOptions, isFullSubject?: boolean, rawResponse?: boolean): Promise<T>;
70
+ protected requestChunked<T>(subject: string, payload?: any, options?: RequestOptions, rawResponse?: boolean, fullSubject?: boolean): Promise<T>;
70
71
  /**
71
72
  * Check if the the response is an error from the server.
72
73
  * @param {any} response
package/lib/BaseClient.js CHANGED
@@ -45,15 +45,6 @@ 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
49
  import { Empty, ErrorCode, JSONCodec, StringCodec, connect, headers, } from 'nats.ws';
59
50
  import { EventEmitter } from 'events';
@@ -116,14 +107,14 @@ var BaseClient = /** @class */ (function () {
116
107
  * without appending it to the baseSubject
117
108
  * @returns Promise of an object
118
109
  */
119
- BaseClient.prototype.request = function (subject, payload, options, isFullSubject) {
110
+ BaseClient.prototype.request = function (subject, payload, options) {
120
111
  return __awaiter(this, void 0, void 0, function () {
121
112
  var stringCodec, data, headers, timeout, opts;
122
113
  var _this = this;
123
114
  return __generator(this, function (_a) {
124
115
  switch (_a.label) {
125
116
  case 0:
126
- subject = isFullSubject ? subject : "".concat(this.baseSubject, ".Request.").concat(subject);
117
+ subject = "".concat(this.baseSubject, ".Request.").concat(subject);
127
118
  if (!this.connection)
128
119
  return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
129
120
  if (this.connection.isClosed())
@@ -204,10 +195,9 @@ var BaseClient = /** @class */ (function () {
204
195
  * @param options (optional)
205
196
  * @returns Promise of an object
206
197
  */
207
- BaseClient.prototype.sendChunked = function (subject, payload, options) {
198
+ BaseClient.prototype.sendChunked = function (subject, payload, options, rawResponse) {
208
199
  return __awaiter(this, void 0, void 0, function () {
209
- var stringCodec, data, headers, opts, fileDescriptor, getChunk, dataSubject, i;
210
- var _this = this;
200
+ var stringCodec, data, headers, opts, fileDescriptor, msg, jsonCodec, response, error, getChunk, dataSubject, i;
211
201
  return __generator(this, function (_a) {
212
202
  switch (_a.label) {
213
203
  case 0:
@@ -215,58 +205,108 @@ var BaseClient = /** @class */ (function () {
215
205
  return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
216
206
  if (this.connection.isClosed())
217
207
  return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
208
+ subject = "".concat(this.baseSubject, ".Request.").concat(subject);
218
209
  stringCodec = StringCodec();
219
- data = payload ? stringCodec.encode(JSON.stringify(payload)) : Empty;
210
+ if (payload) {
211
+ data = payload instanceof Uint8Array ? payload : stringCodec.encode(JSON.stringify(payload));
212
+ }
213
+ else {
214
+ data = Empty;
215
+ }
220
216
  headers = this.buildHeaders();
221
217
  opts = __assign(__assign({}, options), { timeout: this.timeout, headers: headers });
222
218
  fileDescriptor = new FileDescriptor(data.length);
223
219
  if (!fileDescriptor.numberOfChunks)
224
220
  return [2 /*return*/, Promise.reject("".concat(subject, ": File is empty!"))];
221
+ return [4 /*yield*/, this.connection.request(subject, stringCodec.encode(JSON.stringify(fileDescriptor)), opts)];
222
+ case 1:
223
+ msg = _a.sent();
224
+ if (msg.data.length > 0) {
225
+ jsonCodec = JSONCodec();
226
+ response = jsonCodec.decode(msg.data);
227
+ if (this.isErrorResponse(response)) {
228
+ error = ErrorResponse.fromJS(response);
229
+ this.eventEmitter.emit(Events.ERROR, error);
230
+ return [2 /*return*/, Promise.reject(error)];
231
+ }
232
+ }
233
+ if (!msg.reply) {
234
+ return [2 /*return*/, Promise.reject('Send chunks: Runner did not return a reply subject.')];
235
+ }
225
236
  getChunk = function (chunk) {
226
237
  var offset = chunk * fileDescriptor.chunkSize;
227
238
  return data.slice(offset, offset + fileDescriptor.chunkSize);
228
239
  };
229
- return [4 /*yield*/, this.request(subject, fileDescriptor, opts)];
240
+ dataSubject = msg.reply;
241
+ i = 0;
242
+ _a.label = 2;
243
+ case 2:
244
+ if (!(i < fileDescriptor.numberOfChunks)) return [3 /*break*/, 5];
245
+ return [4 /*yield*/, this.connection.request(dataSubject, getChunk(i), opts)];
246
+ case 3:
247
+ msg = _a.sent();
248
+ dataSubject = msg.reply;
249
+ _a.label = 4;
250
+ case 4:
251
+ i++;
252
+ return [3 /*break*/, 2];
253
+ case 5:
254
+ // Receive a response based on the last message
255
+ return [2 /*return*/, this.receiveFile(msg, rawResponse, opts)];
256
+ }
257
+ });
258
+ });
259
+ };
260
+ BaseClient.prototype.receiveFile = function (msg, rawResponse, opts) {
261
+ return __awaiter(this, void 0, void 0, function () {
262
+ var jsonCodec, fdResponse, error, fd, dataSubject, result, k, i, j, resultCodec;
263
+ return __generator(this, function (_a) {
264
+ switch (_a.label) {
265
+ case 0:
266
+ if (!this.connection)
267
+ return [2 /*return*/, Promise.reject('Chunking: Connection is down! Please try again!')];
268
+ if (this.connection.isClosed())
269
+ return [2 /*return*/, Promise.reject("Chunking: Connection has been closed! Please reconnect!")];
270
+ jsonCodec = JSONCodec();
271
+ fdResponse = jsonCodec.decode(msg.data);
272
+ if (this.isErrorResponse(fdResponse)) {
273
+ error = ErrorResponse.fromJS(fdResponse);
274
+ this.eventEmitter.emit(Events.ERROR, error);
275
+ return [2 /*return*/, Promise.reject(error)];
276
+ }
277
+ fd = FileDescriptor.fromJS(fdResponse);
278
+ if (!msg.reply || !fd || fd.numberOfChunks === 0) {
279
+ return [2 /*return*/, Promise.reject('Chunking: Response payload does not indicate a chunked response.')];
280
+ }
281
+ dataSubject = msg.reply;
282
+ result = new Uint8Array(fd.fileSize);
283
+ k = 0;
284
+ i = 0;
285
+ _a.label = 1;
230
286
  case 1:
231
- dataSubject = _a.sent();
232
- for (i = 0; i < fileDescriptor.numberOfChunks; i++) {
233
- this.connection.publish(dataSubject, getChunk(i), opts);
287
+ if (!(i < fd.numberOfChunks)) return [3 /*break*/, 4];
288
+ return [4 /*yield*/, this.connection.request(dataSubject, Empty, opts)];
289
+ case 2:
290
+ msg = _a.sent();
291
+ dataSubject = msg.reply;
292
+ for (j = 0; j < msg.data.length; j++) {
293
+ result[k++] = msg.data[j];
234
294
  }
235
- return [2 /*return*/, new Promise(function (resolve, reject) {
236
- if (!_this.connection) {
237
- return Promise.reject('No nats connection.');
238
- }
239
- var combinedResult = new Uint8Array([]);
240
- // Subscribe to dataSubject before sending the terminating message to ensure we are listening before the server starts responding
241
- var subscription = _this.connection.subscribe(dataSubject);
242
- // Publish an empty message to indicate that we are ready to listen
243
- _this.connection.publish(dataSubject, Empty, opts);
244
- var first = true;
245
- subscription.callback = function (error, message) {
246
- // The first message is the empty message we published above.
247
- // We should discard this message before we proceed
248
- if (first === true) {
249
- first = false;
250
- return;
251
- }
252
- if (error) {
253
- reject(error);
254
- }
255
- combinedResult = new Uint8Array(__spreadArray(__spreadArray([], Array.from(combinedResult), true), Array.from(message === null || message === void 0 ? void 0 : message.data), true));
256
- if ((message === null || message === void 0 ? void 0 : message.data.length) === 0) {
257
- resolve(combinedResult);
258
- }
259
- };
260
- }).then(function (byteArray) {
261
- var jsonCodec = JSONCodec();
262
- var response = jsonCodec.decode(byteArray);
263
- if (_this.isErrorResponse(response)) {
264
- var error = ErrorResponse.fromJS(response);
265
- _this.eventEmitter.emit(Events.ERROR, error);
266
- return Promise.reject(error);
267
- }
268
- return Promise.resolve(response);
269
- })];
295
+ _a.label = 3;
296
+ case 3:
297
+ i++;
298
+ return [3 /*break*/, 1];
299
+ case 4:
300
+ // Send an empty message to acknowledge the final chunk was received
301
+ this.connection.publish(dataSubject, Empty);
302
+ if (k !== fd.fileSize) {
303
+ return [2 /*return*/, Promise.reject("Unexpected repsonse size. Got ".concat(k, " bytes, but expected ").concat(fd.fileSize, "."))];
304
+ }
305
+ // Avoid decoding the response if the expected response is bytearray
306
+ if (rawResponse)
307
+ return [2 /*return*/, Promise.resolve(result)];
308
+ resultCodec = JSONCodec();
309
+ return [2 /*return*/, resultCodec.decode(result)];
270
310
  }
271
311
  });
272
312
  });
@@ -278,63 +318,33 @@ var BaseClient = /** @class */ (function () {
278
318
  * @param options (optional)
279
319
  * @returns Promise of an object
280
320
  */
281
- BaseClient.prototype.requestChunked = function (subject, replySubject, payload, options, isFullSubject, rawResponse) {
321
+ BaseClient.prototype.requestChunked = function (subject, payload, options, rawResponse, fullSubject) {
282
322
  return __awaiter(this, void 0, void 0, function () {
283
- var fileSize;
284
- var _this = this;
323
+ var data, stringCodec, headers, opts, msg;
285
324
  return __generator(this, function (_a) {
286
- if (!this.connection)
287
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
288
- if (this.connection.isClosed())
289
- return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
290
- fileSize = -1;
291
- return [2 /*return*/, new Promise(function (resolve, reject) {
292
- if (!_this.connection)
293
- return Promise.reject("".concat(subject, ": Connection is down! Please try again!"));
294
- var combinedResult = new Uint8Array([]);
295
- // Subscribe to the subject before starting the process
296
- var subscription = _this.connection.subscribe(replySubject);
297
- var resolveIfCompleted = function () {
298
- if (combinedResult.length === fileSize) {
299
- subscription.unsubscribe();
300
- resolve(combinedResult);
301
- }
302
- };
303
- subscription.callback = function (error, message) {
304
- if (error) {
305
- reject(error);
306
- }
307
- combinedResult = new Uint8Array(__spreadArray(__spreadArray([], Array.from(combinedResult), true), Array.from(message === null || message === void 0 ? void 0 : message.data), true));
308
- resolveIfCompleted();
309
- };
310
- var headers = _this.buildHeaders();
311
- var opts = __assign(__assign({}, options), { timeout: _this.timeout, headers: headers });
312
- // Request the file descriptor from the runner by posting the reply subject
313
- return _this.request(subject, payload, opts, isFullSubject)
314
- .then(function (fileDescriptor) { return FileDescriptor.fromJS(fileDescriptor); })
315
- .then(function (fileDescriptor) {
316
- fileSize = fileDescriptor.fileSize;
317
- resolveIfCompleted();
318
- })
319
- .catch(function (error) {
320
- subscription.unsubscribe();
321
- throw error;
322
- });
323
- }).then(function (byteArray) {
324
- if (byteArray.length !== fileSize)
325
- return Promise.reject("Unexpected response size. Expected ".concat(fileSize, " bytes, but got ").concat(byteArray.length, "."));
326
- // Avoid decoding the response if the expected response is bytearray
327
- if (rawResponse)
328
- return Promise.resolve(byteArray);
329
- var jsonCodec = JSONCodec();
330
- var response = jsonCodec.decode(byteArray);
331
- if (_this.isErrorResponse(response)) {
332
- var error = ErrorResponse.fromJS(response);
333
- _this.eventEmitter.emit(Events.ERROR, error);
334
- return Promise.reject(error);
325
+ switch (_a.label) {
326
+ case 0:
327
+ if (!this.connection)
328
+ return [2 /*return*/, Promise.reject("".concat(subject, ": Connection is down! Please try again!"))];
329
+ if (this.connection.isClosed())
330
+ return [2 /*return*/, Promise.reject("".concat(subject, ": Connection has been closed! Please reconnect!"))];
331
+ if (!fullSubject) {
332
+ subject = "".concat(this.baseSubject, ".Request.").concat(subject);
335
333
  }
336
- return Promise.resolve(response);
337
- })];
334
+ if (payload instanceof Uint8Array) {
335
+ data = payload;
336
+ }
337
+ else {
338
+ stringCodec = StringCodec();
339
+ data = payload ? stringCodec.encode(JSON.stringify(payload)) : Empty;
340
+ }
341
+ headers = this.buildHeaders();
342
+ opts = __assign(__assign({}, options), { timeout: this.timeout, headers: headers });
343
+ return [4 /*yield*/, this.connection.request(subject, data, opts)];
344
+ case 1:
345
+ msg = _a.sent();
346
+ return [2 /*return*/, this.receiveFile(msg, rawResponse, opts)];
347
+ }
338
348
  });
339
349
  });
340
350
  };
package/lib/DTOs.d.ts CHANGED
@@ -442,14 +442,6 @@ export declare class Resource implements IResource {
442
442
  export interface IResource {
443
443
  source?: string | undefined;
444
444
  }
445
- export declare class TestPlanRequest implements ITestPlanRequest {
446
- properties?: string[] | null | undefined;
447
- subject?: string | undefined;
448
- }
449
- export interface ITestPlanRequest {
450
- properties?: string[] | null | undefined;
451
- subject?: string | undefined;
452
- }
453
445
  export declare class FileDescriptor implements IFileDescriptor {
454
446
  numberOfChunks: number;
455
447
  fileSize: number;
package/lib/DTOs.js CHANGED
@@ -1303,12 +1303,6 @@ var Resource = /** @class */ (function () {
1303
1303
  return Resource;
1304
1304
  }());
1305
1305
  export { Resource };
1306
- var TestPlanRequest = /** @class */ (function () {
1307
- function TestPlanRequest() {
1308
- }
1309
- return TestPlanRequest;
1310
- }());
1311
- export { TestPlanRequest };
1312
1306
  var FileDescriptor = /** @class */ (function () {
1313
1307
  function FileDescriptor(fileSize, chunkSize) {
1314
1308
  var _a;
@@ -27,7 +27,6 @@ var __assign = (this && this.__assign) || function () {
27
27
  import { BreakPoints, CommonContext, CommonSettings, DataGridControl, Image, Interaction, ListItemType, LogList, Result, RunStatus, SessionEvent, Setting, TestPlan, TestRun, TestStepType, TestStepValidationError, WatchDog, } from './DTOs';
28
28
  import { JSONCodec } from 'nats.ws';
29
29
  import { BaseClient } from './BaseClient';
30
- import { v4 as uuidv4 } from 'uuid';
31
30
  var SessionClient = /** @class */ (function (_super) {
32
31
  __extends(SessionClient, _super);
33
32
  function SessionClient(baseSubject, options) {
@@ -206,8 +205,7 @@ var SessionClient = /** @class */ (function (_super) {
206
205
  */
207
206
  SessionClient.prototype.getTestPlanXML = function () {
208
207
  // Generate a unique subject where the Runner will publish the chunks
209
- var replySubject = "_INBOX.".concat(uuidv4());
210
- return this.requestChunked('GetTestPlanXML', replySubject, replySubject).then(this.success()).catch(this.error());
208
+ return this.requestChunked('GetTestPlanXML').then(this.success()).catch(this.error());
211
209
  };
212
210
  /**
213
211
  * Downloads the resource from the runner and returns it as raw bytes
@@ -219,11 +217,8 @@ var SessionClient = /** @class */ (function (_super) {
219
217
  if (!((_a = resource.source) === null || _a === void 0 ? void 0 : _a.startsWith(runnerResourcePrefix))) {
220
218
  return Promise.reject('The source of the provided resource is not a nats subject.');
221
219
  }
222
- var replySubject = "_INBOX.".concat(uuidv4());
223
220
  var subject = resource.source.slice(runnerResourcePrefix.length);
224
- return this.requestChunked(subject, replySubject, replySubject, undefined, true, true)
225
- .then(this.success())
226
- .catch(this.error());
221
+ return this.requestChunked(subject, undefined, undefined, true, true).then(this.success()).catch(this.error());
227
222
  };
228
223
  /**
229
224
  * Load test plan using a test plan TapPackage from a repository
@@ -246,8 +241,7 @@ var SessionClient = /** @class */ (function (_super) {
246
241
  * @return Test plan resources opened.
247
242
  */
248
243
  SessionClient.prototype.resourcesOpen = function () {
249
- var replySubject = "_INBOX.".concat(uuidv4());
250
- return this.requestChunked('ResourcesOpen', replySubject, replySubject)
244
+ return this.requestChunked('ResourcesOpen')
251
245
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
252
246
  .then(this.success())
253
247
  .catch(this.error());
@@ -257,8 +251,7 @@ var SessionClient = /** @class */ (function (_super) {
257
251
  * @return Test plan resources closed.
258
252
  */
259
253
  SessionClient.prototype.resourcesClose = function () {
260
- var replySubject = "_INBOX.".concat(uuidv4());
261
- return this.requestChunked('ResourcesClose', replySubject, replySubject)
254
+ return this.requestChunked('ResourcesClose')
262
255
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
263
256
  .then(this.success())
264
257
  .catch(this.error());
@@ -293,12 +286,7 @@ var SessionClient = /** @class */ (function (_super) {
293
286
  * @return Test plan retrieved
294
287
  */
295
288
  SessionClient.prototype.getTestPlan = function (properties) {
296
- var replySubject = "_INBOX.".concat(uuidv4());
297
- var payload = {
298
- subject: replySubject,
299
- properties: properties,
300
- };
301
- return this.requestChunked('GetTestPlan', replySubject, payload)
289
+ return this.requestChunked('GetTestPlan', properties)
302
290
  .then(function (testPlanJs) { return TestPlan.fromJS(testPlanJs); })
303
291
  .then(this.success())
304
292
  .catch(this.error());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentap/runner-client",
3
- "version": "2.2.3",
3
+ "version": "2.2.4-alpha.1.1",
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",
@@ -41,11 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "nats.ws": "^1.9.0",
44
- "uuid": "^9.0.0"
44
+ "uuid": "^9.0.0",
45
+ "events": "^3.3.0"
45
46
  },
46
47
  "lint-staged": {
47
48
  "*.{ts,js,html}": [
48
49
  "npm run lint --fix"
49
50
  ]
50
51
  }
51
- }
52
+ }