@alwaysai/device-agent 1.3.0 → 1.3.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.
Files changed (123) 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 +5 -5
  10. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  11. package/lib/cloud-connection/device-agent-cloud-connection.js +203 -178
  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 +10 -21
  27. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  28. package/lib/cloud-connection/shadow-handler.js +154 -100
  29. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  30. package/lib/cloud-connection/shadow-handler.test.js +140 -72
  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/device-control/device-control.d.ts +2 -2
  39. package/lib/device-control/device-control.d.ts.map +1 -1
  40. package/lib/device-control/device-control.js.map +1 -1
  41. package/lib/secure-tunneling/secure-tunneling.d.ts +105 -0
  42. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -0
  43. package/lib/secure-tunneling/secure-tunneling.js +435 -0
  44. package/lib/secure-tunneling/secure-tunneling.js.map +1 -0
  45. package/lib/secure-tunneling/secure-tunneling.test.d.ts +2 -0
  46. package/lib/secure-tunneling/secure-tunneling.test.d.ts.map +1 -0
  47. package/lib/secure-tunneling/secure-tunneling.test.js +1070 -0
  48. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -0
  49. package/lib/secure-tunneling/spawner-detached.d.ts +6 -0
  50. package/lib/secure-tunneling/spawner-detached.d.ts.map +1 -0
  51. package/lib/secure-tunneling/spawner-detached.js +107 -0
  52. package/lib/secure-tunneling/spawner-detached.js.map +1 -0
  53. package/lib/subcommands/app/analytics.d.ts +10 -0
  54. package/lib/subcommands/app/analytics.d.ts.map +1 -0
  55. package/lib/subcommands/app/analytics.js +79 -0
  56. package/lib/subcommands/app/analytics.js.map +1 -0
  57. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  58. package/lib/subcommands/app/env-vars.js +11 -16
  59. package/lib/subcommands/app/env-vars.js.map +1 -1
  60. package/lib/subcommands/app/index.d.ts.map +1 -1
  61. package/lib/subcommands/app/index.js +3 -1
  62. package/lib/subcommands/app/index.js.map +1 -1
  63. package/lib/subcommands/app/models.d.ts +0 -5
  64. package/lib/subcommands/app/models.d.ts.map +1 -1
  65. package/lib/subcommands/app/models.js +16 -56
  66. package/lib/subcommands/app/models.js.map +1 -1
  67. package/lib/subcommands/app/status.d.ts +1 -0
  68. package/lib/subcommands/app/status.d.ts.map +1 -1
  69. package/lib/subcommands/app/status.js +14 -3
  70. package/lib/subcommands/app/status.js.map +1 -1
  71. package/lib/subcommands/app/version.d.ts +2 -1
  72. package/lib/subcommands/app/version.d.ts.map +1 -1
  73. package/lib/subcommands/app/version.js +16 -3
  74. package/lib/subcommands/app/version.js.map +1 -1
  75. package/lib/util/cloud-mode-ready.d.ts +1 -0
  76. package/lib/util/cloud-mode-ready.d.ts.map +1 -1
  77. package/lib/util/cloud-mode-ready.js +36 -1
  78. package/lib/util/cloud-mode-ready.js.map +1 -1
  79. package/lib/util/parsing.d.ts +2 -0
  80. package/lib/util/parsing.d.ts.map +1 -0
  81. package/lib/util/parsing.js +17 -0
  82. package/lib/util/parsing.js.map +1 -0
  83. package/package.json +4 -6
  84. package/readme.md +146 -92
  85. package/src/application-control/environment-variables.test.ts +43 -3
  86. package/src/application-control/environment-variables.ts +29 -19
  87. package/src/application-control/install.ts +1 -1
  88. package/src/cloud-connection/device-agent-cloud-connection.ts +272 -247
  89. package/src/cloud-connection/live-updates-handler.test.ts +20 -0
  90. package/src/cloud-connection/live-updates-handler.ts +45 -52
  91. package/src/cloud-connection/messages.ts +1 -14
  92. package/src/cloud-connection/publisher.ts +4 -0
  93. package/src/cloud-connection/shadow-handler.test.ts +150 -73
  94. package/src/cloud-connection/shadow-handler.ts +247 -126
  95. package/src/cloud-connection/transaction-manager.test.ts +193 -18
  96. package/src/cloud-connection/transaction-manager.ts +174 -26
  97. package/src/device-control/device-control.ts +3 -3
  98. package/src/secure-tunneling/secure-tunneling.test.ts +1239 -0
  99. package/src/secure-tunneling/secure-tunneling.ts +606 -0
  100. package/src/secure-tunneling/spawner-detached.ts +123 -0
  101. package/src/subcommands/app/analytics.ts +102 -0
  102. package/src/subcommands/app/env-vars.ts +18 -16
  103. package/src/subcommands/app/index.ts +4 -3
  104. package/src/subcommands/app/models.ts +25 -57
  105. package/src/subcommands/app/status.ts +20 -3
  106. package/src/subcommands/app/version.ts +19 -4
  107. package/src/util/cloud-mode-ready.ts +36 -0
  108. package/src/util/parsing.ts +11 -0
  109. package/lib/cloud-connection/cmd-status.d.ts +0 -8
  110. package/lib/cloud-connection/cmd-status.d.ts.map +0 -1
  111. package/lib/cloud-connection/cmd-status.js +0 -62
  112. package/lib/cloud-connection/cmd-status.js.map +0 -1
  113. package/lib/cloud-connection/message-builder.d.ts +0 -7
  114. package/lib/cloud-connection/message-builder.d.ts.map +0 -1
  115. package/lib/cloud-connection/message-builder.js +0 -63
  116. package/lib/cloud-connection/message-builder.js.map +0 -1
  117. package/lib/secure-tunneling/index.d.ts +0 -5
  118. package/lib/secure-tunneling/index.d.ts.map +0 -1
  119. package/lib/secure-tunneling/index.js +0 -64
  120. package/lib/secure-tunneling/index.js.map +0 -1
  121. package/src/cloud-connection/cmd-status.ts +0 -71
  122. package/src/cloud-connection/message-builder.ts +0 -117
  123. package/src/secure-tunneling/index.ts +0 -74
@@ -3,27 +3,22 @@ import {
3
3
  validateAppConfig
4
4
  } from '@alwaysai/app-configuration-schemas';
5
5
  import { EnvVars, getAllEnvs, readAppCfgFile } from '../application-control';
6
+ import { getSystemInformation } from '../device-control/device-control';
6
7
  import { logger } from '../util/logger';
7
8
  import { Publisher } from './publisher';
8
9
  import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
9
- import { getSystemInformation } from '../device-control/device-control';
10
- import { generateTxId } from '@alwaysai/device-agent-schemas';
11
-
12
- export interface ShadowTopics {
13
- projects: {
14
- get: string;
15
- getAccepted: string;
16
- getRejected: string;
17
- update: string;
18
- updateDelta: string;
19
- updateAccepted: string;
20
- updateRejected: string;
21
- delete: string;
22
- };
23
- systemInfo: {
24
- update: string;
25
- };
26
- }
10
+ import {
11
+ generateTxId,
12
+ validateProjectShadowUpdate,
13
+ buildBaseShadowMessage,
14
+ buildUpdateProjectShadowMessage,
15
+ buildUpdateSystemInfoShadowMessage,
16
+ getShadowTopic,
17
+ ShadowProjectsUpdateAll,
18
+ getDesiredFromMessage,
19
+ ShadowTopics,
20
+ ProjectShadowUpdate
21
+ } from '@alwaysai/device-agent-schemas';
27
22
 
28
23
  export type AppConfigUpdate = {
29
24
  newAppCfg: AppConfig;
@@ -44,92 +39,191 @@ export type ShadowUpdate = {
44
39
  export class ShadowHandler {
45
40
  private clientId: string;
46
41
  private publisher: Publisher;
47
- public readonly shadowPrefix: string;
48
- public readonly shadowTopics: ShadowTopics;
49
-
42
+ public projectShadowTopics: string[] = [];
43
+ public readonly shadowTopics: { [key: string]: any };
50
44
  constructor(clientId: string, publisher: Publisher) {
51
45
  this.clientId = clientId;
52
46
  this.publisher = publisher;
53
- this.shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
54
47
  this.shadowTopics = {
55
- 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`
48
+ project: {
49
+ get: getShadowTopic(clientId, 'projects', 'get'),
50
+ getAccepted: getShadowTopic(clientId, 'projects', 'get/accepted'),
51
+ getRejected: getShadowTopic(clientId, 'projects', 'get/rejected'),
52
+ update: getShadowTopic(clientId, 'projects', 'update'),
53
+ updateDelta: getShadowTopic(clientId, 'projects', 'update/delta'),
54
+ updateAccepted: getShadowTopic(clientId, 'projects', 'update/accepted'),
55
+ updateRejected: getShadowTopic(clientId, 'projects', 'update/rejected')
64
56
  },
65
57
  systemInfo: {
66
- update: `${this.shadowPrefix}system-info/update`
58
+ update: getShadowTopic(clientId, 'system-info', 'update')
59
+ },
60
+ secureTunnel: {
61
+ get: getShadowTopic(clientId, 'secure-tunnel', 'get'),
62
+ getAccepted: getShadowTopic(clientId, 'secure-tunnel', 'get/accepted'),
63
+ getRejected: getShadowTopic(clientId, 'secure-tunnel', 'get/rejected'),
64
+ update: getShadowTopic(clientId, 'secure-tunnel', 'update'),
65
+ updateDelta: getShadowTopic(clientId, 'secure-tunnel', 'update/delta'),
66
+ updateAccepted: getShadowTopic(
67
+ clientId,
68
+ 'secure-tunnel',
69
+ 'update/accepted'
70
+ ),
71
+ updateRejected: getShadowTopic(
72
+ clientId,
73
+ 'secure-tunnel',
74
+ 'update/rejected'
75
+ ),
76
+ delete: getShadowTopic(clientId, 'secure-tunnel', 'delete'),
77
+ deleteAccepted: getShadowTopic(
78
+ clientId,
79
+ 'secure-tunnel',
80
+ 'delete/accepted'
81
+ ),
82
+ deleteRejected: getShadowTopic(
83
+ clientId,
84
+ 'secure-tunnel',
85
+ 'delete/rejected'
86
+ )
67
87
  }
68
88
  };
89
+ this.projectShadowTopics.push(this.shadowTopics.project.getAccepted);
90
+ this.projectShadowTopics.push(this.shadowTopics.project.getRejected);
91
+ this.projectShadowTopics.push(this.shadowTopics.project.updateDelta);
92
+ this.projectShadowTopics.push(this.shadowTopics.project.updateAccepted);
93
+ this.projectShadowTopics.push(this.shadowTopics.project.updateRejected);
69
94
  }
70
95
 
71
- private async handleNamedShadowUpdate({
96
+ private async generateAppConfigUpdate({
97
+ appConfig,
98
+ txId,
99
+ projectId
100
+ }: {
101
+ appConfig: string;
102
+ txId: string;
103
+ projectId: string;
104
+ }): Promise<AppConfigUpdate | null> {
105
+ let appCfgUpdate: any;
106
+ let newAppCfg: any;
107
+
108
+ // Handle errors and validation
109
+ try {
110
+ newAppCfg = JSON.parse(appConfig);
111
+ } catch (error) {
112
+ logger.error(
113
+ `Could not parse the appConfig for transaction ${txId}!\n${error}`
114
+ );
115
+ return null;
116
+ }
117
+
118
+ if (!validateAppConfig(newAppCfg)) {
119
+ // FIXME: Raise an exception to be handled at higher layer
120
+ logger.error(
121
+ `Received invalid app config for ${projectId}!\n${JSON.stringify(
122
+ validateAppConfig.errors,
123
+ null,
124
+ 2
125
+ )}`
126
+ );
127
+ return null;
128
+ }
129
+
130
+ // If all ok, return the AppConfigUpdate
131
+ logger.info(`Found a delta for app config shadow. Updating ${projectId}`);
132
+ const { updatedModels } = await getAppCfgModelsDiff({
133
+ newAppCfg,
134
+ projectId
135
+ });
136
+
137
+ if (updatedModels && Object.keys(updatedModels).length) {
138
+ appCfgUpdate = { newAppCfg, updatedModels };
139
+ } else {
140
+ appCfgUpdate = { newAppCfg };
141
+ }
142
+ return appCfgUpdate;
143
+ }
144
+
145
+ private async processProjectShadowUpdates({
72
146
  delta
73
147
  }: {
74
148
  delta: any;
75
149
  }): Promise<ShadowUpdate[]> {
76
- const updates: ShadowUpdate[] = [];
77
-
78
- const deltaKeys = Object.keys(delta);
150
+ const shadowUpdates: ShadowUpdate[] = [];
79
151
 
80
- for (const projectId of deltaKeys) {
81
- const projectShadow = delta[projectId];
82
- // For incoming shadow updates, there will be no TxID, so it needs to be generated here.
83
- const txId = generateTxId();
84
- const shadowUpdate: ShadowUpdate = { projectId, txId };
85
-
86
- if (projectShadow.appConfig) {
87
- const newAppCfg = JSON.parse(projectShadow.appConfig);
88
- if (!validateAppConfig(newAppCfg)) {
89
- // FIXME: Raise an exception to be handled at higher layer
152
+ for (const [projectId, projectDelta] of Object.entries(delta)) {
153
+ if (projectDelta) {
154
+ const valid = validateProjectShadowUpdate(projectDelta);
155
+ if (!valid) {
90
156
  logger.error(
91
- `Received invalid app config for ${projectId}!\n${JSON.stringify(
92
- validateAppConfig.errors,
157
+ `Error validating shadow update: ${JSON.stringify(
158
+ { projectDelta, errors: validateProjectShadowUpdate.errors },
93
159
  null,
94
160
  2
95
161
  )}`
96
162
  );
97
- continue;
98
- }
99
- const { updatedModels } = await getAppCfgModelsDiff({
100
- newAppCfg,
101
- projectId
102
- });
103
-
104
- if (updatedModels && Object.keys(updatedModels).length) {
105
- shadowUpdate.appCfgUpdate = { newAppCfg, updatedModels };
106
163
  } else {
107
- shadowUpdate.appCfgUpdate = { newAppCfg };
164
+ const shadowUpdate = await this.processProjectShadowUpdate({
165
+ projectId,
166
+ projectDelta
167
+ });
168
+ if (shadowUpdate) {
169
+ shadowUpdates.push(shadowUpdate);
170
+ }
108
171
  }
109
- } else {
110
- logger.info(
111
- `Ignoring app config shadow update for ${projectId} due to no delta`
112
- );
113
172
  }
173
+ }
174
+ return shadowUpdates;
175
+ }
114
176
 
115
- if (projectShadow.envVars) {
116
- const envVars = projectShadow.envVars;
117
- shadowUpdate.envVarUpdate = { envVars };
118
- } else {
119
- logger.info(
120
- `Ignoring app environment variable shadow update for ${projectId} due to no delta`
121
- );
122
- }
123
- if (shadowUpdate.appCfgUpdate || shadowUpdate.envVarUpdate) {
124
- updates.push(shadowUpdate);
177
+ private async processProjectShadowUpdate({
178
+ projectId,
179
+ projectDelta
180
+ }: {
181
+ projectId: string;
182
+ projectDelta: ProjectShadowUpdate;
183
+ }): Promise<ShadowUpdate | null> {
184
+ const txId = generateTxId();
185
+ const shadowUpdate: ShadowUpdate = { projectId, txId };
186
+
187
+ // Handle appConfig Updates
188
+ if (!projectDelta.appConfig) {
189
+ logger.info(
190
+ `Ignoring app config shadow update for ${projectId} due to no delta`
191
+ );
192
+ } else {
193
+ logger.info(`Found a delta for app config shadow. Updating ${projectId}`);
194
+ const appConfig = projectDelta.appConfig;
195
+ const appCfgUpdate = await this.generateAppConfigUpdate({
196
+ appConfig,
197
+ txId,
198
+ projectId
199
+ });
200
+
201
+ if (appCfgUpdate) {
202
+ shadowUpdate.appCfgUpdate = appCfgUpdate;
125
203
  }
126
204
  }
127
- return updates;
205
+
206
+ // Handle envVars Updates
207
+ if (!projectDelta.envVars) {
208
+ logger.info(
209
+ `Ignoring app environment variable shadow update for ${projectId} due to no delta`
210
+ );
211
+ } else {
212
+ logger.info(
213
+ `Found a delta for app environment variable shadow. Updating ${projectId}`
214
+ );
215
+ const envVars = projectDelta.envVars;
216
+ shadowUpdate.envVarUpdate = { envVars };
217
+ }
218
+
219
+ return shadowUpdate.appCfgUpdate || shadowUpdate.envVarUpdate
220
+ ? shadowUpdate
221
+ : null;
128
222
  }
129
223
 
130
224
  // Public interface
131
225
 
132
- public async handleShadowTopic({
226
+ public async handleProjectShadow({
133
227
  topic,
134
228
  payload,
135
229
  clientToken
@@ -139,12 +233,35 @@ export class ShadowHandler {
139
233
  clientToken: string;
140
234
  }): Promise<ShadowUpdate[]> {
141
235
  // TODO: make use a function like the other topic getters
142
- const shadowName = topic.split('/')[5];
236
+ if (!this.projectShadowTopics.includes(topic)) {
237
+ throw Error(`Topic ${topic} is not in the ${this.projectShadowTopics}`);
238
+ }
143
239
  switch (topic) {
144
- case this.shadowTopics.projects.updateDelta:
240
+ case this.shadowTopics.project.updateAccepted: {
145
241
  if (clientToken === this.clientId) {
146
242
  logger.debug(
147
- `Ignoring delta caused by Device Agent: ${JSON.stringify(
243
+ `Ignoring message as it was caused by Device Agent itself: ${JSON.stringify(
244
+ { topic, payload },
245
+ null,
246
+ 2
247
+ )}`
248
+ );
249
+ break;
250
+ }
251
+ const desired = getDesiredFromMessage(payload);
252
+ if (!desired) {
253
+ logger.debug(
254
+ `No desired state found in message: ${JSON.stringify(payload)}`
255
+ );
256
+ } else {
257
+ return await this.processProjectShadowUpdates({ delta: desired });
258
+ }
259
+ break;
260
+ }
261
+ case this.shadowTopics.project.getAccepted: {
262
+ if (clientToken !== this.clientId) {
263
+ logger.debug(
264
+ `Ignoring message as it was caused by Device Agent itself: ${JSON.stringify(
148
265
  { topic, payload },
149
266
  null,
150
267
  2
@@ -152,19 +269,28 @@ export class ShadowHandler {
152
269
  );
153
270
  break;
154
271
  }
155
- return await this.handleNamedShadowUpdate({ delta: payload });
156
- case this.shadowTopics.projects.getAccepted:
157
- if (payload['delta']) {
158
- return await this.handleNamedShadowUpdate({
159
- delta: payload['delta']
272
+ if (payload.state.delta) {
273
+ return await this.processProjectShadowUpdates({
274
+ delta: payload.state.delta
160
275
  });
161
276
  } else {
162
- logger.info(`No delta updates in named shadow '${shadowName}'`);
277
+ logger.info(
278
+ `No delta in projects.getAccepted in named shadow '${
279
+ topic.split('/')[5]
280
+ }'`
281
+ );
163
282
  }
164
283
  break;
284
+ }
285
+ case this.shadowTopics.project.getRejected:
286
+ case this.shadowTopics.project.updateDelta:
287
+ case this.shadowTopics.project.updateRejected: {
288
+ // Not handling these for now
289
+ break;
290
+ }
165
291
  default:
166
292
  logger.info(
167
- `Ignoring shadow message: ${JSON.stringify(
293
+ `Did not match a correct topic. Ignoring shadow message: ${JSON.stringify(
168
294
  { topic, payload },
169
295
  null,
170
296
  2
@@ -176,57 +302,52 @@ export class ShadowHandler {
176
302
 
177
303
  public async updateSystemInfoShadow() {
178
304
  const systemInfo = await getSystemInformation();
179
- const packet = {
180
- state: {
181
- reported: systemInfo
182
- },
183
- clientToken: this.clientId
184
- };
185
- const topic = this.shadowTopics.systemInfo.update;
186
- this.publisher.publish(topic, JSON.stringify(packet));
305
+ this.publisher.publish(
306
+ getShadowTopic(this.clientId, 'system-info', 'update'),
307
+ JSON.stringify(
308
+ buildUpdateSystemInfoShadowMessage(this.clientId, systemInfo)
309
+ )
310
+ );
187
311
  }
188
312
 
189
313
  public async updateProjectShadow(projectId: string) {
190
314
  const appCfg = await readAppCfgFile({ projectId });
191
315
  const envVars = await getAllEnvs({ projectId });
192
- const packet = {
193
- state: {
194
- reported: {
195
- [projectId]: {
196
- appConfig: JSON.stringify(appCfg),
197
- envVars
198
- }
199
- }
200
- },
201
- clientToken: this.clientId
316
+
317
+ const toReport: ShadowProjectsUpdateAll = {
318
+ [projectId]: {
319
+ appConfig: JSON.stringify(appCfg),
320
+ envVars
321
+ }
202
322
  };
203
- const topic = this.shadowTopics.projects.update;
204
- this.publisher.publish(topic, JSON.stringify(packet));
323
+ this.publisher.publish(
324
+ getShadowTopic(this.clientId, 'projects', 'update'),
325
+ JSON.stringify(
326
+ buildUpdateProjectShadowMessage({
327
+ clientId: this.clientId,
328
+ reported: toReport
329
+ })
330
+ )
331
+ );
205
332
  }
206
333
 
207
- public getShadowUpdates() {
208
- const topic = this.shadowTopics.projects.get;
209
- const packet = {
210
- clientToken: this.clientId
211
- };
212
- this.publisher.publish(topic, JSON.stringify(packet));
334
+ public getProjectShadowUpdates() {
335
+ this.publisher.publish(
336
+ getShadowTopic(this.clientId, 'projects', 'get'),
337
+ JSON.stringify(buildBaseShadowMessage(this.clientId))
338
+ );
213
339
  }
214
340
 
215
- public clearAppConfig(projectId: string) {
216
- const topic = this.shadowTopics.projects.update;
217
- // TODO: We should actually send only desired and handle the delta
218
- // to update reported
219
- const packet = {
220
- state: {
221
- desired: {
222
- [projectId]: null
223
- },
224
- reported: {
225
- [projectId]: null
226
- }
227
- },
228
- clientToken: this.clientId
229
- };
230
- this.publisher.publish(topic, JSON.stringify(packet));
341
+ public clearProjectShadow(projectId: string) {
342
+ this.publisher.publish(
343
+ getShadowTopic(this.clientId, 'projects', 'update'),
344
+ JSON.stringify(
345
+ buildUpdateProjectShadowMessage({
346
+ clientId: this.clientId,
347
+ reported: { [projectId]: null },
348
+ desired: { [projectId]: null }
349
+ })
350
+ )
351
+ );
231
352
  }
232
353
  }