@contrail/cli 1.0.35 → 1.0.36
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/lib/apps/app-api.d.ts +6 -0
- package/lib/apps/app-api.js +59 -0
- package/lib/apps/deploy-app/docker-action-builder.d.ts +10 -0
- package/lib/apps/deploy-app/docker-action-builder.js +97 -0
- package/lib/apps/deploy-app/interfaces.d.ts +76 -0
- package/lib/apps/deploy-app/interfaces.js +4 -0
- package/lib/apps/deploy-app/workflow-deployer.d.ts +10 -0
- package/lib/apps/deploy-app/workflow-deployer.js +87 -0
- package/lib/apps/install-app/app-installer.d.ts +2 -0
- package/lib/apps/install-app/app-installer.js +84 -0
- package/lib/apps/upgrade-app/app-upgrader.d.ts +3 -0
- package/lib/apps/upgrade-app/app-upgrader.js +125 -0
- package/lib/cli.js +16 -0
- package/package.json +1 -1
- package/src/apps/app-api.ts +66 -0
- package/src/apps/install-app/app-installer.ts +87 -0
- package/src/apps/upgrade-app/app-upgrader.ts +131 -0
- package/src/cli.ts +20 -4
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function getAppVersions(app: any): Promise<any>;
|
|
2
|
+
export declare function upgradeAppVersion(appOrg: any, appVersionId: any): Promise<any>;
|
|
3
|
+
export declare function deleteAppOrg(appOrgId: string): Promise<any>;
|
|
4
|
+
export declare function getInstalledApps(): Promise<any>;
|
|
5
|
+
export declare function installAppToOrg(app: any, configProperties: any): Promise<any>;
|
|
6
|
+
export declare function getAvailableApps(): Promise<any[]>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import { Auth } from '../auth/auth';
|
|
3
|
+
export async function getAppVersions(app) {
|
|
4
|
+
return await new Entities().get({
|
|
5
|
+
entityName: 'app-version',
|
|
6
|
+
criteria: {
|
|
7
|
+
appId: app.id
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export async function upgradeAppVersion(appOrg, appVersionId) {
|
|
12
|
+
return await new Entities().update({
|
|
13
|
+
entityName: 'app-org',
|
|
14
|
+
id: appOrg.id,
|
|
15
|
+
object: {
|
|
16
|
+
appVersionId: appVersionId
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export async function deleteAppOrg(appOrgId) {
|
|
21
|
+
return await new Entities().delete({
|
|
22
|
+
entityName: 'app-org',
|
|
23
|
+
id: appOrgId
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export async function getInstalledApps() {
|
|
27
|
+
return await new Entities().get({
|
|
28
|
+
entityName: 'app-org',
|
|
29
|
+
relations: ['app']
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export async function installAppToOrg(app, configProperties) {
|
|
33
|
+
return await new Entities().create({
|
|
34
|
+
entityName: 'app-org',
|
|
35
|
+
object: {
|
|
36
|
+
appId: app.id,
|
|
37
|
+
appConfig: configProperties
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function getAvailableApps() {
|
|
42
|
+
const orgId = Auth.config.orgId;
|
|
43
|
+
const apps = [];
|
|
44
|
+
apps.push(...await new Entities().get({
|
|
45
|
+
entityName: 'app',
|
|
46
|
+
criteria: {
|
|
47
|
+
orgId,
|
|
48
|
+
private: true,
|
|
49
|
+
},
|
|
50
|
+
}));
|
|
51
|
+
apps.push(...await new Entities().get({
|
|
52
|
+
entityName: 'app',
|
|
53
|
+
criteria: {
|
|
54
|
+
orgId,
|
|
55
|
+
private: false,
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
return apps;
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { App, AppDeclaration, AppActionDeclaration } from '../../interfaces';
|
|
2
|
+
export declare class ActionsDeployer {
|
|
3
|
+
deployActions(appDeclaration: AppDeclaration, app: App): Promise<void>;
|
|
4
|
+
getActionsForApp(app: App): Promise<any>;
|
|
5
|
+
deployAction(actionKey: string, actionDeclaration: AppActionDeclaration, app: App, existingActions: Array<any>): Promise<void>;
|
|
6
|
+
private createNewActionDefinition;
|
|
7
|
+
private updateActionDefinition;
|
|
8
|
+
getActionDeclarationIdentifier(actionKey: any, app: App): string;
|
|
9
|
+
getFunction(actionDeclaration: AppActionDeclaration): Promise<string>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { ConfigPropertiesParser } from './app-template-parsers';
|
|
4
|
+
import { getSeparatorByPlatform } from '../../common';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
export class ActionsDeployer {
|
|
7
|
+
async deployActions(appDeclaration, app) {
|
|
8
|
+
// FETCH ALL ACTIONS FROM THE API.
|
|
9
|
+
const existingActions = await this.getActionsForApp(app);
|
|
10
|
+
// UPSERT / DELETE BASED ON ACTION IDENTIFIER
|
|
11
|
+
console.log('%s \tDeploying actions.', chalk.yellow.bold('ACTION'));
|
|
12
|
+
for (const actionKey of Object.keys(appDeclaration.actions)) {
|
|
13
|
+
await this.deployAction(actionKey, appDeclaration.actions[actionKey], app, existingActions);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async getActionsForApp(app) {
|
|
17
|
+
return await new Entities().get({
|
|
18
|
+
entityName: 'event-workflow-action-definition',
|
|
19
|
+
criteria: { ownerKey: `app:${app.id}` },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async deployAction(actionKey, actionDeclaration, app, existingActions) {
|
|
23
|
+
console.log(`%s \tDeploying action: ${actionKey}`, chalk.yellow.bold('ACTION'));
|
|
24
|
+
const identifier = this.getActionDeclarationIdentifier(actionKey, app);
|
|
25
|
+
const existingDefinition = existingActions.find(ad => ad.identifier === identifier);
|
|
26
|
+
if (!existingDefinition) {
|
|
27
|
+
await this.createNewActionDefinition(actionKey, actionDeclaration, app);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await this.updateActionDefinition(existingDefinition, actionDeclaration);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async createNewActionDefinition(actionKey, actionDeclaration, app) {
|
|
34
|
+
let configProperties;
|
|
35
|
+
if (actionDeclaration === null || actionDeclaration === void 0 ? void 0 : actionDeclaration.actionConfiguration) {
|
|
36
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(actionDeclaration.actionConfiguration);
|
|
37
|
+
}
|
|
38
|
+
const newObject = {
|
|
39
|
+
identifier: this.getActionDeclarationIdentifier(actionKey, app),
|
|
40
|
+
name: actionDeclaration.name,
|
|
41
|
+
ownerKey: `app:${app.id}`,
|
|
42
|
+
public: app.public,
|
|
43
|
+
configProperties,
|
|
44
|
+
};
|
|
45
|
+
const functionString = await this.getFunction(actionDeclaration);
|
|
46
|
+
if (functionString) {
|
|
47
|
+
newObject.executeFunctionString = functionString;
|
|
48
|
+
}
|
|
49
|
+
const actionDefinition = await new Entities().create({
|
|
50
|
+
entityName: 'event-workflow-action-definition',
|
|
51
|
+
object: newObject,
|
|
52
|
+
});
|
|
53
|
+
console.log(`%s Created new action definition: ${actionKey}`, chalk.yellow.bold('APP'));
|
|
54
|
+
return actionDefinition;
|
|
55
|
+
}
|
|
56
|
+
async updateActionDefinition(existingActionDefinition, actionDeclaration) {
|
|
57
|
+
let configProperties;
|
|
58
|
+
if (actionDeclaration === null || actionDeclaration === void 0 ? void 0 : actionDeclaration.actionConfiguration) {
|
|
59
|
+
configProperties = ConfigPropertiesParser.getConfigProperties(actionDeclaration.actionConfiguration);
|
|
60
|
+
}
|
|
61
|
+
else if (!(actionDeclaration === null || actionDeclaration === void 0 ? void 0 : actionDeclaration.actionConfiguration) && existingActionDefinition.configProperties) {
|
|
62
|
+
configProperties = null;
|
|
63
|
+
}
|
|
64
|
+
const updates = {
|
|
65
|
+
name: actionDeclaration.name,
|
|
66
|
+
// public: actionDeclaration.public, // TODO - an actions visibility is allowed to change
|
|
67
|
+
configProperties,
|
|
68
|
+
};
|
|
69
|
+
const functionString = await this.getFunction(actionDeclaration);
|
|
70
|
+
if (functionString) {
|
|
71
|
+
updates.executeFunctionString = functionString;
|
|
72
|
+
}
|
|
73
|
+
const actionDefinition = await new Entities().update({
|
|
74
|
+
entityName: 'event-workflow-action-definition',
|
|
75
|
+
id: existingActionDefinition.id,
|
|
76
|
+
object: updates,
|
|
77
|
+
});
|
|
78
|
+
console.log(`%s Updated existing action definition: ${actionDefinition.identifier}`, chalk.yellow.bold('APP'));
|
|
79
|
+
return actionDefinition;
|
|
80
|
+
}
|
|
81
|
+
getActionDeclarationIdentifier(actionKey, app) {
|
|
82
|
+
return app.identifier + ':' + actionKey;
|
|
83
|
+
}
|
|
84
|
+
async getFunction(actionDeclaration) {
|
|
85
|
+
if (!actionDeclaration.handler) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!actionDeclaration.handler.endsWith('.js')) {
|
|
89
|
+
throw new Error('Action handler must be a .js file.');
|
|
90
|
+
}
|
|
91
|
+
const CURRENT_DIR = process.cwd();
|
|
92
|
+
const separator = getSeparatorByPlatform();
|
|
93
|
+
const functionLocation = CURRENT_DIR + separator + actionDeclaration.handler;
|
|
94
|
+
const content = fs.readFileSync(functionLocation);
|
|
95
|
+
return content.toString();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Describes the derivative JSON / object represented
|
|
2
|
+
* in app.yml file.
|
|
3
|
+
*/
|
|
4
|
+
export interface AppDeclaration {
|
|
5
|
+
appIdentifier: string;
|
|
6
|
+
appName: string;
|
|
7
|
+
appDescription?: string;
|
|
8
|
+
visibility: 'private' | 'public';
|
|
9
|
+
publisher: string;
|
|
10
|
+
version: string;
|
|
11
|
+
eventWorkflowTriggerKeyMapping: string;
|
|
12
|
+
thumbnail?: string;
|
|
13
|
+
keywords?: Array<string>;
|
|
14
|
+
actions?: {
|
|
15
|
+
[key: string]: AppActionDeclaration;
|
|
16
|
+
};
|
|
17
|
+
appConfiguration: ConfigPropertyElements;
|
|
18
|
+
triggers: {
|
|
19
|
+
[key: string]: AppTriggerDeclaration;
|
|
20
|
+
};
|
|
21
|
+
isDockerBased?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ConfigPropertyElements {
|
|
24
|
+
[key: string]: ConfigPropertyElement;
|
|
25
|
+
}
|
|
26
|
+
export interface ConfigPropertyElement {
|
|
27
|
+
propertyType: string;
|
|
28
|
+
options?: any[];
|
|
29
|
+
label: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AppActionDeclaration {
|
|
32
|
+
handler: string;
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
actionConfiguration?: ConfigPropertyElements;
|
|
36
|
+
}
|
|
37
|
+
export interface AppTriggerDeclaration {
|
|
38
|
+
key: string;
|
|
39
|
+
name: string;
|
|
40
|
+
external?: boolean;
|
|
41
|
+
description?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface App {
|
|
44
|
+
id: string;
|
|
45
|
+
identifier: string;
|
|
46
|
+
name: string;
|
|
47
|
+
public: boolean;
|
|
48
|
+
description?: string;
|
|
49
|
+
publisher: string;
|
|
50
|
+
thumbnail: string;
|
|
51
|
+
eventWorkflowTriggerKeyMapping: string;
|
|
52
|
+
configProperties?: ConfigProperty[];
|
|
53
|
+
triggerKeyDefinitions?: TriggerKeyDefinition[];
|
|
54
|
+
}
|
|
55
|
+
export declare class ConfigProperty {
|
|
56
|
+
slug: string;
|
|
57
|
+
propertyType: string;
|
|
58
|
+
options?: any[];
|
|
59
|
+
label: string;
|
|
60
|
+
}
|
|
61
|
+
export interface EventWorkflowActionDefinition {
|
|
62
|
+
id?: string;
|
|
63
|
+
identifier?: string;
|
|
64
|
+
name?: string;
|
|
65
|
+
executeFunctionString?: string;
|
|
66
|
+
ownerKey?: string;
|
|
67
|
+
public?: boolean;
|
|
68
|
+
configProperties?: ConfigProperty[];
|
|
69
|
+
isDockerBased?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export declare class TriggerKeyDefinition {
|
|
72
|
+
name: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
triggerKey: string;
|
|
75
|
+
external?: boolean;
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { App, AppDeclaration, WorkflowTemplateDeclaration } from './interfaces';
|
|
2
|
+
export declare class WorkflowDeployer {
|
|
3
|
+
deployWorkflows(appDeclaration: AppDeclaration, app: App): Promise<void>;
|
|
4
|
+
getWorkflowsForApp(app: App): Promise<any>;
|
|
5
|
+
deployWorkflow(workflowKey: string, workflow: WorkflowTemplateDeclaration, app: App, existingWorkflows: Array<any>, appDeclaration: AppDeclaration): Promise<any>;
|
|
6
|
+
private createNewWorkflowDefinition;
|
|
7
|
+
private populateActionIds;
|
|
8
|
+
private updateWorkflowDefinition;
|
|
9
|
+
getWorkflowDeclarationIdentifier(actionKey: any, app: App): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { getActionDeclarationIdentifier, getActionsForApp } from './actions-deployer';
|
|
4
|
+
export class WorkflowDeployer {
|
|
5
|
+
async deployWorkflows(appDeclaration, app) {
|
|
6
|
+
// FETCH ALL WORKFLOWS FROM THE API.
|
|
7
|
+
const existingWorkflows = await this.getWorkflowsForApp(app);
|
|
8
|
+
// UPSERT / DELETE BASED ON WORKFLOW IDENTIFIER
|
|
9
|
+
console.log('%s \tDeploying workflows.', chalk.yellow.bold('WORKFLOW'));
|
|
10
|
+
for (const workflowKey of Object.keys(appDeclaration.workflows)) {
|
|
11
|
+
await this.deployWorkflow(workflowKey, appDeclaration.workflows[workflowKey], app, existingWorkflows, appDeclaration);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async getWorkflowsForApp(app) {
|
|
15
|
+
return await new Entities().get({
|
|
16
|
+
entityName: 'event-workflow-template',
|
|
17
|
+
criteria: { ownerKey: `app:${app.id}` },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async deployWorkflow(workflowKey, workflow, app, existingWorkflows, appDeclaration) {
|
|
21
|
+
console.log(`%s \tDeploying workflow: ${workflowKey}`, chalk.yellow.bold('WORKFLOW'));
|
|
22
|
+
const identifier = this.getWorkflowDeclarationIdentifier(workflowKey, app);
|
|
23
|
+
const existingDefinition = existingWorkflows.find(ad => ad.identifier === identifier);
|
|
24
|
+
if (existingDefinition) {
|
|
25
|
+
return await this.updateWorkflowDefinition(existingDefinition, workflow, app, appDeclaration);
|
|
26
|
+
}
|
|
27
|
+
return await this.createNewWorkflowDefinition(workflowKey, workflow, app, appDeclaration);
|
|
28
|
+
}
|
|
29
|
+
async createNewWorkflowDefinition(workflowKey, workflowDeclaration, app, appDeclaration) {
|
|
30
|
+
await this.populateActionIds(workflowDeclaration, appDeclaration, app);
|
|
31
|
+
const newObject = {
|
|
32
|
+
identifier: this.getWorkflowDeclarationIdentifier(workflowKey, app),
|
|
33
|
+
name: workflowDeclaration.name,
|
|
34
|
+
ownerKey: `app:${app.id}`,
|
|
35
|
+
triggerKey: workflowDeclaration.triggerKey,
|
|
36
|
+
workflowDefinition: workflowDeclaration.workflowDefinition,
|
|
37
|
+
isActive: workflowDeclaration.isActive,
|
|
38
|
+
supportingAppId: workflowDeclaration.supportingAppId,
|
|
39
|
+
messageGroupId: workflowDeclaration.messageGroupId,
|
|
40
|
+
parallelWorkerCount: workflowDeclaration.parallelWorkerCount,
|
|
41
|
+
};
|
|
42
|
+
const workflowTemplate = await new Entities().create({
|
|
43
|
+
entityName: 'event-workflow-template',
|
|
44
|
+
object: newObject,
|
|
45
|
+
});
|
|
46
|
+
console.log(`%s Created new workflow definition: ${workflowKey}`, chalk.yellow.bold('WORKFLOW'));
|
|
47
|
+
return workflowTemplate;
|
|
48
|
+
}
|
|
49
|
+
async populateActionIds(workflowDeclaration, appDeclaration, app) {
|
|
50
|
+
var _a;
|
|
51
|
+
let availableAppActions = await getActionsForApp(app);
|
|
52
|
+
if (!((_a = workflowDeclaration.workflowDefinition) === null || _a === void 0 ? void 0 : _a.paths)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
for (const path of workflowDeclaration.workflowDefinition.paths) {
|
|
56
|
+
if (!(path === null || path === void 0 ? void 0 : path.actionDefinitionConfigs)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const actionConfiguration of path.actionDefinitionConfigs) {
|
|
60
|
+
const actionKey = actionConfiguration.action;
|
|
61
|
+
const action = availableAppActions.find(action => action.identifier === getActionDeclarationIdentifier(actionKey, app));
|
|
62
|
+
if (!action) {
|
|
63
|
+
throw new Error(`Could not find action ${actionKey} declared in ${workflowDeclaration.name}`);
|
|
64
|
+
}
|
|
65
|
+
actionConfiguration.id = action.id;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async updateWorkflowDefinition(existingWorkflowDefinition, workflowDeclaration, app, appDeclaration) {
|
|
70
|
+
await this.populateActionIds(workflowDeclaration, appDeclaration, app);
|
|
71
|
+
const updates = Object.assign(new Object(), {
|
|
72
|
+
name: workflowDeclaration.name,
|
|
73
|
+
triggerKey: workflowDeclaration.triggerKey,
|
|
74
|
+
workflowDefinition: workflowDeclaration.workflowDefinition
|
|
75
|
+
});
|
|
76
|
+
const updatedWorkflowDefinition = await new Entities().update({
|
|
77
|
+
entityName: 'event-workflow-template',
|
|
78
|
+
id: existingWorkflowDefinition.id,
|
|
79
|
+
object: updates,
|
|
80
|
+
});
|
|
81
|
+
console.log(`%s Updated existing workflow definition: ${updatedWorkflowDefinition.identifier}`, chalk.yellow.bold('WORKFLOW'));
|
|
82
|
+
return updatedWorkflowDefinition;
|
|
83
|
+
}
|
|
84
|
+
getWorkflowDeclarationIdentifier(actionKey, app) {
|
|
85
|
+
return app.identifier + ':' + actionKey;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Entities } from '@contrail/sdk';
|
|
4
|
+
import { authUser } from '../../auth/login';
|
|
5
|
+
import { deleteAppOrg, getAvailableApps, getInstalledApps, installAppToOrg } from '../app-api';
|
|
6
|
+
async function getConfigProperties(app) {
|
|
7
|
+
const config = {};
|
|
8
|
+
for (const key of Object.keys(app.configProperties)) {
|
|
9
|
+
const slug = app.configProperties[key]['slug'];
|
|
10
|
+
const name = app.configProperties[key]['label'];
|
|
11
|
+
const type = app.configProperties[key]['propertyType'];
|
|
12
|
+
const questions = [];
|
|
13
|
+
questions.push({
|
|
14
|
+
type: 'input',
|
|
15
|
+
name: 'configAnswer',
|
|
16
|
+
message: `[App config] ${name} (${type}):`,
|
|
17
|
+
});
|
|
18
|
+
const answers = await inquirer.prompt(questions);
|
|
19
|
+
config[slug] = answers.configAnswer;
|
|
20
|
+
}
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
export async function uninstallApp() {
|
|
24
|
+
await authUser();
|
|
25
|
+
const installedApps = await getInstalledApps();
|
|
26
|
+
const answers = await inquirer.prompt([{
|
|
27
|
+
type: 'list',
|
|
28
|
+
name: 'appIdentifier',
|
|
29
|
+
message: 'Which app would you like to uninstall?',
|
|
30
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
31
|
+
}]);
|
|
32
|
+
const appToDelete = installedApps.find(x => x.app.identifier === answers.appIdentifier);
|
|
33
|
+
console.log('%s Uninstalling app', chalk.green.bold('APP INSTALLATION'));
|
|
34
|
+
await deleteAppOrg(appToDelete.id);
|
|
35
|
+
// eslint-disable-next-line no-constant-condition
|
|
36
|
+
while (true) {
|
|
37
|
+
await delay(1000);
|
|
38
|
+
try {
|
|
39
|
+
const appStillDeleting = await new Entities().get({ entityName: 'app-org', id: appToDelete.id });
|
|
40
|
+
if (!appStillDeleting) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
process.stdout.write('.');
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
console.log('Error is:', e);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('%s App Uninstalled', chalk.green.bold('APP INSTALLATION'));
|
|
51
|
+
}
|
|
52
|
+
export async function installApp() {
|
|
53
|
+
await authUser();
|
|
54
|
+
const apps = await getAvailableApps();
|
|
55
|
+
if (!apps.length) {
|
|
56
|
+
throw new Error('No apps are available to install...');
|
|
57
|
+
}
|
|
58
|
+
const installedApps = await getInstalledApps();
|
|
59
|
+
const installedAppsIds = installedApps.map(x => x.app.id);
|
|
60
|
+
const installableApps = apps.filter(x => !installedAppsIds.includes(x.id));
|
|
61
|
+
console.log(chalk.yellow.bold('Installed Apps:'));
|
|
62
|
+
for (const appOrg of installedApps) {
|
|
63
|
+
console.log(chalk.yellow.bold(`${appOrg.app.name}: ${appOrg.app.identifier}`));
|
|
64
|
+
}
|
|
65
|
+
const questions = [];
|
|
66
|
+
questions.push({
|
|
67
|
+
type: 'list',
|
|
68
|
+
name: 'appIdentifier',
|
|
69
|
+
message: 'Which app would you like to install?',
|
|
70
|
+
choices: installableApps.map(x => x.identifier)
|
|
71
|
+
});
|
|
72
|
+
const answers = await inquirer.prompt(questions);
|
|
73
|
+
const app = installableApps.find(x => x.identifier === answers.appIdentifier);
|
|
74
|
+
let configProperties;
|
|
75
|
+
if (app.configProperties) {
|
|
76
|
+
configProperties = await getConfigProperties(app);
|
|
77
|
+
}
|
|
78
|
+
console.log('%s Installing apps', chalk.green.bold('APP INSTALLATION'));
|
|
79
|
+
await installAppToOrg(app, configProperties);
|
|
80
|
+
console.log('%s App installed', chalk.green.bold('APP INSTALLATION'));
|
|
81
|
+
}
|
|
82
|
+
function delay(ms) {
|
|
83
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
84
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Entities } from '@contrail/sdk';
|
|
4
|
+
import { authUser } from '../../auth/login';
|
|
5
|
+
import { deleteAppOrg, getAppVersions, getAvailableApps, getInstalledApps, installAppToOrg, upgradeAppVersion } from '../app-api';
|
|
6
|
+
async function getConfigProperties(app) {
|
|
7
|
+
const config = {};
|
|
8
|
+
for (const key of Object.keys(app.configProperties)) {
|
|
9
|
+
const slug = app.configProperties[key]['slug'];
|
|
10
|
+
const name = app.configProperties[key]['label'];
|
|
11
|
+
const type = app.configProperties[key]['propertyType'];
|
|
12
|
+
const questions = [];
|
|
13
|
+
questions.push({
|
|
14
|
+
type: 'input',
|
|
15
|
+
name: 'configAnswer',
|
|
16
|
+
message: `[App config] ${name} (${type}):`,
|
|
17
|
+
});
|
|
18
|
+
const answers = await inquirer.prompt(questions);
|
|
19
|
+
config[slug] = answers.configAnswer;
|
|
20
|
+
}
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
export async function uninstallApp() {
|
|
24
|
+
await authUser();
|
|
25
|
+
const installedApps = await getInstalledApps();
|
|
26
|
+
const answers = await inquirer.prompt([{
|
|
27
|
+
type: 'list',
|
|
28
|
+
name: 'appIdentifier',
|
|
29
|
+
message: 'Which app would you like to uninstall?',
|
|
30
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
31
|
+
}]);
|
|
32
|
+
const appToDelete = installedApps.find(x => x.app.identifier === answers.appIdentifier);
|
|
33
|
+
console.log('%s Uninstalling app', chalk.green.bold('APP INSTALLATION'));
|
|
34
|
+
await deleteAppOrg(appToDelete.id);
|
|
35
|
+
// eslint-disable-next-line no-constant-condition
|
|
36
|
+
while (true) {
|
|
37
|
+
await delay(1000);
|
|
38
|
+
try {
|
|
39
|
+
const appStillDeleting = await new Entities().get({ entityName: 'app-org', id: appToDelete.id });
|
|
40
|
+
if (!appStillDeleting) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
process.stdout.write('.');
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
console.log('Error is:', e);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('%s App Uninstalled', chalk.green.bold('APP INSTALLATION'));
|
|
51
|
+
}
|
|
52
|
+
export async function upgradeApp() {
|
|
53
|
+
await authUser();
|
|
54
|
+
const allApps = await getAvailableApps();
|
|
55
|
+
const installedApps = await getInstalledApps();
|
|
56
|
+
let answers = await inquirer.prompt([{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'appIdentifier',
|
|
59
|
+
message: 'Which app would you like to upgrade?',
|
|
60
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
61
|
+
}]);
|
|
62
|
+
const appOrgToUpgrade = installedApps.find(x => x.app.identifier === answers.appIdentifier);
|
|
63
|
+
const appToUpgrade = allApps.find(x => x.identifier === answers.appIdentifier);
|
|
64
|
+
const versions = await getAppVersions(appToUpgrade);
|
|
65
|
+
answers = await inquirer.prompt([{
|
|
66
|
+
type: 'list',
|
|
67
|
+
name: 'appVersion',
|
|
68
|
+
message: 'Which app version would you like to upgrade to?',
|
|
69
|
+
choices: versions.reverse().map(x => {
|
|
70
|
+
const name = x.id === appOrgToUpgrade.appVersionId ? `${x.versionNumber} (${x.createdOn}) (current)` : `${x.versionNumber} (${x.createdOn})`;
|
|
71
|
+
return {
|
|
72
|
+
value: x.id, name
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
}]);
|
|
76
|
+
console.log(`%s Upgrading app to version ${answers.appVersion}`, chalk.green.bold('APP INSTALLATION'));
|
|
77
|
+
await upgradeAppVersion(appOrgToUpgrade, answers.appVersion);
|
|
78
|
+
// eslint-disable-next-line no-constant-condition
|
|
79
|
+
while (true) {
|
|
80
|
+
await delay(1000);
|
|
81
|
+
const appStillUpgrading = await new Entities().get({ entityName: 'app-org', id: appOrgToUpgrade.id });
|
|
82
|
+
if (appStillUpgrading.status === 'READY') {
|
|
83
|
+
console.log('%s App Upgraded', chalk.green.bold('APP INSTALLATION'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (appStillUpgrading.status === 'FAILED_INSTALLING') {
|
|
87
|
+
console.log('%s App Upgrade FAILED!', chalk.red.bold('APP INSTALLATION'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log('checking app status...');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function installApp() {
|
|
94
|
+
await authUser();
|
|
95
|
+
const apps = await getAvailableApps();
|
|
96
|
+
if (!apps.length) {
|
|
97
|
+
throw new Error('No apps are available to install...');
|
|
98
|
+
}
|
|
99
|
+
const installedApps = await getInstalledApps();
|
|
100
|
+
const installedAppsIds = installedApps.map(x => x.app.id);
|
|
101
|
+
const installableApps = apps.filter(x => !installedAppsIds.includes(x.id));
|
|
102
|
+
console.log(chalk.yellow.bold('Installed Apps:'));
|
|
103
|
+
for (const appOrg of installedApps) {
|
|
104
|
+
console.log(chalk.yellow.bold(`${appOrg.app.name}: ${appOrg.app.identifier}`));
|
|
105
|
+
}
|
|
106
|
+
const questions = [];
|
|
107
|
+
questions.push({
|
|
108
|
+
type: 'list',
|
|
109
|
+
name: 'appIdentifier',
|
|
110
|
+
message: 'Which app would you like to install?',
|
|
111
|
+
choices: installableApps.map(x => x.identifier)
|
|
112
|
+
});
|
|
113
|
+
const answers = await inquirer.prompt(questions);
|
|
114
|
+
const app = installableApps.find(x => x.identifier === answers.appIdentifier);
|
|
115
|
+
let configProperties;
|
|
116
|
+
if (app.configProperties) {
|
|
117
|
+
configProperties = await getConfigProperties(app);
|
|
118
|
+
}
|
|
119
|
+
console.log('%s Installing apps', chalk.green.bold('APP INSTALLATION'));
|
|
120
|
+
await installAppToOrg(app, configProperties);
|
|
121
|
+
console.log('%s App installed', chalk.green.bold('APP INSTALLATION'));
|
|
122
|
+
}
|
|
123
|
+
function delay(ms) {
|
|
124
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
125
|
+
}
|
package/lib/cli.js
CHANGED
|
@@ -10,6 +10,8 @@ import { ParseTypesFromFiles, ParseTypesFromLocalFiles, ParseTypesFromTemplateFr
|
|
|
10
10
|
import { loadColors, deleteColors } from './colors/color-loader';
|
|
11
11
|
import { listConfig, removeConfig } from './config/configCommands';
|
|
12
12
|
import { deleteWorkflowTemplate, deployWorkflowTemplate, listWorkflowTemplate } from './workflows/workflowCommands';
|
|
13
|
+
import { installApp, uninstallApp } from './apps/install-app/app-installer';
|
|
14
|
+
import { upgradeApp } from './apps/upgrade-app/app-upgrader';
|
|
13
15
|
const cliUsage = require('command-line-usage');
|
|
14
16
|
/**
|
|
15
17
|
* Built using documentation here:
|
|
@@ -49,6 +51,18 @@ export async function cli(args) {
|
|
|
49
51
|
removeApp(args);
|
|
50
52
|
break;
|
|
51
53
|
}
|
|
54
|
+
case 'install': {
|
|
55
|
+
installApp();
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case 'uninstall': {
|
|
59
|
+
uninstallApp();
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'upgrade': {
|
|
63
|
+
upgradeApp();
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
52
66
|
default: {
|
|
53
67
|
printIllegalCommand();
|
|
54
68
|
}
|
|
@@ -197,6 +211,8 @@ const helpSections = [
|
|
|
197
211
|
'. a template directly to the org',
|
|
198
212
|
'deploy Deploy your app.',
|
|
199
213
|
'remove Remove your app.',
|
|
214
|
+
'install Install an app to your org.',
|
|
215
|
+
'uninstall Uninstall an app from your org.',
|
|
200
216
|
],
|
|
201
217
|
},
|
|
202
218
|
{
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
import { Auth } from '../auth/auth';
|
|
3
|
+
|
|
4
|
+
export async function getAppVersions(app) {
|
|
5
|
+
return await new Entities().get({
|
|
6
|
+
entityName: 'app-version',
|
|
7
|
+
criteria: {
|
|
8
|
+
appId: app.id
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function upgradeAppVersion(appOrg, appVersionId) {
|
|
14
|
+
return await new Entities().update({
|
|
15
|
+
entityName: 'app-org',
|
|
16
|
+
id: appOrg.id,
|
|
17
|
+
object: {
|
|
18
|
+
appVersionId: appVersionId
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function deleteAppOrg(appOrgId: string) {
|
|
24
|
+
return await new Entities().delete({
|
|
25
|
+
entityName: 'app-org',
|
|
26
|
+
id: appOrgId
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function getInstalledApps() {
|
|
31
|
+
return await new Entities().get({
|
|
32
|
+
entityName: 'app-org',
|
|
33
|
+
relations: ['app']
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function installAppToOrg(app, configProperties) {
|
|
38
|
+
return await new Entities().create({
|
|
39
|
+
entityName: 'app-org',
|
|
40
|
+
object: {
|
|
41
|
+
appId: app.id,
|
|
42
|
+
appConfig: configProperties
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function getAvailableApps() {
|
|
48
|
+
const orgId = Auth.config.orgId;
|
|
49
|
+
const apps = [];
|
|
50
|
+
apps.push(...await new Entities().get({
|
|
51
|
+
entityName: 'app',
|
|
52
|
+
criteria: {
|
|
53
|
+
orgId,
|
|
54
|
+
private: true,
|
|
55
|
+
},
|
|
56
|
+
}));
|
|
57
|
+
apps.push(...await new Entities().get({
|
|
58
|
+
entityName: 'app',
|
|
59
|
+
criteria: {
|
|
60
|
+
orgId,
|
|
61
|
+
private: false,
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
return apps;
|
|
66
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Entities } from '@contrail/sdk';
|
|
4
|
+
import {authUser} from '../../auth/login';
|
|
5
|
+
import { deleteAppOrg, getAvailableApps, getInstalledApps, installAppToOrg } from '../app-api';
|
|
6
|
+
|
|
7
|
+
async function getConfigProperties(app) {
|
|
8
|
+
const config = {};
|
|
9
|
+
for (const key of Object.keys(app.configProperties)) {
|
|
10
|
+
const slug = app.configProperties[key]['slug'];
|
|
11
|
+
const name = app.configProperties[key]['label'];
|
|
12
|
+
const type = app.configProperties[key]['propertyType'];
|
|
13
|
+
const questions = [];
|
|
14
|
+
questions.push({
|
|
15
|
+
type: 'input',
|
|
16
|
+
name: 'configAnswer',
|
|
17
|
+
message: `[App config] ${name} (${type}):`,
|
|
18
|
+
});
|
|
19
|
+
const answers = await inquirer.prompt(questions);
|
|
20
|
+
config[slug] = answers.configAnswer;
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
export async function uninstallApp() {
|
|
25
|
+
await authUser();
|
|
26
|
+
const installedApps = await getInstalledApps();
|
|
27
|
+
const answers = await inquirer.prompt([{
|
|
28
|
+
type: 'list',
|
|
29
|
+
name: 'appIdentifier',
|
|
30
|
+
message: 'Which app would you like to uninstall?',
|
|
31
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
32
|
+
}]);
|
|
33
|
+
const appToDelete = installedApps.find(x=> x.app.identifier === answers.appIdentifier);
|
|
34
|
+
console.log('%s Uninstalling app', chalk.green.bold('APP INSTALLATION'));
|
|
35
|
+
await deleteAppOrg(appToDelete.id);
|
|
36
|
+
// eslint-disable-next-line no-constant-condition
|
|
37
|
+
while (true) {
|
|
38
|
+
await delay(1000);
|
|
39
|
+
try{
|
|
40
|
+
const appStillDeleting = await new Entities().get({entityName: 'app-org', id: appToDelete.id});
|
|
41
|
+
if (!appStillDeleting) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write('.');
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.log('Error is:', e);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('%s App Uninstalled', chalk.green.bold('APP INSTALLATION'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function installApp() {
|
|
54
|
+
await authUser();
|
|
55
|
+
const apps = await getAvailableApps();
|
|
56
|
+
if (!apps.length) {
|
|
57
|
+
throw new Error('No apps are available to install...');
|
|
58
|
+
}
|
|
59
|
+
const installedApps = await getInstalledApps();
|
|
60
|
+
const installedAppsIds = installedApps.map(x => x.app.id);
|
|
61
|
+
const installableApps = apps.filter(x => !installedAppsIds.includes(x.id));
|
|
62
|
+
console.log(chalk.yellow.bold('Installed Apps:'));
|
|
63
|
+
for (const appOrg of installedApps) {
|
|
64
|
+
console.log(chalk.yellow.bold(`${appOrg.app.name}: ${appOrg.app.identifier}`));
|
|
65
|
+
}
|
|
66
|
+
const questions = [];
|
|
67
|
+
questions.push({
|
|
68
|
+
type: 'list',
|
|
69
|
+
name: 'appIdentifier',
|
|
70
|
+
message: 'Which app would you like to install?',
|
|
71
|
+
choices: installableApps.map(x => x.identifier)
|
|
72
|
+
});
|
|
73
|
+
const answers = await inquirer.prompt(questions);
|
|
74
|
+
const app = installableApps.find(x => x.identifier === answers.appIdentifier);
|
|
75
|
+
let configProperties: any;
|
|
76
|
+
if (app.configProperties) {
|
|
77
|
+
configProperties = await getConfigProperties(app);
|
|
78
|
+
}
|
|
79
|
+
console.log('%s Installing apps', chalk.green.bold('APP INSTALLATION'));
|
|
80
|
+
|
|
81
|
+
await installAppToOrg(app, configProperties);
|
|
82
|
+
console.log('%s App installed', chalk.green.bold('APP INSTALLATION'));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function delay(ms: number) {
|
|
86
|
+
return new Promise( resolve => setTimeout(resolve, ms) );
|
|
87
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { Entities } from '@contrail/sdk';
|
|
4
|
+
import { authUser } from '../../auth/login';
|
|
5
|
+
import { deleteAppOrg, getAppVersions, getAvailableApps, getInstalledApps, installAppToOrg, upgradeAppVersion } from '../app-api';
|
|
6
|
+
|
|
7
|
+
async function getConfigProperties(app) {
|
|
8
|
+
const config = {};
|
|
9
|
+
for (const key of Object.keys(app.configProperties)) {
|
|
10
|
+
const slug = app.configProperties[key]['slug'];
|
|
11
|
+
const name = app.configProperties[key]['label'];
|
|
12
|
+
const type = app.configProperties[key]['propertyType'];
|
|
13
|
+
const questions = [];
|
|
14
|
+
questions.push({
|
|
15
|
+
type: 'input',
|
|
16
|
+
name: 'configAnswer',
|
|
17
|
+
message: `[App config] ${name} (${type}):`,
|
|
18
|
+
});
|
|
19
|
+
const answers = await inquirer.prompt(questions);
|
|
20
|
+
config[slug] = answers.configAnswer;
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function uninstallApp() {
|
|
26
|
+
await authUser();
|
|
27
|
+
const installedApps = await getInstalledApps();
|
|
28
|
+
const answers = await inquirer.prompt([{
|
|
29
|
+
type: 'list',
|
|
30
|
+
name: 'appIdentifier',
|
|
31
|
+
message: 'Which app would you like to uninstall?',
|
|
32
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
33
|
+
}]);
|
|
34
|
+
const appToDelete = installedApps.find(x => x.app.identifier === answers.appIdentifier);
|
|
35
|
+
console.log('%s Uninstalling app', chalk.green.bold('APP INSTALLATION'));
|
|
36
|
+
await deleteAppOrg(appToDelete.id);
|
|
37
|
+
// eslint-disable-next-line no-constant-condition
|
|
38
|
+
while (true) {
|
|
39
|
+
await delay(1000);
|
|
40
|
+
try {
|
|
41
|
+
const appStillDeleting = await new Entities().get({ entityName: 'app-org', id: appToDelete.id });
|
|
42
|
+
if (!appStillDeleting) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
process.stdout.write('.');
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.log('Error is:', e);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
console.log('%s App Uninstalled', chalk.green.bold('APP INSTALLATION'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function upgradeApp() {
|
|
55
|
+
await authUser();
|
|
56
|
+
const allApps = await getAvailableApps();
|
|
57
|
+
const installedApps = await getInstalledApps();
|
|
58
|
+
let answers = await inquirer.prompt([{
|
|
59
|
+
type: 'list',
|
|
60
|
+
name: 'appIdentifier',
|
|
61
|
+
message: 'Which app would you like to upgrade?',
|
|
62
|
+
choices: installedApps.map(x => x.app.identifier)
|
|
63
|
+
}]);
|
|
64
|
+
const appOrgToUpgrade = installedApps.find(x => x.app.identifier === answers.appIdentifier);
|
|
65
|
+
const appToUpgrade = allApps.find(x => x.identifier === answers.appIdentifier);
|
|
66
|
+
const versions = await getAppVersions(appToUpgrade);
|
|
67
|
+
answers = await inquirer.prompt([{
|
|
68
|
+
type: 'list',
|
|
69
|
+
name: 'appVersion',
|
|
70
|
+
message: 'Which app version would you like to upgrade to?',
|
|
71
|
+
choices: versions.reverse().map(x => {
|
|
72
|
+
const name = x.id === appOrgToUpgrade.appVersionId? `${x.versionNumber} (${x.createdOn}) (current)`: `${x.versionNumber} (${x.createdOn})`;
|
|
73
|
+
return {
|
|
74
|
+
value: x.id, name
|
|
75
|
+
};
|
|
76
|
+
})
|
|
77
|
+
}]);
|
|
78
|
+
|
|
79
|
+
console.log(`%s Upgrading app to version ${answers.appVersion}`, chalk.green.bold('APP INSTALLATION'));
|
|
80
|
+
await upgradeAppVersion(appOrgToUpgrade, answers.appVersion);
|
|
81
|
+
// eslint-disable-next-line no-constant-condition
|
|
82
|
+
while (true) {
|
|
83
|
+
await delay(1000);
|
|
84
|
+
const appStillUpgrading = await new Entities().get({ entityName: 'app-org', id: appOrgToUpgrade.id });
|
|
85
|
+
if (appStillUpgrading.status === 'READY') {
|
|
86
|
+
console.log('%s App Upgraded', chalk.green.bold('APP INSTALLATION'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (appStillUpgrading.status === 'FAILED_INSTALLING') {
|
|
90
|
+
console.log('%s App Upgrade FAILED!', chalk.red.bold('APP INSTALLATION'));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log('checking app status...');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function installApp() {
|
|
98
|
+
await authUser();
|
|
99
|
+
const apps = await getAvailableApps();
|
|
100
|
+
if (!apps.length) {
|
|
101
|
+
throw new Error('No apps are available to install...');
|
|
102
|
+
}
|
|
103
|
+
const installedApps = await getInstalledApps();
|
|
104
|
+
const installedAppsIds = installedApps.map(x => x.app.id);
|
|
105
|
+
const installableApps = apps.filter(x => !installedAppsIds.includes(x.id));
|
|
106
|
+
console.log(chalk.yellow.bold('Installed Apps:'));
|
|
107
|
+
for (const appOrg of installedApps) {
|
|
108
|
+
console.log(chalk.yellow.bold(`${appOrg.app.name}: ${appOrg.app.identifier}`));
|
|
109
|
+
}
|
|
110
|
+
const questions = [];
|
|
111
|
+
questions.push({
|
|
112
|
+
type: 'list',
|
|
113
|
+
name: 'appIdentifier',
|
|
114
|
+
message: 'Which app would you like to install?',
|
|
115
|
+
choices: installableApps.map(x => x.identifier)
|
|
116
|
+
});
|
|
117
|
+
const answers = await inquirer.prompt(questions);
|
|
118
|
+
const app = installableApps.find(x => x.identifier === answers.appIdentifier);
|
|
119
|
+
let configProperties: any;
|
|
120
|
+
if (app.configProperties) {
|
|
121
|
+
configProperties = await getConfigProperties(app);
|
|
122
|
+
}
|
|
123
|
+
console.log('%s Installing apps', chalk.green.bold('APP INSTALLATION'));
|
|
124
|
+
|
|
125
|
+
await installAppToOrg(app, configProperties);
|
|
126
|
+
console.log('%s App installed', chalk.green.bold('APP INSTALLATION'));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function delay(ms: number) {
|
|
130
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
131
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -9,8 +9,10 @@ import { MergeOptionSets } from './types/option-set-merger';
|
|
|
9
9
|
import { MergeTypeProperties } from './types/types-properties-merger';
|
|
10
10
|
import { ParseTypesFromFiles, ParseTypesFromLocalFiles, ParseTypesFromTemplateFromLocalFiles } from './types/parse-types';
|
|
11
11
|
import { loadColors, deleteColors } from './colors/color-loader';
|
|
12
|
-
import {listConfig, removeConfig} from './config/configCommands';
|
|
13
|
-
import {deleteWorkflowTemplate, deployWorkflowTemplate, listWorkflowTemplate} from './workflows/workflowCommands';
|
|
12
|
+
import { listConfig, removeConfig } from './config/configCommands';
|
|
13
|
+
import { deleteWorkflowTemplate, deployWorkflowTemplate, listWorkflowTemplate } from './workflows/workflowCommands';
|
|
14
|
+
import { installApp, uninstallApp } from './apps/install-app/app-installer';
|
|
15
|
+
import { upgradeApp } from './apps/upgrade-app/app-upgrader';
|
|
14
16
|
const cliUsage = require('command-line-usage');
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -22,7 +24,7 @@ export async function cli(args) {
|
|
|
22
24
|
const fullCommand = args[2];
|
|
23
25
|
let domain;
|
|
24
26
|
let command;
|
|
25
|
-
if(!fullCommand) {
|
|
27
|
+
if (!fullCommand) {
|
|
26
28
|
printIllegalCommand();
|
|
27
29
|
return;
|
|
28
30
|
}
|
|
@@ -33,7 +35,7 @@ export async function cli(args) {
|
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
if (commandParts?.length < 2 && commandParts[0].toLowerCase() !== 'help'
|
|
38
|
+
if (commandParts?.length < 2 && commandParts[0].toLowerCase() !== 'help' && commandParts[0].toLowerCase() !== '-help' && commandParts[0].toLowerCase() !== '--help') {
|
|
37
39
|
printIllegalCommand();
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
@@ -56,6 +58,18 @@ export async function cli(args) {
|
|
|
56
58
|
removeApp(args);
|
|
57
59
|
break;
|
|
58
60
|
}
|
|
61
|
+
case 'install': {
|
|
62
|
+
installApp();
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case 'uninstall': {
|
|
66
|
+
uninstallApp();
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case 'upgrade': {
|
|
70
|
+
upgradeApp();
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
59
73
|
default: {
|
|
60
74
|
printIllegalCommand();
|
|
61
75
|
}
|
|
@@ -210,6 +224,8 @@ const helpSections = [
|
|
|
210
224
|
'. a template directly to the org',
|
|
211
225
|
'deploy Deploy your app.',
|
|
212
226
|
'remove Remove your app.',
|
|
227
|
+
'install Install an app to your org.',
|
|
228
|
+
'uninstall Uninstall an app from your org.',
|
|
213
229
|
],
|
|
214
230
|
},
|
|
215
231
|
{
|