@contrail/flexplm 1.6.1-alpha.75cfe7f → 1.7.0-alpha.71b4d30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/cli/commands/{create.d.ts → config-file/create.d.ts} +0 -1
  2. package/lib/cli/commands/config-file/create.js +61 -0
  3. package/lib/cli/commands/config-file/create.spec.js +74 -0
  4. package/lib/cli/commands/config-file/index.d.ts +2 -0
  5. package/lib/cli/commands/config-file/index.js +47 -0
  6. package/lib/cli/commands/config-file/upload.d.ts +8 -0
  7. package/lib/cli/commands/config-file/upload.js +84 -0
  8. package/lib/cli/commands/config-file/upload.spec.js +51 -0
  9. package/lib/cli/commands/mapping-file/create.d.ts +4 -0
  10. package/lib/cli/commands/{create.js → mapping-file/create.js} +4 -20
  11. package/lib/cli/commands/mapping-file/create.spec.d.ts +1 -0
  12. package/lib/cli/commands/{create.spec.js → mapping-file/create.spec.js} +1 -1
  13. package/lib/cli/commands/mapping-file/index.d.ts +2 -0
  14. package/lib/cli/commands/mapping-file/index.js +57 -0
  15. package/lib/cli/commands/mapping-file/upload.d.ts +3 -0
  16. package/lib/cli/commands/mapping-file/upload.js +70 -0
  17. package/lib/cli/commands/shared/git.d.ts +6 -0
  18. package/lib/cli/commands/shared/git.js +100 -0
  19. package/lib/cli/commands/shared/prompts.d.ts +2 -0
  20. package/lib/cli/commands/shared/prompts.js +61 -0
  21. package/lib/cli/commands/shared/upload-base.d.ts +24 -0
  22. package/lib/cli/commands/shared/upload-base.js +138 -0
  23. package/lib/cli/commands/shared/upload-base.spec.d.ts +1 -0
  24. package/lib/cli/commands/shared/upload-base.spec.js +99 -0
  25. package/lib/cli/index.js +18 -37
  26. package/lib/cli/index.spec.js +111 -53
  27. package/lib/cli/template/config-template.json.template +5 -0
  28. package/package.json +2 -2
  29. package/scripts/copy-template.js +16 -5
  30. package/lib/cli/commands/upload.d.ts +0 -18
  31. package/lib/cli/commands/upload.js +0 -251
  32. package/lib/cli/commands/upload.spec.js +0 -95
  33. /package/lib/cli/commands/{create.spec.d.ts → config-file/create.spec.d.ts} +0 -0
  34. /package/lib/cli/commands/{upload.spec.d.ts → config-file/upload.spec.d.ts} +0 -0
  35. /package/lib/cli/commands/{compile.d.ts → mapping-file/compile.d.ts} +0 -0
  36. /package/lib/cli/commands/{compile.js → mapping-file/compile.js} +0 -0
  37. /package/lib/cli/commands/{compile.spec.d.ts → mapping-file/compile.spec.d.ts} +0 -0
  38. /package/lib/cli/commands/{compile.spec.js → mapping-file/compile.spec.js} +0 -0
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.promptHidden = exports.prompt = void 0;
27
+ const readline = __importStar(require("readline"));
28
+ function prompt(question) {
29
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30
+ const onSigint = () => {
31
+ rl.close();
32
+ process.stdout.write('\n');
33
+ process.exit(130);
34
+ };
35
+ process.once('SIGINT', onSigint);
36
+ return new Promise((resolve) => {
37
+ rl.question(question, (answer) => {
38
+ process.removeListener('SIGINT', onSigint);
39
+ rl.close();
40
+ resolve(answer.trim());
41
+ });
42
+ });
43
+ }
44
+ exports.prompt = prompt;
45
+ function promptHidden(question) {
46
+ return new Promise((resolve) => {
47
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
48
+ const rlAny = rl;
49
+ rlAny._writeToOutput = (str) => {
50
+ if (str.includes(question)) {
51
+ rlAny.output.write(str);
52
+ }
53
+ };
54
+ rl.question(question, (answer) => {
55
+ rl.close();
56
+ process.stdout.write('\n');
57
+ resolve(answer);
58
+ });
59
+ });
60
+ }
61
+ exports.promptHidden = promptHidden;
@@ -0,0 +1,24 @@
1
+ /// <reference types="node" />
2
+ export interface UploadOptions {
3
+ filePath: string;
4
+ message?: string;
5
+ branch?: string;
6
+ skipGit: boolean;
7
+ updateConfig: boolean;
8
+ }
9
+ export interface PreparedUpload {
10
+ orgName: string;
11
+ appIdentifier: string;
12
+ uploadBuffer: Buffer;
13
+ uploadFileName: string;
14
+ uploadContentType: string;
15
+ configPropertyName: string;
16
+ filesToCommit: string[];
17
+ }
18
+ export interface UploadStrategy {
19
+ topicLabel: string;
20
+ sourceExtension: string;
21
+ prepare(absSourcePath: string): Promise<PreparedUpload>;
22
+ }
23
+ export declare function parseUploadArgs(args: string[], sourceLabel: string): UploadOptions;
24
+ export declare function runUpload(strategy: UploadStrategy, args: string[]): Promise<void>;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.runUpload = exports.parseUploadArgs = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const sdk_1 = require("@contrail/sdk");
30
+ const prompts_1 = require("./prompts");
31
+ const git_1 = require("./git");
32
+ function parseUploadArgs(args, sourceLabel) {
33
+ let filePath;
34
+ let message;
35
+ let branch;
36
+ let skipGit = false;
37
+ let updateConfig = false;
38
+ for (let i = 0; i < args.length; i++) {
39
+ const a = args[i];
40
+ if (a === '-m') {
41
+ message = args[++i];
42
+ if (message === undefined) {
43
+ throw new Error('-m requires a commit message argument');
44
+ }
45
+ }
46
+ else if (a === '-b') {
47
+ branch = args[++i];
48
+ if (branch === undefined) {
49
+ throw new Error('-b requires a branch name argument');
50
+ }
51
+ }
52
+ else if (a === '--skip-git' || a === '--skipGit') {
53
+ skipGit = true;
54
+ }
55
+ else if (a === '--update-config') {
56
+ updateConfig = true;
57
+ }
58
+ else if (a.startsWith('-')) {
59
+ throw new Error(`Unknown option: ${a}`);
60
+ }
61
+ else if (!filePath) {
62
+ filePath = a;
63
+ }
64
+ else {
65
+ throw new Error(`Unexpected argument: ${a}`);
66
+ }
67
+ }
68
+ if (!filePath) {
69
+ throw new Error(`upload: missing <${sourceLabel}> argument`);
70
+ }
71
+ return { filePath, message, branch, skipGit, updateConfig };
72
+ }
73
+ exports.parseUploadArgs = parseUploadArgs;
74
+ async function runUpload(strategy, args) {
75
+ const sourceLabel = `path${strategy.sourceExtension}`;
76
+ const options = parseUploadArgs(args, sourceLabel);
77
+ const absSourcePath = path.resolve(process.cwd(), options.filePath);
78
+ if (!fs.existsSync(absSourcePath)) {
79
+ throw new Error(`File not found: ${absSourcePath}`);
80
+ }
81
+ if (!absSourcePath.endsWith(strategy.sourceExtension)) {
82
+ throw new Error(`Expected a ${strategy.sourceExtension} file, got: ${absSourcePath}`);
83
+ }
84
+ const prepared = await strategy.prepare(absSourcePath);
85
+ let email = process.env.CONTRAIL_CLI_EMAIL;
86
+ let password = process.env.CONTRAIL_CLI_PASSWORD;
87
+ if (!email) {
88
+ email = await (0, prompts_1.prompt)('Email: ');
89
+ }
90
+ if (!password) {
91
+ password = await (0, prompts_1.promptHidden)('Password: ');
92
+ }
93
+ if (!email || !password) {
94
+ throw new Error('Email and password are required');
95
+ }
96
+ await (0, sdk_1.login)({ orgSlug: prepared.orgName, email, password });
97
+ console.log(`Logged in to org "${prepared.orgName}" as ${email}`);
98
+ const apps = await new sdk_1.Entities().get({
99
+ entityName: 'app',
100
+ criteria: { identifier: prepared.appIdentifier },
101
+ });
102
+ if (!apps || apps.length !== 1) {
103
+ throw new Error(`Expected exactly one app with identifier "${prepared.appIdentifier}" in org "${prepared.orgName}", found ${apps ? apps.length : 0}`);
104
+ }
105
+ const app = apps[0];
106
+ const fileOwner = `app:${app.id}`;
107
+ const uploadedFile = await new sdk_1.Files().createAndUploadFileFromBuffer(prepared.uploadBuffer, prepared.uploadContentType, prepared.uploadFileName, fileOwner);
108
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
109
+ const responsePath = `${absSourcePath}.uploaded-${timestamp}.json`;
110
+ fs.writeFileSync(responsePath, JSON.stringify(uploadedFile, null, 2), 'utf8');
111
+ console.log(`Wrote response to ${responsePath}`);
112
+ console.log(`FILE ID: ${uploadedFile.id}`);
113
+ if (options.updateConfig) {
114
+ const appOrgs = await new sdk_1.Entities().get({
115
+ entityName: 'app-org',
116
+ criteria: { appId: app.id },
117
+ });
118
+ if (!appOrgs || appOrgs.length === 0) {
119
+ throw new Error(`App "${prepared.appIdentifier}" is not installed in org "${prepared.orgName}". Install it via the admin console before using --update-config.`);
120
+ }
121
+ if (appOrgs.length > 1) {
122
+ throw new Error(`Expected exactly one app-org for app "${prepared.appIdentifier}" in org "${prepared.orgName}", found ${appOrgs.length}`);
123
+ }
124
+ const appOrg = appOrgs[0];
125
+ const nextAppConfig = { ...(appOrg.appConfig || {}), [prepared.configPropertyName]: uploadedFile.id };
126
+ await new sdk_1.Entities().update({
127
+ entityName: 'app-org',
128
+ id: appOrg.id,
129
+ object: { appConfig: nextAppConfig },
130
+ });
131
+ console.log(`Updated app-org ${appOrg.id}: appConfig.${prepared.configPropertyName} = ${uploadedFile.id}`);
132
+ }
133
+ if (options.skipGit) {
134
+ return;
135
+ }
136
+ await (0, git_1.commitFiles)(prepared.filesToCommit, uploadedFile.id, options);
137
+ }
138
+ exports.runUpload = runUpload;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock('@contrail/sdk', () => ({
4
+ Entities: jest.fn(),
5
+ Files: jest.fn(),
6
+ login: jest.fn(),
7
+ }));
8
+ const upload_base_1 = require("./upload-base");
9
+ const git_1 = require("./git");
10
+ describe('parseUploadArgs', () => {
11
+ it('parses a bare file path', () => {
12
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts'], 'path.ts');
13
+ expect(opts).toEqual({
14
+ filePath: 'mapping.ts',
15
+ message: undefined,
16
+ branch: undefined,
17
+ skipGit: false,
18
+ updateConfig: false,
19
+ });
20
+ });
21
+ it('parses -m commit message option', () => {
22
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '-m', 'my message'], 'path.ts');
23
+ expect(opts.message).toEqual('my message');
24
+ expect(opts.skipGit).toBe(false);
25
+ });
26
+ it('parses -b branch option', () => {
27
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '-b', 'feature/x'], 'path.ts');
28
+ expect(opts.branch).toEqual('feature/x');
29
+ });
30
+ it('parses --skip-git flag', () => {
31
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '--skip-git'], 'path.ts');
32
+ expect(opts.skipGit).toBe(true);
33
+ });
34
+ it('accepts the legacy --skipGit alias', () => {
35
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '--skipGit'], 'path.ts');
36
+ expect(opts.skipGit).toBe(true);
37
+ });
38
+ it('parses --update-config flag', () => {
39
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '--update-config'], 'path.ts');
40
+ expect(opts.updateConfig).toBe(true);
41
+ });
42
+ it('parses options before the file path', () => {
43
+ const opts = (0, upload_base_1.parseUploadArgs)(['-m', 'msg', '-b', 'br', 'mapping.ts'], 'path.ts');
44
+ expect(opts).toEqual({
45
+ filePath: 'mapping.ts',
46
+ message: 'msg',
47
+ branch: 'br',
48
+ skipGit: false,
49
+ updateConfig: false,
50
+ });
51
+ });
52
+ it('parses all options together', () => {
53
+ const opts = (0, upload_base_1.parseUploadArgs)(['mapping.ts', '-m', 'msg', '-b', 'br', '--skip-git', '--update-config'], 'path.ts');
54
+ expect(opts).toEqual({
55
+ filePath: 'mapping.ts',
56
+ message: 'msg',
57
+ branch: 'br',
58
+ skipGit: true,
59
+ updateConfig: true,
60
+ });
61
+ });
62
+ it('throws when -m is missing its value', () => {
63
+ expect(() => (0, upload_base_1.parseUploadArgs)(['mapping.ts', '-m'], 'path.ts')).toThrow(/-m requires a commit message/);
64
+ });
65
+ it('throws when -b is missing its value', () => {
66
+ expect(() => (0, upload_base_1.parseUploadArgs)(['mapping.ts', '-b'], 'path.ts')).toThrow(/-b requires a branch name/);
67
+ });
68
+ it('throws on unknown option', () => {
69
+ expect(() => (0, upload_base_1.parseUploadArgs)(['mapping.ts', '--bogus'], 'path.ts')).toThrow(/Unknown option: --bogus/);
70
+ });
71
+ it('throws when an extra positional argument is supplied', () => {
72
+ expect(() => (0, upload_base_1.parseUploadArgs)(['mapping.ts', 'extra.ts'], 'path.ts')).toThrow(/Unexpected argument: extra\.ts/);
73
+ });
74
+ it('throws when no file path is provided', () => {
75
+ expect(() => (0, upload_base_1.parseUploadArgs)([], 'path.ts')).toThrow(/missing <path\.ts>/);
76
+ });
77
+ it('throws when only options are provided', () => {
78
+ expect(() => (0, upload_base_1.parseUploadArgs)(['--skip-git'], 'path.ts')).toThrow(/missing <path\.ts>/);
79
+ });
80
+ it('uses the supplied source label in the missing-argument error', () => {
81
+ expect(() => (0, upload_base_1.parseUploadArgs)([], 'path.json')).toThrow(/missing <path\.json>/);
82
+ });
83
+ });
84
+ describe('buildCommitMessage', () => {
85
+ it('appends fileId to the first line of a single-line message', () => {
86
+ expect((0, git_1.buildCommitMessage)('initial commit', 'abc123')).toEqual('initial commit [fileId: abc123]');
87
+ });
88
+ it('only appends fileId to the first line of a multi-line message', () => {
89
+ const result = (0, git_1.buildCommitMessage)('header line\nbody line 1\nbody line 2', 'xyz');
90
+ expect(result).toEqual('header line [fileId: xyz]\nbody line 1\nbody line 2');
91
+ });
92
+ it('handles CRLF line endings', () => {
93
+ const result = (0, git_1.buildCommitMessage)('header\r\nbody', 'fid');
94
+ expect(result).toEqual('header [fileId: fid]\nbody');
95
+ });
96
+ it('handles an empty message', () => {
97
+ expect((0, git_1.buildCommitMessage)('', 'fid')).toEqual(' [fileId: fid]');
98
+ });
99
+ });
package/lib/cli/index.js CHANGED
@@ -2,57 +2,38 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.main = exports.Cli = void 0;
5
- const create_1 = require("./commands/create");
6
- const compile_1 = require("./commands/compile");
7
- const upload_1 = require("./commands/upload");
8
- const USAGE = `Usage: flexplm-mapping <command> [args]
5
+ const mapping_file_1 = require("./commands/mapping-file");
6
+ const config_file_1 = require("./commands/config-file");
7
+ const USAGE = `Usage: flexplm <topic> <subcommand> [args]
9
8
 
10
- Commands:
11
- create Scaffold a new mapping .ts file in the current directory
12
- compile <path.ts> Transpile a mapping .ts file to .js alongside it
13
- upload <path.ts> [opts] Compile a mapping .ts file and upload the resulting .js to VibeIQ
9
+ Topics:
10
+ mapping-file <create|compile|upload> Manage transform mapping .ts files
11
+ config-file <create|upload> Manage connector config .json files
14
12
 
15
- Upload options:
16
- -m <message> Git commit message (prompted if omitted)
17
- -b <branch> Create a new git branch before committing
18
- --skip-git Skip the post-upload git commit (default: commit)
19
- --update-config Patch the app-org appConfig.transformMapFile with the uploaded file ID without needing to paste into the admin console
20
-
21
- Environment (upload):
22
- CONTRAIL_CLI_EMAIL VibeIQ user email
23
- CONTRAIL_CLI_PASSWORD VibeIQ user password
13
+ Run "flexplm <topic>" for topic-specific help. Examples:
14
+ flexplm mapping-file
15
+ flexplm config-file
24
16
  `;
25
17
  class Cli {
26
18
  async main() {
27
- const [, , command, ...rest] = process.argv;
28
- switch (command) {
29
- case 'create':
30
- await new create_1.CreateCommand().run();
31
- return;
32
- case 'compile':
33
- if (!rest[0]) {
34
- console.error('compile: missing <path.ts> argument');
35
- console.error(USAGE);
36
- process.exit(1);
37
- }
38
- await new compile_1.CompileCommand().run(rest[0]);
19
+ const [, , topic, subcommand, ...rest] = process.argv;
20
+ switch (topic) {
21
+ case 'mapping-file':
22
+ await (0, mapping_file_1.runMappingFile)(subcommand, rest);
39
23
  return;
40
- case 'upload':
41
- if (!rest[0]) {
42
- console.error('upload: missing <path.ts> argument');
43
- console.error(USAGE);
44
- process.exit(1);
45
- }
46
- await new upload_1.UploadCommand().run(rest);
24
+ case 'config-file':
25
+ await (0, config_file_1.runConfigFile)(subcommand, rest);
47
26
  return;
48
27
  case undefined:
49
28
  case '-h':
50
29
  case '--help':
51
30
  case 'help':
52
31
  console.log(USAGE);
32
+ console.log(mapping_file_1.MAPPING_FILE_USAGE);
33
+ console.log(config_file_1.CONFIG_FILE_USAGE);
53
34
  return;
54
35
  default:
55
- console.error(`Unknown command: ${command}`);
36
+ console.error(`Unknown topic: ${topic}`);
56
37
  console.error(USAGE);
57
38
  process.exit(1);
58
39
  }
@@ -1,16 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const createRunMock = jest.fn().mockResolvedValue(undefined);
4
- const compileRunMock = jest.fn().mockResolvedValue(undefined);
5
- const uploadRunMock = jest.fn().mockResolvedValue(undefined);
6
- jest.mock('./commands/create', () => ({
7
- CreateCommand: jest.fn().mockImplementation(() => ({ run: createRunMock })),
3
+ const mappingCreateRun = jest.fn().mockResolvedValue(undefined);
4
+ const mappingCompileRun = jest.fn().mockResolvedValue(undefined);
5
+ const mappingUploadRun = jest.fn().mockResolvedValue(undefined);
6
+ const configCreateRun = jest.fn().mockResolvedValue(undefined);
7
+ const configUploadRun = jest.fn().mockResolvedValue(undefined);
8
+ jest.mock('./commands/mapping-file/create', () => ({
9
+ CreateCommand: jest.fn().mockImplementation(() => ({ run: mappingCreateRun })),
8
10
  }));
9
- jest.mock('./commands/compile', () => ({
10
- CompileCommand: jest.fn().mockImplementation(() => ({ run: compileRunMock })),
11
+ jest.mock('./commands/mapping-file/compile', () => ({
12
+ CompileCommand: jest.fn().mockImplementation(() => ({ run: mappingCompileRun })),
11
13
  }));
12
- jest.mock('./commands/upload', () => ({
13
- UploadCommand: jest.fn().mockImplementation(() => ({ run: uploadRunMock })),
14
+ jest.mock('./commands/mapping-file/upload', () => ({
15
+ UploadCommand: jest.fn().mockImplementation(() => ({ run: mappingUploadRun })),
16
+ }));
17
+ jest.mock('./commands/config-file/create', () => ({
18
+ CreateCommand: jest.fn().mockImplementation(() => ({ run: configCreateRun })),
19
+ }));
20
+ jest.mock('./commands/config-file/upload', () => ({
21
+ UploadCommand: jest.fn().mockImplementation(() => ({ run: configUploadRun })),
14
22
  }));
15
23
  const index_1 = require("./index");
16
24
  describe('cli main dispatcher', () => {
@@ -20,9 +28,11 @@ describe('cli main dispatcher', () => {
20
28
  let exitSpy;
21
29
  beforeEach(() => {
22
30
  originalArgv = process.argv;
23
- createRunMock.mockClear();
24
- compileRunMock.mockClear();
25
- uploadRunMock.mockClear();
31
+ mappingCreateRun.mockClear();
32
+ mappingCompileRun.mockClear();
33
+ mappingUploadRun.mockClear();
34
+ configCreateRun.mockClear();
35
+ configUploadRun.mockClear();
26
36
  logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
27
37
  errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
28
38
  exitSpy = jest.spyOn(process, 'exit').mockImplementation(((code) => {
@@ -38,48 +48,96 @@ describe('cli main dispatcher', () => {
38
48
  function setArgv(...args) {
39
49
  process.argv = ['node', 'cli', ...args];
40
50
  }
41
- it('dispatches the create command', async () => {
42
- setArgv('create');
43
- await (0, index_1.main)();
44
- expect(createRunMock).toHaveBeenCalledTimes(1);
45
- expect(compileRunMock).not.toHaveBeenCalled();
46
- expect(uploadRunMock).not.toHaveBeenCalled();
47
- });
48
- it('dispatches the compile command with its argument', async () => {
49
- setArgv('compile', 'mapping.ts');
50
- await (0, index_1.main)();
51
- expect(compileRunMock).toHaveBeenCalledWith('mapping.ts');
52
- });
53
- it('exits when compile is missing its argument', async () => {
54
- setArgv('compile');
55
- await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
56
- expect(compileRunMock).not.toHaveBeenCalled();
57
- expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.ts>/));
58
- });
59
- it('dispatches the upload command and forwards remaining args', async () => {
60
- setArgv('upload', 'mapping.ts', '-m', 'msg', '--skip-git');
61
- await (0, index_1.main)();
62
- expect(uploadRunMock).toHaveBeenCalledWith(['mapping.ts', '-m', 'msg', '--skip-git']);
63
- });
64
- it('exits when upload is missing its argument', async () => {
65
- setArgv('upload');
66
- await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
67
- expect(uploadRunMock).not.toHaveBeenCalled();
68
- expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.ts>/));
69
- });
70
- it.each(['help', '-h', '--help'])('prints usage for %s', async (helpFlag) => {
71
- setArgv(helpFlag);
72
- await (0, index_1.main)();
73
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm-mapping/));
51
+ describe('mapping-file topic', () => {
52
+ it('dispatches mapping-file create', async () => {
53
+ setArgv('mapping-file', 'create');
54
+ await (0, index_1.main)();
55
+ expect(mappingCreateRun).toHaveBeenCalledTimes(1);
56
+ expect(mappingCompileRun).not.toHaveBeenCalled();
57
+ expect(mappingUploadRun).not.toHaveBeenCalled();
58
+ });
59
+ it('dispatches mapping-file compile with its argument', async () => {
60
+ setArgv('mapping-file', 'compile', 'mapping.ts');
61
+ await (0, index_1.main)();
62
+ expect(mappingCompileRun).toHaveBeenCalledWith('mapping.ts');
63
+ });
64
+ it('exits when mapping-file compile is missing its argument', async () => {
65
+ setArgv('mapping-file', 'compile');
66
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
67
+ expect(mappingCompileRun).not.toHaveBeenCalled();
68
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.ts>/));
69
+ });
70
+ it('dispatches mapping-file upload and forwards remaining args', async () => {
71
+ setArgv('mapping-file', 'upload', 'mapping.ts', '-m', 'msg', '--skip-git');
72
+ await (0, index_1.main)();
73
+ expect(mappingUploadRun).toHaveBeenCalledWith(['mapping.ts', '-m', 'msg', '--skip-git']);
74
+ });
75
+ it('exits when mapping-file upload is missing its argument', async () => {
76
+ setArgv('mapping-file', 'upload');
77
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
78
+ expect(mappingUploadRun).not.toHaveBeenCalled();
79
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.ts>/));
80
+ });
81
+ it.each(['help', '-h', '--help'])('prints mapping-file usage on %s', async (helpFlag) => {
82
+ setArgv('mapping-file', helpFlag);
83
+ await (0, index_1.main)();
84
+ expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm mapping-file/));
85
+ });
86
+ it('prints mapping-file usage when no subcommand is provided', async () => {
87
+ setArgv('mapping-file');
88
+ await (0, index_1.main)();
89
+ expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm mapping-file/));
90
+ });
91
+ it('exits on an unknown mapping-file subcommand', async () => {
92
+ setArgv('mapping-file', 'bogus');
93
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
94
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/Unknown mapping-file subcommand: bogus/));
95
+ });
74
96
  });
75
- it('prints usage when no command is provided', async () => {
76
- setArgv();
77
- await (0, index_1.main)();
78
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm-mapping/));
97
+ describe('config-file topic', () => {
98
+ it('dispatches config-file create', async () => {
99
+ setArgv('config-file', 'create');
100
+ await (0, index_1.main)();
101
+ expect(configCreateRun).toHaveBeenCalledTimes(1);
102
+ expect(configUploadRun).not.toHaveBeenCalled();
103
+ });
104
+ it('dispatches config-file upload and forwards remaining args', async () => {
105
+ setArgv('config-file', 'upload', 'cfg.json', '--update-config', '--skip-git');
106
+ await (0, index_1.main)();
107
+ expect(configUploadRun).toHaveBeenCalledWith(['cfg.json', '--update-config', '--skip-git']);
108
+ });
109
+ it('exits when config-file upload is missing its argument', async () => {
110
+ setArgv('config-file', 'upload');
111
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
112
+ expect(configUploadRun).not.toHaveBeenCalled();
113
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.json>/));
114
+ });
115
+ it('prints config-file usage when no subcommand is provided', async () => {
116
+ setArgv('config-file');
117
+ await (0, index_1.main)();
118
+ expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm config-file/));
119
+ });
120
+ it('exits on an unknown config-file subcommand', async () => {
121
+ setArgv('config-file', 'bogus');
122
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
123
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/Unknown config-file subcommand: bogus/));
124
+ });
79
125
  });
80
- it('exits on an unknown command', async () => {
81
- setArgv('bogus');
82
- await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
83
- expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/Unknown command: bogus/));
126
+ describe('top-level dispatcher', () => {
127
+ it.each(['help', '-h', '--help'])('prints top-level usage on %s', async (helpFlag) => {
128
+ setArgv(helpFlag);
129
+ await (0, index_1.main)();
130
+ expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm <topic>/));
131
+ });
132
+ it('prints top-level usage when no topic is provided', async () => {
133
+ setArgv();
134
+ await (0, index_1.main)();
135
+ expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm <topic>/));
136
+ });
137
+ it('exits on an unknown topic', async () => {
138
+ setArgv('bogus');
139
+ await expect((0, index_1.main)()).rejects.toThrow('__EXIT__:1');
140
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/Unknown topic: bogus/));
141
+ });
84
142
  });
85
143
  });
@@ -0,0 +1,5 @@
1
+ {
2
+ "orgName": "<ORG_NAME>",
3
+ "appIdentifier": "@vibeiq/flexplm-connector",
4
+ "config": {}
5
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.6.1-alpha.75cfe7f",
3
+ "version": "1.7.0-alpha.71b4d30",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "bin": {
8
- "flexplm-mapping": "lib/cli/index.js"
8
+ "flexplm": "lib/cli/index.js"
9
9
  },
10
10
  "files": [
11
11
  "lib/**/*",
@@ -2,9 +2,20 @@
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
 
5
- const SRC = path.join('src', 'cli', 'template', 'mapping-template.ts.template');
6
- const DST = path.join('lib', 'cli', 'template', 'mapping-template.ts.template');
5
+ const SRC_DIR = path.join('src', 'cli', 'template');
6
+ const DST_DIR = path.join('lib', 'cli', 'template');
7
7
 
8
- fs.mkdirSync(path.dirname(DST), { recursive: true });
9
- fs.copyFileSync(SRC, DST);
10
- console.log(`Copied ${SRC} -> ${DST}`);
8
+ fs.mkdirSync(DST_DIR, { recursive: true });
9
+
10
+ const entries = fs.readdirSync(SRC_DIR).filter((name) => name.endsWith('.template'));
11
+ if (entries.length === 0) {
12
+ console.warn(`No .template files found in ${SRC_DIR}`);
13
+ process.exit(0);
14
+ }
15
+
16
+ for (const name of entries) {
17
+ const src = path.join(SRC_DIR, name);
18
+ const dst = path.join(DST_DIR, name);
19
+ fs.copyFileSync(src, dst);
20
+ console.log(`Copied ${src} -> ${dst}`);
21
+ }