@contrail/cli 3.5.3-alpha.0e9434c → 3.5.3-alpha.317fe63

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 CHANGED
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
17
17
  $ contrail COMMAND
18
18
  running command...
19
19
  $ contrail (--version)
20
- @contrail/cli/3.5.3-alpha.0e9434c linux-x64 node-v24.16.0
20
+ @contrail/cli/3.5.3-alpha.317fe63 linux-x64 node-v24.19.0
21
21
  $ contrail --help [COMMAND]
22
22
  USAGE
23
23
  $ contrail COMMAND
@@ -779,23 +779,29 @@ EXAMPLES
779
779
 
780
780
  ## `contrail projects export PROJECTNAME`
781
781
 
782
- Export a project.
782
+ Export a project with optional post-processing.
783
783
 
784
784
  ```
785
785
  USAGE
786
- $ contrail projects export PROJECTNAME [-l error|warn|info|debug] [-e true|false]
786
+ $ contrail projects export PROJECTNAME [-l error|warn|info|debug] [-e true|false] [--process]
787
+
788
+ ARGUMENTS
789
+ PROJECTNAME Name or identifier of the project to export
787
790
 
788
791
  FLAGS
789
792
  -e, --alwaysConfirmEnv=<option> [default: true] The Environment variables
790
793
  <options: true|false>
791
794
  -l, --logLevel=<option> The Log Level
792
795
  <options: error|warn|info|debug>
796
+ --process Enable post-processing of exported project data
793
797
 
794
798
  DESCRIPTION
795
- Export a project.
799
+ Export a project with optional post-processing.
796
800
 
797
801
  EXAMPLES
798
802
  $ contrail project export <PROJECT_NAME>
803
+
804
+ $ contrail project export <PROJECT_NAME> --process
799
805
  ```
800
806
 
801
807
  ## `contrail projects list`
@@ -1611,31 +1617,25 @@ EXAMPLES
1611
1617
 
1612
1618
  ## `contrail workflow-templates export`
1613
1619
 
1614
- Export all workflows in the org. By default exports one file per app to the current directory.
1620
+ Export all workflows in the org.
1615
1621
 
1616
1622
  ```
1617
1623
  USAGE
1618
- $ contrail workflow-templates export [-l error|warn|info|debug] [-e true|false] [-f <value>] [-d <value>]
1619
- [--overwrite]
1624
+ $ contrail workflow-templates export [-l error|warn|info|debug] [-e true|false] [-f <value>] [--overwrite]
1620
1625
 
1621
1626
  FLAGS
1622
- -d, --dir=<value> [default: .] Directory to write per-app workflow files
1623
1627
  -e, --alwaysConfirmEnv=<option> [default: true] The Environment variables
1624
1628
  <options: true|false>
1625
- -f, --outFile=<value> Export all workflows to a single file at this path (overrides per-app export)
1629
+ -f, --outFile=<value> [default: workflows.yml] The location for the file to be saved
1626
1630
  -l, --logLevel=<option> The Log Level
1627
1631
  <options: error|warn|info|debug>
1628
1632
  --overwrite Replaces existing files if they exist
1629
1633
 
1630
1634
  DESCRIPTION
1631
- Export all workflows in the org. By default exports one file per app to the current directory.
1635
+ Export all workflows in the org.
1632
1636
 
1633
1637
  EXAMPLES
1634
1638
  $ contrail workflow-templates export
1635
-
1636
- $ contrail workflow-templates export --dir ./workflows
1637
-
1638
- $ contrail workflow-templates export --outFile workflows.yml
1639
1639
  ```
1640
1640
 
1641
1641
  ## `contrail workflow-templates list`
@@ -3,6 +3,7 @@ export default class ExportProjects extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ process: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
6
7
  alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
7
8
  logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
9
  };
@@ -7,23 +7,37 @@ const login_1 = require("../../auth/login");
7
7
  const helpers_1 = require("../../helpers/projects/helpers");
8
8
  const basecommand_1 = require("../../basecommand");
9
9
  class ExportProjects extends basecommand_1.BaseCommand {
10
- static description = 'Export a project.';
10
+ static description = 'Export a project with optional post-processing.';
11
11
  static examples = [
12
- `$ contrail project export <PROJECT_NAME>
13
- `,
12
+ '$ contrail project export <PROJECT_NAME>',
13
+ '$ contrail project export <PROJECT_NAME> --process',
14
14
  ];
15
15
  static flags = {
16
16
  ...basecommand_1.BaseCommand.baseFlags,
17
+ process: core_1.Flags.boolean({
18
+ description: 'Enable post-processing of exported project data',
19
+ default: false,
20
+ }),
17
21
  };
18
22
  static args = {
19
- projectName: core_1.Args.string({ description: '', required: true }),
23
+ projectName: core_1.Args.string({
24
+ description: 'Name or identifier of the project to export',
25
+ required: true,
26
+ }),
20
27
  };
21
28
  async run() {
22
29
  try {
23
30
  await (0, login_1.authUser)();
24
31
  this.log(`${chalk_1.default.blue.bold('START')} Exporting Project.`);
32
+ const processingOptions = {
33
+ removeBackings: true,
34
+ fixMissingIdentifiers: true,
35
+ cleanProperties: true,
36
+ analyzeOnly: false,
37
+ verbose: false,
38
+ };
25
39
  this.log(`${chalk_1.default.yellow.bold('PROJECT')} Exporting`);
26
- await (0, helpers_1.exportProject)(this.args.projectName);
40
+ await (0, helpers_1.exportProject)(this.args.projectName, this.flags.process ? processingOptions : null);
27
41
  this.log(`${chalk_1.default.yellow.bold('PROJECT')} Exported`);
28
42
  }
29
43
  catch (e) {
@@ -5,12 +5,10 @@ export default class ExportWorkflows extends BaseCommand {
5
5
  static examples: string[];
6
6
  static flags: {
7
7
  outFile: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
- dir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
9
8
  overwrite: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
9
  alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
10
  logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
12
11
  };
13
12
  run(): Promise<void>;
14
13
  }
15
- export declare function exportWorkflowsByApp(outputDir: string, overwrite: boolean): Promise<void>;
16
- export declare function exportWorkflowsToFile(fileLocation: string, overwrite: boolean): Promise<void>;
14
+ export declare function exportWorkflows(fileLocation: string, overwrite: boolean): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.exportWorkflowsToFile = exports.exportWorkflowsByApp = exports.DEFAULT_FILE_LOCATION = void 0;
3
+ exports.exportWorkflows = exports.DEFAULT_FILE_LOCATION = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path = tslib_1.__importStar(require("path"));
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
@@ -11,30 +11,22 @@ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
11
11
  const core_1 = require("@oclif/core");
12
12
  const file_helpers_1 = require("../../helpers/file-helpers");
13
13
  const event_workflow_templates_1 = require("../../api/event-workflow-templates");
14
- const schedules_1 = require("../../api/schedules");
15
- const apps_1 = require("../../api/apps");
16
14
  const helper_1 = require("../../helpers/workflow-templates/helper");
17
15
  const basecommand_1 = require("../../basecommand");
18
16
  exports.DEFAULT_FILE_LOCATION = 'workflows.yml';
19
17
  class ExportWorkflows extends basecommand_1.BaseCommand {
20
- static description = 'Export all workflows in the org. By default exports one file per app to the current directory.';
18
+ static description = 'Export all workflows in the org.';
21
19
  static examples = [
22
- '$ contrail workflow-templates export',
23
- '$ contrail workflow-templates export --dir ./workflows',
24
- '$ contrail workflow-templates export --outFile workflows.yml',
20
+ `$ contrail workflow-templates export
21
+ `,
25
22
  ];
26
23
  static flags = {
27
24
  ...basecommand_1.BaseCommand.baseFlags,
28
25
  outFile: core_1.Flags.string({
29
26
  char: 'f',
30
- description: 'Export all workflows to a single file at this path (overrides per-app export)',
27
+ description: 'The location for the file to be saved',
31
28
  required: false,
32
- }),
33
- dir: core_1.Flags.string({
34
- char: 'd',
35
- description: 'Directory to write per-app workflow files',
36
- required: false,
37
- default: '.',
29
+ default: exports.DEFAULT_FILE_LOCATION,
38
30
  }),
39
31
  overwrite: core_1.Flags.boolean({
40
32
  description: 'Replaces existing files if they exist',
@@ -46,12 +38,7 @@ class ExportWorkflows extends basecommand_1.BaseCommand {
46
38
  try {
47
39
  await (0, login_1.authUser)();
48
40
  this.log(`${chalk_1.default.blue.bold('START')} Getting Workflows.`);
49
- if (this.flags.outFile) {
50
- await exportWorkflowsToFile(this.flags.outFile, this.flags.overwrite);
51
- }
52
- else {
53
- await exportWorkflowsByApp(this.flags.dir ?? '.', this.flags.overwrite);
54
- }
41
+ await exportWorkflows(this.flags.outFile ?? exports.DEFAULT_FILE_LOCATION, this.flags.overwrite);
55
42
  this.log(`${chalk_1.default.yellow.bold('WORKFLOWS')} Done`);
56
43
  }
57
44
  catch (e) {
@@ -62,95 +49,31 @@ class ExportWorkflows extends basecommand_1.BaseCommand {
62
49
  }
63
50
  }
64
51
  exports.default = ExportWorkflows;
65
- async function exportWorkflowsByApp(outputDir, overwrite) {
52
+ async function exportWorkflows(fileLocation, overwrite) {
66
53
  const allWorkflowsInOrg = await (0, event_workflow_templates_1.getExistingWorkflowTemplates)();
67
- const allSchedules = await (0, schedules_1.getExistingSchedules)();
68
- const installedApps = await (0, apps_1.getInstalledApps)();
69
- const appIdToIdentifier = new Map(installedApps.map((a) => [a.appId, a.app.identifier]));
70
- const workflowsByApp = new Map();
71
- const unassigned = [];
72
- for (const workflow of allWorkflowsInOrg) {
73
- let appIdentifier = workflow.supportingAppId
74
- ? appIdToIdentifier.get(workflow.supportingAppId)
75
- : undefined;
76
- // If no supportingAppId, try to extract app identifier from workflow identifier
77
- // Format: @{appIdentifier}@{randomId}:{workflowName}
78
- if (!appIdentifier && workflow.identifier) {
79
- const match = workflow.identifier.match(/^@([^@]+)@/);
80
- if (match) {
81
- // Keep the @ prefix in the appIdentifier
82
- appIdentifier = `@${match[1]}`;
83
- }
84
- }
85
- if (appIdentifier) {
86
- if (!workflowsByApp.has(appIdentifier)) {
87
- workflowsByApp.set(appIdentifier, []);
88
- }
89
- const workflows = workflowsByApp.get(appIdentifier);
90
- if (workflows) {
91
- workflows.push(workflow);
92
- }
93
- }
94
- else {
95
- unassigned.push(workflow);
96
- }
97
- }
98
- const resolvedDir = path.resolve(process.cwd(), outputDir);
99
- for (const [appIdentifier, workflows] of workflowsByApp.entries()) {
100
- const exportObj = { appIdentifier, workflows: [] };
101
- for (const workflow of workflows) {
102
- exportObj.workflows.push(await buildExportWorkflow(workflow, allSchedules));
103
- }
104
- const safeAppIdentifier = appIdentifier.replace(/[\\/]/g, '-');
105
- const filePath = path.join(resolvedDir, `workflows-${safeAppIdentifier}.yml`);
106
- await writeYamlFile(exportObj, filePath, overwrite);
107
- console.log(`${chalk_1.default.green('EXPORTED')} ${filePath}`);
108
- }
109
- if (unassigned.length > 0) {
110
- const unassignedObj = { workflows: [] };
111
- for (const workflow of unassigned) {
112
- unassignedObj.workflows.push(await buildExportWorkflow(workflow, allSchedules));
113
- }
114
- const unassignedFilePath = path.join(resolvedDir, 'workflows-unassigned.yml');
115
- await writeYamlFile(unassignedObj, unassignedFilePath, overwrite);
116
- console.log(`${chalk_1.default.yellow('EXPORTED')} ${unassignedFilePath} (unassigned workflows)`);
117
- }
118
- }
119
- exports.exportWorkflowsByApp = exportWorkflowsByApp;
120
- async function exportWorkflowsToFile(fileLocation, overwrite) {
121
- const allWorkflowsInOrg = await (0, event_workflow_templates_1.getExistingWorkflowTemplates)();
122
- const allSchedules = await (0, schedules_1.getExistingSchedules)();
123
54
  const workflowsObj = { workflows: [] };
124
55
  for (const workflow of allWorkflowsInOrg) {
125
- workflowsObj.workflows.push(await buildExportWorkflow(workflow, allSchedules));
126
- }
127
- const pathToWrite = path.resolve(process.cwd(), fileLocation);
128
- await writeYamlFile(workflowsObj, pathToWrite, overwrite);
129
- }
130
- exports.exportWorkflowsToFile = exportWorkflowsToFile;
131
- async function buildExportWorkflow(workflow, allSchedules) {
132
- const exportWorkflow = lodash_1.default.pick(workflow, [
133
- 'identifier',
134
- 'name',
135
- 'triggerKey',
136
- 'isActive',
137
- 'messageGroup',
138
- 'dynamicMessageGroupId',
139
- 'parallelWorkerCount',
140
- 'workflowDefinition',
141
- ]);
142
- const schedules = allSchedules.filter((s) => s.ownerReference === `event-workflow-template:${workflow.id}`);
143
- if (schedules.length > 0) {
144
- exportWorkflow.crons = schedules.map((s) => ({ name: s.name, cron: s.cron }));
56
+ const exportWorkflow = lodash_1.default.pick(workflow, [
57
+ 'triggerKey',
58
+ 'workflowDefinition',
59
+ 'name',
60
+ 'isActive',
61
+ 'identifier',
62
+ 'messageGroup',
63
+ 'dynamicMessageGroupId',
64
+ ]);
65
+ await (0, helper_1.populateActionIdentifiers)(exportWorkflow);
66
+ workflowsObj.workflows.push(exportWorkflow);
145
67
  }
146
- await (0, helper_1.populateActionIdentifiers)(exportWorkflow);
147
- (0, helper_1.extractShortActionKeys)(exportWorkflow);
148
- return exportWorkflow;
68
+ await saveData(workflowsObj, fileLocation, overwrite);
149
69
  }
150
- async function writeYamlFile(data, absolutePath, overwrite) {
151
- if (fs_1.default.existsSync(absolutePath)) {
152
- await (0, file_helpers_1.safeFileOverwrite)(absolutePath, overwrite);
70
+ exports.exportWorkflows = exportWorkflows;
71
+ async function saveData(data, fileLocation, overwrite) {
72
+ const yamlStr = js_yaml_1.default.dump(data);
73
+ const CURRENT_DIR = process.cwd();
74
+ const pathToWrite = path.resolve(path.join(CURRENT_DIR, fileLocation));
75
+ if (fs_1.default.existsSync(pathToWrite)) {
76
+ await (0, file_helpers_1.safeFileOverwrite)(pathToWrite, overwrite);
153
77
  }
154
- fs_1.default.mkdirSync(path.dirname(absolutePath), { recursive: true });
155
- fs_1.default.writeFileSync(absolutePath, js_yaml_1.default.dump(data), 'utf8');
78
+ fs_1.default.writeFileSync(pathToWrite, yamlStr, 'utf8');
156
79
  }
@@ -1,3 +1,4 @@
1
1
  import { ProjectConfiguration } from '../../interfaces';
2
+ import { ProcessingOptions } from './post-processor';
2
3
  export declare function getProjectFile(fileName: any): ProjectConfiguration;
3
- export declare function exportProject(name: any): Promise<void>;
4
+ export declare function exportProject(name: string, processingOptions?: ProcessingOptions | null): Promise<void>;
@@ -8,6 +8,7 @@ const fs_1 = tslib_1.__importDefault(require("fs"));
8
8
  const sdk_1 = require("@contrail/sdk");
9
9
  const util_1 = require("@contrail/util");
10
10
  const logger_1 = tslib_1.__importDefault(require("../../logger"));
11
+ const post_processor_1 = require("./post-processor");
11
12
  function getProjectFile(fileName) {
12
13
  const CURRENT_DIR = process.cwd();
13
14
  try {
@@ -21,7 +22,7 @@ function getProjectFile(fileName) {
21
22
  }
22
23
  }
23
24
  exports.getProjectFile = getProjectFile;
24
- async function exportProject(name) {
25
+ async function exportProject(name, processingOptions) {
25
26
  const allWorkspaces = await new sdk_1.Entities().get({
26
27
  entityName: 'workspace',
27
28
  useSearch: false,
@@ -42,7 +43,41 @@ async function exportProject(name) {
42
43
  const allAssortments = [];
43
44
  await getAssortmentsAndSubFolders(workspace.id, projectConfiguration.assortments, projectConfiguration.subFolders, allAssortments);
44
45
  await convertRelationshipsInFolders(projectConfiguration, allAssortments);
45
- saveData({ projectConfiguration: projectConfiguration }, name);
46
+ let finalData = { projectConfiguration: projectConfiguration };
47
+ // Apply post-processing if requested
48
+ if (processingOptions) {
49
+ logger_1.default.info('Starting post-processing...');
50
+ const processor = new post_processor_1.ProjectPostProcessor(processingOptions);
51
+ const { data, results } = processor.processProjectData(finalData);
52
+ finalData = data;
53
+ // Log analysis results
54
+ if (processingOptions.verbose || processingOptions.analyzeOnly) {
55
+ logger_1.default.info('========== ANALYSIS RESULTS ==========');
56
+ logger_1.default.info(`Total objects analyzed: ${results.stats.totalObjects}`);
57
+ logger_1.default.info(`- Assortments: ${results.stats.totalAssortments}`);
58
+ logger_1.default.info(`- Plans: ${results.stats.totalPlans}`);
59
+ logger_1.default.info(`- Backings: ${results.stats.totalBackings}`);
60
+ logger_1.default.info(`- Folders: ${results.stats.totalFolders}`);
61
+ logger_1.default.info('\nIDENTIFIER ANALYSIS:');
62
+ logger_1.default.info(`- Missing identifiers: ${results.stats.missingIdentifiers}`);
63
+ logger_1.default.info(`- Duplicate identifiers: ${results.stats.duplicateIdentifiers}`);
64
+ logger_1.default.info(`- Empty identifiers: ${results.stats.emptyIdentifiers}`);
65
+ if (Object.keys(results.duplicates).length > 0) {
66
+ logger_1.default.info('\n=== DUPLICATE IDENTIFIERS ===');
67
+ for (const [id, info] of Object.entries(results.duplicates)) {
68
+ logger_1.default.info(`\n"${id}" (${info.count} occurrences):`);
69
+ info.names.forEach((nameItem, i) => {
70
+ logger_1.default.info(` ${i + 1}. "${nameItem}"`);
71
+ });
72
+ }
73
+ }
74
+ }
75
+ if (processingOptions.analyzeOnly) {
76
+ logger_1.default.info('Analysis complete. No file written (analyze-only mode).');
77
+ return;
78
+ }
79
+ }
80
+ saveData(finalData, name);
46
81
  }
47
82
  exports.exportProject = exportProject;
48
83
  async function getAssortmentsAndSubFolders(parentId, assortments, subFolders, allAssortments) {
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Configuration interface for processing options
3
+ */
4
+ export interface ProcessingOptions {
5
+ removeBackings: boolean;
6
+ fixMissingIdentifiers: boolean;
7
+ cleanProperties: boolean;
8
+ analyzeOnly: boolean;
9
+ verbose: boolean;
10
+ }
11
+ /**
12
+ * Statistics tracking for all operations
13
+ */
14
+ export interface ProcessingStats {
15
+ itemTypeConverted: number;
16
+ nbFXRatesRemoved: number;
17
+ createdByRemoved: number;
18
+ updatedByRemoved: number;
19
+ emptyAssortmentAttsRemoved: number;
20
+ itemTypeIdRemoved?: number;
21
+ identifiersCreated: number;
22
+ planIdentifiers: number;
23
+ backingIdentifiers: number;
24
+ folderIdentifiers: number;
25
+ viewsIdentifiers: number;
26
+ assortmentIdentifiers: number;
27
+ backingsRemoved: number;
28
+ removedBackingNames: string[];
29
+ totalObjects: number;
30
+ totalAssortments: number;
31
+ totalPlans: number;
32
+ totalBackings: number;
33
+ totalFolders: number;
34
+ duplicateIdentifiers: number;
35
+ emptyIdentifiers: number;
36
+ missingIdentifiers: number;
37
+ }
38
+ /**
39
+ * Duplicate identifier information
40
+ */
41
+ export interface DuplicateInfo {
42
+ count: number;
43
+ names: string[];
44
+ paths: string[];
45
+ }
46
+ /**
47
+ * Analysis results interface
48
+ */
49
+ export interface AnalysisResults {
50
+ stats: ProcessingStats;
51
+ duplicates: Record<string, DuplicateInfo>;
52
+ emptyIdentifierObjects: Array<{
53
+ name: string;
54
+ path: string;
55
+ }>;
56
+ missingIdentifierObjects: {
57
+ assortments: Array<{
58
+ name: string;
59
+ path: string;
60
+ }>;
61
+ plans: Array<{
62
+ name: string;
63
+ path: string;
64
+ }>;
65
+ folders: Array<{
66
+ name: string;
67
+ path: string;
68
+ }>;
69
+ };
70
+ }
71
+ /**
72
+ * Main class for processing project YAML data
73
+ */
74
+ export declare class ProjectPostProcessor {
75
+ private stats;
76
+ private options;
77
+ private projectId;
78
+ constructor(options?: Partial<ProcessingOptions>);
79
+ private initializeStats;
80
+ /**
81
+ * Main processing method for project data
82
+ */
83
+ processProjectData(projectData: any): {
84
+ data: any;
85
+ results: AnalysisResults;
86
+ };
87
+ /**
88
+ * Extract project ID from project data
89
+ */
90
+ private extractProjectId;
91
+ /**
92
+ * Analyze project data for identifiers, types, and structure
93
+ */
94
+ private analyzeProjectData;
95
+ /**
96
+ * Clean properties from project data
97
+ */
98
+ private cleanProperties;
99
+ /**
100
+ * Process assortmentAtts object
101
+ */
102
+ private processAssortmentAtts;
103
+ /**
104
+ * Fix missing identifiers
105
+ */
106
+ private fixMissingIdentifiers;
107
+ /**
108
+ * Remove backing assortments
109
+ */
110
+ private removeBackings;
111
+ /**
112
+ * Generate identifier for an object
113
+ */
114
+ private generateIdentifier;
115
+ /**
116
+ * Determine object type
117
+ */
118
+ private getObjectType;
119
+ /**
120
+ * Check if object should have an identifier
121
+ */
122
+ private shouldHaveIdentifier;
123
+ /**
124
+ * Log processing summary
125
+ */
126
+ private logProcessingSummary;
127
+ /**
128
+ * Get processing statistics
129
+ */
130
+ getStats(): ProcessingStats;
131
+ }
@@ -0,0 +1,519 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProjectPostProcessor = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const logger_1 = tslib_1.__importDefault(require("../../logger"));
6
+ /**
7
+ * Main class for processing project YAML data
8
+ */
9
+ class ProjectPostProcessor {
10
+ stats;
11
+ options;
12
+ projectId = null;
13
+ constructor(options = {}) {
14
+ this.options = {
15
+ removeBackings: true,
16
+ fixMissingIdentifiers: true,
17
+ cleanProperties: true,
18
+ analyzeOnly: false,
19
+ verbose: false,
20
+ ...options,
21
+ };
22
+ this.stats = this.initializeStats();
23
+ }
24
+ initializeStats() {
25
+ return {
26
+ itemTypeConverted: 0,
27
+ nbFXRatesRemoved: 0,
28
+ createdByRemoved: 0,
29
+ updatedByRemoved: 0,
30
+ emptyAssortmentAttsRemoved: 0,
31
+ identifiersCreated: 0,
32
+ planIdentifiers: 0,
33
+ backingIdentifiers: 0,
34
+ folderIdentifiers: 0,
35
+ viewsIdentifiers: 0,
36
+ assortmentIdentifiers: 0,
37
+ backingsRemoved: 0,
38
+ removedBackingNames: [],
39
+ totalObjects: 0,
40
+ totalAssortments: 0,
41
+ totalPlans: 0,
42
+ totalBackings: 0,
43
+ totalFolders: 0,
44
+ duplicateIdentifiers: 0,
45
+ emptyIdentifiers: 0,
46
+ missingIdentifiers: 0,
47
+ };
48
+ }
49
+ /**
50
+ * Main processing method for project data
51
+ */
52
+ processProjectData(projectData) {
53
+ try {
54
+ if (this.options.verbose) {
55
+ logger_1.default.info('Starting project data processing...');
56
+ }
57
+ // Extract project ID
58
+ this.extractProjectId(projectData);
59
+ // Analysis phase
60
+ const analysisResults = this.analyzeProjectData(projectData);
61
+ if (this.options.analyzeOnly) {
62
+ return { data: projectData, results: analysisResults };
63
+ }
64
+ let processedData = projectData;
65
+ // Processing phase
66
+ if (this.options.cleanProperties) {
67
+ if (this.options.verbose)
68
+ logger_1.default.info('Cleaning properties...');
69
+ processedData = this.cleanProperties(processedData);
70
+ }
71
+ if (this.options.fixMissingIdentifiers) {
72
+ if (this.options.verbose)
73
+ logger_1.default.info('Fixing missing identifiers...');
74
+ processedData = this.fixMissingIdentifiers(processedData);
75
+ }
76
+ if (this.options.removeBackings) {
77
+ if (this.options.verbose)
78
+ logger_1.default.info('Removing backing assortments...');
79
+ processedData = this.removeBackings(processedData);
80
+ }
81
+ // Final analysis after processing
82
+ const finalAnalysis = this.analyzeProjectData(processedData);
83
+ if (this.options.verbose) {
84
+ this.logProcessingSummary();
85
+ }
86
+ return { data: processedData, results: finalAnalysis };
87
+ }
88
+ catch (error) {
89
+ logger_1.default.error('Error processing project data:', error);
90
+ throw error;
91
+ }
92
+ }
93
+ /**
94
+ * Extract project ID from project data
95
+ */
96
+ extractProjectId(projectData) {
97
+ if (projectData.projectConfiguration?.projectName) {
98
+ this.projectId = projectData.projectConfiguration.projectName;
99
+ }
100
+ else if (projectData.name?.match(/^S\d-\d+/)) {
101
+ const match = projectData.name.match(/^(S\d-\d+)/);
102
+ this.projectId = match ? match[1] : null;
103
+ }
104
+ if (this.options.verbose && this.projectId) {
105
+ logger_1.default.info(`Project ID identified: ${this.projectId}`);
106
+ }
107
+ }
108
+ /**
109
+ * Analyze project data for identifiers, types, and structure
110
+ */
111
+ analyzeProjectData(projectData) {
112
+ const identifiers = {};
113
+ const duplicates = {};
114
+ const emptyIdentifierObjects = [];
115
+ const missingIdentifierObjects = {
116
+ assortments: [],
117
+ plans: [],
118
+ folders: [],
119
+ };
120
+ const analyzeObject = (obj, path = []) => {
121
+ if (!obj || typeof obj !== 'object')
122
+ return;
123
+ if (Array.isArray(obj)) {
124
+ obj.forEach((item, index) => {
125
+ analyzeObject(item, [...path, index.toString()]);
126
+ });
127
+ return;
128
+ }
129
+ this.stats.totalObjects++;
130
+ // Analyze object with name
131
+ if (obj.name) {
132
+ // Determine object type
133
+ let objType = 'unknown';
134
+ if (obj.subFolders) {
135
+ objType = 'folder';
136
+ this.stats.totalFolders++;
137
+ }
138
+ if (obj.type) {
139
+ objType = obj.type.toLowerCase();
140
+ if (objType === 'plan')
141
+ this.stats.totalPlans++;
142
+ if (objType === 'assortment')
143
+ this.stats.totalAssortments++;
144
+ if (objType === 'backing')
145
+ this.stats.totalBackings++;
146
+ }
147
+ // Check identifier
148
+ if (obj.identifier !== undefined) {
149
+ const id = obj.identifier;
150
+ if (!id || id.trim() === '') {
151
+ this.stats.emptyIdentifiers++;
152
+ emptyIdentifierObjects.push({
153
+ name: obj.name,
154
+ path: path.join('.'),
155
+ });
156
+ }
157
+ else {
158
+ if (identifiers[id]) {
159
+ identifiers[id].count++;
160
+ identifiers[id].names.push(obj.name);
161
+ identifiers[id].paths.push(path.join('.'));
162
+ }
163
+ else {
164
+ identifiers[id] = {
165
+ count: 1,
166
+ names: [obj.name],
167
+ paths: [path.join('.')],
168
+ };
169
+ }
170
+ }
171
+ }
172
+ else if (this.shouldHaveIdentifier(obj)) {
173
+ this.stats.missingIdentifiers++;
174
+ if (objType === 'assortment') {
175
+ missingIdentifierObjects.assortments.push({
176
+ name: obj.name,
177
+ path: path.join('.'),
178
+ });
179
+ }
180
+ else if (objType === 'plan') {
181
+ missingIdentifierObjects.plans.push({
182
+ name: obj.name,
183
+ path: path.join('.'),
184
+ });
185
+ }
186
+ else if (objType === 'folder') {
187
+ missingIdentifierObjects.folders.push({
188
+ name: obj.name,
189
+ path: path.join('.'),
190
+ });
191
+ }
192
+ }
193
+ }
194
+ // Recursively analyze nested objects
195
+ for (const key in obj) {
196
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
197
+ analyzeObject(obj[key], [...path, key]);
198
+ }
199
+ }
200
+ };
201
+ analyzeObject(projectData);
202
+ // Find duplicates
203
+ for (const [id, info] of Object.entries(identifiers)) {
204
+ if (info.count > 1) {
205
+ duplicates[id] = info;
206
+ }
207
+ }
208
+ this.stats.duplicateIdentifiers = Object.keys(duplicates).length;
209
+ return {
210
+ stats: { ...this.stats },
211
+ duplicates,
212
+ emptyIdentifierObjects,
213
+ missingIdentifierObjects,
214
+ };
215
+ }
216
+ /**
217
+ * Clean properties from project data
218
+ */
219
+ cleanProperties(projectData) {
220
+ const processObject = (obj) => {
221
+ if (!obj || typeof obj !== 'object')
222
+ return obj;
223
+ if (Array.isArray(obj)) {
224
+ return obj.map((item) => processObject(item));
225
+ }
226
+ const newObj = {};
227
+ for (const key in obj) {
228
+ if (!Object.prototype.hasOwnProperty.call(obj, key))
229
+ continue;
230
+ if (key === 'itemType') {
231
+ // Handle itemType conversion
232
+ if (typeof obj[key] === 'object' && obj[key]?.typePath) {
233
+ newObj[key] = obj[key].typePath;
234
+ this.stats.itemTypeConverted++;
235
+ }
236
+ else {
237
+ newObj[key] = obj[key];
238
+ }
239
+ }
240
+ else if (key === 'nbFXRates') {
241
+ // Remove nbFXRates
242
+ this.stats.nbFXRatesRemoved++;
243
+ if (this.options.verbose) {
244
+ logger_1.default.debug(`Removed nbFXRates from object: ${obj.name || 'unnamed'}`);
245
+ }
246
+ }
247
+ else if (key === 'updatedBy') {
248
+ // Remove updatedBy
249
+ this.stats.updatedByRemoved++;
250
+ }
251
+ else if (key === 'createdBy') {
252
+ // Remove createdBy
253
+ this.stats.createdByRemoved++;
254
+ }
255
+ else if (key === 'assortmentAtts' && typeof obj[key] === 'object') {
256
+ // Process assortmentAtts
257
+ const processedAtts = this.processAssortmentAtts(obj[key]);
258
+ if (Object.keys(processedAtts).length > 0) {
259
+ newObj[key] = processedAtts;
260
+ }
261
+ else {
262
+ this.stats.emptyAssortmentAttsRemoved++;
263
+ }
264
+ }
265
+ else if (obj[key] === null) {
266
+ // Skip null values
267
+ }
268
+ else if (typeof obj[key] === 'object') {
269
+ newObj[key] = processObject(obj[key]);
270
+ }
271
+ else {
272
+ newObj[key] = obj[key];
273
+ }
274
+ }
275
+ return newObj;
276
+ };
277
+ return processObject(projectData);
278
+ }
279
+ /**
280
+ * Process assortmentAtts object
281
+ */
282
+ processAssortmentAtts(assortmentAtts) {
283
+ const processed = {};
284
+ for (const key in assortmentAtts) {
285
+ if (key === 'updatedBy' || key === 'createdBy') {
286
+ // Skip these properties
287
+ if (key === 'updatedBy')
288
+ this.stats.updatedByRemoved++;
289
+ if (key === 'createdBy')
290
+ this.stats.createdByRemoved++;
291
+ }
292
+ else if (key === 'nbFXRates') {
293
+ // Skip nbFXRates
294
+ this.stats.nbFXRatesRemoved++;
295
+ }
296
+ else if (key === 'itemTypeId') {
297
+ // Skip itemTypeId
298
+ this.stats.itemTypeIdRemoved++;
299
+ }
300
+ else if (key === 'itemType' &&
301
+ typeof assortmentAtts[key] === 'object' &&
302
+ assortmentAtts[key]?.typePath) {
303
+ // Convert itemType object to string
304
+ processed[key] = assortmentAtts[key].typePath;
305
+ this.stats.itemTypeConverted++;
306
+ }
307
+ else if (assortmentAtts[key] !== null && assortmentAtts[key] !== undefined) {
308
+ // Keep other properties
309
+ processed[key] = assortmentAtts[key];
310
+ }
311
+ }
312
+ return processed;
313
+ }
314
+ /**
315
+ * Fix missing identifiers
316
+ */
317
+ fixMissingIdentifiers(projectData) {
318
+ const processObject = (obj, path = [], parentIdentifier = '') => {
319
+ if (!obj || typeof obj !== 'object')
320
+ return obj;
321
+ if (Array.isArray(obj)) {
322
+ return obj.map((item, index) => processObject(item, [...path, index.toString()], parentIdentifier));
323
+ }
324
+ const newObj = { ...obj };
325
+ // Generate identifier if missing
326
+ if (newObj.name && !newObj.identifier && this.shouldHaveIdentifier(newObj)) {
327
+ const identifier = this.generateIdentifier(newObj, parentIdentifier);
328
+ if (identifier) {
329
+ newObj.identifier = identifier;
330
+ this.stats.identifiersCreated++;
331
+ const objType = this.getObjectType(newObj);
332
+ if (objType === 'plan')
333
+ this.stats.planIdentifiers++;
334
+ else if (objType === 'assortment')
335
+ this.stats.assortmentIdentifiers++;
336
+ else if (objType === 'backing')
337
+ this.stats.backingIdentifiers++;
338
+ else if (objType === 'folder')
339
+ this.stats.folderIdentifiers++;
340
+ if (newObj.name.toLowerCase().includes('views')) {
341
+ this.stats.viewsIdentifiers++;
342
+ }
343
+ if (this.options.verbose) {
344
+ logger_1.default.debug(`Generated identifier "${identifier}" for "${newObj.name}"`);
345
+ }
346
+ }
347
+ }
348
+ else if (newObj.identifier) {
349
+ parentIdentifier = newObj.identifier;
350
+ }
351
+ // Process nested objects
352
+ for (const key in newObj) {
353
+ if (typeof newObj[key] === 'object' && newObj[key] !== null && key !== 'identifier') {
354
+ newObj[key] = processObject(newObj[key], [...path, key], parentIdentifier);
355
+ }
356
+ }
357
+ return newObj;
358
+ };
359
+ return processObject(projectData);
360
+ }
361
+ /**
362
+ * Remove backing assortments
363
+ */
364
+ removeBackings(projectData) {
365
+ const processObject = (obj) => {
366
+ if (!obj || typeof obj !== 'object')
367
+ return obj;
368
+ if (Array.isArray(obj)) {
369
+ const filtered = obj.filter((item) => {
370
+ if (item && item.type === 'BACKING') {
371
+ this.stats.backingsRemoved++;
372
+ if (item.name) {
373
+ this.stats.removedBackingNames.push(item.name);
374
+ }
375
+ if (this.options.verbose) {
376
+ logger_1.default.debug(`Removed backing: ${item.name || 'unnamed'}`);
377
+ }
378
+ return false;
379
+ }
380
+ return true;
381
+ });
382
+ return filtered.map((item) => processObject(item));
383
+ }
384
+ const newObj = {};
385
+ for (const key in obj) {
386
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
387
+ newObj[key] = processObject(obj[key]);
388
+ }
389
+ }
390
+ return newObj;
391
+ };
392
+ return processObject(projectData);
393
+ }
394
+ /**
395
+ * Generate identifier for an object
396
+ */
397
+ generateIdentifier(obj, parentIdentifier) {
398
+ const objType = this.getObjectType(obj);
399
+ let typeSuffix = 'folder';
400
+ if (objType === 'plan')
401
+ typeSuffix = 'product.plan';
402
+ else if (objType === 'assortment')
403
+ typeSuffix = 'product.assortment';
404
+ else if (objType === 'backing')
405
+ typeSuffix = 'backing';
406
+ const sanitizeName = (name) => {
407
+ return name
408
+ .toLowerCase()
409
+ .replace(/'/g, '')
410
+ .replace(/&/g, 'and')
411
+ .replace(/\s+/g, '')
412
+ .replace(/[^\w]/g, '');
413
+ };
414
+ // Strategy 1: Extract from name pattern
415
+ const nameMatch = obj.name.match(/^(S\d-\d+)\s+(.+)$/);
416
+ if (nameMatch) {
417
+ const projectId = nameMatch[1];
418
+ const sanitizedName = sanitizeName(nameMatch[2]);
419
+ return `${projectId}:${sanitizedName}:${typeSuffix}`;
420
+ }
421
+ // Strategy 2: Views objects
422
+ if (obj.name.toLowerCase().includes('views')) {
423
+ let sanitizedName = 'main';
424
+ if (obj.name.startsWith('Views -')) {
425
+ sanitizedName = sanitizeName(obj.name.substring(8).trim());
426
+ }
427
+ else if (obj.name.toLowerCase() !== 'views') {
428
+ sanitizedName = sanitizeName(obj.name);
429
+ }
430
+ const baseId = this.projectId || parentIdentifier;
431
+ return `${baseId}:views:${sanitizedName}:${typeSuffix}`;
432
+ }
433
+ // Strategy 3: Training objects
434
+ if (obj.name.toLowerCase().includes('training')) {
435
+ let sanitizedName;
436
+ if (obj.name.startsWith('(Training)')) {
437
+ sanitizedName = sanitizeName(obj.name.substring(10).trim());
438
+ }
439
+ else {
440
+ sanitizedName = sanitizeName(obj.name);
441
+ }
442
+ const baseId = this.projectId || parentIdentifier;
443
+ return `${baseId}:training:${sanitizedName}:${typeSuffix}`;
444
+ }
445
+ // Strategy 4: Seasonal/Regional patterns
446
+ if (obj.name.match(/^(SS|FW|SU|FA)\d{2}/i) || obj.name.match(/^(APAC|NA|EMEA|LATAM|ANZ)/i)) {
447
+ const sanitizedName = sanitizeName(obj.name);
448
+ const baseId = this.projectId || parentIdentifier;
449
+ if (obj.name.match(/^(SS|FW|SU|FA)\d{2}/i)) {
450
+ return `${baseId}:seasons:${sanitizedName}:${typeSuffix}`;
451
+ }
452
+ else {
453
+ return `${baseId}:regions:${sanitizedName}:${typeSuffix}`;
454
+ }
455
+ }
456
+ // Strategy 5: Use parent identifier
457
+ if (parentIdentifier) {
458
+ const sanitizedName = sanitizeName(obj.name);
459
+ return `${parentIdentifier}:${sanitizedName}:${typeSuffix}`;
460
+ }
461
+ // Strategy 6: Use project ID
462
+ if (this.projectId) {
463
+ const sanitizedName = sanitizeName(obj.name);
464
+ return `${this.projectId}:${sanitizedName}:${typeSuffix}`;
465
+ }
466
+ return null;
467
+ }
468
+ /**
469
+ * Determine object type
470
+ */
471
+ getObjectType(obj) {
472
+ if (obj.subFolders)
473
+ return 'folder';
474
+ if (obj.type)
475
+ return obj.type.toLowerCase();
476
+ return 'unknown';
477
+ }
478
+ /**
479
+ * Check if object should have an identifier
480
+ */
481
+ shouldHaveIdentifier(obj) {
482
+ return (obj.name &&
483
+ (obj.type === 'ASSORTMENT' || obj.type === 'PLAN' || obj.type === 'BACKING' || obj.subFolders));
484
+ }
485
+ /**
486
+ * Log processing summary
487
+ */
488
+ logProcessingSummary() {
489
+ logger_1.default.info('========== PROCESSING SUMMARY ==========');
490
+ if (this.options.cleanProperties) {
491
+ logger_1.default.info('PROPERTY CLEANING:');
492
+ logger_1.default.info(`- ItemType objects converted: ${this.stats.itemTypeConverted}`);
493
+ logger_1.default.info(`- nbFXRates properties removed: ${this.stats.nbFXRatesRemoved}`);
494
+ logger_1.default.info(`- updatedBy properties removed: ${this.stats.updatedByRemoved}`);
495
+ logger_1.default.info(`- createdBy properties removed: ${this.stats.createdByRemoved}`);
496
+ logger_1.default.info(`- Empty assortmentAtts removed: ${this.stats.emptyAssortmentAttsRemoved}`);
497
+ }
498
+ if (this.options.fixMissingIdentifiers) {
499
+ logger_1.default.info('IDENTIFIER GENERATION:');
500
+ logger_1.default.info(`- Total identifiers created: ${this.stats.identifiersCreated}`);
501
+ logger_1.default.info(` - Assortments: ${this.stats.assortmentIdentifiers}`);
502
+ logger_1.default.info(` - Plans: ${this.stats.planIdentifiers}`);
503
+ logger_1.default.info(` - Backings: ${this.stats.backingIdentifiers}`);
504
+ logger_1.default.info(` - Folders: ${this.stats.folderIdentifiers}`);
505
+ logger_1.default.info(` - Views: ${this.stats.viewsIdentifiers}`);
506
+ }
507
+ if (this.options.removeBackings) {
508
+ logger_1.default.info('BACKING REMOVAL:');
509
+ logger_1.default.info(`- Backing assortments removed: ${this.stats.backingsRemoved}`);
510
+ }
511
+ }
512
+ /**
513
+ * Get processing statistics
514
+ */
515
+ getStats() {
516
+ return { ...this.stats };
517
+ }
518
+ }
519
+ exports.ProjectPostProcessor = ProjectPostProcessor;
@@ -18,4 +18,3 @@ export declare class WorkflowTemplateDeployer {
18
18
  export declare function deleteWorkflowTemplates(): Promise<void>;
19
19
  export declare function localDeployWorkflowTemplate(saveFileLocation: string): Promise<void>;
20
20
  export declare function populateActionIdentifiers(workflowTemplate: WorkflowTemplate): Promise<void>;
21
- export declare function extractShortActionKeys(workflowTemplate: WorkflowTemplate): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractShortActionKeys = exports.populateActionIdentifiers = exports.localDeployWorkflowTemplate = exports.deleteWorkflowTemplates = exports.WorkflowTemplateDeployer = exports.getActionByIdentifier = exports.getWorkflowTemplateIdentifier = exports.LocalWorkflowTemplateDeployer = void 0;
3
+ exports.populateActionIdentifiers = exports.localDeployWorkflowTemplate = exports.deleteWorkflowTemplates = exports.WorkflowTemplateDeployer = exports.getActionByIdentifier = exports.getWorkflowTemplateIdentifier = exports.LocalWorkflowTemplateDeployer = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const util_1 = require("@contrail/util");
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
@@ -20,6 +20,7 @@ class LocalWorkflowTemplateDeployer {
20
20
  const existingWorkflows = await (0, event_workflow_templates_1.getExistingWorkflowTemplates)();
21
21
  // UPSERT / DELETE BASED ON WORKFLOW TEMPLATES IDENTIFIER
22
22
  logger_1.default.info(`${chalk_1.default.yellow.bold('WORKFLOW TEMPLATES')} Deploying workflow templates.`);
23
+ logger_1.default.info(wtDeclaration);
23
24
  for (const workflowKey of Object.keys(wtDeclaration.workflows)) {
24
25
  await this.deployWorkflowTemplate(wtDeclaration.workflows[workflowKey], existingWorkflows, wtDeclaration.appIdentifier);
25
26
  }
@@ -34,7 +35,7 @@ class LocalWorkflowTemplateDeployer {
34
35
  return await this.createNewWorkflowTemplate(identifier, workflow, appIdentifier);
35
36
  }
36
37
  async createNewWorkflowTemplate(workflowKey, workflowTemplate, appIdentifier) {
37
- this.populateActionIds(workflowTemplate, workflowKey, appIdentifier);
38
+ await this.populateActionIds(workflowTemplate, workflowKey, appIdentifier);
38
39
  const newObject = {
39
40
  identifier: getWorkflowTemplateIdentifier(workflowTemplate),
40
41
  name: workflowTemplate.name,
@@ -43,8 +44,6 @@ class LocalWorkflowTemplateDeployer {
43
44
  isActive: workflowTemplate.isActive,
44
45
  supportingAppId: workflowTemplate.supportingAppId,
45
46
  messageGroupId: workflowTemplate.messageGroupId,
46
- messageGroup: workflowTemplate.messageGroup,
47
- dynamicMessageGroupId: workflowTemplate.dynamicMessageGroupId,
48
47
  parallelWorkerCount: workflowTemplate.parallelWorkerCount,
49
48
  };
50
49
  const newWorkflowTemplate = await (0, event_workflow_templates_1.createWorkflowTemplate)(newObject);
@@ -56,10 +55,8 @@ class LocalWorkflowTemplateDeployer {
56
55
  }
57
56
  async populateActionIds(workflowTemplate, workflowKey, appIdentifier) {
58
57
  const installedApps = await (0, apps_1.getInstalledApps)();
59
- const appOrg = installedApps.find((x) => x.app?.identifier === appIdentifier);
60
- if (!appOrg) {
61
- throw new Error(`Could not find installed app with identifier "${appIdentifier}". Make sure the app is installed in this org before importing workflows.`);
62
- }
58
+ const appOrg = installedApps.find((x) => x.app.identifier === appIdentifier);
59
+ logger_1.default.info('workflow template:', JSON.stringify(workflowTemplate, null, 2));
63
60
  if (!workflowTemplate?.workflowDefinition?.paths) {
64
61
  return;
65
62
  }
@@ -89,7 +86,7 @@ class LocalWorkflowTemplateDeployer {
89
86
  }
90
87
  }
91
88
  async createWorkflowSchedule(workflowTemplate, workflowId) {
92
- if (!workflowTemplate?.crons?.length) {
89
+ if (!workflowTemplate?.crons?.length || !workflowTemplate.id) {
93
90
  return;
94
91
  }
95
92
  const crons = [];
@@ -286,21 +283,3 @@ async function populateActionIdentifiers(workflowTemplate) {
286
283
  }
287
284
  }
288
285
  exports.populateActionIdentifiers = populateActionIdentifiers;
289
- function extractShortActionKeys(workflowTemplate) {
290
- if (!workflowTemplate?.workflowDefinition?.paths) {
291
- return;
292
- }
293
- for (const wfPath of workflowTemplate.workflowDefinition.paths) {
294
- if (!wfPath?.actionDefinitionConfigs) {
295
- continue;
296
- }
297
- for (const actionConfig of wfPath.actionDefinitionConfigs) {
298
- if (actionConfig.action?.includes(':')) {
299
- // Full identifier format: "app-id@version:action-name" -> "action-name"
300
- actionConfig.action = actionConfig.action.split(':').pop();
301
- }
302
- delete actionConfig.identifier;
303
- }
304
- }
305
- }
306
- exports.extractShortActionKeys = extractShortActionKeys;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.5.3-alpha.0e9434c",
2
+ "version": "3.5.3-alpha.317fe63",
3
3
  "commands": {
4
4
  "app:create": {
5
5
  "id": "app:create",
@@ -1241,14 +1241,15 @@
1241
1241
  },
1242
1242
  "projects:export": {
1243
1243
  "id": "projects:export",
1244
- "description": "Export a project.",
1244
+ "description": "Export a project with optional post-processing.",
1245
1245
  "strict": true,
1246
1246
  "pluginName": "@contrail/cli",
1247
1247
  "pluginAlias": "@contrail/cli",
1248
1248
  "pluginType": "core",
1249
1249
  "aliases": [],
1250
1250
  "examples": [
1251
- "$ contrail project export <PROJECT_NAME>\n"
1251
+ "$ contrail project export <PROJECT_NAME>",
1252
+ "$ contrail project export <PROJECT_NAME> --process"
1252
1253
  ],
1253
1254
  "flags": {
1254
1255
  "logLevel": {
@@ -1277,12 +1278,18 @@
1277
1278
  "false"
1278
1279
  ],
1279
1280
  "default": "true"
1281
+ },
1282
+ "process": {
1283
+ "name": "process",
1284
+ "type": "boolean",
1285
+ "description": "Enable post-processing of exported project data",
1286
+ "allowNo": false
1280
1287
  }
1281
1288
  },
1282
1289
  "args": {
1283
1290
  "projectName": {
1284
1291
  "name": "projectName",
1285
- "description": "",
1292
+ "description": "Name or identifier of the project to export",
1286
1293
  "required": true
1287
1294
  }
1288
1295
  }
@@ -2956,16 +2963,14 @@
2956
2963
  },
2957
2964
  "workflow-templates:export": {
2958
2965
  "id": "workflow-templates:export",
2959
- "description": "Export all workflows in the org. By default exports one file per app to the current directory.",
2966
+ "description": "Export all workflows in the org.",
2960
2967
  "strict": true,
2961
2968
  "pluginName": "@contrail/cli",
2962
2969
  "pluginAlias": "@contrail/cli",
2963
2970
  "pluginType": "core",
2964
2971
  "aliases": [],
2965
2972
  "examples": [
2966
- "$ contrail workflow-templates export",
2967
- "$ contrail workflow-templates export --dir ./workflows",
2968
- "$ contrail workflow-templates export --outFile workflows.yml"
2973
+ "$ contrail workflow-templates export\n"
2969
2974
  ],
2970
2975
  "flags": {
2971
2976
  "logLevel": {
@@ -2999,18 +3004,10 @@
2999
3004
  "name": "outFile",
3000
3005
  "type": "option",
3001
3006
  "char": "f",
3002
- "description": "Export all workflows to a single file at this path (overrides per-app export)",
3003
- "required": false,
3004
- "multiple": false
3005
- },
3006
- "dir": {
3007
- "name": "dir",
3008
- "type": "option",
3009
- "char": "d",
3010
- "description": "Directory to write per-app workflow files",
3007
+ "description": "The location for the file to be saved",
3011
3008
  "required": false,
3012
3009
  "multiple": false,
3013
- "default": "."
3010
+ "default": "workflows.yml"
3014
3011
  },
3015
3012
  "overwrite": {
3016
3013
  "name": "overwrite",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/cli",
3
- "version": "3.5.3-alpha.0e9434c",
3
+ "version": "3.5.3-alpha.317fe63",
4
4
  "description": "VibeIQ's Contrail Platform CLI.",
5
5
  "bin": {
6
6
  "contrail-cli": "./bin/run",