@capawesome/cli 4.1.0-dev.f5ef8ba.1771047437 → 4.2.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/commands/apps/builds/cancel.js +6 -41
  3. package/dist/commands/apps/builds/create.js +10 -55
  4. package/dist/commands/apps/builds/download.js +6 -41
  5. package/dist/commands/apps/builds/logs.js +6 -41
  6. package/dist/commands/apps/channels/create.js +6 -36
  7. package/dist/commands/apps/channels/create.test.js +5 -15
  8. package/dist/commands/apps/channels/delete.js +6 -36
  9. package/dist/commands/apps/channels/delete.test.js +6 -18
  10. package/dist/commands/apps/channels/get.js +3 -7
  11. package/dist/commands/apps/channels/list.js +6 -36
  12. package/dist/commands/apps/channels/list.test.js +4 -1
  13. package/dist/commands/apps/channels/pause.js +6 -40
  14. package/dist/commands/apps/channels/resume.js +6 -40
  15. package/dist/commands/apps/channels/update.js +6 -36
  16. package/dist/commands/apps/channels/update.test.js +14 -31
  17. package/dist/commands/apps/create.js +5 -23
  18. package/dist/commands/apps/create.test.js +13 -31
  19. package/dist/commands/apps/delete.js +6 -35
  20. package/dist/commands/apps/delete.test.js +11 -35
  21. package/dist/commands/apps/deployments/cancel.js +6 -41
  22. package/dist/commands/apps/deployments/create.js +6 -41
  23. package/dist/commands/apps/deployments/logs.js +6 -41
  24. package/dist/commands/apps/devices/delete.js +6 -36
  25. package/dist/commands/apps/devices/delete.test.js +14 -36
  26. package/dist/commands/apps/environments/create.js +6 -36
  27. package/dist/commands/apps/environments/delete.js +6 -36
  28. package/dist/commands/apps/environments/list.js +6 -36
  29. package/dist/commands/apps/environments/set.js +6 -36
  30. package/dist/commands/apps/environments/unset.js +6 -36
  31. package/dist/commands/apps/liveupdates/register.js +6 -40
  32. package/dist/commands/apps/liveupdates/register.test.js +2 -1
  33. package/dist/commands/apps/liveupdates/rollback.js +6 -41
  34. package/dist/commands/apps/liveupdates/rollout.js +6 -41
  35. package/dist/commands/apps/liveupdates/set-native-versions.js +3 -7
  36. package/dist/commands/apps/liveupdates/upload.js +6 -40
  37. package/dist/commands/apps/liveupdates/upload.test.js +2 -1
  38. package/dist/commands/organizations/create.js +3 -7
  39. package/dist/commands/organizations/create.test.js +3 -1
  40. package/dist/utils/auth.js +27 -0
  41. package/dist/utils/prompt.js +56 -11
  42. package/dist/utils/signature.js +2 -1
  43. package/package.json +6 -2
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_API_BASE_URL } from '../../config/consts.js';
2
2
  import authorizationService from '../../services/authorization-service.js';
3
- import { prompt } from '../../utils/prompt.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../utils/prompt.js';
4
4
  import userConfig from '../../utils/user-config.js';
5
5
  import consola from 'consola';
6
6
  import nock from 'nock';
@@ -17,6 +17,8 @@ vi.mock('@/utils/environment.js', () => ({
17
17
  describe('apps-delete', () => {
18
18
  const mockUserConfig = vi.mocked(userConfig);
19
19
  const mockPrompt = vi.mocked(prompt);
20
+ const mockPromptOrganizationSelection = vi.mocked(promptOrganizationSelection);
21
+ const mockPromptAppSelection = vi.mocked(promptAppSelection);
20
22
  const mockConsola = vi.mocked(consola);
21
23
  const mockAuthorizationService = vi.mocked(authorizationService);
22
24
  beforeEach(() => {
@@ -62,51 +64,25 @@ describe('apps-delete', () => {
62
64
  const orgId = 'org-1';
63
65
  const appId = 'app-1';
64
66
  const testToken = 'test-token';
65
- const organization = { id: orgId, name: 'Org 1' };
66
- const app = { id: appId, name: 'App 1' };
67
67
  const options = {};
68
- const orgsScope = nock(DEFAULT_API_BASE_URL)
69
- .get('/v1/organizations')
70
- .matchHeader('Authorization', `Bearer ${testToken}`)
71
- .reply(200, [organization]);
72
- const appsScope = nock(DEFAULT_API_BASE_URL)
73
- .get('/v1/apps')
74
- .query({ organizationId: orgId })
75
- .matchHeader('Authorization', `Bearer ${testToken}`)
76
- .reply(200, [app]);
77
68
  const deleteScope = nock(DEFAULT_API_BASE_URL)
78
69
  .delete(`/v1/apps/${appId}`)
79
70
  .matchHeader('Authorization', `Bearer ${testToken}`)
80
71
  .reply(200);
81
- mockPrompt
82
- .mockResolvedValueOnce(orgId) // organization selection
83
- .mockResolvedValueOnce(appId) // app selection
84
- .mockResolvedValueOnce(true); // confirmation
72
+ mockPromptOrganizationSelection.mockResolvedValueOnce(orgId);
73
+ mockPromptAppSelection.mockResolvedValueOnce(appId);
74
+ mockPrompt.mockResolvedValueOnce(true); // confirmation
85
75
  await deleteAppCommand.action(options, undefined);
86
- expect(orgsScope.isDone()).toBe(true);
87
- expect(appsScope.isDone()).toBe(true);
88
76
  expect(deleteScope.isDone()).toBe(true);
89
77
  expect(mockConsola.success).toHaveBeenCalledWith('App deleted successfully.');
90
78
  });
91
- it('should handle error when no organizations exist', async () => {
92
- const testToken = 'test-token';
79
+ it('should exit when promptOrganizationSelection exits', async () => {
93
80
  const options = {};
94
- const scope = nock(DEFAULT_API_BASE_URL)
95
- .get('/v1/organizations')
96
- .matchHeader('Authorization', `Bearer ${testToken}`)
97
- .reply(200, []);
98
- const exitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => {
99
- throw new Error(`process.exit called with code ${code}`);
81
+ mockPromptOrganizationSelection.mockImplementation(() => {
82
+ process.exit(1);
83
+ return Promise.resolve('');
100
84
  });
101
- try {
102
- await deleteAppCommand.action(options, undefined);
103
- }
104
- catch (error) {
105
- expect(error.message).toBe('process.exit called with code 1');
106
- }
107
- expect(scope.isDone()).toBe(true);
108
- expect(mockConsola.error).toHaveBeenCalledWith('You must create an organization before deleting an app.');
109
- expect(exitSpy).toHaveBeenCalledWith(1);
85
+ await expect(deleteAppCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
110
86
  });
111
87
  it('should handle API error during deletion', async () => {
112
88
  const appId = 'app-123';
@@ -1,10 +1,8 @@
1
1
  import appDeploymentsService from '../../../services/app-deployments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
2
  import jobsService from '../../../services/jobs.js';
5
- import organizationsService from '../../../services/organizations.js';
3
+ import { withAuth } from '../../../utils/auth.js';
6
4
  import { isInteractive } from '../../../utils/environment.js';
7
- import { prompt } from '../../../utils/prompt.js';
5
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
10
8
  import { z } from 'zod';
@@ -24,49 +22,16 @@ export default defineCommand({
24
22
  .optional()
25
23
  .describe('Deployment ID to cancel.'),
26
24
  })),
27
- action: async (options) => {
25
+ action: withAuth(async (options) => {
28
26
  let { appId, deploymentId } = options;
29
- // Check if the user is logged in
30
- if (!authorizationService.hasAuthorizationToken()) {
31
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
32
- process.exit(1);
33
- }
34
27
  // Prompt for app ID if not provided
35
28
  if (!appId) {
36
29
  if (!isInteractive()) {
37
30
  consola.error('You must provide an app ID when running in non-interactive environment.');
38
31
  process.exit(1);
39
32
  }
40
- const organizations = await organizationsService.findAll();
41
- if (organizations.length === 0) {
42
- consola.error('You must create an organization before canceling a deployment.');
43
- process.exit(1);
44
- }
45
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
46
- const organizationId = await prompt('Select the organization of the app for which you want to cancel a deployment.', {
47
- type: 'select',
48
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
49
- });
50
- if (!organizationId) {
51
- consola.error('You must select the organization of an app for which you want to cancel a deployment.');
52
- process.exit(1);
53
- }
54
- const apps = await appsService.findAll({
55
- organizationId,
56
- });
57
- if (apps.length === 0) {
58
- consola.error('You must create an app before canceling a deployment.');
59
- process.exit(1);
60
- }
61
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
62
- appId = await prompt('Which app do you want to cancel a deployment for:', {
63
- type: 'select',
64
- options: apps.map((app) => ({ label: app.name, value: app.id })),
65
- });
66
- if (!appId) {
67
- consola.error('You must select an app to cancel a deployment for.');
68
- process.exit(1);
69
- }
33
+ const organizationId = await promptOrganizationSelection();
34
+ appId = await promptAppSelection(organizationId);
70
35
  }
71
36
  // Prompt for deployment ID if not provided
72
37
  if (!deploymentId) {
@@ -107,5 +72,5 @@ export default defineCommand({
107
72
  },
108
73
  });
109
74
  consola.success('Deployment successfully canceled.');
110
- },
75
+ }),
111
76
  });
@@ -2,12 +2,10 @@ import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
2
2
  import appBuildsService from '../../../services/app-builds.js';
3
3
  import appDeploymentsService from '../../../services/app-deployments.js';
4
4
  import appDestinationsService from '../../../services/app-destinations.js';
5
- import appsService from '../../../services/apps.js';
6
- import authorizationService from '../../../services/authorization-service.js';
7
- import organizationsService from '../../../services/organizations.js';
8
5
  import { unescapeAnsi } from '../../../utils/ansi.js';
6
+ import { withAuth } from '../../../utils/auth.js';
9
7
  import { isInteractive } from '../../../utils/environment.js';
10
- import { prompt } from '../../../utils/prompt.js';
8
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
11
9
  import { wait } from '../../../utils/wait.js';
12
10
  import { defineCommand, defineOptions } from '@robingenz/zli';
13
11
  import consola from 'consola';
@@ -35,49 +33,16 @@ export default defineCommand({
35
33
  .optional()
36
34
  .describe('Exit immediately after creating the deployment without waiting for completion.'),
37
35
  })),
38
- action: async (options) => {
36
+ action: withAuth(async (options) => {
39
37
  let { appId, buildId, buildNumber, channel, destination } = options;
40
- // Check if the user is logged in
41
- if (!authorizationService.hasAuthorizationToken()) {
42
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
43
- process.exit(1);
44
- }
45
38
  // Prompt for app ID if not provided
46
39
  if (!appId) {
47
40
  if (!isInteractive()) {
48
41
  consola.error('You must provide an app ID when running in non-interactive environment.');
49
42
  process.exit(1);
50
43
  }
51
- const organizations = await organizationsService.findAll();
52
- if (organizations.length === 0) {
53
- consola.error('You must create an organization before creating a deployment.');
54
- process.exit(1);
55
- }
56
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
57
- const organizationId = await prompt('Select the organization of the app for which you want to create a deployment.', {
58
- type: 'select',
59
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
60
- });
61
- if (!organizationId) {
62
- consola.error('You must select the organization of an app for which you want to create a deployment.');
63
- process.exit(1);
64
- }
65
- const apps = await appsService.findAll({
66
- organizationId,
67
- });
68
- if (apps.length === 0) {
69
- consola.error('You must create an app before creating a deployment.');
70
- process.exit(1);
71
- }
72
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
73
- appId = await prompt('Which app do you want to create a deployment for:', {
74
- type: 'select',
75
- options: apps.map((app) => ({ label: app.name, value: app.id })),
76
- });
77
- if (!appId) {
78
- consola.error('You must select an app to create a deployment for.');
79
- process.exit(1);
80
- }
44
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
45
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
81
46
  }
82
47
  // Convert build number to build ID if provided
83
48
  if (!buildId && buildNumber) {
@@ -269,5 +234,5 @@ export default defineCommand({
269
234
  }
270
235
  }
271
236
  }
272
- },
237
+ }),
273
238
  });
@@ -1,9 +1,7 @@
1
1
  import appDeploymentsService from '../../../services/app-deployments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
2
  import { unescapeAnsi } from '../../../utils/ansi.js';
6
- import { prompt } from '../../../utils/prompt.js';
3
+ import { withAuth } from '../../../utils/auth.js';
4
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { wait } from '../../../utils/wait.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
@@ -25,49 +23,16 @@ export default defineCommand({
25
23
  .optional()
26
24
  .describe('Deployment ID to view logs.'),
27
25
  })),
28
- action: async (options) => {
26
+ action: withAuth(async (options) => {
29
27
  let { appId, deploymentId } = options;
30
- // Check if the user is logged in
31
- if (!authorizationService.hasAuthorizationToken()) {
32
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
33
- process.exit(1);
34
- }
35
28
  // Prompt for app ID if not provided
36
29
  if (!appId) {
37
30
  if (!isInteractive()) {
38
31
  consola.error('You must provide an app ID when running in non-interactive environment.');
39
32
  process.exit(1);
40
33
  }
41
- const organizations = await organizationsService.findAll();
42
- if (organizations.length === 0) {
43
- consola.error('You must create an organization before viewing deployment logs.');
44
- process.exit(1);
45
- }
46
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
47
- const organizationId = await prompt('Select the organization that contains the app whose deployment logs you want to view?', {
48
- type: 'select',
49
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
50
- });
51
- if (!organizationId) {
52
- consola.error('You must select the organization containing the app whose deployment logs you want to view.');
53
- process.exit(1);
54
- }
55
- const apps = await appsService.findAll({
56
- organizationId,
57
- });
58
- if (apps.length === 0) {
59
- consola.error('You must create an app before viewing deployment logs.');
60
- process.exit(1);
61
- }
62
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
63
- appId = await prompt('Which app do you want to view deployment logs for?', {
64
- type: 'select',
65
- options: apps.map((app) => ({ label: app.name, value: app.id })),
66
- });
67
- if (!appId) {
68
- consola.error('You must select an app to view its deployment logs.');
69
- process.exit(1);
70
- }
34
+ const organizationId = await promptOrganizationSelection();
35
+ appId = await promptAppSelection(organizationId);
71
36
  }
72
37
  // Prompt for deployment ID if not provided
73
38
  if (!deploymentId) {
@@ -125,5 +90,5 @@ export default defineCommand({
125
90
  }
126
91
  }
127
92
  }
128
- },
93
+ }),
129
94
  });
@@ -1,8 +1,6 @@
1
1
  import appDevicesService from '../../../services/app-devices.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
- import { prompt } from '../../../utils/prompt.js';
2
+ import { withAuth } from '../../../utils/auth.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
6
4
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
5
  import consola from 'consola';
8
6
  import { isInteractive } from '../../../utils/environment.js';
@@ -14,44 +12,16 @@ export default defineCommand({
14
12
  deviceId: z.string().optional().describe('ID of the device.'),
15
13
  yes: z.boolean().optional().describe('Skip confirmation prompt.'),
16
14
  }), { y: 'yes' }),
17
- action: async (options, args) => {
15
+ action: withAuth(async (options, args) => {
18
16
  let { appId, deviceId } = options;
19
- if (!authorizationService.hasAuthorizationToken()) {
20
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
21
- process.exit(1);
22
- }
23
17
  // Prompt for app ID if not provided
24
18
  if (!appId) {
25
19
  if (!isInteractive()) {
26
20
  consola.error('You must provide an app ID when running in non-interactive environment.');
27
21
  process.exit(1);
28
22
  }
29
- const organizations = await organizationsService.findAll();
30
- if (organizations.length === 0) {
31
- consola.error('You must create an organization before deleting a device.');
32
- process.exit(1);
33
- }
34
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
35
- const organizationId = await prompt('Select the organization of the app from which you want to delete a device.', {
36
- type: 'select',
37
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
38
- });
39
- if (!organizationId) {
40
- consola.error('You must select the organization of an app from which you want to delete a device.');
41
- process.exit(1);
42
- }
43
- const apps = await appsService.findAll({
44
- organizationId,
45
- });
46
- if (!apps.length) {
47
- consola.error('You must create an app before deleting a device.');
48
- process.exit(1);
49
- }
50
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
51
- appId = await prompt('Which app do you want to delete the device from?', {
52
- type: 'select',
53
- options: apps.map((app) => ({ label: app.name, value: app.id })),
54
- });
23
+ const organizationId = await promptOrganizationSelection();
24
+ appId = await promptAppSelection(organizationId);
55
25
  }
56
26
  // Prompt for device ID if not provided
57
27
  if (!deviceId) {
@@ -78,5 +48,5 @@ export default defineCommand({
78
48
  deviceId,
79
49
  });
80
50
  consola.success('Device deleted successfully.');
81
- },
51
+ }),
82
52
  });
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
2
2
  import authorizationService from '../../../services/authorization-service.js';
3
- import { prompt } from '../../../utils/prompt.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
4
4
  import userConfig from '../../../utils/user-config.js';
5
5
  import consola from 'consola';
6
6
  import nock from 'nock';
@@ -17,6 +17,8 @@ vi.mock('@/utils/environment.js', () => ({
17
17
  describe('apps-devices-delete', () => {
18
18
  const mockUserConfig = vi.mocked(userConfig);
19
19
  const mockPrompt = vi.mocked(prompt);
20
+ const mockPromptOrganizationSelection = vi.mocked(promptOrganizationSelection);
21
+ const mockPromptAppSelection = vi.mocked(promptAppSelection);
20
22
  const mockConsola = vi.mocked(consola);
21
23
  const mockAuthorizationService = vi.mocked(authorizationService);
22
24
  beforeEach(() => {
@@ -24,7 +26,9 @@ describe('apps-devices-delete', () => {
24
26
  mockUserConfig.read.mockReturnValue({ token: 'test-token' });
25
27
  mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
26
28
  mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
27
- vi.spyOn(process, 'exit').mockImplementation(() => undefined);
29
+ vi.spyOn(process, 'exit').mockImplementation((code) => {
30
+ throw new Error(`Process exited with code ${code}`);
31
+ });
28
32
  });
29
33
  afterEach(() => {
30
34
  nock.cleanAll();
@@ -63,29 +67,15 @@ describe('apps-devices-delete', () => {
63
67
  const appId = 'app-1';
64
68
  const deviceId = 'device-456';
65
69
  const testToken = 'test-token';
66
- const organization = { id: orgId, name: 'Org 1' };
67
- const app = { id: appId, name: 'App 1' };
68
70
  const options = { deviceId };
69
- const orgsScope = nock(DEFAULT_API_BASE_URL)
70
- .get('/v1/organizations')
71
- .matchHeader('Authorization', `Bearer ${testToken}`)
72
- .reply(200, [organization]);
73
- const appsScope = nock(DEFAULT_API_BASE_URL)
74
- .get('/v1/apps')
75
- .query({ organizationId: orgId })
76
- .matchHeader('Authorization', `Bearer ${testToken}`)
77
- .reply(200, [app]);
78
71
  const deleteScope = nock(DEFAULT_API_BASE_URL)
79
72
  .delete(`/v1/apps/${appId}/devices/${deviceId}`)
80
73
  .matchHeader('Authorization', `Bearer ${testToken}`)
81
74
  .reply(200);
82
- mockPrompt
83
- .mockResolvedValueOnce(orgId) // organization selection
84
- .mockResolvedValueOnce(appId) // app selection
85
- .mockResolvedValueOnce(true); // confirmation
75
+ mockPromptOrganizationSelection.mockResolvedValueOnce(orgId);
76
+ mockPromptAppSelection.mockResolvedValueOnce(appId);
77
+ mockPrompt.mockResolvedValueOnce(true); // confirmation
86
78
  await deleteDeviceCommand.action(options, undefined);
87
- expect(orgsScope.isDone()).toBe(true);
88
- expect(appsScope.isDone()).toBe(true);
89
79
  expect(deleteScope.isDone()).toBe(true);
90
80
  expect(mockConsola.success).toHaveBeenCalledWith('Device deleted successfully.');
91
81
  });
@@ -106,25 +96,13 @@ describe('apps-devices-delete', () => {
106
96
  expect(mockPrompt).toHaveBeenCalledWith('Enter the device ID:', { type: 'text' });
107
97
  expect(mockConsola.success).toHaveBeenCalledWith('Device deleted successfully.');
108
98
  });
109
- it('should handle error when no organizations exist', async () => {
110
- const testToken = 'test-token';
99
+ it('should exit when promptOrganizationSelection exits', async () => {
111
100
  const options = {};
112
- const scope = nock(DEFAULT_API_BASE_URL)
113
- .get('/v1/organizations')
114
- .matchHeader('Authorization', `Bearer ${testToken}`)
115
- .reply(200, []);
116
- const exitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => {
117
- throw new Error(`process.exit called with code ${code}`);
101
+ mockPromptOrganizationSelection.mockImplementation(() => {
102
+ process.exit(1);
103
+ return Promise.resolve('');
118
104
  });
119
- try {
120
- await deleteDeviceCommand.action(options, undefined);
121
- }
122
- catch (error) {
123
- expect(error.message).toBe('process.exit called with code 1');
124
- }
125
- expect(scope.isDone()).toBe(true);
126
- expect(mockConsola.error).toHaveBeenCalledWith('You must create an organization before deleting a device.');
127
- expect(exitSpy).toHaveBeenCalledWith(1);
105
+ await expect(deleteDeviceCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
128
106
  });
129
107
  it('should handle API error during deletion', async () => {
130
108
  const appId = 'app-123';
@@ -1,9 +1,7 @@
1
1
  import appEnvironmentsService from '../../../services/app-environments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
- import { prompt } from '../../../utils/prompt.js';
4
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
6
  import consola from 'consola';
9
7
  import { z } from 'zod';
@@ -13,43 +11,15 @@ export default defineCommand({
13
11
  appId: z.string().optional().describe('ID of the app.'),
14
12
  name: z.string().optional().describe('Name of the environment.'),
15
13
  })),
16
- action: async (options, args) => {
14
+ action: withAuth(async (options, args) => {
17
15
  let { appId, name } = options;
18
- if (!authorizationService.hasAuthorizationToken()) {
19
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
20
- process.exit(1);
21
- }
22
16
  if (!appId) {
23
17
  if (!isInteractive()) {
24
18
  consola.error('You must provide an app ID when running in non-interactive environment.');
25
19
  process.exit(1);
26
20
  }
27
- const organizations = await organizationsService.findAll();
28
- if (organizations.length === 0) {
29
- consola.error('You must create an organization before creating an environment.');
30
- process.exit(1);
31
- }
32
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
33
- const organizationId = await prompt('Select the organization of the app for which you want to create an environment.', {
34
- type: 'select',
35
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
36
- });
37
- if (!organizationId) {
38
- consola.error('You must select the organization of an app for which you want to create an environment.');
39
- process.exit(1);
40
- }
41
- const apps = await appsService.findAll({
42
- organizationId,
43
- });
44
- if (!apps.length) {
45
- consola.error('You must create an app before creating an environment.');
46
- process.exit(1);
47
- }
48
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
49
- appId = await prompt('Which app do you want to create the environment for?', {
50
- type: 'select',
51
- options: apps.map((app) => ({ label: app.name, value: app.id })),
52
- });
21
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
22
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
53
23
  }
54
24
  if (!name) {
55
25
  if (!isInteractive()) {
@@ -64,5 +34,5 @@ export default defineCommand({
64
34
  });
65
35
  consola.info(`Environment ID: ${response.id}`);
66
36
  consola.success('Environment created successfully.');
67
- },
37
+ }),
68
38
  });
@@ -1,9 +1,7 @@
1
1
  import appEnvironmentsService from '../../../services/app-environments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
- import { prompt } from '../../../utils/prompt.js';
4
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
6
  import consola from 'consola';
9
7
  import { z } from 'zod';
@@ -15,43 +13,15 @@ export default defineCommand({
15
13
  name: z.string().optional().describe('Name of the environment. Either the ID or name must be provided.'),
16
14
  yes: z.boolean().optional().describe('Skip confirmation prompt.'),
17
15
  }), { y: 'yes' }),
18
- action: async (options, args) => {
16
+ action: withAuth(async (options, args) => {
19
17
  let { appId, environmentId, name } = options;
20
- if (!authorizationService.hasAuthorizationToken()) {
21
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
22
- process.exit(1);
23
- }
24
18
  if (!appId) {
25
19
  if (!isInteractive()) {
26
20
  consola.error('You must provide an app ID when running in non-interactive environment.');
27
21
  process.exit(1);
28
22
  }
29
- const organizations = await organizationsService.findAll();
30
- if (organizations.length === 0) {
31
- consola.error('You must create an organization before deleting an environment.');
32
- process.exit(1);
33
- }
34
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
35
- const organizationId = await prompt('Select the organization of the app from which you want to delete an environment.', {
36
- type: 'select',
37
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
38
- });
39
- if (!organizationId) {
40
- consola.error('You must select the organization of an app from which you want to delete an environment.');
41
- process.exit(1);
42
- }
43
- const apps = await appsService.findAll({
44
- organizationId,
45
- });
46
- if (!apps.length) {
47
- consola.error('You must create an app before deleting an environment.');
48
- process.exit(1);
49
- }
50
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
51
- appId = await prompt('Which app do you want to delete the environment from?', {
52
- type: 'select',
53
- options: apps.map((app) => ({ label: app.name, value: app.id })),
54
- });
23
+ const organizationId = await promptOrganizationSelection();
24
+ appId = await promptAppSelection(organizationId);
55
25
  }
56
26
  if (!environmentId && !name) {
57
27
  if (!isInteractive()) {
@@ -84,5 +54,5 @@ export default defineCommand({
84
54
  name,
85
55
  });
86
56
  consola.success('Environment deleted successfully.');
87
- },
57
+ }),
88
58
  });
@@ -1,9 +1,7 @@
1
1
  import appEnvironmentsService from '../../../services/app-environments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
- import { prompt } from '../../../utils/prompt.js';
4
+ import { promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
6
  import consola from 'consola';
9
7
  import { z } from 'zod';
@@ -15,43 +13,15 @@ export default defineCommand({
15
13
  limit: z.coerce.number().optional().describe('Limit for pagination.'),
16
14
  offset: z.coerce.number().optional().describe('Offset for pagination.'),
17
15
  })),
18
- action: async (options, args) => {
16
+ action: withAuth(async (options, args) => {
19
17
  let { appId, json, limit, offset } = options;
20
- if (!authorizationService.hasAuthorizationToken()) {
21
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
22
- process.exit(1);
23
- }
24
18
  if (!appId) {
25
19
  if (!isInteractive()) {
26
20
  consola.error('You must provide an app ID when running in non-interactive environment.');
27
21
  process.exit(1);
28
22
  }
29
- const organizations = await organizationsService.findAll();
30
- if (organizations.length === 0) {
31
- consola.error('You must create an organization before listing environments.');
32
- process.exit(1);
33
- }
34
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
35
- const organizationId = await prompt('Select the organization of the app for which you want to list environments.', {
36
- type: 'select',
37
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
38
- });
39
- if (!organizationId) {
40
- consola.error('You must select the organization of an app for which you want to list environments.');
41
- process.exit(1);
42
- }
43
- const apps = await appsService.findAll({
44
- organizationId,
45
- });
46
- if (!apps.length) {
47
- consola.error('You must create an app before listing environments.');
48
- process.exit(1);
49
- }
50
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
51
- appId = await prompt('Which app do you want to list the environments for?', {
52
- type: 'select',
53
- options: apps.map((app) => ({ label: app.name, value: app.id })),
54
- });
23
+ const organizationId = await promptOrganizationSelection();
24
+ appId = await promptAppSelection(organizationId);
55
25
  }
56
26
  const environments = await appEnvironmentsService.findAll({
57
27
  appId,
@@ -65,5 +35,5 @@ export default defineCommand({
65
35
  console.table(environments);
66
36
  consola.success('Environments retrieved successfully.');
67
37
  }
68
- },
38
+ }),
69
39
  });