@alwaysai/device-agent 1.3.0 → 1.3.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.
Files changed (115) 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 +4 -3
  10. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  11. package/lib/cloud-connection/device-agent-cloud-connection.js +149 -113
  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 +12 -0
  27. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  28. package/lib/cloud-connection/shadow-handler.js +36 -22
  29. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  30. package/lib/cloud-connection/shadow-handler.test.js +84 -40
  31. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  32. package/lib/cloud-connection/transaction-manager.d.ts +26 -6
  33. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  34. package/lib/cloud-connection/transaction-manager.js +103 -22
  35. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  36. package/lib/cloud-connection/transaction-manager.test.js +179 -13
  37. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  38. package/lib/secure-tunneling/secure-tunneling.d.ts +105 -0
  39. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -0
  40. package/lib/secure-tunneling/secure-tunneling.js +435 -0
  41. package/lib/secure-tunneling/secure-tunneling.js.map +1 -0
  42. package/lib/secure-tunneling/secure-tunneling.test.d.ts +2 -0
  43. package/lib/secure-tunneling/secure-tunneling.test.d.ts.map +1 -0
  44. package/lib/secure-tunneling/secure-tunneling.test.js +1070 -0
  45. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -0
  46. package/lib/secure-tunneling/spawner-detached.d.ts +6 -0
  47. package/lib/secure-tunneling/spawner-detached.d.ts.map +1 -0
  48. package/lib/secure-tunneling/spawner-detached.js +90 -0
  49. package/lib/secure-tunneling/spawner-detached.js.map +1 -0
  50. package/lib/subcommands/app/analytics.d.ts +10 -0
  51. package/lib/subcommands/app/analytics.d.ts.map +1 -0
  52. package/lib/subcommands/app/analytics.js +83 -0
  53. package/lib/subcommands/app/analytics.js.map +1 -0
  54. package/lib/subcommands/app/index.d.ts.map +1 -1
  55. package/lib/subcommands/app/index.js +3 -1
  56. package/lib/subcommands/app/index.js.map +1 -1
  57. package/lib/subcommands/app/models.d.ts +0 -5
  58. package/lib/subcommands/app/models.d.ts.map +1 -1
  59. package/lib/subcommands/app/models.js +11 -47
  60. package/lib/subcommands/app/models.js.map +1 -1
  61. package/lib/subcommands/app/status.d.ts +1 -0
  62. package/lib/subcommands/app/status.d.ts.map +1 -1
  63. package/lib/subcommands/app/status.js +14 -3
  64. package/lib/subcommands/app/status.js.map +1 -1
  65. package/lib/subcommands/app/version.d.ts +2 -1
  66. package/lib/subcommands/app/version.d.ts.map +1 -1
  67. package/lib/subcommands/app/version.js +16 -3
  68. package/lib/subcommands/app/version.js.map +1 -1
  69. package/lib/util/cloud-mode-ready.d.ts +1 -0
  70. package/lib/util/cloud-mode-ready.d.ts.map +1 -1
  71. package/lib/util/cloud-mode-ready.js +36 -1
  72. package/lib/util/cloud-mode-ready.js.map +1 -1
  73. package/lib/util/parsing.d.ts +2 -0
  74. package/lib/util/parsing.d.ts.map +1 -0
  75. package/lib/util/parsing.js +17 -0
  76. package/lib/util/parsing.js.map +1 -0
  77. package/package.json +4 -6
  78. package/readme.md +146 -92
  79. package/src/application-control/environment-variables.test.ts +43 -3
  80. package/src/application-control/environment-variables.ts +29 -19
  81. package/src/application-control/install.ts +1 -1
  82. package/src/cloud-connection/device-agent-cloud-connection.ts +216 -172
  83. package/src/cloud-connection/live-updates-handler.test.ts +20 -0
  84. package/src/cloud-connection/live-updates-handler.ts +45 -52
  85. package/src/cloud-connection/messages.ts +1 -14
  86. package/src/cloud-connection/publisher.ts +4 -0
  87. package/src/cloud-connection/shadow-handler.test.ts +93 -41
  88. package/src/cloud-connection/shadow-handler.ts +57 -21
  89. package/src/cloud-connection/transaction-manager.test.ts +193 -18
  90. package/src/cloud-connection/transaction-manager.ts +174 -26
  91. package/src/secure-tunneling/secure-tunneling.test.ts +1239 -0
  92. package/src/secure-tunneling/secure-tunneling.ts +606 -0
  93. package/src/secure-tunneling/spawner-detached.ts +107 -0
  94. package/src/subcommands/app/analytics.ts +99 -0
  95. package/src/subcommands/app/index.ts +4 -3
  96. package/src/subcommands/app/models.ts +13 -49
  97. package/src/subcommands/app/status.ts +20 -3
  98. package/src/subcommands/app/version.ts +19 -4
  99. package/src/util/cloud-mode-ready.ts +36 -0
  100. package/src/util/parsing.ts +11 -0
  101. package/lib/cloud-connection/cmd-status.d.ts +0 -8
  102. package/lib/cloud-connection/cmd-status.d.ts.map +0 -1
  103. package/lib/cloud-connection/cmd-status.js +0 -62
  104. package/lib/cloud-connection/cmd-status.js.map +0 -1
  105. package/lib/cloud-connection/message-builder.d.ts +0 -7
  106. package/lib/cloud-connection/message-builder.d.ts.map +0 -1
  107. package/lib/cloud-connection/message-builder.js +0 -63
  108. package/lib/cloud-connection/message-builder.js.map +0 -1
  109. package/lib/secure-tunneling/index.d.ts +0 -5
  110. package/lib/secure-tunneling/index.d.ts.map +0 -1
  111. package/lib/secure-tunneling/index.js +0 -64
  112. package/lib/secure-tunneling/index.js.map +0 -1
  113. package/src/cloud-connection/cmd-status.ts +0 -71
  114. package/src/cloud-connection/message-builder.ts +0 -117
  115. package/src/secure-tunneling/index.ts +0 -74
@@ -1,27 +1,21 @@
1
1
  import {
2
2
  AppLogsPayload,
3
- ToClientMessagePayload,
4
3
  keyMirrors,
5
4
  LiveStateUpdatesTogglePayload,
6
- ToClientMessage
5
+ ToClientMessage,
6
+ buildAppLogsMessage,
7
+ buildAppStateMessage,
8
+ buildDeviceStatsMessage,
9
+ StatusResponsePayload,
10
+ buildToClientStatusResponseMessage
7
11
  } from '@alwaysai/device-agent-schemas';
8
12
  import { getAppLogs } from '../application-control';
9
13
  import { logger } from '../util/logger';
10
14
  import sleep from '../util/sleep';
11
15
  import { Publisher } from './publisher';
12
- import {
13
- getStatusResponsePayload,
14
- getAppStatePayload,
15
- getDeviceStatsPayload
16
- } from './messages';
16
+ import { getAppStatePayload, getDeviceStatsPayload } from './messages';
17
17
  import { ToClientMessageTypeValue } from '@alwaysai/device-agent-schemas';
18
18
  import { ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS } from '../environment';
19
- import {
20
- buildAppLogsMessage,
21
- buildAppStateMessage,
22
- buildDeviceStatsMessage,
23
- buildStatusResponseMessage
24
- } from './message-builder';
25
19
 
26
20
  const LIVE_UPDATES_TIMEOUT = ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS
27
21
  ? parseInt(ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS)
@@ -48,8 +42,9 @@ export class LiveUpdatesHandler {
48
42
  private appLogStreams = new Set<string>();
49
43
  private transactionStatuses = new Set<string>();
50
44
 
51
- private async startAppLogStream(projectId: string) {
45
+ private async startAppLogStream(projectId: string, txId: string) {
52
46
  logger.info(`Starting log stream for ${projectId}`);
47
+
53
48
  this.appLogStreams.add(projectId);
54
49
  const readable = await getAppLogs({
55
50
  projectId,
@@ -68,7 +63,7 @@ export class LiveUpdatesHandler {
68
63
  projectId,
69
64
  logChunk: logStr
70
65
  };
71
- const message = await buildAppLogsMessage(payload, this.clientId);
66
+ const message = buildAppLogsMessage(this.clientId, payload, txId);
72
67
  this.publisher.publishToClient(message, logger.silly);
73
68
  });
74
69
 
@@ -117,7 +112,7 @@ export class LiveUpdatesHandler {
117
112
  this.liveUpdatesAlive.device_stats = toggles.deviceStats;
118
113
  }
119
114
  if (toggles.appState !== undefined) {
120
- this.liveUpdatesAlive.app_logs = toggles.appState;
115
+ this.liveUpdatesAlive.app_state = toggles.appState;
121
116
  }
122
117
  }
123
118
 
@@ -132,36 +127,25 @@ export class LiveUpdatesHandler {
132
127
  }, LIVE_UPDATES_TIMEOUT);
133
128
  }
134
129
 
135
- private async startPublishingLiveUpdates<T extends any[]>(
130
+ private async startPublishingLiveUpdates(
136
131
  messageType: ToClientMessageTypeValue,
137
- payloadBuilderFunction: (...args: T) => Promise<ToClientMessagePayload>,
138
- messageBuilderFunction: (
139
- payload: ToClientMessagePayload,
140
- txId: string
141
- ) => Promise<ToClientMessage>,
142
- args: T,
132
+ getMessage: () => Promise<ToClientMessage>,
143
133
  txId: string
144
134
  ) {
145
135
  logger.info(`Turned on live updates for ${messageType}`);
146
- // eslint-disable-next-line no-constant-condition
147
- while (true) {
148
- try {
149
- if (!this.continuePublishing(messageType, txId)) {
150
- logger.info(`Turned off live updates for ${messageType}`);
151
- break;
152
- }
153
- const payload: ToClientMessagePayload = await payloadBuilderFunction(
154
- ...args
155
- );
156
- const message = await messageBuilderFunction(payload, txId);
136
+ try {
137
+ while (this.continuePublishing(messageType, txId)) {
138
+ const message = await getMessage();
157
139
  this.publisher.publishToClient(message, logger.silly);
158
- } catch (e) {
159
- logger.error(
160
- `Error publishing live updates for ${messageType}: ${e.message}`
161
- );
140
+
141
+ await sleep(this.getLiveUpdatesInterval(messageType));
162
142
  }
163
- await sleep(this.getLiveUpdatesInterval(messageType));
143
+ } catch (e) {
144
+ logger.error(
145
+ `Error publishing live updates for ${messageType}: ${e.message}`
146
+ );
164
147
  }
148
+ logger.info(`Turned off live updates for ${messageType}`);
165
149
  }
166
150
 
167
151
  /*=================================================================
@@ -196,9 +180,16 @@ export class LiveUpdatesHandler {
196
180
  // Don't wait for this call to finish since it loops until disabled
197
181
  void this.startPublishingLiveUpdates(
198
182
  keyMirrors.toClientMessageType.status_response,
199
- getStatusResponsePayload,
200
- buildStatusResponseMessage,
201
- [keyMirrors.statusResponse.in_progress, ''],
183
+ async () => {
184
+ const payload: StatusResponsePayload = {
185
+ status: keyMirrors.statusResponse.in_progress
186
+ };
187
+ return buildToClientStatusResponseMessage(
188
+ this.clientId,
189
+ payload,
190
+ txId
191
+ );
192
+ },
202
193
  txId
203
194
  );
204
195
  }
@@ -220,41 +211,43 @@ export class LiveUpdatesHandler {
220
211
  const { deviceStats, appState, appLogs } = toggles;
221
212
  this.restartLiveUpdatesTimeout();
222
213
 
223
- const currentDeviceStats = this.getDeviceStatsLiveUpdates();
224
214
  if (deviceStats !== undefined) {
215
+ const currentDeviceStats = this.getDeviceStatsLiveUpdates();
225
216
  this.liveUpdatesAlive.device_stats = deviceStats;
226
217
  if (deviceStats && currentDeviceStats !== true) {
227
218
  // Don't wait for this call to finish since it loops until disabled
228
219
  void this.startPublishingLiveUpdates(
229
220
  keyMirrors.toClientMessageType.device_stats,
230
- getDeviceStatsPayload,
231
- buildDeviceStatsMessage,
232
- [],
221
+ async () => {
222
+ const payload = await getDeviceStatsPayload();
223
+ return buildDeviceStatsMessage(this.clientId, payload, txId);
224
+ },
233
225
  txId
234
226
  );
235
227
  }
236
228
  }
237
229
 
238
- const currentAppState = this.getAppStateLiveUpdates();
239
230
  if (appState !== undefined) {
231
+ const currentAppState = this.getAppStateLiveUpdates();
240
232
  this.liveUpdatesAlive.app_state = appState;
241
233
  if (appState && currentAppState !== true) {
242
234
  // Don't wait for this call to finish since it loops until disabled
243
235
  void this.startPublishingLiveUpdates(
244
236
  keyMirrors.toClientMessageType.app_state,
245
- getAppStatePayload,
246
- buildAppStateMessage,
247
- [],
237
+ async () => {
238
+ const payload = await getAppStatePayload();
239
+ return buildAppStateMessage(this.clientId, payload, txId);
240
+ },
248
241
  txId
249
242
  );
250
243
  }
251
244
  }
252
245
 
253
- const currentAppLogs = this.getAppLogsLiveUpdates();
254
246
  if (appLogs !== undefined) {
247
+ const currentAppLogs = this.getAppLogsLiveUpdates();
255
248
  if (appLogs.toggle && currentAppLogs !== true) {
256
249
  // Don't wait for this call to finish since it loops until disabled
257
- void this.startAppLogStream(appLogs.projectId);
250
+ void this.startAppLogStream(appLogs.projectId, txId);
258
251
  } else {
259
252
  this.appLogStreams.delete(appLogs.projectId);
260
253
  }
@@ -1,10 +1,8 @@
1
1
  import {
2
2
  AppState,
3
3
  AppStatePayload,
4
- DeviceStatsPayload,
5
- StatusResponsePayload
4
+ DeviceStatsPayload
6
5
  } from '@alwaysai/device-agent-schemas';
7
- import { StatusResponseValue } from '@alwaysai/device-agent-schemas/lib/constants';
8
6
  import { getAppState } from '../application-control';
9
7
  import {
10
8
  getCpuDetails,
@@ -27,17 +25,6 @@ export async function getAppStatePayload(): Promise<AppStatePayload> {
27
25
  return appStatePayload;
28
26
  }
29
27
 
30
- export async function getStatusResponsePayload(
31
- status: StatusResponseValue,
32
- message: string
33
- ): Promise<StatusResponsePayload> {
34
- const statusResponsePayload: StatusResponsePayload = {
35
- status,
36
- message
37
- };
38
- return statusResponsePayload;
39
- }
40
-
41
28
  export async function getDeviceStatsPayload(): Promise<DeviceStatsPayload> {
42
29
  const cpuDetails = await getCpuDetails();
43
30
  const diskDetails = await getDiskDetails();
@@ -86,4 +86,8 @@ export class Publisher {
86
86
  // Can edit topic field in message here if we want
87
87
  this.publishDeviceAgentMessage(this.toCloudTopic, message, logger);
88
88
  }
89
+
90
+ public getClientId(): string {
91
+ return this.clientId;
92
+ }
89
93
  }
@@ -2,6 +2,8 @@ import { AppConfig } from '@alwaysai/app-configuration-schemas';
2
2
  import { readAppCfgFile } from '../application-control';
3
3
  import { Publisher } from './publisher';
4
4
  import { ShadowHandler } from './shadow-handler';
5
+ import { Logger } from 'winston';
6
+ import { logger } from '../util/logger';
5
7
 
6
8
  jest.mock('../application-control');
7
9
  jest.mock('./publisher');
@@ -23,7 +25,7 @@ describe('Test Shadow Handler', () => {
23
25
  //FIXME: Invalid input is silently ignored, need input validation
24
26
  expect(async () => {
25
27
  await shadowHandler.handleShadowTopic({
26
- topic: shadowHandler.shadowTopics.projects.updateDelta,
28
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
27
29
  payload: Buffer.from('test-payload'),
28
30
  clientToken: ''
29
31
  });
@@ -56,7 +58,7 @@ describe('Test Shadow Handler', () => {
56
58
  };
57
59
 
58
60
  const updates = await shadowHandler.handleShadowTopic({
59
- topic: shadowHandler.shadowTopics.projects.updateDelta,
61
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
60
62
  payload,
61
63
  clientToken: clientId
62
64
  });
@@ -65,11 +67,13 @@ describe('Test Shadow Handler', () => {
65
67
 
66
68
  test('handle project shadow empty delta', async () => {
67
69
  const payload = {
68
- [projectId1]: {}
70
+ desired: {
71
+ [projectId1]: {}
72
+ }
69
73
  };
70
74
 
71
75
  const updates = await shadowHandler.handleShadowTopic({
72
- topic: shadowHandler.shadowTopics.projects.updateDelta,
76
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
73
77
  payload,
74
78
  clientToken: ''
75
79
  });
@@ -98,7 +102,7 @@ describe('Test Shadow Handler', () => {
98
102
  };
99
103
 
100
104
  const payload = {
101
- delta: {
105
+ desired: {
102
106
  [projectId1]: {
103
107
  appConfig: JSON.stringify(appCfg1)
104
108
  }
@@ -106,7 +110,7 @@ describe('Test Shadow Handler', () => {
106
110
  };
107
111
 
108
112
  const updates = await shadowHandler.handleShadowTopic({
109
- topic: shadowHandler.shadowTopics.projects.getAccepted,
113
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
110
114
  payload,
111
115
  clientToken: ''
112
116
  });
@@ -145,13 +149,15 @@ describe('Test Shadow Handler', () => {
145
149
  };
146
150
 
147
151
  const payload = {
148
- [projectId1]: {
149
- appConfig: JSON.stringify(appCfg1)
152
+ desired: {
153
+ [projectId1]: {
154
+ appConfig: JSON.stringify(appCfg1)
155
+ }
150
156
  }
151
157
  };
152
158
 
153
159
  const updates = await shadowHandler.handleShadowTopic({
154
- topic: shadowHandler.shadowTopics.projects.updateDelta,
160
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
155
161
  payload,
156
162
  clientToken: ''
157
163
  });
@@ -208,16 +214,18 @@ describe('Test Shadow Handler', () => {
208
214
  }
209
215
  };
210
216
  const payload = {
211
- [projectId1]: {
212
- appConfig: JSON.stringify(appCfg1)
213
- },
214
- [projectId2]: {
215
- appConfig: JSON.stringify(appCfg2)
217
+ desired: {
218
+ [projectId1]: {
219
+ appConfig: JSON.stringify(appCfg1)
220
+ },
221
+ [projectId2]: {
222
+ appConfig: JSON.stringify(appCfg2)
223
+ }
216
224
  }
217
225
  };
218
226
 
219
227
  const updates = await shadowHandler.handleShadowTopic({
220
- topic: shadowHandler.shadowTopics.projects.updateDelta,
228
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
221
229
  payload,
222
230
  clientToken: ''
223
231
  });
@@ -265,13 +273,15 @@ describe('Test Shadow Handler', () => {
265
273
  };
266
274
 
267
275
  const payload = {
268
- [projectId1]: {
269
- appConfig: JSON.stringify(appCfg1)
276
+ desired: {
277
+ [projectId1]: {
278
+ appConfig: JSON.stringify(appCfg1)
279
+ }
270
280
  }
271
281
  };
272
282
 
273
283
  const updates = await shadowHandler.handleShadowTopic({
274
- topic: shadowHandler.shadowTopics.projects.updateDelta,
284
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
275
285
  payload,
276
286
  clientToken: ''
277
287
  });
@@ -307,27 +317,73 @@ describe('Test Shadow Handler', () => {
307
317
  };
308
318
 
309
319
  const payload = {
310
- [projectId1]: {
311
- appConfig: JSON.stringify(appCfg1)
320
+ desired: {
321
+ [projectId1]: {
322
+ appConfig: JSON.stringify(appCfg1)
323
+ }
312
324
  }
313
325
  };
314
326
 
315
327
  const updates = await shadowHandler.handleShadowTopic({
316
- topic: shadowHandler.shadowTopics.projects.updateDelta,
328
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
317
329
  payload,
318
330
  clientToken: ''
319
331
  });
320
332
  expect(updates.length).toBe(0);
321
333
  });
334
+
335
+ test('handles an unparsable object in a project shadow appCfg delta', async () => {
336
+ const ogAppCfg1: AppConfig = {
337
+ scripts: {
338
+ start: 'python app.py'
339
+ },
340
+ models: {}
341
+ };
342
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
343
+
344
+ // This appCfg is invalid on it's own (see values below)
345
+ const appCfg1 = {
346
+ scripts: {
347
+ start: 'python app.py'
348
+ },
349
+ models: {
350
+ 'alwaysai/mobilenet_ssd': '3', // string instead of int
351
+ 'alwaysai/yolo_v4': '5' // string instead of int
352
+ }
353
+ };
354
+
355
+ const payload = {
356
+ desired: {
357
+ [projectId1]: {
358
+ appConfig: appCfg1 // This is missing JSON.stringify() making this an unparsable object.
359
+ }
360
+ }
361
+ };
362
+
363
+ const loggerSpy = jest
364
+ .spyOn(logger, 'error')
365
+ .mockReturnValue({} as unknown as Logger);
366
+
367
+ const updates = await shadowHandler.handleShadowTopic({
368
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
369
+ payload,
370
+ clientToken: ''
371
+ });
372
+
373
+ expect(updates.length).toBe(0);
374
+ expect(loggerSpy).toHaveBeenCalledWith(
375
+ expect.stringContaining('Could not parse the appConfig')
376
+ );
377
+ });
322
378
  });
323
379
 
324
380
  describe('handle project shadow env vars', () => {
325
- test('handle project shadow env vars get response with delta', async () => {
381
+ test('handle a response from the getAccepted from the cloud', async () => {
326
382
  const envVars1 = {
327
383
  VAR0: 'value0'
328
384
  };
329
385
  const payload = {
330
- delta: {
386
+ reported: {
331
387
  [projectId1]: {
332
388
  envVars: envVars1
333
389
  }
@@ -339,28 +395,22 @@ describe('Test Shadow Handler', () => {
339
395
  payload,
340
396
  clientToken: ''
341
397
  });
342
- expect(updates.length).toBe(1);
343
- expect(updates[0]).toEqual({
344
- projectId: projectId1,
345
- txId: expect.any(String),
346
- envVarUpdate: {
347
- envVars: envVars1
348
- }
349
- });
398
+ expect(updates.length).toBe(0);
350
399
  });
351
-
352
400
  test('handle project shadow env vars update delta', async () => {
353
401
  const envVars1 = {
354
402
  VAR1: 'value1'
355
403
  };
356
404
  const payload = {
357
- [projectId1]: {
358
- envVars: envVars1
405
+ desired: {
406
+ [projectId1]: {
407
+ envVars: envVars1
408
+ }
359
409
  }
360
410
  };
361
411
 
362
412
  const updates = await shadowHandler.handleShadowTopic({
363
- topic: shadowHandler.shadowTopics.projects.updateDelta,
413
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
364
414
  payload,
365
415
  clientToken: ''
366
416
  });
@@ -382,16 +432,18 @@ describe('Test Shadow Handler', () => {
382
432
  VAR2: null
383
433
  };
384
434
  const payload = {
385
- [projectId1]: {
386
- envVars: envVars1
387
- },
388
- [projectId2]: {
389
- envVars: envVars2
435
+ desired: {
436
+ [projectId1]: {
437
+ envVars: envVars1
438
+ },
439
+ [projectId2]: {
440
+ envVars: envVars2
441
+ }
390
442
  }
391
443
  };
392
444
 
393
445
  const updates = await shadowHandler.handleShadowTopic({
394
- topic: shadowHandler.shadowTopics.projects.updateDelta,
446
+ topic: shadowHandler.shadowTopics.projects.updateAccepted,
395
447
  payload,
396
448
  clientToken: ''
397
449
  });
@@ -2,12 +2,12 @@ import {
2
2
  AppConfig,
3
3
  validateAppConfig
4
4
  } from '@alwaysai/app-configuration-schemas';
5
+ import { generateTxId } from '@alwaysai/device-agent-schemas';
5
6
  import { EnvVars, getAllEnvs, readAppCfgFile } from '../application-control';
7
+ import { getSystemInformation } from '../device-control/device-control';
6
8
  import { logger } from '../util/logger';
7
9
  import { Publisher } from './publisher';
8
10
  import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
9
- import { getSystemInformation } from '../device-control/device-control';
10
- import { generateTxId } from '@alwaysai/device-agent-schemas';
11
11
 
12
12
  export interface ShadowTopics {
13
13
  projects: {
@@ -23,6 +23,18 @@ export interface ShadowTopics {
23
23
  systemInfo: {
24
24
  update: string;
25
25
  };
26
+ secureTunnel: {
27
+ get: string;
28
+ getAccepted: string;
29
+ getRejected: string;
30
+ update: string;
31
+ updateDelta: string;
32
+ updateAccepted: string;
33
+ updateRejected: string;
34
+ delete: string;
35
+ deleteAccepted: string;
36
+ deleteRejected: string;
37
+ };
26
38
  }
27
39
 
28
40
  export type AppConfigUpdate = {
@@ -50,20 +62,32 @@ export class ShadowHandler {
50
62
  constructor(clientId: string, publisher: Publisher) {
51
63
  this.clientId = clientId;
52
64
  this.publisher = publisher;
53
- this.shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
65
+ this.shadowPrefix = `$aws/things/${this.clientId}/shadow/name`;
54
66
  this.shadowTopics = {
55
67
  projects: {
56
- get: `${this.shadowPrefix}projects/get`,
57
- getAccepted: `${this.shadowPrefix}projects/get/accepted`,
58
- getRejected: `${this.shadowPrefix}projects/get/rejected`,
59
- update: `${this.shadowPrefix}projects/update`,
60
- updateDelta: `${this.shadowPrefix}projects/update/delta`,
61
- updateAccepted: `${this.shadowPrefix}projects/update/accepted`,
62
- updateRejected: `${this.shadowPrefix}projects/update/rejected`,
63
- delete: `${this.shadowPrefix}projects/delete`
68
+ get: `${this.shadowPrefix}/projects/get`,
69
+ getAccepted: `${this.shadowPrefix}/projects/get/accepted`,
70
+ getRejected: `${this.shadowPrefix}/projects/get/rejected`,
71
+ update: `${this.shadowPrefix}/projects/update`,
72
+ updateDelta: `${this.shadowPrefix}/projects/update/delta`,
73
+ updateAccepted: `${this.shadowPrefix}/projects/update/accepted`,
74
+ updateRejected: `${this.shadowPrefix}/projects/update/rejected`,
75
+ delete: `${this.shadowPrefix}/projects/delete`
64
76
  },
65
77
  systemInfo: {
66
- update: `${this.shadowPrefix}system-info/update`
78
+ update: `${this.shadowPrefix}/system-info/update`
79
+ },
80
+ secureTunnel: {
81
+ get: `${this.shadowPrefix}/secure-tunnel/get`,
82
+ getAccepted: `${this.shadowPrefix}/secure-tunnel/get/accepted`,
83
+ getRejected: `${this.shadowPrefix}/secure-tunnel/get/rejected`,
84
+ update: `${this.shadowPrefix}/secure-tunnel/update`,
85
+ updateDelta: `${this.shadowPrefix}/secure-tunnel/update/delta`,
86
+ updateAccepted: `${this.shadowPrefix}/secure-tunnel/update/accepted`,
87
+ updateRejected: `${this.shadowPrefix}/secure-tunnel/update/rejected`,
88
+ delete: `${this.shadowPrefix}/secure-tunnel/delete`,
89
+ deleteAccepted: `${this.shadowPrefix}/secure-tunnel/delete/accepted`,
90
+ deleteRejected: `${this.shadowPrefix}/secure-tunnel/delete/rejected`
67
91
  }
68
92
  };
69
93
  }
@@ -84,7 +108,15 @@ export class ShadowHandler {
84
108
  const shadowUpdate: ShadowUpdate = { projectId, txId };
85
109
 
86
110
  if (projectShadow.appConfig) {
87
- const newAppCfg = JSON.parse(projectShadow.appConfig);
111
+ let newAppCfg: any;
112
+ try {
113
+ newAppCfg = JSON.parse(projectShadow.appConfig);
114
+ } catch (error) {
115
+ logger.error(
116
+ `Could not parse the appConfig for transaction ${txId}!\n${error}`
117
+ );
118
+ continue;
119
+ }
88
120
  if (!validateAppConfig(newAppCfg)) {
89
121
  // FIXME: Raise an exception to be handled at higher layer
90
122
  logger.error(
@@ -124,6 +156,7 @@ export class ShadowHandler {
124
156
  updates.push(shadowUpdate);
125
157
  }
126
158
  }
159
+
127
160
  return updates;
128
161
  }
129
162
 
@@ -141,7 +174,7 @@ export class ShadowHandler {
141
174
  // TODO: make use a function like the other topic getters
142
175
  const shadowName = topic.split('/')[5];
143
176
  switch (topic) {
144
- case this.shadowTopics.projects.updateDelta:
177
+ case this.shadowTopics.projects.updateAccepted:
145
178
  if (clientToken === this.clientId) {
146
179
  logger.debug(
147
180
  `Ignoring delta caused by Device Agent: ${JSON.stringify(
@@ -152,14 +185,16 @@ export class ShadowHandler {
152
185
  );
153
186
  break;
154
187
  }
155
- return await this.handleNamedShadowUpdate({ delta: payload });
188
+ return await this.handleNamedShadowUpdate({ delta: payload.desired });
156
189
  case this.shadowTopics.projects.getAccepted:
157
- if (payload['delta']) {
158
- return await this.handleNamedShadowUpdate({
159
- delta: payload['delta']
160
- });
161
- } else {
162
- logger.info(`No delta updates in named shadow '${shadowName}'`);
190
+ if (clientToken !== this.clientId) {
191
+ logger.debug(
192
+ `Ignoring get message initiated by the cloud/console: ${JSON.stringify(
193
+ { topic, payload },
194
+ null,
195
+ 2
196
+ )}`
197
+ );
163
198
  }
164
199
  break;
165
200
  default:
@@ -189,6 +224,7 @@ export class ShadowHandler {
189
224
  public async updateProjectShadow(projectId: string) {
190
225
  const appCfg = await readAppCfgFile({ projectId });
191
226
  const envVars = await getAllEnvs({ projectId });
227
+
192
228
  const packet = {
193
229
  state: {
194
230
  reported: {