clever-tools 3.14.0 → 4.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/README.md +130 -37
- package/bin/clever.js +1427 -718
- package/package.json +61 -77
- package/src/clever-client/auth-bridge.js +9 -12
- package/src/clever-client/ng.js +1 -1
- package/src/clever-client/operators.js +7 -7
- package/src/command-options.js +6 -6
- package/src/command-promise-handler.js +6 -6
- package/src/commands/accesslogs.js +38 -29
- package/src/commands/activity.js +62 -40
- package/src/commands/addon.js +65 -69
- package/src/commands/applications.js +33 -31
- package/src/commands/cancel-deploy.js +7 -7
- package/src/commands/config.js +9 -7
- package/src/commands/console.js +3 -3
- package/src/commands/create.js +32 -31
- package/src/commands/curl.js +14 -36
- package/src/commands/database.js +10 -30
- package/src/commands/delete.js +8 -8
- package/src/commands/deploy.js +60 -46
- package/src/commands/diag.js +27 -25
- package/src/commands/domain.js +89 -91
- package/src/commands/drain.js +26 -14
- package/src/commands/emails.js +24 -28
- package/src/commands/env.js +18 -18
- package/src/commands/features.js +12 -18
- package/src/commands/keycloak.js +11 -11
- package/src/commands/kv.js +13 -13
- package/src/commands/link.js +9 -8
- package/src/commands/login.js +19 -27
- package/src/commands/logout.js +1 -1
- package/src/commands/logs.js +20 -12
- package/src/commands/makeDefault.js +5 -5
- package/src/commands/matomo.js +7 -7
- package/src/commands/metabase.js +9 -9
- package/src/commands/ng.js +32 -32
- package/src/commands/notify-email.js +11 -15
- package/src/commands/open.js +1 -1
- package/src/commands/otoroshi.js +11 -11
- package/src/commands/profile.js +8 -8
- package/src/commands/published-config.js +11 -11
- package/src/commands/restart.js +15 -8
- package/src/commands/scale.js +25 -16
- package/src/commands/service.js +18 -9
- package/src/commands/ssh-keys.js +25 -29
- package/src/commands/ssh.js +1 -2
- package/src/commands/status.js +31 -22
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +27 -26
- package/src/commands/tokens.js +44 -43
- package/src/commands/unlink.js +7 -5
- package/src/commands/version.js +2 -4
- package/src/commands/webhooks.js +8 -10
- package/src/experimental-features.js +7 -5
- package/src/format-table.js +46 -15
- package/src/initial-setup.js +2 -32
- package/src/initial-update-notifier.js +18 -0
- package/src/lib/has-param.js +16 -0
- package/src/lib/ng-print.js +21 -24
- package/src/lib/operator-commands.js +58 -53
- package/src/lib/prompts.js +5 -5
- package/src/lib/slugify.js +1 -1
- package/src/logger.js +24 -22
- package/src/models/activity.js +3 -3
- package/src/models/addon.js +52 -53
- package/src/models/app_configuration.js +34 -32
- package/src/models/application.js +90 -77
- package/src/models/application_configuration.js +19 -27
- package/src/models/configuration.js +30 -32
- package/src/models/deployments.js +9 -19
- package/src/models/domain.js +10 -9
- package/src/models/drain.js +16 -15
- package/src/models/exit-strategy-option.js +15 -5
- package/src/models/fs-utils.js +4 -3
- package/src/models/git.js +26 -32
- package/src/models/ids-resolver.js +37 -41
- package/src/models/isomorphic-http-with-agent.js +3 -3
- package/src/models/json-array.js +5 -6
- package/src/models/log-v4.js +44 -52
- package/src/models/log.js +40 -45
- package/src/models/namespaces.js +4 -4
- package/src/models/ng-resources.js +49 -39
- package/src/models/ng.js +41 -40
- package/src/models/node-dns-resolver.js +3 -3
- package/src/models/notification.js +7 -10
- package/src/models/operator.js +8 -10
- package/src/models/organisation.js +5 -8
- package/src/models/send-to-api.js +18 -11
- package/src/models/user.js +6 -5
- package/src/models/utils.js +5 -8
- package/src/models/variables.js +18 -20
- package/src/parsers.js +34 -34
- package/src/load-package-json.cjs +0 -5
package/src/models/addon.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
create as createAddon,
|
|
3
|
+
get as getAddon,
|
|
4
|
+
getAll as getAllAddons,
|
|
5
|
+
getAllEnvVars,
|
|
6
|
+
remove as removeAddon,
|
|
7
|
+
update as updateAddon,
|
|
8
|
+
} from '@clevercloud/client/esm/api/v2/addon.js';
|
|
9
|
+
import { getAllLinkedAddons, linkAddon, unlinkAddon } from '@clevercloud/client/esm/api/v2/application.js';
|
|
4
10
|
import { getAllAddonProviders } from '@clevercloud/client/esm/api/v2/product.js';
|
|
5
11
|
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
6
12
|
import { getAddonProvider } from '@clevercloud/client/esm/api/v4/addon-providers.js';
|
|
7
|
-
|
|
13
|
+
import cliparse from 'cliparse';
|
|
8
14
|
import { confirm } from '../lib/prompts.js';
|
|
9
15
|
import { Logger } from '../logger.js';
|
|
10
|
-
import { sendToApi } from '../models/send-to-api.js';
|
|
11
16
|
import { resolveOwnerId } from './ids-resolver.js';
|
|
12
|
-
import {
|
|
17
|
+
import { sendToApi } from './send-to-api.js';
|
|
13
18
|
|
|
14
|
-
export function listProviders
|
|
19
|
+
export function listProviders() {
|
|
15
20
|
return getAllAddonProviders({}).then(sendToApi);
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
export async function getProvider
|
|
23
|
+
export async function getProvider(providerName) {
|
|
19
24
|
const providers = await listProviders();
|
|
20
25
|
const provider = providers.find((p) => p.id === providerName);
|
|
21
26
|
if (provider == null) {
|
|
@@ -24,8 +29,9 @@ export async function getProvider (providerName) {
|
|
|
24
29
|
return provider;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
export function getProviderInfos
|
|
28
|
-
return getAddonProvider({ providerId: providerName })
|
|
32
|
+
export function getProviderInfos(providerName) {
|
|
33
|
+
return getAddonProvider({ providerId: providerName })
|
|
34
|
+
.then(sendToApi)
|
|
29
35
|
.catch(() => {
|
|
30
36
|
// An error can occur because the add-on api doesn't implement this endpoint yet
|
|
31
37
|
// This is fine, just ignore it
|
|
@@ -34,7 +40,7 @@ export function getProviderInfos (providerName) {
|
|
|
34
40
|
});
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
export async function list
|
|
43
|
+
export async function list(ownerId, appId, showAll) {
|
|
38
44
|
const allAddons = await getAllAddons({ id: ownerId }).then(sendToApi);
|
|
39
45
|
|
|
40
46
|
if (appId == null) {
|
|
@@ -54,7 +60,7 @@ export async function list (ownerId, appId, showAll) {
|
|
|
54
60
|
});
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
function validateAddonVersionAndOptions
|
|
63
|
+
function validateAddonVersionAndOptions(region, version, addonOptions, providerInfos, planType) {
|
|
58
64
|
if (providerInfos != null) {
|
|
59
65
|
if (version != null) {
|
|
60
66
|
const type = planType.value.toLowerCase();
|
|
@@ -62,16 +68,18 @@ function validateAddonVersionAndOptions (region, version, addonOptions, provider
|
|
|
62
68
|
const cluster = providerInfos.clusters.find(({ zone }) => zone === region);
|
|
63
69
|
if (cluster == null) {
|
|
64
70
|
throw new Error(`Can't find cluster for region ${region}`);
|
|
71
|
+
} else if (cluster.version !== version) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Invalid version ${version}, selected shared cluster only supports version ${cluster.version}`,
|
|
74
|
+
);
|
|
65
75
|
}
|
|
66
|
-
|
|
67
|
-
throw new Error(`Invalid version ${version}, selected shared cluster only supports version ${cluster.version}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else if (type === 'dedicated') {
|
|
76
|
+
} else if (type === 'dedicated') {
|
|
71
77
|
const availableVersions = Object.keys(providerInfos.dedicated);
|
|
72
78
|
const hasVersion = availableVersions.find((availableVersion) => availableVersion === version);
|
|
73
79
|
if (hasVersion == null) {
|
|
74
|
-
throw new Error(
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Invalid version ${addonOptions.version}, available versions are: ${availableVersions.join(', ')}`,
|
|
82
|
+
);
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
}
|
|
@@ -90,8 +98,7 @@ function validateAddonVersionAndOptions (region, version, addonOptions, provider
|
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
availableOptions = cluster.features;
|
|
93
|
-
}
|
|
94
|
-
else if (type === 'dedicated') {
|
|
101
|
+
} else if (type === 'dedicated') {
|
|
95
102
|
availableOptions = providerInfos.dedicated[chosenVersion].features;
|
|
96
103
|
}
|
|
97
104
|
|
|
@@ -102,8 +109,7 @@ function validateAddonVersionAndOptions (region, version, addonOptions, provider
|
|
|
102
109
|
let availableOptionsError = null;
|
|
103
110
|
if (optionNames.length > 0) {
|
|
104
111
|
availableOptionsError = `Available options are: ${optionNames}.`;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
112
|
+
} else {
|
|
107
113
|
availableOptionsError = 'No options are available for this plan.';
|
|
108
114
|
}
|
|
109
115
|
|
|
@@ -116,17 +122,15 @@ function validateAddonVersionAndOptions (region, version, addonOptions, provider
|
|
|
116
122
|
version: chosenVersion,
|
|
117
123
|
...addonOptions,
|
|
118
124
|
};
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
125
|
+
} else {
|
|
121
126
|
if (version != null) {
|
|
122
|
-
throw new Error(
|
|
127
|
+
throw new Error("You provided a version for an add-on that doesn't support choosing the version.");
|
|
123
128
|
}
|
|
124
129
|
return {};
|
|
125
130
|
}
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
export async function create
|
|
129
|
-
|
|
133
|
+
export async function create({ ownerId, name, providerName, planName, region, version, addonOptions }) {
|
|
130
134
|
// TODO: We should be able to use it without {}
|
|
131
135
|
const provider = await getProvider(providerName);
|
|
132
136
|
|
|
@@ -147,7 +151,9 @@ export async function create ({ ownerId, name, providerName, planName, region, s
|
|
|
147
151
|
// This missing feature should have been added during the add-on's development phase
|
|
148
152
|
// The console has a similar check so I believe we shouldn't hit this
|
|
149
153
|
if (providerInfos != null && planType == null) {
|
|
150
|
-
throw new Error(
|
|
154
|
+
throw new Error(
|
|
155
|
+
'Internal error. The selected plan misses the TYPE feature. Please contact our support with the command line you used',
|
|
156
|
+
);
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
const createOptions = validateAddonVersionAndOptions(region, version, addonOptions, providerInfos, planType);
|
|
@@ -166,7 +172,7 @@ export async function create ({ ownerId, name, providerName, planName, region, s
|
|
|
166
172
|
return createdAddon;
|
|
167
173
|
}
|
|
168
174
|
|
|
169
|
-
async function getByName
|
|
175
|
+
async function getByName(ownerId, addonNameOrRealId) {
|
|
170
176
|
const addons = await getAllAddons({ id: ownerId }).then(sendToApi);
|
|
171
177
|
const filteredAddons = addons.filter(({ name, realId }) => {
|
|
172
178
|
return name === addonNameOrRealId || realId === addonNameOrRealId;
|
|
@@ -180,7 +186,7 @@ async function getByName (ownerId, addonNameOrRealId) {
|
|
|
180
186
|
throw new Error('Ambiguous addon name');
|
|
181
187
|
}
|
|
182
188
|
|
|
183
|
-
async function getId
|
|
189
|
+
async function getId(ownerId, addon) {
|
|
184
190
|
if (addon.addon_id) {
|
|
185
191
|
return addon.addon_id;
|
|
186
192
|
}
|
|
@@ -188,44 +194,41 @@ async function getId (ownerId, addon) {
|
|
|
188
194
|
return addonDetails.id;
|
|
189
195
|
}
|
|
190
196
|
|
|
191
|
-
export async function link
|
|
197
|
+
export async function link(ownerId, appId, addon) {
|
|
192
198
|
const addonId = await getId(ownerId, addon);
|
|
193
199
|
return linkAddon({ id: ownerId, appId }, JSON.stringify(addonId)).then(sendToApi);
|
|
194
200
|
}
|
|
195
201
|
|
|
196
|
-
export async function unlink
|
|
202
|
+
export async function unlink(ownerId, appId, addon) {
|
|
197
203
|
const addonId = await getId(ownerId, addon);
|
|
198
204
|
return unlinkAddon({ id: ownerId, appId, addonId }).then(sendToApi);
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
export async function deleteAddon
|
|
207
|
+
export async function deleteAddon(ownerId, addonIdOrName, skipConfirmation) {
|
|
202
208
|
const addonId = await getId(ownerId, addonIdOrName);
|
|
203
209
|
|
|
204
210
|
if (!skipConfirmation) {
|
|
205
|
-
await confirm(
|
|
206
|
-
'Deleting the add-on can\'t be undone, are you sure?',
|
|
207
|
-
'No confirmation, aborting add-on deletion',
|
|
208
|
-
);
|
|
211
|
+
await confirm("Deleting the add-on can't be undone, are you sure?", 'No confirmation, aborting add-on deletion');
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
return removeAddon({ id: ownerId, addonId }).then(sendToApi);
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
export async function rename
|
|
217
|
+
export async function rename(ownerId, addon, name) {
|
|
215
218
|
const addonId = await getId(ownerId, addon);
|
|
216
219
|
return updateAddon({ id: ownerId, addonId }, { name }).then(sendToApi);
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
export function completeRegion
|
|
222
|
+
export function completeRegion() {
|
|
220
223
|
return cliparse.autocomplete.words(['par', 'mtl']);
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
// TODO: We need to fix this
|
|
224
|
-
export function completePlan
|
|
227
|
+
export function completePlan() {
|
|
225
228
|
return cliparse.autocomplete.words(['dev', 's', 'm', 'l', 'xl', 'xxl']);
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
export async function findById
|
|
231
|
+
export async function findById(addonId) {
|
|
229
232
|
const { user, organisations } = await getSummary({}).then(sendToApi);
|
|
230
233
|
for (const orga of [user, ...organisations]) {
|
|
231
234
|
for (const simpleAddon of orga.addons) {
|
|
@@ -241,7 +244,7 @@ export async function findById (addonId) {
|
|
|
241
244
|
throw new Error(`Could not find add-on with ID: ${addonId}`);
|
|
242
245
|
}
|
|
243
246
|
|
|
244
|
-
export async function findByName
|
|
247
|
+
export async function findByName(addonName) {
|
|
245
248
|
const { user, organisations } = await getSummary({}).then(sendToApi);
|
|
246
249
|
for (const orga of [user, ...organisations]) {
|
|
247
250
|
for (const simpleAddon of orga.addons) {
|
|
@@ -257,8 +260,7 @@ export async function findByName (addonName) {
|
|
|
257
260
|
throw new Error(`Could not find add-on with name ${addonName}`);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
export async function findOwnerId
|
|
261
|
-
|
|
263
|
+
export async function findOwnerId(org, addonId) {
|
|
262
264
|
if (org != null && org.orga_id != null) {
|
|
263
265
|
return org.orga_id;
|
|
264
266
|
}
|
|
@@ -271,12 +273,12 @@ export async function findOwnerId (org, addonId) {
|
|
|
271
273
|
throw new Error(`Add-on ${addonId} does not exist`);
|
|
272
274
|
}
|
|
273
275
|
|
|
274
|
-
export function parseAddonOptions
|
|
276
|
+
export function parseAddonOptions(options) {
|
|
275
277
|
if (options == null) {
|
|
276
278
|
return {};
|
|
277
279
|
}
|
|
278
280
|
|
|
279
|
-
const pairs = options.split(/,(?=\w
|
|
281
|
+
const pairs = options.split(/,(?=\w+(?:-\w+)*=)/) || [];
|
|
280
282
|
|
|
281
283
|
return pairs.reduce((options, pair) => {
|
|
282
284
|
const [key, ...valueParts] = pair.split('=');
|
|
@@ -289,14 +291,11 @@ export function parseAddonOptions (options) {
|
|
|
289
291
|
let formattedValue = value;
|
|
290
292
|
if (value === 'true' || value === 'enabled') {
|
|
291
293
|
formattedValue = 'true';
|
|
292
|
-
}
|
|
293
|
-
else if (value === 'false' || value === 'disabled') {
|
|
294
|
+
} else if (value === 'false' || value === 'disabled') {
|
|
294
295
|
formattedValue = 'false';
|
|
295
|
-
}
|
|
296
|
-
else if (typeof key === 'string') {
|
|
296
|
+
} else if (typeof key === 'string') {
|
|
297
297
|
formattedValue = value;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
298
|
+
} else {
|
|
300
299
|
throw new Error(`Can't parse option value: ${value}. Accepted values are: enabled, disabled, true, false`);
|
|
301
300
|
}
|
|
302
301
|
|
|
@@ -305,7 +304,7 @@ export function parseAddonOptions (options) {
|
|
|
305
304
|
}, {});
|
|
306
305
|
}
|
|
307
306
|
|
|
308
|
-
function getPlan
|
|
307
|
+
function getPlan(planName, plans) {
|
|
309
308
|
// if no plan specified, pick the cheapest one
|
|
310
309
|
if (planName == null || planName === '') {
|
|
311
310
|
return plans.sort((p1, p2) => p1.price - p2.price)[0];
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
1
2
|
import { promises as fs } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import _ from 'lodash';
|
|
5
4
|
import { slugify } from '../lib/slugify.js';
|
|
6
|
-
|
|
7
5
|
import { Logger } from '../logger.js';
|
|
8
|
-
import * as User from './user.js';
|
|
9
6
|
import { conf } from './configuration.js';
|
|
7
|
+
import * as User from './user.js';
|
|
10
8
|
|
|
11
9
|
// TODO: Maybe use fs-utils findPath()
|
|
12
|
-
export async function loadApplicationConf
|
|
10
|
+
export async function loadApplicationConf(ignoreParentConfig = false, pathToFolder) {
|
|
13
11
|
if (pathToFolder == null) {
|
|
14
12
|
pathToFolder = path.dirname(conf.APP_CONFIGURATION_FILE);
|
|
15
13
|
}
|
|
@@ -19,17 +17,16 @@ export async function loadApplicationConf (ignoreParentConfig = false, pathToFol
|
|
|
19
17
|
try {
|
|
20
18
|
const contents = await fs.readFile(fullPath);
|
|
21
19
|
return JSON.parse(contents);
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
20
|
+
} catch (error) {
|
|
24
21
|
Logger.info('Cannot load app configuration from ' + conf.APP_CONFIGURATION_FILE + ' (' + error + ')');
|
|
25
22
|
if (ignoreParentConfig || path.parse(pathToFolder).root === pathToFolder) {
|
|
26
23
|
return { apps: [] };
|
|
27
24
|
}
|
|
28
25
|
return loadApplicationConf(ignoreParentConfig, path.normalize(path.join(pathToFolder, '..')));
|
|
29
26
|
}
|
|
30
|
-
}
|
|
27
|
+
}
|
|
31
28
|
|
|
32
|
-
export async function addLinkedApplication
|
|
29
|
+
export async function addLinkedApplication(appData, alias, ignoreParentConfig) {
|
|
33
30
|
const currentConfig = await loadApplicationConf(ignoreParentConfig);
|
|
34
31
|
|
|
35
32
|
const appEntry = {
|
|
@@ -50,9 +47,9 @@ export async function addLinkedApplication (appData, alias, ignoreParentConfig)
|
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
return persistConfig(currentConfig);
|
|
53
|
-
}
|
|
50
|
+
}
|
|
54
51
|
|
|
55
|
-
export async function removeLinkedApplication
|
|
52
|
+
export async function removeLinkedApplication({ appId, alias }) {
|
|
56
53
|
const currentConfig = await loadApplicationConf();
|
|
57
54
|
const appToUnlink = currentConfig.apps.find((a) => a.app_id === appId || a.alias === alias);
|
|
58
55
|
if (appToUnlink == null) {
|
|
@@ -70,9 +67,9 @@ export async function removeLinkedApplication ({ appId, alias }) {
|
|
|
70
67
|
|
|
71
68
|
await persistConfig(newConfig);
|
|
72
69
|
return true;
|
|
73
|
-
}
|
|
70
|
+
}
|
|
74
71
|
|
|
75
|
-
export function findApp
|
|
72
|
+
export function findApp(config, alias) {
|
|
76
73
|
if (_.isEmpty(config.apps)) {
|
|
77
74
|
throw new Error('There is no linked or targeted application. Use `--app` option or `clever link` command.');
|
|
78
75
|
}
|
|
@@ -83,7 +80,9 @@ export function findApp (config, alias) {
|
|
|
83
80
|
throw new Error(`There are no applications matching alias ${alias}`);
|
|
84
81
|
}
|
|
85
82
|
if (secondAppByAlias != null) {
|
|
86
|
-
throw new Error(
|
|
83
|
+
throw new Error(
|
|
84
|
+
`There are several applications matching alias ${alias}. This should not happen, your \`.clever.json\` should be fixed.`,
|
|
85
|
+
);
|
|
87
86
|
}
|
|
88
87
|
return appByAlias;
|
|
89
88
|
}
|
|
@@ -91,7 +90,7 @@ export function findApp (config, alias) {
|
|
|
91
90
|
return findDefaultApp(config);
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
export function checkAlreadyLinked
|
|
93
|
+
export function checkAlreadyLinked(apps, name, alias) {
|
|
95
94
|
const appAliasExists = apps.some((app) => app.alias != null && app.alias === alias);
|
|
96
95
|
if (appAliasExists) {
|
|
97
96
|
throw new Error(`An application is already linked with the alias '${alias}'`);
|
|
@@ -103,7 +102,7 @@ export function checkAlreadyLinked (apps, name, alias) {
|
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
function findDefaultApp
|
|
105
|
+
function findDefaultApp(config) {
|
|
107
106
|
if (_.isEmpty(config.apps)) {
|
|
108
107
|
throw new Error('There is no linked or targeted application. Use `--app` option or `clever link` command.');
|
|
109
108
|
}
|
|
@@ -111,7 +110,9 @@ function findDefaultApp (config) {
|
|
|
111
110
|
if (config.default != null) {
|
|
112
111
|
const defaultApp = _.find(config.apps, { app_id: config.default });
|
|
113
112
|
if (defaultApp == null) {
|
|
114
|
-
throw new Error(
|
|
113
|
+
throw new Error(
|
|
114
|
+
'The default application is not listed anymore. This should not happen, your `.clever.json` should be fixed.',
|
|
115
|
+
);
|
|
115
116
|
}
|
|
116
117
|
return defaultApp;
|
|
117
118
|
}
|
|
@@ -121,10 +122,12 @@ function findDefaultApp (config) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
const aliases = _.map(config.apps, 'alias').join(', ');
|
|
124
|
-
throw new Error(
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Several applications are linked. You can specify one with the "--alias" option. Run "clever applications" to list linked applications. Available aliases: ${aliases}`,
|
|
127
|
+
);
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
async function getAppDetailsForId
|
|
130
|
+
async function getAppDetailsForId(appId) {
|
|
128
131
|
const config = await loadApplicationConf();
|
|
129
132
|
|
|
130
133
|
if (_.isEmpty(config.apps)) {
|
|
@@ -136,19 +139,19 @@ async function getAppDetailsForId (appId) {
|
|
|
136
139
|
throw new Error(`There are no applications matching id '${appId}'`);
|
|
137
140
|
}
|
|
138
141
|
if (secondAppById != null) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
|
|
142
|
+
throw new Error(
|
|
143
|
+
`There are several applications matching id '${appId}'.` +
|
|
144
|
+
'This should not happen, your `.clever.json` should be fixed.',
|
|
145
|
+
);
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
return appById;
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
export async function getAppDetails
|
|
151
|
+
export async function getAppDetails({ alias }) {
|
|
147
152
|
const config = await loadApplicationConf();
|
|
148
153
|
const app = findApp(config, alias);
|
|
149
|
-
const ownerId =
|
|
150
|
-
? app.org_id
|
|
151
|
-
: await User.getCurrentId();
|
|
154
|
+
const ownerId = app.org_id != null ? app.org_id : await User.getCurrentId();
|
|
152
155
|
return {
|
|
153
156
|
appId: app.app_id,
|
|
154
157
|
ownerId: ownerId,
|
|
@@ -156,24 +159,23 @@ export async function getAppDetails ({ alias }) {
|
|
|
156
159
|
name: app.name,
|
|
157
160
|
alias: app.alias,
|
|
158
161
|
};
|
|
159
|
-
}
|
|
162
|
+
}
|
|
160
163
|
|
|
161
|
-
export async function getMostNaturalName
|
|
164
|
+
export async function getMostNaturalName(appId) {
|
|
162
165
|
try {
|
|
163
166
|
const details = await getAppDetailsForId(appId);
|
|
164
167
|
return details.alias || details.name || appId;
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
168
|
+
} catch {
|
|
167
169
|
return appId;
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
function persistConfig
|
|
173
|
+
function persistConfig(modifiedConfig) {
|
|
172
174
|
const jsonContents = JSON.stringify(modifiedConfig, null, 2);
|
|
173
175
|
return fs.writeFile(conf.APP_CONFIGURATION_FILE, jsonContents);
|
|
174
|
-
}
|
|
176
|
+
}
|
|
175
177
|
|
|
176
|
-
export async function setDefault
|
|
178
|
+
export async function setDefault(alias) {
|
|
177
179
|
const config = await loadApplicationConf();
|
|
178
180
|
const app = findApp(config, alias);
|
|
179
181
|
const newConfig = { ...config, default: app.app_id };
|