@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.
- package/lib/cli/commands/{create.d.ts → config-file/create.d.ts} +0 -1
- package/lib/cli/commands/config-file/create.js +61 -0
- package/lib/cli/commands/config-file/create.spec.js +74 -0
- package/lib/cli/commands/config-file/index.d.ts +2 -0
- package/lib/cli/commands/config-file/index.js +47 -0
- package/lib/cli/commands/config-file/upload.d.ts +8 -0
- package/lib/cli/commands/config-file/upload.js +84 -0
- package/lib/cli/commands/config-file/upload.spec.js +51 -0
- package/lib/cli/commands/mapping-file/create.d.ts +4 -0
- package/lib/cli/commands/{create.js → mapping-file/create.js} +4 -20
- package/lib/cli/commands/mapping-file/create.spec.d.ts +1 -0
- package/lib/cli/commands/{create.spec.js → mapping-file/create.spec.js} +1 -1
- package/lib/cli/commands/mapping-file/index.d.ts +2 -0
- package/lib/cli/commands/mapping-file/index.js +57 -0
- package/lib/cli/commands/mapping-file/upload.d.ts +3 -0
- package/lib/cli/commands/mapping-file/upload.js +70 -0
- package/lib/cli/commands/shared/git.d.ts +6 -0
- package/lib/cli/commands/shared/git.js +100 -0
- package/lib/cli/commands/shared/prompts.d.ts +2 -0
- package/lib/cli/commands/shared/prompts.js +61 -0
- package/lib/cli/commands/shared/upload-base.d.ts +24 -0
- package/lib/cli/commands/shared/upload-base.js +138 -0
- package/lib/cli/commands/shared/upload-base.spec.d.ts +1 -0
- package/lib/cli/commands/shared/upload-base.spec.js +99 -0
- package/lib/cli/index.js +18 -37
- package/lib/cli/index.spec.js +111 -53
- package/lib/cli/template/config-template.json.template +5 -0
- package/package.json +2 -2
- package/scripts/copy-template.js +16 -5
- package/lib/cli/commands/upload.d.ts +0 -18
- package/lib/cli/commands/upload.js +0 -251
- package/lib/cli/commands/upload.spec.js +0 -95
- /package/lib/cli/commands/{create.spec.d.ts → config-file/create.spec.d.ts} +0 -0
- /package/lib/cli/commands/{upload.spec.d.ts → config-file/upload.spec.d.ts} +0 -0
- /package/lib/cli/commands/{compile.d.ts → mapping-file/compile.d.ts} +0 -0
- /package/lib/cli/commands/{compile.js → mapping-file/compile.js} +0 -0
- /package/lib/cli/commands/{compile.spec.d.ts → mapping-file/compile.spec.d.ts} +0 -0
- /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
|
|
6
|
-
const
|
|
7
|
-
const
|
|
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
|
-
|
|
11
|
-
create
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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 [, ,
|
|
28
|
-
switch (
|
|
29
|
-
case '
|
|
30
|
-
await
|
|
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 '
|
|
41
|
-
|
|
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
|
|
36
|
+
console.error(`Unknown topic: ${topic}`);
|
|
56
37
|
console.error(USAGE);
|
|
57
38
|
process.exit(1);
|
|
58
39
|
}
|
package/lib/cli/index.spec.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
jest.
|
|
7
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrail/flexplm",
|
|
3
|
-
"version": "1.
|
|
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
|
|
8
|
+
"flexplm": "lib/cli/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"lib/**/*",
|
package/scripts/copy-template.js
CHANGED
|
@@ -2,9 +2,20 @@
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const SRC_DIR = path.join('src', 'cli', 'template');
|
|
6
|
+
const DST_DIR = path.join('lib', 'cli', 'template');
|
|
7
7
|
|
|
8
|
-
fs.mkdirSync(
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
}
|