@alwaysai/device-agent 1.3.0 → 1.3.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.
Files changed (89) hide show
  1. package/lib/application-control/environment-variables.d.ts +1 -0
  2. package/lib/application-control/environment-variables.d.ts.map +1 -1
  3. package/lib/application-control/environment-variables.js +22 -20
  4. package/lib/application-control/environment-variables.js.map +1 -1
  5. package/lib/application-control/environment-variables.test.js +37 -2
  6. package/lib/application-control/environment-variables.test.js.map +1 -1
  7. package/lib/application-control/install.js +1 -1
  8. package/lib/application-control/install.js.map +1 -1
  9. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +2 -2
  10. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  11. package/lib/cloud-connection/device-agent-cloud-connection.js +116 -99
  12. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  13. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  14. package/lib/cloud-connection/live-updates-handler.js +30 -25
  15. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  16. package/lib/cloud-connection/live-updates-handler.test.js +15 -0
  17. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  18. package/lib/cloud-connection/messages.d.ts +1 -3
  19. package/lib/cloud-connection/messages.d.ts.map +1 -1
  20. package/lib/cloud-connection/messages.js +1 -9
  21. package/lib/cloud-connection/messages.js.map +1 -1
  22. package/lib/cloud-connection/publisher.d.ts +1 -0
  23. package/lib/cloud-connection/publisher.d.ts.map +1 -1
  24. package/lib/cloud-connection/publisher.js +3 -0
  25. package/lib/cloud-connection/publisher.js.map +1 -1
  26. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  27. package/lib/cloud-connection/shadow-handler.js +10 -3
  28. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  29. package/lib/cloud-connection/shadow-handler.test.js +79 -28
  30. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  31. package/lib/cloud-connection/transaction-manager.d.ts +26 -6
  32. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  33. package/lib/cloud-connection/transaction-manager.js +103 -22
  34. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  35. package/lib/cloud-connection/transaction-manager.test.js +179 -13
  36. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  37. package/lib/subcommands/app/analytics.d.ts +10 -0
  38. package/lib/subcommands/app/analytics.d.ts.map +1 -0
  39. package/lib/subcommands/app/analytics.js +83 -0
  40. package/lib/subcommands/app/analytics.js.map +1 -0
  41. package/lib/subcommands/app/index.d.ts.map +1 -1
  42. package/lib/subcommands/app/index.js +3 -1
  43. package/lib/subcommands/app/index.js.map +1 -1
  44. package/lib/subcommands/app/models.d.ts +0 -5
  45. package/lib/subcommands/app/models.d.ts.map +1 -1
  46. package/lib/subcommands/app/models.js +11 -47
  47. package/lib/subcommands/app/models.js.map +1 -1
  48. package/lib/subcommands/app/status.d.ts +1 -0
  49. package/lib/subcommands/app/status.d.ts.map +1 -1
  50. package/lib/subcommands/app/status.js +14 -3
  51. package/lib/subcommands/app/status.js.map +1 -1
  52. package/lib/subcommands/app/version.d.ts +2 -1
  53. package/lib/subcommands/app/version.d.ts.map +1 -1
  54. package/lib/subcommands/app/version.js +16 -3
  55. package/lib/subcommands/app/version.js.map +1 -1
  56. package/lib/util/parsing.d.ts +2 -0
  57. package/lib/util/parsing.d.ts.map +1 -0
  58. package/lib/util/parsing.js +17 -0
  59. package/lib/util/parsing.js.map +1 -0
  60. package/package.json +4 -6
  61. package/readme.md +146 -92
  62. package/src/application-control/environment-variables.test.ts +43 -3
  63. package/src/application-control/environment-variables.ts +29 -19
  64. package/src/application-control/install.ts +1 -1
  65. package/src/cloud-connection/device-agent-cloud-connection.ts +155 -141
  66. package/src/cloud-connection/live-updates-handler.test.ts +20 -0
  67. package/src/cloud-connection/live-updates-handler.ts +45 -52
  68. package/src/cloud-connection/messages.ts +1 -14
  69. package/src/cloud-connection/publisher.ts +4 -0
  70. package/src/cloud-connection/shadow-handler.test.ts +88 -28
  71. package/src/cloud-connection/shadow-handler.ts +13 -3
  72. package/src/cloud-connection/transaction-manager.test.ts +193 -18
  73. package/src/cloud-connection/transaction-manager.ts +174 -26
  74. package/src/subcommands/app/analytics.ts +99 -0
  75. package/src/subcommands/app/index.ts +4 -3
  76. package/src/subcommands/app/models.ts +13 -49
  77. package/src/subcommands/app/status.ts +20 -3
  78. package/src/subcommands/app/version.ts +19 -4
  79. package/src/util/parsing.ts +11 -0
  80. package/lib/cloud-connection/cmd-status.d.ts +0 -8
  81. package/lib/cloud-connection/cmd-status.d.ts.map +0 -1
  82. package/lib/cloud-connection/cmd-status.js +0 -62
  83. package/lib/cloud-connection/cmd-status.js.map +0 -1
  84. package/lib/cloud-connection/message-builder.d.ts +0 -7
  85. package/lib/cloud-connection/message-builder.d.ts.map +0 -1
  86. package/lib/cloud-connection/message-builder.js +0 -63
  87. package/lib/cloud-connection/message-builder.js.map +0 -1
  88. package/src/cloud-connection/cmd-status.ts +0 -71
  89. package/src/cloud-connection/message-builder.ts +0 -117
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line
2
- const awsIot = require('aws-iot-device-sdk');
2
+ const awsIot = require("aws-iot-device-sdk");
3
3
  import { getIoTCoreEndpointUrl } from '../infrastructure/urls';
4
4
  import { existsSync } from 'fs';
5
5
  import {
@@ -11,7 +11,6 @@ import {
11
11
  } from '../util/directories';
12
12
  import {
13
13
  keyMirrors,
14
- validateToClientMessage,
15
14
  SignedUrlsRequestPayload,
16
15
  getToDeviceTopic,
17
16
  AppInstallResponsePayload,
@@ -21,8 +20,10 @@ import {
21
20
  AppStateControlPayload,
22
21
  AppVersionControlInstallPayload,
23
22
  AppVersionControlUninstallPayload,
24
- ToClientMessage,
25
- DeviceActionPayload
23
+ DeviceActionPayload,
24
+ buildSignedUrlsRequestMessage,
25
+ buildToClientStatusResponseMessage,
26
+ StatusResponsePayload
26
27
  } from '@alwaysai/device-agent-schemas';
27
28
  import { getDeviceUuid } from '../util/get-device-id';
28
29
  import { logger } from '../util/logger';
@@ -41,32 +42,31 @@ import {
41
42
  import { ShadowHandler, ShadowTopics, ShadowUpdate } from './shadow-handler';
42
43
  import { secureTunnelNotifyHandler } from '../secure-tunneling/index';
43
44
  import { Publisher } from './publisher';
44
- import { LiveUpdatesHandler } from './live-updates-handler';
45
45
  import { bootstrapProvision } from './bootstrap-provision';
46
- import { CmdStatusManager } from './cmd-status';
47
46
  import { PassthroughHandler, runChannel } from './passthrough-handler';
48
47
  import { ALWAYSAI_ANALYTICS_PASSTHROUGH } from '../environment';
49
- import { getStatusResponsePayload } from './messages';
50
48
  import { ModelsInstallResponsePayload } from '@alwaysai/device-agent-schemas';
51
49
  import sleep from '../util/sleep';
52
50
  import { createAppBackup, rollbackApp } from '../application-control/backup';
53
51
  import { TransactionManager } from './transaction-manager';
54
- import {
55
- buildSignedUrlsRequestMessage,
56
- buildStatusResponseMessage
57
- } from './message-builder';
58
52
  import { reboot } from '../device-control/device-control';
59
53
 
54
+ import { exec } from 'child_process';
55
+ import { promisify } from 'util';
56
+ import { LiveUpdatesHandler } from './live-updates-handler';
57
+
58
+ const exec_promise = promisify(exec);
59
+
60
60
  export class DeviceAgentCloudConnection {
61
61
  private shadowHandler: ShadowHandler;
62
62
  public publisher: Publisher;
63
- private cmdStatusMgr: CmdStatusManager;
64
63
  private liveUpdatesHandler: LiveUpdatesHandler;
65
64
  private txnMgr: TransactionManager;
66
65
  private device = awsIot.device;
67
66
 
68
67
  private clientId = getDeviceUuid();
69
68
  private host = getIoTCoreEndpointUrl();
69
+ private port = 8883;
70
70
  private readonly toDeviceTopic = getToDeviceTopic(this.clientId);
71
71
  private readonly secureTunnelNotifyTopic = `$aws/things/${this.clientId}/tunnels/notify`;
72
72
  // FIXME: Add support for multiple simultaneous project updates
@@ -106,7 +106,8 @@ export class DeviceAgentCloudConnection {
106
106
  appReleaseHash
107
107
  }
108
108
  };
109
- const message = await buildSignedUrlsRequestMessage(
109
+ const message = buildSignedUrlsRequestMessage(
110
+ this.clientId,
110
111
  signedUrlsRequestPayload,
111
112
  txId
112
113
  );
@@ -272,62 +273,6 @@ export class DeviceAgentCloudConnection {
272
273
  }
273
274
  }
274
275
 
275
- // eslint-disable-next-line
276
- private async atomicCmd<T extends any[]>(props: {
277
- func: (...args: T) => Promise<boolean>;
278
- args: T;
279
- projectId: string;
280
- txId: string;
281
- }): Promise<boolean> {
282
- const { func, args, projectId, txId } = props;
283
- try {
284
- await this.cmdStatusMgr.start(projectId);
285
- await this.liveUpdatesHandler.enableTransactionStatus({
286
- txId
287
- });
288
- const completed = await func(...args);
289
- if (completed) {
290
- await this.cmdStatusMgr.stop(projectId);
291
- await this.liveUpdatesHandler.disableTransactionStatus({
292
- txId
293
- });
294
- const successStatusResponsePayload = await getStatusResponsePayload(
295
- keyMirrors.statusResponse.success,
296
- ''
297
- );
298
- // Send final status message
299
- const message = await buildStatusResponseMessage(
300
- successStatusResponsePayload,
301
- txId
302
- );
303
- this.publisher.publishToClient(message);
304
- }
305
- return completed;
306
- } catch (e) {
307
- logger.error(
308
- `Failed to execute cmd for ${projectId}:\n${e.message}\n${e.stack}`
309
- );
310
- const message: string = e.message;
311
-
312
- // uninstall the failed app to put system back in good state
313
- await this.cmdStatusMgr.stop(projectId);
314
- await this.liveUpdatesHandler.disableTransactionStatus({
315
- txId
316
- });
317
- const failureStatusResponsePayload = await getStatusResponsePayload(
318
- keyMirrors.statusResponse.failure,
319
- message
320
- );
321
- // Send final status message
322
- const failureStatusResponseMessage = await buildStatusResponseMessage(
323
- failureStatusResponsePayload,
324
- txId
325
- );
326
- this.publisher.publishToClient(failureStatusResponseMessage);
327
- return true;
328
- }
329
- }
330
-
331
276
  private handleAppConfigUpdate = async (
332
277
  update: ShadowUpdate,
333
278
  txId: string
@@ -353,7 +298,8 @@ export class DeviceAgentCloudConnection {
353
298
  models: updatedModels
354
299
  }
355
300
  };
356
- const message = await buildSignedUrlsRequestMessage(
301
+ const message = buildSignedUrlsRequestMessage(
302
+ this.clientId,
357
303
  modelsOnlyUrlsRequestPayload,
358
304
  txId
359
305
  );
@@ -397,17 +343,19 @@ export class DeviceAgentCloudConnection {
397
343
  caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
398
344
  clientId: this.clientId,
399
345
  host: this.host,
400
- port: 8883,
346
+ port: this.port,
401
347
  keepalive: 1 // time before re-connect attempt on dropped connection, default is 400 seconds
402
348
  });
403
349
  this.publisher = new Publisher(this.device, this.clientId);
404
350
  this.shadowHandler = new ShadowHandler(this.clientId, this.publisher);
405
- this.cmdStatusMgr = new CmdStatusManager();
406
351
  this.liveUpdatesHandler = new LiveUpdatesHandler(
407
352
  this.publisher,
408
353
  this.clientId
409
354
  );
410
- this.txnMgr = new TransactionManager();
355
+ this.txnMgr = new TransactionManager(
356
+ this.publisher,
357
+ this.liveUpdatesHandler
358
+ );
411
359
 
412
360
  this.subscribe(this.toDeviceTopic);
413
361
  this.subscribe(this.secureTunnelNotifyTopic);
@@ -431,7 +379,7 @@ export class DeviceAgentCloudConnection {
431
379
  }
432
380
 
433
381
  public isCmdInProgress(projectId: string): boolean {
434
- return this.cmdStatusMgr.isCmdInProgress(projectId);
382
+ return this.txnMgr.isOngoingTransactionForProjectID(projectId);
435
383
  }
436
384
 
437
385
  public async updateProjectShadow(projectId: string) {
@@ -458,23 +406,28 @@ export class DeviceAgentCloudConnection {
458
406
  return;
459
407
  }
460
408
  const txId = message.txId;
409
+ const {
410
+ app_state_control,
411
+ app_version_control,
412
+ live_state_updates,
413
+ app_install_response,
414
+ models_install_response,
415
+ status_response
416
+ } = keyMirrors.toDeviceAgentMessageType;
461
417
  switch (message.messageType) {
462
- case keyMirrors.toDeviceAgentMessageType.app_state_control: {
418
+ case app_state_control: {
463
419
  // txId sent from cloud, just need to continue it
464
420
  const payload = message.payload;
465
421
  const projectId = payload.projectId;
466
422
 
467
423
  try {
468
- this.txnMgr.addTransaction(txId, projectId);
469
- const completed = await this.atomicCmd({
470
- func: this.handleAppStateControl,
471
- args: [message.payload],
424
+ await this.txnMgr.runTransactionStep({
425
+ func: () => this.handleAppStateControl(message.payload),
472
426
  projectId,
473
- txId
427
+ txId,
428
+ start: true,
429
+ stepName: payload.baseCommand
474
430
  });
475
- if (completed) {
476
- this.txnMgr.completeTransaction(txId);
477
- }
478
431
  } catch (e) {
479
432
  logger.error(
480
433
  `Error processing application state control request: ${e}!`
@@ -483,40 +436,33 @@ export class DeviceAgentCloudConnection {
483
436
 
484
437
  break;
485
438
  }
486
- case keyMirrors.toDeviceAgentMessageType.app_version_control: {
439
+ case app_version_control: {
487
440
  // txId sent from cloud, just need to continue it
488
441
  const payload = message.payload;
489
442
  const projectId = payload.projectId;
490
- const appReleaseHash =
491
- payload.baseCommand === keyMirrors.appVersionControl.install
492
- ? payload.appReleaseHash
493
- : undefined;
494
443
  try {
495
- this.txnMgr.addTransaction(txId, projectId);
496
- const completed = await this.atomicCmd({
497
- func: this.handleAppVersionControl,
498
- args: [payload, txId],
444
+ await this.txnMgr.runTransactionStep({
445
+ func: () => this.handleAppVersionControl(payload, txId),
499
446
  projectId,
500
- txId
447
+ txId,
448
+ start: true,
449
+ stepName: payload.baseCommand
501
450
  });
502
- if (completed) {
503
- this.txnMgr.completeTransaction(txId);
504
- }
505
451
  } catch (e) {
506
452
  logger.error(`Error processing application install request: ${e}!`);
507
453
  }
508
454
 
509
455
  break;
510
456
  }
511
- case keyMirrors.toDeviceAgentMessageType.live_state_updates: {
457
+ case live_state_updates: {
512
458
  const payload = message.payload;
513
459
  // TODO: Send response?
514
- await this.liveUpdatesHandler.handleToggles(payload, txId);
460
+ void this.liveUpdatesHandler.handleToggles(payload, txId);
515
461
  break;
516
462
  }
517
- case keyMirrors.toDeviceAgentMessageType.app_install_response: {
463
+ case app_install_response: {
518
464
  const payload = message.payload;
519
- const { projectId, appReleaseHash } = payload.appInstallResponse;
465
+ const { projectId } = payload.appInstallResponse;
520
466
  if (txId !== this.txnMgr.getTransactionFromProject(projectId)) {
521
467
  throw new Error(
522
468
  `App install response received a message for a transaction ID ${txId} that is not currently underway (${this.txnMgr.getTransactionFromProject(
@@ -524,19 +470,16 @@ export class DeviceAgentCloudConnection {
524
470
  )})!`
525
471
  );
526
472
  }
527
- const completed = await this.atomicCmd({
528
- func: this.handleAppInstallCloudResponsePayload,
529
- args: [payload],
473
+ await this.txnMgr.runTransactionStep({
474
+ func: () => this.handleAppInstallCloudResponsePayload(payload),
530
475
  projectId,
531
- txId
476
+ txId,
477
+ start: false,
478
+ stepName: message.messageType
532
479
  });
533
- if (completed) {
534
- this.txnMgr.completeTransaction(txId);
535
- }
536
-
537
480
  break;
538
481
  }
539
- case keyMirrors.toDeviceAgentMessageType.models_install_response: {
482
+ case models_install_response: {
540
483
  // This message doesn't have appReleaseHash in it's payload, but
541
484
  // atomicCmd should be able to read it from the installed app
542
485
  const payload = message.payload;
@@ -548,28 +491,46 @@ export class DeviceAgentCloudConnection {
548
491
  )})!`
549
492
  );
550
493
  }
551
- const completed = await this.atomicCmd({
552
- func: this.handleModelsInstallCloudResponsePayload,
553
- args: [payload],
494
+ await this.txnMgr.runTransactionStep({
495
+ func: () => this.handleModelsInstallCloudResponsePayload(payload),
554
496
  projectId,
555
- txId
497
+ txId,
498
+ start: false,
499
+ stepName: message.messageType
556
500
  });
557
- if (completed) {
501
+ break;
502
+ }
503
+ case status_response: {
504
+ const { failure } = keyMirrors.statusResponse;
505
+ if (message.payload.status === failure) {
558
506
  this.txnMgr.completeTransaction(txId);
559
- }
560
507
 
508
+ const failureStatusResponsePayload: StatusResponsePayload = {
509
+ status: keyMirrors.statusResponse.failure,
510
+ message: message.payload.message
511
+ };
512
+ // Send final status message
513
+ const failureStatusResponseMessage =
514
+ buildToClientStatusResponseMessage(
515
+ this.clientId,
516
+ failureStatusResponsePayload,
517
+ txId
518
+ );
519
+ this.publisher.publishToClient(failureStatusResponseMessage);
520
+ }
561
521
  break;
562
522
  }
563
523
  case keyMirrors.toDeviceAgentMessageType.device_action: {
564
524
  try {
565
- const successStatusResponsePayload = await getStatusResponsePayload(
566
- keyMirrors.statusResponse.success,
567
- ''
568
- );
569
- const successStatusResponseMessage = await buildStatusResponseMessage(
570
- successStatusResponsePayload,
571
- txId
572
- );
525
+ const successStatusResponsePayload: StatusResponsePayload = {
526
+ status: keyMirrors.statusResponse.success
527
+ };
528
+ const successStatusResponseMessage =
529
+ buildToClientStatusResponseMessage(
530
+ this.clientId,
531
+ successStatusResponsePayload,
532
+ txId
533
+ );
573
534
  this.publisher.publishToClient(successStatusResponseMessage);
574
535
 
575
536
  await this.handleDeviceAction(message.payload);
@@ -577,14 +538,16 @@ export class DeviceAgentCloudConnection {
577
538
  logger.error(
578
539
  `There was a problem performing device action '${message.payload.action}': ${e.message}`
579
540
  );
580
- const failureStatusResponsePayload = await getStatusResponsePayload(
581
- keyMirrors.statusResponse.failure,
582
- e.message
583
- );
584
- const failureStatusResponseMessage = await buildStatusResponseMessage(
585
- failureStatusResponsePayload,
586
- txId
587
- );
541
+ const failureStatusResponsePayload: StatusResponsePayload = {
542
+ status: keyMirrors.statusResponse.failure,
543
+ message: e.message
544
+ };
545
+ const failureStatusResponseMessage =
546
+ buildToClientStatusResponseMessage(
547
+ this.clientId,
548
+ failureStatusResponsePayload,
549
+ txId
550
+ );
588
551
  this.publisher.publishToClient(failureStatusResponseMessage);
589
552
  }
590
553
  break;
@@ -609,7 +572,7 @@ export class DeviceAgentCloudConnection {
609
572
  );
610
573
  switch (topic) {
611
574
  case this.shadowHandler.shadowTopics.projects.getAccepted:
612
- case this.shadowHandler.shadowTopics.projects.updateDelta: {
575
+ case this.shadowHandler.shadowTopics.projects.updateAccepted: {
613
576
  const shadowUpdates = await this.shadowHandler.handleShadowTopic({
614
577
  topic,
615
578
  payload: message.state,
@@ -620,16 +583,13 @@ export class DeviceAgentCloudConnection {
620
583
  const projectId = shadowUpdate.projectId;
621
584
  const txId = shadowUpdate.txId;
622
585
  try {
623
- this.txnMgr.addTransaction(txId, projectId);
624
- const completed = await this.atomicCmd({
625
- func: this.handleAppConfigUpdate,
626
- args: [shadowUpdate, txId],
586
+ await this.txnMgr.runTransactionStep({
587
+ func: () => this.handleAppConfigUpdate(shadowUpdate, txId),
627
588
  projectId,
628
- txId
589
+ txId,
590
+ start: true,
591
+ stepName: topic
629
592
  });
630
- if (completed) {
631
- this.txnMgr.completeTransaction(txId);
632
- }
633
593
  } catch (e) {
634
594
  logger.error(`Error handling shadow message: ${e.message}`);
635
595
  }
@@ -638,7 +598,7 @@ export class DeviceAgentCloudConnection {
638
598
  break;
639
599
  }
640
600
  case this.shadowHandler.shadowTopics.projects.getRejected:
641
- case this.shadowHandler.shadowTopics.projects.updateAccepted:
601
+ case this.shadowHandler.shadowTopics.projects.updateDelta:
642
602
  case this.shadowHandler.shadowTopics.projects.updateRejected:
643
603
  // Not handling these for now
644
604
  break;
@@ -692,9 +652,63 @@ export class DeviceAgentCloudConnection {
692
652
 
693
653
  this.device.on('offline', () => {
694
654
  logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
655
+ void this.logConnectionInfo();
695
656
  });
696
657
  }
697
658
 
659
+ public async logConnectionInfo() {
660
+ try {
661
+ /**
662
+ * We're using the 'netcat' or 'nc' command to test the connection to the IoT Core endpoint.
663
+ * This command doesn't always exit (see below), so
664
+ * we use timeout to break out of the prompt
665
+ * and catch the resulting error/parse the resulting stderr
666
+ *
667
+ * Sample command for current host and port:
668
+ * nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
669
+ *
670
+ * Sample output when port is not blocked and host is reachable:
671
+ * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443
672
+ * Connection to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443 port [tcp/https] succeeded!
673
+ *
674
+ *
675
+ * Sample output when port is blocked (will repeatedly try until ctrl-C out):
676
+ * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
677
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
678
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
679
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
680
+ * ^C
681
+ *
682
+ *
683
+ * Sample command/output when the port isn't enable on that host:
684
+ * $ nc -zv -w 1 localhost 8883
685
+ * nc: connect to localhost port 8883 (tcp) failed: Connection refused
686
+ */
687
+ await exec_promise(`nc -zv -w 1 ${this.host} ${this.port}`, {
688
+ timeout: 2000
689
+ });
690
+ } catch (err) {
691
+ const output = JSON.stringify(err['stderr']);
692
+ if (output.indexOf('not known') !== -1) {
693
+ logger.warn(
694
+ 'Iot Core endpoint appears to be unreachable, internet connection may be unstable or the host may be down.'
695
+ );
696
+ } else if (output.indexOf('timed out') !== -1) {
697
+ logger.warn(
698
+ `Internet connection appears fine, however the endpoint was not reachable on the current connection port: ${this.port}\nPlease check if a firewall is in place.`
699
+ );
700
+ } else if (output.indexOf('refused') !== -1) {
701
+ logger.warn(
702
+ `The connection was refused, likely ${this.host} is not running a service on ${this.port}.`
703
+ );
704
+ } else {
705
+ logger.warn(
706
+ `Output from checking connection to ${this.host} on ${this.port}: ${output}`
707
+ );
708
+ }
709
+ }
710
+ }
711
+
698
712
  public async stop() {
699
713
  // FIXME: This method is currently only used by the CLI, and shadow messages
700
714
  // can be lost since we aren't waiting for responses so sleep for a short
@@ -3,6 +3,11 @@ import { Publisher } from './publisher';
3
3
 
4
4
  global.setTimeout = jest.fn() as unknown as typeof setTimeout;
5
5
 
6
+ // https://github.com/facebook/react-native/issues/35701
7
+ Object.defineProperty(global, 'performance', {
8
+ writable: true
9
+ });
10
+
6
11
  const testTrueToggles = {
7
12
  deviceStats: true,
8
13
  appState: true
@@ -66,4 +71,19 @@ describe('Test Live Updates Handler', () => {
66
71
  expect(liveUpdatesHandler.getAppStateLiveUpdates()).toBe(false);
67
72
  expect(liveUpdatesHandler.getAppLogsLiveUpdates()).toBe(false);
68
73
  });
74
+
75
+ test('timeout turns off live updates', async () => {
76
+ jest.useFakeTimers({ legacyFakeTimers: true });
77
+
78
+ void liveUpdatesHandler.handleToggles(testTrueToggles, emptyTxId);
79
+ expect(liveUpdatesHandler.getDeviceStatsLiveUpdates()).toBe(true);
80
+ expect(liveUpdatesHandler.getAppStateLiveUpdates()).toBe(true);
81
+ expect(liveUpdatesHandler.getAppLogsLiveUpdates()).toBe(false);
82
+
83
+ jest.runAllTimers();
84
+
85
+ expect(liveUpdatesHandler.getDeviceStatsLiveUpdates()).toBe(false);
86
+ expect(liveUpdatesHandler.getAppStateLiveUpdates()).toBe(false);
87
+ expect(liveUpdatesHandler.getAppLogsLiveUpdates()).toBe(false);
88
+ });
69
89
  });