@contrail/cli 1.0.18 → 1.0.20

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.
@@ -1,10 +1,10 @@
1
1
  import { App, AppDeclaration, AppActionDeclaration } from './interfaces';
2
+ export declare function getActionsForApp(app: App): Promise<any>;
3
+ export declare function getActionDeclarationIdentifier(actionKey: any, app: App): string;
2
4
  export declare class ActionsDeployer {
3
5
  deployActions(appDeclaration: AppDeclaration, app: App): Promise<void>;
4
- getActionsForApp(app: App): Promise<any>;
5
6
  deployAction(actionKey: string, actionDeclaration: AppActionDeclaration, app: App, existingActions: Array<any>): Promise<void>;
6
7
  private createNewActionDefinition;
7
8
  private updateActionDefinition;
8
- getActionDeclarationIdentifier(actionKey: any, app: App): string;
9
9
  getFunction(actionDeclaration: AppActionDeclaration): Promise<string>;
10
10
  }
@@ -3,25 +3,28 @@ import chalk from 'chalk';
3
3
  import { ConfigPropertiesParser } from './app-template-parsers';
4
4
  import { getSeparatorByPlatform } from '../../common';
5
5
  import fs from 'fs';
6
+ export async function getActionsForApp(app) {
7
+ return await new Entities().get({
8
+ entityName: 'event-workflow-action-definition',
9
+ criteria: { ownerKey: `app:${app.id}` },
10
+ });
11
+ }
12
+ export function getActionDeclarationIdentifier(actionKey, app) {
13
+ return app.identifier + ':' + actionKey;
14
+ }
6
15
  export class ActionsDeployer {
7
16
  async deployActions(appDeclaration, app) {
8
17
  // FETCH ALL ACTIONS FROM THE API.
9
- const existingActions = await this.getActionsForApp(app);
18
+ const existingActions = await getActionsForApp(app);
10
19
  // UPSERT / DELETE BASED ON ACTION IDENTIFIER
11
20
  console.log('%s \tDeploying actions.', chalk.yellow.bold('ACTION'));
12
21
  for (const actionKey of Object.keys(appDeclaration.actions)) {
13
22
  await this.deployAction(actionKey, appDeclaration.actions[actionKey], app, existingActions);
14
23
  }
15
24
  }
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
25
  async deployAction(actionKey, actionDeclaration, app, existingActions) {
23
26
  console.log(`%s \tDeploying action: ${actionKey}`, chalk.yellow.bold('ACTION'));
24
- const identifier = this.getActionDeclarationIdentifier(actionKey, app);
27
+ const identifier = getActionDeclarationIdentifier(actionKey, app);
25
28
  const existingDefinition = existingActions.find(ad => ad.identifier === identifier);
26
29
  if (!existingDefinition) {
27
30
  await this.createNewActionDefinition(actionKey, actionDeclaration, app);
@@ -36,7 +39,7 @@ export class ActionsDeployer {
36
39
  configProperties = ConfigPropertiesParser.getConfigProperties(actionDeclaration.actionConfiguration);
37
40
  }
38
41
  const newObject = {
39
- identifier: this.getActionDeclarationIdentifier(actionKey, app),
42
+ identifier: getActionDeclarationIdentifier(actionKey, app),
40
43
  name: actionDeclaration.name,
41
44
  ownerKey: `app:${app.id}`,
42
45
  public: app.public,
@@ -78,9 +81,6 @@ export class ActionsDeployer {
78
81
  console.log(`%s Updated existing action definition: ${actionDefinition.identifier}`, chalk.yellow.bold('APP'));
79
82
  return actionDefinition;
80
83
  }
81
- getActionDeclarationIdentifier(actionKey, app) {
82
- return app.identifier + ':' + actionKey;
83
- }
84
84
  async getFunction(actionDeclaration) {
85
85
  if (!actionDeclaration.handler) {
86
86
  return;
@@ -1,6 +1,6 @@
1
1
  import { App, AppDeclaration } from './interfaces';
2
2
  export declare class AppDeployer {
3
- deploy(appDeclaration: AppDeclaration): Promise<void>;
3
+ deploy(appDeclaration: AppDeclaration, deployWorkflowTemplateOnly?: boolean): Promise<void>;
4
4
  getExistingApp(appDeclaration: AppDeclaration): Promise<App>;
5
5
  private createNewApp;
6
6
  updateExistingApp(appDeclaration: AppDeclaration, existingApp: any): Promise<any>;
@@ -3,8 +3,10 @@ import chalk from 'chalk';
3
3
  import { Auth } from '../../auth/auth';
4
4
  import { ActionsDeployer } from './actions-deployer';
5
5
  import { TriggerKeyDefinitionParser, ConfigPropertiesParser } from './app-template-parsers';
6
+ import { WorkflowDefinitionDeployer } from './workflow-definition-deployer';
7
+ import { WorkflowDeployer } from './workflow-deployer';
6
8
  export class AppDeployer {
7
- async deploy(appDeclaration) {
9
+ async deploy(appDeclaration, deployWorkflowTemplateOnly = false) {
8
10
  let app = await this.getExistingApp(appDeclaration);
9
11
  if (!app) {
10
12
  console.log(`%s Creating new app.`, chalk.yellow.bold('APP'));
@@ -15,6 +17,12 @@ export class AppDeployer {
15
17
  app = await this.updateExistingApp(appDeclaration, app);
16
18
  }
17
19
  await new ActionsDeployer().deployActions(appDeclaration, app);
20
+ if (deployWorkflowTemplateOnly) {
21
+ await new WorkflowDeployer().deployWorkflows(appDeclaration, app);
22
+ }
23
+ else {
24
+ await new WorkflowDefinitionDeployer().deployWorkflowDefinitions(appDeclaration, app);
25
+ }
18
26
  }
19
27
  async getExistingApp(appDeclaration) {
20
28
  const orgId = Auth.config.orgId;
@@ -26,9 +26,10 @@ export async function deployApp(args) {
26
26
  return;
27
27
  }
28
28
  console.log(`%s Deploying Application. ${appDeclaration.appIdentifier}`, chalk.blue.bold('START'));
29
+ const templateOnly = args && args.includes('--template-only');
29
30
  try {
30
31
  await authUser();
31
- await new AppDeployer().deploy(appDeclaration);
32
+ await new AppDeployer().deploy(appDeclaration, templateOnly);
32
33
  }
33
34
  catch (e) {
34
35
  console.log(`%s Application Deployment Failed. ${e}`, chalk.red.bold('FAIL'));
@@ -18,6 +18,9 @@ export interface AppDeclaration {
18
18
  triggers: {
19
19
  [key: string]: AppTriggerDeclaration;
20
20
  };
21
+ workflows?: {
22
+ [key: string]: WorkflowDefinitionDeclaration | WorkflowTemplateDeclaration;
23
+ };
21
24
  }
22
25
  export interface ConfigPropertyElements {
23
26
  [key: string]: ConfigPropertyElement;
@@ -39,6 +42,44 @@ export interface AppTriggerDeclaration {
39
42
  external?: boolean;
40
43
  description?: string;
41
44
  }
45
+ export interface WorkflowDefinitionDeclaration {
46
+ id?: string;
47
+ identifier: string;
48
+ name: string;
49
+ triggerKey: string;
50
+ workflowDefinition: WorkflowDefinition;
51
+ ownerKey: string;
52
+ isActive: boolean;
53
+ public: boolean;
54
+ supportingAppId?: string;
55
+ }
56
+ export interface WorkflowTemplateDeclaration {
57
+ id?: string;
58
+ identifier: string;
59
+ name: string;
60
+ triggerKey: string;
61
+ workflowDefinition: WorkflowDefinition;
62
+ ownerKey: string;
63
+ isActive: boolean;
64
+ supportingAppId?: string;
65
+ messageGroupId?: string;
66
+ parallelWorkerCount?: number;
67
+ }
68
+ export interface WorkflowDefinition {
69
+ paths: WorkflowConditional[];
70
+ }
71
+ export interface WorkflowConditional {
72
+ conditional: string;
73
+ actionDefinitionConfigs: ActionDefinitionConfig[];
74
+ }
75
+ export interface ActionDefinitionConfig {
76
+ name: string;
77
+ action: string;
78
+ config?: {
79
+ [key: string]: any;
80
+ };
81
+ id?: string;
82
+ }
42
83
  export interface App {
43
84
  id: string;
44
85
  identifier: string;
@@ -0,0 +1,10 @@
1
+ import { App, AppDeclaration, WorkflowDefinitionDeclaration } from './interfaces';
2
+ export declare class WorkflowDefinitionDeployer {
3
+ deployWorkflowDefinitions(appDeclaration: AppDeclaration, app: App): Promise<void>;
4
+ getWorkflowDefinitionsForApp(app: App): Promise<any>;
5
+ deployWorkflow(workflowKey: string, workflow: WorkflowDefinitionDeclaration, 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,85 @@
1
+ import { Entities } from '@contrail/sdk';
2
+ import chalk from 'chalk';
3
+ import { getActionDeclarationIdentifier, getActionsForApp } from './actions-deployer';
4
+ export class WorkflowDefinitionDeployer {
5
+ async deployWorkflowDefinitions(appDeclaration, app) {
6
+ // FETCH ALL WORKFLOWS FROM THE API.
7
+ const existingWorkflows = await this.getWorkflowDefinitionsForApp(app);
8
+ // UPSERT / DELETE BASED ON WORKFLOW IDENTIFIER
9
+ console.log('%s \tDeploying workflow definitions.', chalk.yellow.bold('WORKFLOW DEFINITION'));
10
+ for (const workflowKey of Object.keys(appDeclaration.workflows)) {
11
+ await this.deployWorkflow(workflowKey, appDeclaration.workflows[workflowKey], app, existingWorkflows, appDeclaration);
12
+ }
13
+ }
14
+ async getWorkflowDefinitionsForApp(app) {
15
+ return await new Entities().get({
16
+ entityName: 'event-workflow-template-definition',
17
+ criteria: { ownerKey: `app:${app.id}` },
18
+ });
19
+ }
20
+ async deployWorkflow(workflowKey, workflow, app, existingWorkflows, appDeclaration) {
21
+ console.log(`%s \tDeploying workflow definitions: ${workflowKey}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
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
+ public: workflowDeclaration.public,
38
+ isActive: workflowDeclaration.isActive,
39
+ };
40
+ const workflowTemplate = await new Entities().create({
41
+ entityName: 'event-workflow-template-definition',
42
+ object: newObject,
43
+ });
44
+ console.log(`%s Created new workflow definition definitions: ${workflowKey}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
45
+ return workflowTemplate;
46
+ }
47
+ async populateActionIds(workflowDeclaration, appDeclaration, app) {
48
+ var _a;
49
+ let availableAppActions = await getActionsForApp(app);
50
+ if (!((_a = workflowDeclaration.workflowDefinition) === null || _a === void 0 ? void 0 : _a.paths)) {
51
+ return;
52
+ }
53
+ for (const path of workflowDeclaration.workflowDefinition.paths) {
54
+ if (!(path === null || path === void 0 ? void 0 : path.actionDefinitionConfigs)) {
55
+ continue;
56
+ }
57
+ for (const actionConfiguration of path.actionDefinitionConfigs) {
58
+ const actionKey = actionConfiguration.action;
59
+ const action = availableAppActions.find(action => action.identifier === getActionDeclarationIdentifier(actionKey, app));
60
+ if (!action) {
61
+ throw new Error(`Could not find action ${actionKey} declared in workflow definition ${workflowDeclaration.name}`);
62
+ }
63
+ actionConfiguration.id = action.id;
64
+ }
65
+ }
66
+ }
67
+ async updateWorkflowDefinition(existingWorkflowDefinition, workflowDeclaration, app, appDeclaration) {
68
+ await this.populateActionIds(workflowDeclaration, appDeclaration, app);
69
+ const updates = Object.assign(new Object(), {
70
+ name: workflowDeclaration.name,
71
+ triggerKey: workflowDeclaration.triggerKey,
72
+ workflowDefinition: workflowDeclaration.workflowDefinition
73
+ });
74
+ const updatedWorkflowDefinition = await new Entities().update({
75
+ entityName: 'event-workflow-template-definition',
76
+ id: existingWorkflowDefinition.id,
77
+ object: updates,
78
+ });
79
+ console.log(`%s Updated existing workflow definition: ${updatedWorkflowDefinition.identifier}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
80
+ return updatedWorkflowDefinition;
81
+ }
82
+ getWorkflowDeclarationIdentifier(actionKey, app) {
83
+ return app.identifier + ':' + actionKey;
84
+ }
85
+ }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/cli",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "VibeIQ's Contrail Platform CLI.",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -6,11 +6,24 @@ import { getSeparatorByPlatform } from '../../common';
6
6
 
7
7
  import fs from 'fs';
8
8
 
9
+
10
+ export async function getActionsForApp(app: App) {
11
+ return await new Entities().get({
12
+ entityName: 'event-workflow-action-definition',
13
+ criteria: { ownerKey: `app:${app.id}` },
14
+ });
15
+ }
16
+
17
+
18
+ export function getActionDeclarationIdentifier(actionKey, app: App) {
19
+ return app.identifier + ':' + actionKey;
20
+ }
21
+
9
22
  export class ActionsDeployer {
10
23
  async deployActions(appDeclaration: AppDeclaration, app: App) {
11
24
 
12
25
  // FETCH ALL ACTIONS FROM THE API.
13
- const existingActions = await this.getActionsForApp(app);
26
+ const existingActions = await getActionsForApp(app);
14
27
 
15
28
  // UPSERT / DELETE BASED ON ACTION IDENTIFIER
16
29
  console.log('%s \tDeploying actions.', chalk.yellow.bold('ACTION'));
@@ -19,18 +32,10 @@ export class ActionsDeployer {
19
32
  await this.deployAction(actionKey, appDeclaration.actions[actionKey], app, existingActions);
20
33
  }
21
34
  }
22
-
23
- async getActionsForApp(app: App) {
24
- return await new Entities().get({
25
- entityName: 'event-workflow-action-definition',
26
- criteria: { ownerKey: `app:${app.id}` },
27
- });
28
- }
29
-
30
35
  async deployAction(actionKey: string, actionDeclaration: AppActionDeclaration, app: App, existingActions: Array<any>) {
31
36
  console.log(`%s \tDeploying action: ${actionKey}`, chalk.yellow.bold('ACTION'));
32
37
 
33
- const identifier = this.getActionDeclarationIdentifier(actionKey, app);
38
+ const identifier = getActionDeclarationIdentifier(actionKey, app);
34
39
  const existingDefinition = existingActions.find(ad => ad.identifier === identifier);
35
40
 
36
41
  if (!existingDefinition) {
@@ -46,7 +51,7 @@ export class ActionsDeployer {
46
51
  configProperties = ConfigPropertiesParser.getConfigProperties(actionDeclaration.actionConfiguration);
47
52
  }
48
53
  const newObject: EventWorkflowActionDefinition = {
49
- identifier: this.getActionDeclarationIdentifier(actionKey, app),
54
+ identifier: getActionDeclarationIdentifier(actionKey, app),
50
55
  name: actionDeclaration.name,
51
56
  ownerKey: `app:${app.id}`,
52
57
  public: app.public, // TODO - an actions visibility is not tied to the visibility of the app
@@ -100,9 +105,6 @@ export class ActionsDeployer {
100
105
  return actionDefinition;
101
106
  }
102
107
 
103
- getActionDeclarationIdentifier(actionKey, app: App) {
104
- return app.identifier + ':' + actionKey;
105
- }
106
108
 
107
109
  async getFunction(actionDeclaration: AppActionDeclaration): Promise<string> {
108
110
  if (!actionDeclaration.handler) {
@@ -4,9 +4,11 @@ import { Auth } from '../../auth/auth';
4
4
  import { ActionsDeployer } from './actions-deployer';
5
5
  import { App, AppDeclaration } from './interfaces';
6
6
  import { TriggerKeyDefinitionParser, ConfigPropertiesParser } from './app-template-parsers';
7
+ import { WorkflowDefinitionDeployer } from './workflow-definition-deployer';
8
+ import { WorkflowDeployer } from './workflow-deployer';
7
9
 
8
10
  export class AppDeployer {
9
- public async deploy(appDeclaration: AppDeclaration) {
11
+ public async deploy(appDeclaration: AppDeclaration, deployWorkflowTemplateOnly = false) {
10
12
  let app = await this.getExistingApp(appDeclaration);
11
13
  if (!app) {
12
14
  console.log(`%s Creating new app.`, chalk.yellow.bold('APP'));
@@ -16,6 +18,11 @@ export class AppDeployer {
16
18
  app = await this.updateExistingApp(appDeclaration, app);
17
19
  }
18
20
  await new ActionsDeployer().deployActions(appDeclaration, app);
21
+ if (deployWorkflowTemplateOnly) {
22
+ await new WorkflowDeployer().deployWorkflows(appDeclaration, app);
23
+ } else {
24
+ await new WorkflowDefinitionDeployer().deployWorkflowDefinitions(appDeclaration, app);
25
+ }
19
26
  }
20
27
 
21
28
  public async getExistingApp(appDeclaration: AppDeclaration): Promise<App> {
@@ -29,10 +29,10 @@ export async function deployApp(args) {
29
29
  }
30
30
 
31
31
  console.log(`%s Deploying Application. ${appDeclaration.appIdentifier}`, chalk.blue.bold('START'));
32
-
32
+ const templateOnly = args && args.includes('--template-only')
33
33
  try {
34
34
  await authUser();
35
- await new AppDeployer().deploy(appDeclaration);
35
+ await new AppDeployer().deploy(appDeclaration, templateOnly);
36
36
  }
37
37
  catch (e) {
38
38
  console.log(`%s Application Deployment Failed. ${e}`, chalk.red.bold('FAIL'));
@@ -14,6 +14,7 @@ export interface AppDeclaration {
14
14
  actions?: { [key: string]: AppActionDeclaration };
15
15
  appConfiguration: ConfigPropertyElements;
16
16
  triggers: { [key: string]: AppTriggerDeclaration };
17
+ workflows?: { [key: string]: WorkflowDefinitionDeclaration|WorkflowTemplateDeclaration }
17
18
  }
18
19
 
19
20
  export interface ConfigPropertyElements {
@@ -40,6 +41,47 @@ export interface AppTriggerDeclaration {
40
41
  description?: string;
41
42
  }
42
43
 
44
+
45
+ export interface WorkflowDefinitionDeclaration {
46
+ id?: string;
47
+ identifier: string;
48
+ name: string;
49
+ triggerKey: string;
50
+ workflowDefinition: WorkflowDefinition;
51
+ ownerKey: string;
52
+ isActive: boolean;
53
+ public: boolean;
54
+ supportingAppId?: string;
55
+ }
56
+
57
+ export interface WorkflowTemplateDeclaration {
58
+ id?: string;
59
+ identifier: string;
60
+ name: string;
61
+ triggerKey: string;
62
+ workflowDefinition: WorkflowDefinition;
63
+ ownerKey: string;
64
+ isActive: boolean;
65
+ supportingAppId?: string;
66
+ messageGroupId?: string;
67
+ parallelWorkerCount?: number;
68
+ }
69
+
70
+ export interface WorkflowDefinition {
71
+ paths: WorkflowConditional[]
72
+ }
73
+
74
+ export interface WorkflowConditional {
75
+ conditional: string;
76
+ actionDefinitionConfigs: ActionDefinitionConfig[]
77
+ }
78
+ export interface ActionDefinitionConfig {
79
+ name: string;
80
+ action: string;
81
+ config?: {[key: string]: any};
82
+ id?: string;
83
+ }
84
+
43
85
  export interface App {
44
86
  id: string;
45
87
  identifier: string;
@@ -0,0 +1,110 @@
1
+ import { Entities } from '@contrail/sdk';
2
+ import chalk from 'chalk';
3
+ import { App, AppDeclaration, WorkflowDefinitionDeclaration } from './interfaces';
4
+ import { getActionDeclarationIdentifier, getActionsForApp } from './actions-deployer'
5
+
6
+ export class WorkflowDefinitionDeployer {
7
+ async deployWorkflowDefinitions(appDeclaration: AppDeclaration, app: App) {
8
+
9
+ // FETCH ALL WORKFLOWS FROM THE API.
10
+ const existingWorkflows = await this.getWorkflowDefinitionsForApp(app);
11
+
12
+ // UPSERT / DELETE BASED ON WORKFLOW IDENTIFIER
13
+ console.log('%s \tDeploying workflow definitions.', chalk.yellow.bold('WORKFLOW DEFINITION'));
14
+
15
+ for (const workflowKey of Object.keys(appDeclaration.workflows)) {
16
+ await this.deployWorkflow(
17
+ workflowKey, appDeclaration.workflows[workflowKey] as WorkflowDefinitionDeclaration,
18
+ app,
19
+ existingWorkflows,
20
+ appDeclaration
21
+ );
22
+ }
23
+ }
24
+
25
+ async getWorkflowDefinitionsForApp(app: App) {
26
+ return await new Entities().get({
27
+ entityName: 'event-workflow-template-definition',
28
+ criteria: { ownerKey: `app:${app.id}` },
29
+ });
30
+ }
31
+
32
+ async deployWorkflow(workflowKey: string, workflow: WorkflowDefinitionDeclaration, app: App, existingWorkflows: Array<any>, appDeclaration: AppDeclaration) {
33
+ console.log(`%s \tDeploying workflow definitions: ${workflowKey}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
34
+
35
+ const identifier = this.getWorkflowDeclarationIdentifier(workflowKey, app);
36
+ const existingDefinition = existingWorkflows.find(ad => ad.identifier === identifier);
37
+
38
+ if (existingDefinition) {
39
+ return await this.updateWorkflowDefinition(existingDefinition, workflow, app, appDeclaration);
40
+ }
41
+ return await this.createNewWorkflowDefinition(workflowKey, workflow, app, appDeclaration);
42
+ }
43
+
44
+ private async createNewWorkflowDefinition(workflowKey: string, workflowDeclaration: WorkflowDefinitionDeclaration, app: App, appDeclaration: AppDeclaration) {
45
+ await this.populateActionIds(workflowDeclaration, appDeclaration, app);
46
+ const newObject: WorkflowDefinitionDeclaration = {
47
+ identifier: this.getWorkflowDeclarationIdentifier(workflowKey, app),
48
+ name: workflowDeclaration.name,
49
+ ownerKey: `app:${app.id}`,
50
+ triggerKey: workflowDeclaration.triggerKey,
51
+ workflowDefinition: workflowDeclaration.workflowDefinition,
52
+ public: workflowDeclaration.public,
53
+ isActive: workflowDeclaration.isActive,
54
+ };
55
+
56
+ const workflowTemplate = await new Entities().create({
57
+ entityName: 'event-workflow-template-definition',
58
+ object: newObject,
59
+ });
60
+
61
+ console.log(`%s Created new workflow definition definitions: ${workflowKey}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
62
+
63
+ return workflowTemplate;
64
+ }
65
+
66
+ private async populateActionIds(workflowDeclaration: WorkflowDefinitionDeclaration, appDeclaration: AppDeclaration, app: App) {
67
+ let availableAppActions = await getActionsForApp(app);
68
+ if (!workflowDeclaration.workflowDefinition?.paths) {
69
+ return;
70
+ }
71
+ for (const path of workflowDeclaration.workflowDefinition.paths) {
72
+ if (!path?.actionDefinitionConfigs) {
73
+ continue;
74
+ }
75
+ for (const actionConfiguration of path.actionDefinitionConfigs) {
76
+ const actionKey = actionConfiguration.action;
77
+ const action = availableAppActions.find(
78
+ action => action.identifier === getActionDeclarationIdentifier(actionKey, app)
79
+ )
80
+ if (!action) {
81
+ throw new Error(`Could not find action ${actionKey} declared in workflow definition ${workflowDeclaration.name}`);
82
+ }
83
+ actionConfiguration.id = action.id;
84
+ }
85
+ }
86
+ }
87
+
88
+ private async updateWorkflowDefinition(existingWorkflowDefinition: WorkflowDefinitionDeclaration, workflowDeclaration: WorkflowDefinitionDeclaration, app: App, appDeclaration: AppDeclaration) {
89
+ await this.populateActionIds(workflowDeclaration, appDeclaration, app);
90
+ const updates = Object.assign(new Object(), {
91
+ name: workflowDeclaration.name,
92
+ triggerKey: workflowDeclaration.triggerKey,
93
+ workflowDefinition: workflowDeclaration.workflowDefinition
94
+ });
95
+
96
+ const updatedWorkflowDefinition = await new Entities().update({
97
+ entityName: 'event-workflow-template-definition',
98
+ id: existingWorkflowDefinition.id,
99
+ object: updates,
100
+ });
101
+
102
+ console.log(`%s Updated existing workflow definition: ${updatedWorkflowDefinition.identifier}`, chalk.yellow.bold('WORKFLOW DEFINITION'));
103
+
104
+ return updatedWorkflowDefinition;
105
+ }
106
+
107
+ getWorkflowDeclarationIdentifier(actionKey, app: App) {
108
+ return app.identifier + ':' + actionKey;
109
+ }
110
+ }
@@ -0,0 +1,111 @@
1
+ import { Entities } from '@contrail/sdk';
2
+ import chalk from 'chalk';
3
+ import { App, AppDeclaration, WorkflowTemplateDeclaration } from './interfaces';
4
+ import { getActionDeclarationIdentifier, getActionsForApp } from './actions-deployer'
5
+
6
+ export class WorkflowDeployer {
7
+ async deployWorkflows(appDeclaration: AppDeclaration, app: App) {
8
+
9
+ // FETCH ALL WORKFLOWS FROM THE API.
10
+ const existingWorkflows = await this.getWorkflowsForApp(app);
11
+
12
+ // UPSERT / DELETE BASED ON WORKFLOW IDENTIFIER
13
+ console.log('%s \tDeploying workflows.', chalk.yellow.bold('WORKFLOW'));
14
+
15
+ for (const workflowKey of Object.keys(appDeclaration.workflows)) {
16
+ await this.deployWorkflow(workflowKey,
17
+ appDeclaration.workflows[workflowKey] as WorkflowTemplateDeclaration,
18
+ app,
19
+ existingWorkflows,
20
+ appDeclaration);
21
+ }
22
+ }
23
+
24
+ async getWorkflowsForApp(app: App) {
25
+ return await new Entities().get({
26
+ entityName: 'event-workflow-template',
27
+ criteria: { ownerKey: `app:${app.id}` },
28
+ });
29
+ }
30
+
31
+ async deployWorkflow(workflowKey: string, workflow: WorkflowTemplateDeclaration, app: App, existingWorkflows: Array<any>, appDeclaration: AppDeclaration) {
32
+ console.log(`%s \tDeploying workflow: ${workflowKey}`, chalk.yellow.bold('WORKFLOW'));
33
+
34
+ const identifier = this.getWorkflowDeclarationIdentifier(workflowKey, app);
35
+ const existingDefinition = existingWorkflows.find(ad => ad.identifier === identifier);
36
+
37
+ if (existingDefinition) {
38
+ return await this.updateWorkflowDefinition(existingDefinition, workflow, app, appDeclaration);
39
+ }
40
+ return await this.createNewWorkflowDefinition(workflowKey, workflow, app, appDeclaration);
41
+ }
42
+
43
+ private async createNewWorkflowDefinition(workflowKey: string, workflowDeclaration: WorkflowTemplateDeclaration, app: App, appDeclaration: AppDeclaration) {
44
+ await this.populateActionIds(workflowDeclaration, appDeclaration, app);
45
+ const newObject: WorkflowTemplateDeclaration = {
46
+ identifier: this.getWorkflowDeclarationIdentifier(workflowKey, app),
47
+ name: workflowDeclaration.name,
48
+ ownerKey: `app:${app.id}`,
49
+ triggerKey: workflowDeclaration.triggerKey,
50
+ workflowDefinition: workflowDeclaration.workflowDefinition,
51
+ isActive: workflowDeclaration.isActive,
52
+ supportingAppId: workflowDeclaration.supportingAppId,
53
+ messageGroupId: workflowDeclaration.messageGroupId,
54
+ parallelWorkerCount: workflowDeclaration.parallelWorkerCount,
55
+ };
56
+
57
+ const workflowTemplate = await new Entities().create({
58
+ entityName: 'event-workflow-template',
59
+ object: newObject,
60
+ });
61
+
62
+ console.log(`%s Created new workflow definition: ${workflowKey}`, chalk.yellow.bold('WORKFLOW'));
63
+
64
+ return workflowTemplate;
65
+ }
66
+
67
+ private async populateActionIds(workflowDeclaration: WorkflowTemplateDeclaration, appDeclaration: AppDeclaration, app: App) {
68
+ let availableAppActions = await getActionsForApp(app);
69
+ if (!workflowDeclaration.workflowDefinition?.paths) {
70
+ return;
71
+ }
72
+ for (const path of workflowDeclaration.workflowDefinition.paths) {
73
+ if (!path?.actionDefinitionConfigs) {
74
+ continue;
75
+ }
76
+ for (const actionConfiguration of path.actionDefinitionConfigs) {
77
+ const actionKey = actionConfiguration.action;
78
+ const action = availableAppActions.find(
79
+ action => action.identifier === getActionDeclarationIdentifier(actionKey, app)
80
+ )
81
+ if (!action) {
82
+ throw new Error(`Could not find action ${actionKey} declared in ${workflowDeclaration.name}`);
83
+ }
84
+ actionConfiguration.id = action.id;
85
+ }
86
+ }
87
+ }
88
+
89
+ private async updateWorkflowDefinition(existingWorkflowDefinition: WorkflowTemplateDeclaration, workflowDeclaration: WorkflowTemplateDeclaration, app: App, appDeclaration: AppDeclaration) {
90
+ await this.populateActionIds(workflowDeclaration, appDeclaration, app);
91
+ const updates = Object.assign(new Object(), {
92
+ name: workflowDeclaration.name,
93
+ triggerKey: workflowDeclaration.triggerKey,
94
+ workflowDefinition: workflowDeclaration.workflowDefinition
95
+ });
96
+
97
+ const updatedWorkflowDefinition = await new Entities().update({
98
+ entityName: 'event-workflow-template',
99
+ id: existingWorkflowDefinition.id,
100
+ object: updates,
101
+ });
102
+
103
+ console.log(`%s Updated existing workflow definition: ${updatedWorkflowDefinition.identifier}`, chalk.yellow.bold('WORKFLOW'));
104
+
105
+ return updatedWorkflowDefinition;
106
+ }
107
+
108
+ getWorkflowDeclarationIdentifier(actionKey, app: App) {
109
+ return app.identifier + ':' + actionKey;
110
+ }
111
+ }
@@ -38,17 +38,16 @@ triggers:
38
38
  external: true
39
39
  description: "Triggers on externally created products"
40
40
 
41
- # TODO - This is not read
42
- workflows:
43
- workflow1:
44
- name: "Update item on product creation"
45
- triggerKey: product|create
46
- paths:
47
- path1:
48
- conditional: "{retailPrice} > 10"
49
- actions:
50
- updateItem:
51
- actionId: updateItem
52
- configuration:
53
-
54
-
41
+ # workflows:
42
+ # workflow1:
43
+ # triggerKey: item|update
44
+ # isActive: true
45
+ # name: test wf from cli
46
+ # workflowDefinition:
47
+ # paths:
48
+ # - conditional: "true"
49
+ # actionDefinitionConfigs:
50
+ # - action: updateProduct
51
+ # name: handle product update
52
+ # config:
53
+ # message: "hello world"