@alwaysai/device-agent 0.0.20 → 0.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 (96) hide show
  1. package/lib/application-control/config.d.ts +0 -1
  2. package/lib/application-control/config.d.ts.map +1 -1
  3. package/lib/application-control/config.js +15 -29
  4. package/lib/application-control/config.js.map +1 -1
  5. package/lib/application-control/environment-variables.d.ts +7 -3
  6. package/lib/application-control/environment-variables.d.ts.map +1 -1
  7. package/lib/application-control/environment-variables.js +71 -35
  8. package/lib/application-control/environment-variables.js.map +1 -1
  9. package/lib/application-control/environment-variables.test.d.ts +2 -0
  10. package/lib/application-control/environment-variables.test.d.ts.map +1 -0
  11. package/lib/application-control/environment-variables.test.js +163 -0
  12. package/lib/application-control/environment-variables.test.js.map +1 -0
  13. package/lib/application-control/index.d.ts +3 -3
  14. package/lib/application-control/index.d.ts.map +1 -1
  15. package/lib/application-control/index.js +1 -3
  16. package/lib/application-control/index.js.map +1 -1
  17. package/lib/application-control/models.d.ts +0 -1
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +12 -26
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/status.d.ts +3 -0
  22. package/lib/application-control/status.d.ts.map +1 -1
  23. package/lib/application-control/status.js +19 -1
  24. package/lib/application-control/status.js.map +1 -1
  25. package/lib/application-control/utils.d.ts.map +1 -1
  26. package/lib/application-control/utils.js +2 -2
  27. package/lib/application-control/utils.js.map +1 -1
  28. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +6 -3
  29. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  30. package/lib/cloud-connection/device-agent-cloud-connection.js +201 -151
  31. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  32. package/lib/cloud-connection/live-updates-handler.d.ts +3 -0
  33. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  34. package/lib/cloud-connection/live-updates-handler.js +23 -7
  35. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  36. package/lib/cloud-connection/live-updates-handler.test.d.ts +2 -0
  37. package/lib/cloud-connection/live-updates-handler.test.d.ts.map +1 -0
  38. package/lib/cloud-connection/live-updates-handler.test.js +57 -0
  39. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -0
  40. package/lib/cloud-connection/shadow-handler.d.ts +11 -3
  41. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  42. package/lib/cloud-connection/shadow-handler.js +22 -7
  43. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  44. package/lib/cloud-connection/shadow-handler.test.js +313 -228
  45. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  46. package/lib/cloud-connection/shadow.js +1 -1
  47. package/lib/cloud-connection/shadow.js.map +1 -1
  48. package/lib/environment.d.ts +1 -0
  49. package/lib/environment.d.ts.map +1 -1
  50. package/lib/environment.js +2 -1
  51. package/lib/environment.js.map +1 -1
  52. package/lib/infrastructure/agent-config.d.ts +3 -1
  53. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  54. package/lib/subcommands/app/env-vars.d.ts +1 -1
  55. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  56. package/lib/subcommands/app/env-vars.js +32 -5
  57. package/lib/subcommands/app/env-vars.js.map +1 -1
  58. package/lib/subcommands/app/index.d.ts.map +1 -1
  59. package/lib/subcommands/app/index.js +4 -1
  60. package/lib/subcommands/app/index.js.map +1 -1
  61. package/lib/subcommands/app/models.d.ts.map +1 -1
  62. package/lib/subcommands/app/models.js +6 -1
  63. package/lib/subcommands/app/models.js.map +1 -1
  64. package/lib/subcommands/app/shadow.d.ts +7 -0
  65. package/lib/subcommands/app/shadow.d.ts.map +1 -0
  66. package/lib/subcommands/app/shadow.js +48 -0
  67. package/lib/subcommands/app/shadow.js.map +1 -0
  68. package/lib/subcommands/app/version.js +2 -2
  69. package/lib/subcommands/app/version.js.map +1 -1
  70. package/lib/util/cloud-mode-ready.d.ts +2 -0
  71. package/lib/util/cloud-mode-ready.d.ts.map +1 -0
  72. package/lib/util/cloud-mode-ready.js +22 -0
  73. package/lib/util/cloud-mode-ready.js.map +1 -0
  74. package/package.json +1 -1
  75. package/readme.md +140 -22
  76. package/src/application-control/config.ts +30 -31
  77. package/src/application-control/environment-variables.test.ts +171 -0
  78. package/src/application-control/environment-variables.ts +102 -43
  79. package/src/application-control/index.ts +3 -9
  80. package/src/application-control/models.ts +14 -29
  81. package/src/application-control/status.ts +20 -0
  82. package/src/application-control/utils.ts +4 -2
  83. package/src/cloud-connection/device-agent-cloud-connection.ts +220 -155
  84. package/src/cloud-connection/live-updates-handler.test.ts +68 -0
  85. package/src/cloud-connection/live-updates-handler.ts +30 -7
  86. package/src/cloud-connection/shadow-handler.test.ts +329 -239
  87. package/src/cloud-connection/shadow-handler.ts +38 -12
  88. package/src/cloud-connection/shadow.ts +1 -1
  89. package/src/environment.ts +2 -0
  90. package/src/infrastructure/agent-config.ts +1 -1
  91. package/src/subcommands/app/env-vars.ts +38 -8
  92. package/src/subcommands/app/index.ts +4 -1
  93. package/src/subcommands/app/models.ts +10 -1
  94. package/src/subcommands/app/shadow.ts +48 -0
  95. package/src/subcommands/app/version.ts +2 -2
  96. package/src/util/cloud-mode-ready.ts +23 -0
package/readme.md CHANGED
@@ -36,7 +36,7 @@ and add the following line to the end of the file:
36
36
 
37
37
  On the target device, run:
38
38
 
39
- ```
39
+ ```bash
40
40
  $ curl -fsSL https://artifacts.alwaysai.co/device-agent/install-device-agent.sh | sudo -E bash -
41
41
  ```
42
42
 
@@ -52,7 +52,7 @@ Provisioning the device performs the following:
52
52
 
53
53
  Run the following command on the target device to provision it:
54
54
 
55
- ```
55
+ ```bash
56
56
  $ curl -fsSL https://artifacts.alwaysai.co/device-agent/provision.sh | bash -s -- --email <email> --password <password> [--device-name <device_name>]
57
57
  ```
58
58
 
@@ -63,6 +63,8 @@ Where:
63
63
  devices page of the alwaysAI Dashboard. If a device name is not provided, one
64
64
  will be generated for you and logged to the console for reference.
65
65
 
66
+ **Important note**: If your password contains one or more special characters and you receive an error message that your password does not match your username but you are sure that it is correctly entered, please try enclosing your password with double quotes and re-running the provisioning command. You can also reset your password at https://console.alwaysai.co/dashboard by logging out, navigating to the sign in page, and clicking '`Forgot Password?`'.
67
+
66
68
  Confirm the Device Agent is running with the following command:
67
69
 
68
70
  ```bash
@@ -74,7 +76,7 @@ $ pm2 list
74
76
  └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
75
77
  ```
76
78
 
77
- To restart the Device Agent run the following command:
79
+ To restart and update the Device Agent run the following command:
78
80
 
79
81
  ```bash
80
82
  $ pm2 restart aai-agent
@@ -91,7 +93,7 @@ Use --update-env to update environment variables
91
93
  If you'd like to only provision the device, but not start the Device Agent in
92
94
  the background (skip step 3), run with the `--provision-only` flag:
93
95
 
94
- ```
96
+ ```bash
95
97
  $ curl -fsSL https://artifacts.alwaysai.co/device-agent/provision.sh | bash -s -- --email <email> --password <password> [--device-name <device_name>] --provision-only
96
98
  ```
97
99
 
@@ -116,11 +118,129 @@ project page of the alwaysAI Dashboard as well!
116
118
 
117
119
  Now you can deploy to your device from the alwaysAI Dashboard.
118
120
 
121
+ ## Enable Analytics through the alwaysAI Device Agent
122
+
123
+ ### Configure the Device Agent
124
+ You can send information from your device to the alwaysAI cloud securely using
125
+ the Device Agent. These instructions assume you have provisioned your device
126
+ using the default script parameters and have the Device agent running (i.e. the
127
+ provisioning script was not run with the `--provision-only` flag set).
128
+
129
+ First, the Device Agent must be configured to enable Analytics Pass-through support. Confirm that the `ALWAYSAI_ANALYTICS_PASSTHROUGH` environment variable is set:
130
+
131
+ ```bash
132
+ $ pm2 env 0 | grep ALWAYSAI
133
+ ALWAYSAI_ANALYTICS_PASSTHROUGH: 1
134
+ ALWAYSAI_LOG_TO_CONSOLE:
135
+ ALWAYSAI_LOG_LEVEL: debug
136
+ ALWAYSAI_DEVICE_AGENT_MODE: cloud
137
+ ```
138
+
139
+ Then confirm the RabbitMQ container is up and running:
140
+
141
+ ```bash
142
+ $ docker ps
143
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
144
+ 596157124a4b rabbitmq:3.11 "docker-entrypoint.s…" 32 minutes ago Up 21 minutes 4369/tcp, 5671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:5672->5672/tcp alwaysAIRabbitMQContainer
145
+ ```
146
+
147
+ ### Configure the Application
148
+
149
+ From the application side, the `ALWAYSAI_CONNECT_TO_DEVICE_AGENT` environment
150
+ variable must be set. There are a few options:
151
+
152
+ #### In the app source
153
+
154
+ In your `app.py` file, make sure the top of your import statements looks like
155
+ this:
156
+
157
+ ```python
158
+ import os
159
+ os.environ['ALWAYSAI_CONNECT_TO_DEVICE_AGENT']='1'
160
+ import edgeiq
161
+ ```
162
+ You can import any other modules after `edgeiq`, but the other orders must be
163
+ maintained.
164
+
165
+ #### In the Dockerfile
166
+
167
+ ```bash
168
+ ENV ALWAYSAI_CONNECT_TO_DEVICE_AGENT=1
169
+ ```
170
+
171
+ #### In a docker-compose.yaml
172
+
173
+ Add the following section to the service for your app:
174
+
175
+ ```bash
176
+ environment:
177
+ - ALWAYSAI_CONNECT_TO_DEVICE_AGENT=1
178
+ ```
179
+
180
+ ### Enable Publishing to Cloud
181
+
182
+ To enable cloud publishing for your application you can either run
183
+ `aai app enable-cloud-publish`, or update your `alwaysai.app.json` by adding the
184
+ following component in addition to any `models` or `scripts` components:
185
+
186
+ ```json
187
+ "analytics: {
188
+ "enable_cloud_publish": true
189
+ }
190
+ ```
191
+
192
+ So, a valid `alwaysai.app.json` for publishing analytics might look like this:
193
+
194
+ ```json
195
+ {
196
+ "scripts": {
197
+ "start": "python app.py"
198
+ },
199
+ "models": {
200
+ "alwaysai/mobilenet_ssd": 4
201
+ },
202
+ "analytics": {
203
+ "enable_cloud_publish": true
204
+ }
205
+ }
206
+ ```
207
+
208
+ Finally, add a command to publish analytics to your `app.py`. Whenever this
209
+ command is used, it will publish the contents of the analytics message to the
210
+ cloud -- you can choose to call this every frame, or once every event
211
+ occurrence, however your app is designed. Each core computer vision service has
212
+ it's own `publish_analytics` method, which is called on the instance of the
213
+ class. Or, you can published a JSON-serializable message with
214
+ `edgeiq.publish_analytics()`. For instance, to publish object detection results
215
+ you can use:
216
+
217
+ ```python
218
+ results = obj_detect.detect_objects(frame, confidence_level=.5)
219
+ try:
220
+ obj_detect.publish_analytics(results, tag=frame_count)
221
+ except edgeiq.PublishError as e:
222
+ print(e)
223
+ ```
224
+
225
+ Finally, make sure to save all of your changes, and publish your application with
226
+
227
+ ```bash
228
+ $ aai app publish
229
+ ```
230
+
231
+ You can test that analytics are being viewed with `wscat`, using your application's project ID and a secure API key:
232
+
233
+ ```bash
234
+ $ wscat -c "wss://analytics.alwaysai.co?projectId=[PROJECT_ID]&apiKey=[API_KEY]"
235
+ ```
236
+
237
+ Please contact the alwaysAI team if you need a secure API key.
238
+
119
239
  ## The alwaysAI Device Agent Command Line interface
120
240
 
121
241
  The Device Agent can also be used directly on the device with it's command line interface.
122
242
 
123
- ```
243
+ ```bash
124
244
  $ aai-agent --help
125
245
  Usage: aai-agent <subcommand> ...
126
246
 
@@ -140,8 +260,6 @@ Subcommands:
140
260
  app show-models : Show the application models
141
261
  app add-model : Add a model to an alwaysAI app
142
262
  app remove-model : Remove a model from an alwaysAI app
143
- app replace-models : Replace all models of an alwaysAI app with new models
144
- app update-models : Update all models for an alwaysAI app
145
263
  app get-all-envs : Get environment variables for an application
146
264
  app set-env : Set environment variables for a service
147
265
  device init : Initialize device
@@ -152,8 +270,8 @@ Subcommands:
152
270
 
153
271
  To see the output logs, run the following command:
154
272
 
155
- ```
156
- export ALWAYSAI_LOG_TO_CONSOLE=1
273
+ ```bash
274
+ $ export ALWAYSAI_LOG_TO_CONSOLE=1
157
275
  ```
158
276
 
159
277
  ### Install the application on the device
@@ -161,7 +279,7 @@ export ALWAYSAI_LOG_TO_CONSOLE=1
161
279
  Now you can install the application on the device using the device agent. Run
162
280
  the following on the device where the Device Agent is installed:
163
281
 
164
- ```
282
+ ```bash
165
283
  $ aai-agent app install --project <project_id> --release <release_hash>
166
284
  ```
167
285
 
@@ -170,27 +288,27 @@ $ aai-agent app install --project <project_id> --release <release_hash>
170
288
  Run the following commands on the device where the Device Agent is installed:
171
289
 
172
290
  Get application status:
173
- ```
291
+ ```bash
174
292
  $ aai-agent app status --project <project_id>
175
293
  ```
176
294
 
177
295
  Start the application:
178
- ```
296
+ ```bash
179
297
  $ aai-agent app start --project <project_id>
180
298
  ```
181
299
 
182
300
  Show the application logs:
183
- ```
301
+ ```bash
184
302
  $ aai-agent app logs --project <project_id>
185
303
  ```
186
304
 
187
305
  Stop the application:
188
- ```
306
+ ```bash
189
307
  $ aai-agent app stop --project <project_id>
190
308
  ```
191
309
 
192
310
  Uninstall the application:
193
- ```
311
+ ```bash
194
312
  $ aai-agent app uninstall --project <project_id>
195
313
  ```
196
314
 
@@ -204,7 +322,7 @@ If a new version of a model is published for an existing model ID, and an
204
322
  application is already configured to be using that model ID, updating to the
205
323
  new model can simply be done with:
206
324
 
207
- ```
325
+ ```bash
208
326
  $ aai-agent app stop --project <project_id>
209
327
  $ aai-agent app update-models --project <project_id>
210
328
  $ aai-agent app start --project <project_id>
@@ -215,7 +333,7 @@ $ aai-agent app start --project <project_id>
215
333
  If you'd like to install an entirely new model to an application, replacing the
216
334
  model the app was originally configured with, run the following command:
217
335
 
218
- ```
336
+ ```bash
219
337
  $ aai-agent app stop --project <project_id>
220
338
  $ aai-agent app replace-models --project <project_id> --models <model_id_1> [<model_id_2> ...]
221
339
  $ aai-agent app start --project <project_id>
@@ -225,7 +343,7 @@ If you plan on using this method, you can make your application source model ID
225
343
  agnostic by providing the model ID to edgeIQ in the following way:
226
344
 
227
345
  Select the first model in the config list:
228
- ```
346
+ ```python
229
347
  obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
230
348
  ```
231
349
 
@@ -233,13 +351,13 @@ obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
233
351
 
234
352
  To download a model package from the alwaysAI cloud and unpack to a specific directory, run:
235
353
 
236
- ```
354
+ ```bash
237
355
  $ aai-agent get-model-package <model ID> [--path <destination path>]
238
356
  ```
239
357
 
240
358
  For example, to download `alwaysai/yolo_v3` to `~/alwaysai` run:
241
359
 
242
- ```
360
+ ```bash
243
361
  $ aai-agent get-model-package alwaysai/yolo_v3 --path ~/alwaysai
244
362
  ```
245
363
 
@@ -251,13 +369,13 @@ Once the command completes, you'll see the model package in the
251
369
  The Device Agent enables you to set and update environment variables for all
252
370
  services or a single service of you application.
253
371
 
254
- ```
372
+ ```bash
255
373
  $ aai-agent app set-env <key=val> --project <project_id> [--service <service>]
256
374
  ```
257
375
 
258
376
  For example, to set the following environment variable for the `alwaysai`
259
377
  service run the following command on the device:
260
378
 
261
- ```
379
+ ```bash
262
380
  $ aai-agent app set-env TEST_ENV=1 --project <project_id> --service alwaysai
263
381
  ```
@@ -1,21 +1,24 @@
1
1
  import { AgentConfigFile } from '../infrastructure/agent-config';
2
2
  import { parse, stringify } from 'yaml';
3
- import { buildApp, getAppDir } from './utils';
3
+ import {
4
+ buildApp,
5
+ getAppDir,
6
+ requireAppInstalled,
7
+ requireAppReady
8
+ } from './utils';
4
9
  import { JsSpawner } from 'alwaysai/lib/util';
5
10
  import compose from 'docker-compose';
6
11
  import { assign, merge } from 'lodash';
7
12
  import { AppJsonFile } from 'alwaysai/lib/core/app';
8
13
  import { AppConfig } from '@alwaysai/app-configuration-schemas';
9
- import { restartApp } from './status';
14
+ import { isAppStarted, restartApp } from './status';
10
15
  import { logger } from '../util/logger';
11
16
 
12
17
  export async function readAppCfgFile(props: {
13
18
  projectId: string;
14
19
  }): Promise<AppConfig> {
15
20
  const { projectId } = props;
16
- if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
17
- throw new Error(`App ${projectId} is not present!`);
18
- }
21
+ await requireAppInstalled({ projectId });
19
22
  const appDir = getAppDir(projectId);
20
23
  const appJson = AppJsonFile(appDir);
21
24
  if (!appJson.exists()) {
@@ -37,9 +40,6 @@ export async function writeAppCfgFile(props: {
37
40
  appCfg: AppConfig;
38
41
  }) {
39
42
  const { projectId, appCfg } = props;
40
- if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
41
- throw new Error(`App ${projectId} is not present!`);
42
- }
43
43
  const appDir = getAppDir(projectId);
44
44
  const appJson = AppJsonFile(appDir);
45
45
  try {
@@ -65,25 +65,22 @@ export async function updateAppCfgFile(props: {
65
65
 
66
66
  export async function updateAppCfg(props: {
67
67
  projectId: string;
68
- appReleaseHash: string;
69
68
  newAppCfg: AppConfig;
70
69
  }) {
71
- const { projectId, appReleaseHash, newAppCfg } = props;
72
- logger.info(`Updating app config for ${projectId}:${appReleaseHash}.`);
70
+ const { projectId, newAppCfg } = props;
71
+ logger.info(`Updating app config for ${projectId}`);
73
72
  const appDir = getAppDir(projectId);
74
73
 
75
- if (await AgentConfigFile().isAppPresent({ projectId })) {
76
- if (!(await AgentConfigFile().isAppReady({ projectId }))) {
77
- throw new Error('Application already has installation in progress!');
78
- }
79
- logger.info('Application is already installed, updating');
80
- await AgentConfigFile().setAppInstalling({
81
- projectId,
82
- version: appReleaseHash
83
- });
84
- } else {
85
- throw new Error('Application is not installed!');
86
- }
74
+ await requireAppReady({ projectId });
75
+
76
+ const appReleaseHash = await AgentConfigFile().getAppVersion({
77
+ projectId
78
+ });
79
+
80
+ await AgentConfigFile().setAppInstalling({
81
+ projectId,
82
+ version: appReleaseHash
83
+ });
87
84
 
88
85
  await writeAppCfgFile({ projectId, appCfg: newAppCfg });
89
86
  await buildApp({ appDir });
@@ -93,16 +90,21 @@ export async function updateAppCfg(props: {
93
90
  version: appReleaseHash
94
91
  });
95
92
 
96
- await restartApp({ projectId });
93
+ if (await isAppStarted({ projectId })) {
94
+ await restartApp({ projectId });
95
+ }
97
96
 
98
- logger.info(`Updated app config and rebuilt ${projectId}:${appReleaseHash}.`);
97
+ logger.info(
98
+ `Updated app config and rebuilt ${projectId}: ${JSON.stringify(
99
+ newAppCfg,
100
+ null,
101
+ 2
102
+ )}`
103
+ );
99
104
  }
100
105
 
101
106
  export async function readDockerCompose(props: { projectId: string }) {
102
107
  const { projectId } = props;
103
- if (!(await AgentConfigFile().isAppReady({ projectId }))) {
104
- throw new Error(`App ${projectId} is not ready!`);
105
- }
106
108
  const appDir = getAppDir(projectId);
107
109
  const spawner = JsSpawner({ path: appDir });
108
110
  const composeContents = await spawner.readFile('docker-compose.yaml');
@@ -115,9 +117,6 @@ export async function writeDockerCompose(props: {
115
117
  dockerCompose: any;
116
118
  }) {
117
119
  const { projectId, dockerCompose } = props;
118
- if (!(await AgentConfigFile().isAppReady({ projectId }))) {
119
- throw new Error(`App ${projectId} is not ready!`);
120
- }
121
120
  const appDir = getAppDir(projectId);
122
121
  const spawner = JsSpawner({ path: appDir });
123
122
  const composeOutput = stringify(dockerCompose);
@@ -0,0 +1,171 @@
1
+ import { AgentConfigFile } from '../infrastructure/agent-config';
2
+ import { readDockerCompose, writeDockerCompose } from './config';
3
+ import { getAllEnvs, setEnv } from './environment-variables';
4
+ import { isAppStarted, restartApp } from './status';
5
+ import { buildApp, requireAppReady } from './utils';
6
+
7
+ jest.mock('./config');
8
+
9
+ jest.mock('./utils');
10
+ jest.mocked(requireAppReady).mockResolvedValue();
11
+ jest.mocked(buildApp).mockResolvedValue();
12
+
13
+ jest.mock('./status');
14
+ jest.mocked(isAppStarted).mockResolvedValue(true);
15
+ jest.mocked(restartApp).mockResolvedValue();
16
+
17
+ jest.mock('../infrastructure/agent-config');
18
+ const mockSetAppInstalling = jest.fn().mockResolvedValue({});
19
+ const mockSetAppInstalled = jest.fn().mockResolvedValue({});
20
+ const mockGetAppVersion = jest.fn().mockResolvedValue('test-version');
21
+ jest.mocked(AgentConfigFile as jest.Mock).mockReturnValue({
22
+ setAppInstalling: mockSetAppInstalling,
23
+ setAppInstalled: mockSetAppInstalled,
24
+ getAppVersion: mockGetAppVersion
25
+ });
26
+ const projectId1 = 'test-project';
27
+
28
+ describe('Test environment variable get and set', () => {
29
+ beforeEach(() => {
30
+ jest.clearAllMocks();
31
+ });
32
+ describe('Test getAllEnvs()', () => {
33
+ test('get all envs with one service and no envs', async () => {
34
+ const compose = {
35
+ services: {
36
+ alwaysai: {}
37
+ }
38
+ };
39
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
40
+
41
+ const envVars = await getAllEnvs({ projectId: projectId1 });
42
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
43
+ projectId: projectId1
44
+ });
45
+ expect(envVars).toEqual({
46
+ alwaysai: {}
47
+ });
48
+ });
49
+ // TODO: Add test with env file
50
+ test('get all envs with one service and two envs', async () => {
51
+ const compose = {
52
+ services: {
53
+ alwaysai: {
54
+ environment: ['TEST=1', 'TEST2=2']
55
+ }
56
+ }
57
+ };
58
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
59
+
60
+ const envVars = await getAllEnvs({ projectId: projectId1 });
61
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
62
+ projectId: projectId1
63
+ });
64
+ expect(envVars).toEqual({
65
+ alwaysai: { TEST: '1', TEST2: '2' }
66
+ });
67
+ });
68
+ test('get all envs with two services and envs', async () => {
69
+ const compose = {
70
+ services: {
71
+ alwaysai: {
72
+ environment: ['TEST=3', 'TEST2=4']
73
+ },
74
+ edgeiq: {
75
+ environment: ['TEST3=5', 'TEST4=6']
76
+ }
77
+ }
78
+ };
79
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
80
+
81
+ const envVars = await getAllEnvs({ projectId: projectId1 });
82
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
83
+ projectId: projectId1
84
+ });
85
+ expect(envVars).toEqual({
86
+ alwaysai: { TEST: '3', TEST2: '4' },
87
+ edgeiq: { TEST3: '5', TEST4: '6' }
88
+ });
89
+ });
90
+ });
91
+ describe('Test setEnv()', () => {
92
+ test('Set one env', async () => {
93
+ const compose = {
94
+ services: {
95
+ alwaysai: {}
96
+ }
97
+ };
98
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
99
+
100
+ const envVars = { alwaysai: { TEST: '1' } };
101
+ await setEnv({ projectId: projectId1, envVars });
102
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
103
+ projectId: projectId1
104
+ });
105
+ expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
106
+ projectId: projectId1,
107
+ dockerCompose: {
108
+ services: {
109
+ alwaysai: {
110
+ environment: ['TEST=1']
111
+ }
112
+ }
113
+ }
114
+ });
115
+ });
116
+ test('Update one env', async () => {
117
+ const environment = ['TEST1=1', 'TEST2=2'];
118
+ const compose = {
119
+ services: {
120
+ alwaysai: { environment }
121
+ }
122
+ };
123
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
124
+
125
+ const envVars = { alwaysai: { TEST1: '2' } };
126
+ await setEnv({ projectId: projectId1, envVars });
127
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
128
+ projectId: projectId1
129
+ });
130
+ expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
131
+ projectId: projectId1,
132
+ dockerCompose: {
133
+ services: {
134
+ alwaysai: {
135
+ environment: ['TEST1=1', 'TEST2=2', 'TEST1=2']
136
+ }
137
+ }
138
+ }
139
+ });
140
+ });
141
+ test('Update one env with two services', async () => {
142
+ const environment = ['TEST1=3', 'TEST2=4'];
143
+ const compose = {
144
+ services: {
145
+ alwaysai: { environment },
146
+ other: { environment: ['OTHER_TEST=1'] }
147
+ }
148
+ };
149
+ jest.mocked(readDockerCompose).mockResolvedValue(compose);
150
+
151
+ const envVars = { alwaysai: { TEST1: '2' } };
152
+ await setEnv({ projectId: projectId1, envVars });
153
+ expect(jest.mocked(readDockerCompose)).toBeCalledWith({
154
+ projectId: projectId1
155
+ });
156
+ expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
157
+ projectId: projectId1,
158
+ dockerCompose: {
159
+ services: {
160
+ alwaysai: {
161
+ environment: ['TEST1=3', 'TEST2=4', 'TEST1=2']
162
+ },
163
+ other: {
164
+ environment: ['OTHER_TEST=1']
165
+ }
166
+ }
167
+ }
168
+ });
169
+ });
170
+ });
171
+ });