@alwaysai/device-agent 0.2.0 → 1.0.0
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.
- package/LICENSE +12 -0
- package/lib/application-control/config.d.ts.map +1 -1
- package/lib/application-control/config.js +6 -1
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/index.d.ts +2 -2
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +2 -2
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/install.d.ts +2 -2
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +10 -0
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/status.d.ts +3 -3
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +4 -4
- package/lib/application-control/status.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +7 -7
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +158 -100
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +9 -9
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +45 -42
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +6 -5
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/message-builder.d.ts +7 -0
- package/lib/cloud-connection/message-builder.d.ts.map +1 -0
- package/lib/cloud-connection/message-builder.js +63 -0
- package/lib/cloud-connection/message-builder.js.map +1 -0
- package/lib/cloud-connection/messages.d.ts +5 -15
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +22 -31
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/publisher.d.ts +4 -5
- package/lib/cloud-connection/publisher.d.ts.map +1 -1
- package/lib/cloud-connection/publisher.js +12 -21
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/transaction-queue.d.ts +12 -0
- package/lib/cloud-connection/transaction-queue.d.ts.map +1 -0
- package/lib/cloud-connection/transaction-queue.js +38 -0
- package/lib/cloud-connection/transaction-queue.js.map +1 -0
- package/lib/cloud-connection/transaction-queue.test.d.ts +2 -0
- package/lib/cloud-connection/transaction-queue.test.d.ts.map +1 -0
- package/lib/cloud-connection/transaction-queue.test.js +46 -0
- package/lib/cloud-connection/transaction-queue.test.js.map +1 -0
- package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
- package/lib/local-connection/rabbitmq-connection.js +5 -1
- package/lib/local-connection/rabbitmq-connection.js.map +1 -1
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +1 -0
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts +5 -0
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +42 -1
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/status.js +1 -1
- package/lib/subcommands/app/status.js.map +1 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +9 -11
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/util/logger.d.ts.map +1 -1
- package/lib/util/logger.js +3 -1
- package/lib/util/logger.js.map +1 -1
- package/package.json +5 -4
- package/readme.md +30 -1
- package/src/application-control/config.ts +5 -1
- package/src/application-control/index.ts +2 -2
- package/src/application-control/install.ts +17 -4
- package/src/application-control/status.ts +9 -8
- package/src/cloud-connection/device-agent-cloud-connection.ts +225 -132
- package/src/cloud-connection/live-updates-handler.test.ts +6 -5
- package/src/cloud-connection/live-updates-handler.ts +90 -64
- package/src/cloud-connection/message-builder.ts +117 -0
- package/src/cloud-connection/messages.ts +27 -35
- package/src/cloud-connection/publisher.ts +17 -30
- package/src/cloud-connection/transaction-queue.test.ts +55 -0
- package/src/cloud-connection/transaction-queue.ts +40 -0
- package/src/local-connection/rabbitmq-connection.ts +5 -1
- package/src/subcommands/app/index.ts +3 -1
- package/src/subcommands/app/models.ts +44 -0
- package/src/subcommands/app/status.ts +2 -2
- package/src/subcommands/app/version.ts +16 -14
- package/src/util/logger.ts +5 -1
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AppLogsPayload,
|
|
3
|
+
ToClientMessagePayload,
|
|
4
4
|
keyMirrors,
|
|
5
|
-
|
|
5
|
+
LiveStateUpdatesTogglePayload,
|
|
6
|
+
ToClientMessage
|
|
6
7
|
} from '@alwaysai/device-agent-schemas';
|
|
7
8
|
import { getAppLogs } from '../application-control';
|
|
8
9
|
import { logger } from '../util/logger';
|
|
9
10
|
import sleep from '../util/sleep';
|
|
10
11
|
import { Publisher } from './publisher';
|
|
11
12
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
getStatusResponsePayload,
|
|
14
|
+
getAppStatePayload,
|
|
15
|
+
getDeviceStatsPayload
|
|
15
16
|
} from './messages';
|
|
16
|
-
import {
|
|
17
|
+
import { ToClientMessageTypeValue } from '@alwaysai/device-agent-schemas';
|
|
17
18
|
import { ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS } from '../environment';
|
|
19
|
+
import {
|
|
20
|
+
buildAppLogsMessage,
|
|
21
|
+
buildAppStateMessage,
|
|
22
|
+
buildDeviceStatsMessage,
|
|
23
|
+
buildStatusResponseMessage
|
|
24
|
+
} from './message-builder';
|
|
18
25
|
|
|
19
26
|
const LIVE_UPDATES_TIMEOUT = ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS
|
|
20
27
|
? parseInt(ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS)
|
|
@@ -22,22 +29,24 @@ const LIVE_UPDATES_TIMEOUT = ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS
|
|
|
22
29
|
|
|
23
30
|
export class LiveUpdatesHandler {
|
|
24
31
|
private publisher: Publisher;
|
|
32
|
+
private clientId: string;
|
|
25
33
|
|
|
26
34
|
private liveUpdatesTimeout: ReturnType<typeof setTimeout>;
|
|
27
35
|
private liveUpdatesAlive = {
|
|
28
|
-
[keyMirrors.
|
|
29
|
-
[keyMirrors.
|
|
30
|
-
[keyMirrors.
|
|
31
|
-
[keyMirrors.
|
|
36
|
+
[keyMirrors.toClientMessageType.device_stats]: false,
|
|
37
|
+
[keyMirrors.toClientMessageType.app_state]: false,
|
|
38
|
+
[keyMirrors.toClientMessageType.app_logs]: false,
|
|
39
|
+
[keyMirrors.toClientMessageType.status_response]: false
|
|
32
40
|
};
|
|
33
41
|
private liveUpdatesSleepIntervals = {
|
|
34
|
-
[keyMirrors.
|
|
35
|
-
[keyMirrors.
|
|
36
|
-
[keyMirrors.
|
|
37
|
-
[keyMirrors.
|
|
42
|
+
[keyMirrors.toClientMessageType.device_stats]: 5000,
|
|
43
|
+
[keyMirrors.toClientMessageType.app_state]: 5000,
|
|
44
|
+
[keyMirrors.toClientMessageType.app_logs]: 5000,
|
|
45
|
+
[keyMirrors.toClientMessageType.status_response]: 5000
|
|
38
46
|
};
|
|
47
|
+
|
|
39
48
|
private appLogStreams = new Set<string>();
|
|
40
|
-
private
|
|
49
|
+
private transactionStatuses = new Set<string>();
|
|
41
50
|
|
|
42
51
|
private async startAppLogStream(projectId: string) {
|
|
43
52
|
logger.info(`Starting log stream for ${projectId}`);
|
|
@@ -46,7 +55,7 @@ export class LiveUpdatesHandler {
|
|
|
46
55
|
projectId,
|
|
47
56
|
args: ['--tail', '100', '--no-log-prefix']
|
|
48
57
|
});
|
|
49
|
-
readable.on('data', (chunk: Buffer) => {
|
|
58
|
+
readable.on('data', async (chunk: Buffer) => {
|
|
50
59
|
if (!this.appLogStreams.has(projectId)) {
|
|
51
60
|
// why doesn't typescript know about this function?
|
|
52
61
|
// @ts-ignore
|
|
@@ -55,13 +64,11 @@ export class LiveUpdatesHandler {
|
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
const logStr = chunk.toString();
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
projectId,
|
|
62
|
-
logChunk: logStr
|
|
63
|
-
}
|
|
67
|
+
const payload: AppLogsPayload = {
|
|
68
|
+
projectId,
|
|
69
|
+
logChunk: logStr
|
|
64
70
|
};
|
|
71
|
+
const message = await buildAppLogsMessage(payload, this.clientId);
|
|
65
72
|
this.publisher.publishToClient(message);
|
|
66
73
|
});
|
|
67
74
|
|
|
@@ -77,18 +84,18 @@ export class LiveUpdatesHandler {
|
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
private continuePublishing(
|
|
80
|
-
flag:
|
|
81
|
-
|
|
87
|
+
flag: ToClientMessageTypeValue,
|
|
88
|
+
txId?: string
|
|
82
89
|
): boolean {
|
|
83
90
|
switch (flag) {
|
|
84
|
-
case keyMirrors.
|
|
85
|
-
case keyMirrors.
|
|
91
|
+
case keyMirrors.toClientMessageType.device_stats:
|
|
92
|
+
case keyMirrors.toClientMessageType.app_state:
|
|
86
93
|
return this.liveUpdatesAlive[flag];
|
|
87
|
-
case keyMirrors.
|
|
88
|
-
if (!
|
|
89
|
-
throw new Error(`
|
|
94
|
+
case keyMirrors.toClientMessageType.status_response: {
|
|
95
|
+
if (!txId) {
|
|
96
|
+
throw new Error(`Transaction ID not provided to continuePublishing!`);
|
|
90
97
|
}
|
|
91
|
-
return this.
|
|
98
|
+
return this.transactionStatuses.has(txId);
|
|
92
99
|
}
|
|
93
100
|
default:
|
|
94
101
|
logger.error(`Unrecognized publishable flag ${flag}`);
|
|
@@ -96,7 +103,7 @@ export class LiveUpdatesHandler {
|
|
|
96
103
|
}
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
private getLiveUpdatesInterval(flag:
|
|
106
|
+
private getLiveUpdatesInterval(flag: ToClientMessageTypeValue): number {
|
|
100
107
|
const exists = this.liveUpdatesSleepIntervals[flag];
|
|
101
108
|
if (exists) {
|
|
102
109
|
return exists;
|
|
@@ -105,12 +112,12 @@ export class LiveUpdatesHandler {
|
|
|
105
112
|
return -1;
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
private setLiveUpdates(toggles:
|
|
115
|
+
private setLiveUpdates(toggles: LiveStateUpdatesTogglePayload) {
|
|
109
116
|
if (toggles.deviceStats !== undefined) {
|
|
110
117
|
this.liveUpdatesAlive.device_stats = toggles.deviceStats;
|
|
111
118
|
}
|
|
112
119
|
if (toggles.appState !== undefined) {
|
|
113
|
-
this.liveUpdatesAlive.
|
|
120
|
+
this.liveUpdatesAlive.app_logs = toggles.appState;
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -126,20 +133,33 @@ export class LiveUpdatesHandler {
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
private async startPublishingLiveUpdates<T extends any[]>(
|
|
129
|
-
messageType:
|
|
130
|
-
|
|
136
|
+
messageType: ToClientMessageTypeValue,
|
|
137
|
+
payloadBuilderFunction: (...args: T) => Promise<ToClientMessagePayload>,
|
|
138
|
+
messageBuilderFunction: (
|
|
139
|
+
payload: ToClientMessagePayload,
|
|
140
|
+
txId: string
|
|
141
|
+
) => Promise<ToClientMessage>,
|
|
131
142
|
args: T,
|
|
132
|
-
|
|
143
|
+
txId: string
|
|
133
144
|
) {
|
|
134
145
|
logger.info(`Turned on live updates for ${messageType}`);
|
|
135
146
|
// eslint-disable-next-line no-constant-condition
|
|
136
147
|
while (true) {
|
|
137
148
|
try {
|
|
138
|
-
|
|
139
|
-
if (!this.continuePublishing(messageType, projectId)) {
|
|
149
|
+
if (!this.continuePublishing(messageType, txId)) {
|
|
140
150
|
logger.info(`Turned off live updates for ${messageType}`);
|
|
141
151
|
break;
|
|
142
152
|
}
|
|
153
|
+
const payload: ToClientMessagePayload = await payloadBuilderFunction(
|
|
154
|
+
...args
|
|
155
|
+
);
|
|
156
|
+
logger.debug(
|
|
157
|
+
`payload returned from builder: ${JSON.stringify(payload)}`
|
|
158
|
+
);
|
|
159
|
+
const message = await messageBuilderFunction(payload, txId);
|
|
160
|
+
logger.debug(
|
|
161
|
+
`message returned from builder: ${JSON.stringify(message)}`
|
|
162
|
+
);
|
|
143
163
|
this.publisher.publishToClient(message);
|
|
144
164
|
} catch (e) {
|
|
145
165
|
logger.error(
|
|
@@ -154,8 +174,9 @@ export class LiveUpdatesHandler {
|
|
|
154
174
|
Public interface
|
|
155
175
|
=================================================================*/
|
|
156
176
|
|
|
157
|
-
constructor(publisher: Publisher) {
|
|
177
|
+
constructor(publisher: Publisher, clientId: string) {
|
|
158
178
|
this.publisher = publisher;
|
|
179
|
+
this.clientId = clientId;
|
|
159
180
|
logger.debug(
|
|
160
181
|
`Toggle live updates timeout set to ${LIVE_UPDATES_TIMEOUT} ms`
|
|
161
182
|
);
|
|
@@ -173,34 +194,35 @@ export class LiveUpdatesHandler {
|
|
|
173
194
|
return this.liveUpdatesAlive.app_logs;
|
|
174
195
|
}
|
|
175
196
|
|
|
176
|
-
public async
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.liveUpdatesAlive.app_install_status = true;
|
|
182
|
-
if (!this.appInstallStatuses.has(projectId)) {
|
|
183
|
-
this.appInstallStatuses.add(projectId);
|
|
197
|
+
public async enableTransactionStatus(props: { txId: string }) {
|
|
198
|
+
const { txId } = props;
|
|
199
|
+
this.liveUpdatesAlive.status_response = true;
|
|
200
|
+
if (!this.transactionStatuses.has(txId)) {
|
|
201
|
+
this.transactionStatuses.add(txId);
|
|
184
202
|
// Don't wait for this call to finish since it loops until disabled
|
|
185
203
|
void this.startPublishingLiveUpdates(
|
|
186
|
-
keyMirrors.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
204
|
+
keyMirrors.toClientMessageType.status_response,
|
|
205
|
+
getStatusResponsePayload,
|
|
206
|
+
buildStatusResponseMessage,
|
|
207
|
+
[keyMirrors.statusResponse.in_progress, ''],
|
|
208
|
+
txId
|
|
190
209
|
);
|
|
191
210
|
}
|
|
192
211
|
}
|
|
193
212
|
|
|
194
|
-
public async
|
|
195
|
-
const {
|
|
196
|
-
this.
|
|
213
|
+
public async disableTransactionStatus(props: { txId: string }) {
|
|
214
|
+
const { txId } = props;
|
|
215
|
+
this.transactionStatuses.delete(txId);
|
|
197
216
|
|
|
198
|
-
if (this.
|
|
199
|
-
this.liveUpdatesAlive.
|
|
217
|
+
if (this.transactionStatuses.size === 0) {
|
|
218
|
+
this.liveUpdatesAlive.status_response = false;
|
|
200
219
|
}
|
|
201
220
|
}
|
|
202
221
|
|
|
203
|
-
public async handleToggles(
|
|
222
|
+
public async handleToggles(
|
|
223
|
+
toggles: LiveStateUpdatesTogglePayload,
|
|
224
|
+
txId: string
|
|
225
|
+
) {
|
|
204
226
|
const { deviceStats, appState, appLogs } = toggles;
|
|
205
227
|
this.restartLiveUpdatesTimeout();
|
|
206
228
|
|
|
@@ -210,9 +232,11 @@ export class LiveUpdatesHandler {
|
|
|
210
232
|
if (deviceStats && currentDeviceStats !== true) {
|
|
211
233
|
// Don't wait for this call to finish since it loops until disabled
|
|
212
234
|
void this.startPublishingLiveUpdates(
|
|
213
|
-
keyMirrors.
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
keyMirrors.toClientMessageType.device_stats,
|
|
236
|
+
getDeviceStatsPayload,
|
|
237
|
+
buildDeviceStatsMessage,
|
|
238
|
+
[],
|
|
239
|
+
txId
|
|
216
240
|
);
|
|
217
241
|
}
|
|
218
242
|
}
|
|
@@ -223,9 +247,11 @@ export class LiveUpdatesHandler {
|
|
|
223
247
|
if (appState && currentAppState !== true) {
|
|
224
248
|
// Don't wait for this call to finish since it loops until disabled
|
|
225
249
|
void this.startPublishingLiveUpdates(
|
|
226
|
-
keyMirrors.
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
keyMirrors.toClientMessageType.app_state,
|
|
251
|
+
getAppStatePayload,
|
|
252
|
+
buildAppStateMessage,
|
|
253
|
+
[],
|
|
254
|
+
txId
|
|
229
255
|
);
|
|
230
256
|
}
|
|
231
257
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppLogsMessage,
|
|
3
|
+
AppLogsMessageType,
|
|
4
|
+
AppLogsPayload,
|
|
5
|
+
AppStateMessage,
|
|
6
|
+
AppStateMessageType,
|
|
7
|
+
AppStatePayload,
|
|
8
|
+
BaseToClientMessage,
|
|
9
|
+
DeviceStatsMessage,
|
|
10
|
+
DeviceStatsMessageType,
|
|
11
|
+
DeviceStatsPayload,
|
|
12
|
+
SignedUrlsRequestMessage,
|
|
13
|
+
SignedUrlsRequestMessageType,
|
|
14
|
+
SignedUrlsRequestPayload,
|
|
15
|
+
StatusResponseMessage,
|
|
16
|
+
StatusResponseMessageType,
|
|
17
|
+
StatusResponsePayload,
|
|
18
|
+
generateTxId,
|
|
19
|
+
getToClientTopic,
|
|
20
|
+
keyMirrors,
|
|
21
|
+
validateToDeviceAgentMessage
|
|
22
|
+
} from '@alwaysai/device-agent-schemas';
|
|
23
|
+
import { getDeviceUuid } from '../util/get-device-id';
|
|
24
|
+
|
|
25
|
+
function buildBaseClientMessage(txId?: string): BaseToClientMessage {
|
|
26
|
+
const clientId = getDeviceUuid();
|
|
27
|
+
const baseClientMessage = {
|
|
28
|
+
timestamp: new Date().toUTCString(),
|
|
29
|
+
deviceId: clientId,
|
|
30
|
+
topic: getToClientTopic(clientId),
|
|
31
|
+
txId: txId || generateTxId()
|
|
32
|
+
};
|
|
33
|
+
return baseClientMessage;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Public Message Builders
|
|
37
|
+
|
|
38
|
+
// app logs
|
|
39
|
+
export async function buildAppLogsMessage(
|
|
40
|
+
payload: AppLogsPayload,
|
|
41
|
+
txId?: string
|
|
42
|
+
): Promise<AppLogsMessage> {
|
|
43
|
+
const baseMessage = buildBaseClientMessage(txId);
|
|
44
|
+
const messageType: AppLogsMessageType =
|
|
45
|
+
keyMirrors.toClientMessageType.app_logs;
|
|
46
|
+
const message = {
|
|
47
|
+
...baseMessage,
|
|
48
|
+
messageType,
|
|
49
|
+
payload
|
|
50
|
+
};
|
|
51
|
+
return message;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// app state
|
|
55
|
+
export async function buildAppStateMessage(
|
|
56
|
+
payload: AppStatePayload,
|
|
57
|
+
txId?: string
|
|
58
|
+
): Promise<AppStateMessage> {
|
|
59
|
+
const baseMessage = buildBaseClientMessage(txId);
|
|
60
|
+
const messageType: AppStateMessageType =
|
|
61
|
+
keyMirrors.toClientMessageType.app_state;
|
|
62
|
+
const message = {
|
|
63
|
+
...baseMessage,
|
|
64
|
+
messageType,
|
|
65
|
+
payload
|
|
66
|
+
};
|
|
67
|
+
return message;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// signed URLs
|
|
71
|
+
export async function buildSignedUrlsRequestMessage(
|
|
72
|
+
payload: SignedUrlsRequestPayload,
|
|
73
|
+
txId?: string
|
|
74
|
+
): Promise<SignedUrlsRequestMessage> {
|
|
75
|
+
const baseMessage = buildBaseClientMessage(txId);
|
|
76
|
+
const messageType: SignedUrlsRequestMessageType =
|
|
77
|
+
keyMirrors.toCloudMessageType.signed_urls_request;
|
|
78
|
+
const message = {
|
|
79
|
+
...baseMessage,
|
|
80
|
+
messageType,
|
|
81
|
+
payload
|
|
82
|
+
};
|
|
83
|
+
return message;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// status response
|
|
87
|
+
export async function buildStatusResponseMessage(
|
|
88
|
+
payload: StatusResponsePayload,
|
|
89
|
+
txId?: string
|
|
90
|
+
): Promise<StatusResponseMessage> {
|
|
91
|
+
const baseMessage = buildBaseClientMessage(txId);
|
|
92
|
+
const messageType: StatusResponseMessageType =
|
|
93
|
+
keyMirrors.toClientMessageType.status_response;
|
|
94
|
+
const message = {
|
|
95
|
+
...baseMessage,
|
|
96
|
+
messageType,
|
|
97
|
+
payload
|
|
98
|
+
};
|
|
99
|
+
const valid = validateToDeviceAgentMessage(message);
|
|
100
|
+
return message;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// device stats
|
|
104
|
+
export async function buildDeviceStatsMessage(
|
|
105
|
+
payload: DeviceStatsPayload,
|
|
106
|
+
txId?: string
|
|
107
|
+
): Promise<DeviceStatsMessage> {
|
|
108
|
+
const messageType: DeviceStatsMessageType =
|
|
109
|
+
keyMirrors.toClientMessageType.device_stats;
|
|
110
|
+
const baseMessage = buildBaseClientMessage(txId);
|
|
111
|
+
const message = {
|
|
112
|
+
...baseMessage,
|
|
113
|
+
messageType,
|
|
114
|
+
payload
|
|
115
|
+
};
|
|
116
|
+
return message;
|
|
117
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
AppState,
|
|
3
|
+
AppStatePayload,
|
|
4
|
+
DeviceStatsPayload,
|
|
5
|
+
StatusResponsePayload
|
|
5
6
|
} from '@alwaysai/device-agent-schemas';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { StatusResponseValue } from '@alwaysai/device-agent-schemas/lib/constants';
|
|
8
|
+
import { getAppState } from '../application-control';
|
|
8
9
|
import {
|
|
9
10
|
getCpuUtil,
|
|
10
11
|
getDiskUtil,
|
|
@@ -12,49 +13,40 @@ import {
|
|
|
12
13
|
} from '../device-control/device-control';
|
|
13
14
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
14
15
|
|
|
15
|
-
export async function
|
|
16
|
-
const
|
|
16
|
+
export async function getAppStatePayload(): Promise<AppStatePayload> {
|
|
17
|
+
const appState: AppState[] = [];
|
|
17
18
|
const apps = await AgentConfigFile().getApps();
|
|
18
19
|
for (const app of apps) {
|
|
19
20
|
const projectId = app.projectId;
|
|
20
|
-
const status = await
|
|
21
|
-
|
|
21
|
+
const status = await getAppState({ projectId });
|
|
22
|
+
appState.push(status);
|
|
22
23
|
}
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
appState: appStateMessage
|
|
24
|
+
const appStatePayload = {
|
|
25
|
+
appState: appState
|
|
26
26
|
};
|
|
27
|
-
return
|
|
27
|
+
return appStatePayload;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export async function
|
|
31
|
-
status:
|
|
32
|
-
message: string
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
appInstallStatus: {
|
|
38
|
-
status,
|
|
39
|
-
message,
|
|
40
|
-
appReleaseHash
|
|
41
|
-
}
|
|
30
|
+
export async function getStatusResponsePayload(
|
|
31
|
+
status: StatusResponseValue,
|
|
32
|
+
message: string
|
|
33
|
+
): Promise<StatusResponsePayload> {
|
|
34
|
+
const statusResponsePayload: StatusResponsePayload = {
|
|
35
|
+
status,
|
|
36
|
+
message
|
|
42
37
|
};
|
|
43
|
-
return
|
|
38
|
+
return statusResponsePayload;
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
export async function
|
|
41
|
+
export async function getDeviceStatsPayload(): Promise<DeviceStatsPayload> {
|
|
47
42
|
const cpuUsage = await getCpuUtil();
|
|
48
43
|
const diskUtil = await getDiskUtil();
|
|
49
44
|
const memUtil = await getMemUtil();
|
|
50
45
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
diskUtil,
|
|
56
|
-
usedMemoryPercentage: memUtil
|
|
57
|
-
}
|
|
46
|
+
const deviceStatsPayload: DeviceStatsPayload = {
|
|
47
|
+
cpuUsage,
|
|
48
|
+
diskUtil,
|
|
49
|
+
usedMemoryPercentage: memUtil
|
|
58
50
|
};
|
|
59
|
-
return
|
|
51
|
+
return deviceStatsPayload;
|
|
60
52
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { logger } from '../util/logger';
|
|
1
2
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
ToClientMessage,
|
|
4
|
+
ToCloudMessage,
|
|
5
|
+
getToClientTopic,
|
|
6
|
+
getToCloudTopic
|
|
6
7
|
} from '@alwaysai/device-agent-schemas';
|
|
7
|
-
import { logger } from '../util/logger';
|
|
8
8
|
|
|
9
9
|
export class Publisher {
|
|
10
10
|
private client: any;
|
|
@@ -15,21 +15,8 @@ export class Publisher {
|
|
|
15
15
|
constructor(client: any, clientId: string) {
|
|
16
16
|
this.client = client;
|
|
17
17
|
this.clientId = clientId;
|
|
18
|
-
this.toClientTopic =
|
|
19
|
-
this.toCloudTopic =
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private buildMessagePacket(
|
|
23
|
-
topic: string,
|
|
24
|
-
payload: DeviceAgentMessagePayload
|
|
25
|
-
): DeviceAgentMessage {
|
|
26
|
-
const packet = {
|
|
27
|
-
timestamp: new Date().toUTCString(),
|
|
28
|
-
deviceId: this.clientId,
|
|
29
|
-
topic,
|
|
30
|
-
payload
|
|
31
|
-
};
|
|
32
|
-
return packet;
|
|
18
|
+
this.toClientTopic = getToClientTopic(this.clientId);
|
|
19
|
+
this.toCloudTopic = getToCloudTopic(this.clientId);
|
|
33
20
|
}
|
|
34
21
|
|
|
35
22
|
public publish(topic: string, payload: string) {
|
|
@@ -63,23 +50,23 @@ export class Publisher {
|
|
|
63
50
|
});
|
|
64
51
|
}
|
|
65
52
|
|
|
66
|
-
public
|
|
53
|
+
public publishDeviceAgentMessage(
|
|
67
54
|
topic: string,
|
|
68
|
-
|
|
55
|
+
message: ToClientMessage | ToCloudMessage
|
|
69
56
|
) {
|
|
70
|
-
const
|
|
71
|
-
const packetStr = JSON.stringify(packet);
|
|
57
|
+
const messageStr = JSON.stringify(message);
|
|
72
58
|
logger.debug(
|
|
73
|
-
`Publishing message:\n${JSON.stringify({ topic,
|
|
59
|
+
`Publishing message:\n${JSON.stringify({ topic, message }, null, 2)}`
|
|
74
60
|
);
|
|
75
|
-
this.publish(topic,
|
|
61
|
+
this.publish(topic, messageStr);
|
|
76
62
|
}
|
|
77
63
|
|
|
78
|
-
public publishToClient(
|
|
79
|
-
this.
|
|
64
|
+
public publishToClient(message: ToClientMessage) {
|
|
65
|
+
this.publishDeviceAgentMessage(this.toClientTopic, message);
|
|
80
66
|
}
|
|
81
67
|
|
|
82
|
-
public publishToCloud(
|
|
83
|
-
|
|
68
|
+
public publishToCloud(message: ToCloudMessage) {
|
|
69
|
+
// Can edit topic field in message here if we want
|
|
70
|
+
this.publishDeviceAgentMessage(this.toCloudTopic, message);
|
|
84
71
|
}
|
|
85
72
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { generateTxId } from '@alwaysai/device-agent-schemas';
|
|
2
|
+
import { TransactionQueue } from './transaction-queue';
|
|
3
|
+
|
|
4
|
+
describe('Test Transaction Queue', () => {
|
|
5
|
+
let txnQueue: TransactionQueue;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
txnQueue = new TransactionQueue();
|
|
9
|
+
jest.clearAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('Add new to transaction queue', async () => {
|
|
13
|
+
const txId = generateTxId();
|
|
14
|
+
const projectId = '1241fad-agah-gfadg-2352dgzg';
|
|
15
|
+
txnQueue.addTxIdToQueue(txId, projectId);
|
|
16
|
+
expect(txnQueue.checkTxnInQueue(txId)).toBeTruthy();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('Attempt to add existing to transaction queue, results in failure', async () => {
|
|
20
|
+
const txId = generateTxId();
|
|
21
|
+
const projectId = '1241fad-agah-gfadg-2352dgzg';
|
|
22
|
+
txnQueue.addTxIdToQueue(txId, projectId);
|
|
23
|
+
expect(() => txnQueue.addTxIdToQueue(txId, projectId)).toThrow(
|
|
24
|
+
`txId ${txId} is already added to the queue!`
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('Check current transaction', async () => {
|
|
29
|
+
const txId = generateTxId();
|
|
30
|
+
const projectId = '1241fad-agah-gfadg-2352dgzg';
|
|
31
|
+
txnQueue.addTxIdToQueue(txId, projectId);
|
|
32
|
+
|
|
33
|
+
const txId2 = generateTxId();
|
|
34
|
+
const projectId2 = '1241faegta-agah-gagadg-2352dgzg';
|
|
35
|
+
txnQueue.addTxIdToQueue(txId2, projectId2);
|
|
36
|
+
expect(txnQueue.getCurrentTxId()).toEqual(txId);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('Test remove transaction from queue', async () => {
|
|
40
|
+
const txId = generateTxId();
|
|
41
|
+
const projectId = '1241fad-agah-gfadg-2352dgzg';
|
|
42
|
+
txnQueue.addTxIdToQueue(txId, projectId);
|
|
43
|
+
txnQueue.completeCurrentTxn();
|
|
44
|
+
|
|
45
|
+
expect(txnQueue.getCurrentTxId()).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('Test get project ID for a given txId', async () => {
|
|
49
|
+
const txId = generateTxId();
|
|
50
|
+
const projectId = '1241fad-agah-gfadg-2352dgzg';
|
|
51
|
+
txnQueue.addTxIdToQueue(txId, projectId);
|
|
52
|
+
|
|
53
|
+
expect(txnQueue.getProjectIdForTxnId(txId)).toEqual(projectId);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class TransactionQueue {
|
|
2
|
+
private transactionList: string[];
|
|
3
|
+
private txIdToProjectIdMap: { [txId: string]: string } = {};
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
this.transactionList = [];
|
|
7
|
+
this.txIdToProjectIdMap = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public addTxIdToQueue(txId: string, projectId: string) {
|
|
11
|
+
if (!this.checkTxnInQueue(txId)) {
|
|
12
|
+
this.transactionList.push(txId);
|
|
13
|
+
this.txIdToProjectIdMap[txId] = projectId;
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error(`txId ${txId} is already added to the queue!`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public getCurrentTxId() {
|
|
20
|
+
return this.transactionList[0];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public checkTxnInQueue(txId: string) {
|
|
24
|
+
return this.transactionList.includes(txId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public getProjectIdForTxnId(txId: string) {
|
|
28
|
+
return this.txIdToProjectIdMap[txId];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public completeTxn(txId: string) {
|
|
32
|
+
delete this.txIdToProjectIdMap[txId];
|
|
33
|
+
this.transactionList.splice(this.transactionList.indexOf(txId), 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public completeCurrentTxn() {
|
|
37
|
+
delete this.txIdToProjectIdMap[this.transactionList[0]];
|
|
38
|
+
this.transactionList.shift();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -57,7 +57,11 @@ export async function writeRabbitMQDockerComposeFile() {
|
|
|
57
57
|
ports: [`${LOCAL_CONNECTION_PORT}:${LOCAL_CONNECTION_PORT}`],
|
|
58
58
|
hostname: 'my-rabbit',
|
|
59
59
|
restart: 'on-failure',
|
|
60
|
-
environment: [`RABBITMQ_NODE_PORT=${LOCAL_CONNECTION_PORT}`]
|
|
60
|
+
environment: [`RABBITMQ_NODE_PORT=${LOCAL_CONNECTION_PORT}`],
|
|
61
|
+
volumes: [
|
|
62
|
+
'./rabbitmq/:/var/lib/rabbitmq/',
|
|
63
|
+
'./rabbitmq/log/:/var/log/rabbitmq/'
|
|
64
|
+
]
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
};
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
addModelCliLeaf,
|
|
6
6
|
removeModelCliLeaf,
|
|
7
7
|
replaceModelsCliLeaf,
|
|
8
|
-
updateModelsCliLeaf
|
|
8
|
+
updateModelsCliLeaf,
|
|
9
|
+
installModelCliLeaf
|
|
9
10
|
} from './models';
|
|
10
11
|
import {
|
|
11
12
|
getAppStatusCliLeaf,
|
|
@@ -39,6 +40,7 @@ export const appCliBranch = CliBranch({
|
|
|
39
40
|
addModelCliLeaf,
|
|
40
41
|
removeModelCliLeaf,
|
|
41
42
|
replaceModelsCliLeaf,
|
|
43
|
+
installModelCliLeaf,
|
|
42
44
|
updateModelsCliLeaf,
|
|
43
45
|
getAllEnvsCliLeaf,
|
|
44
46
|
setEnvCliLeaf,
|