@contrail/flexplm 1.7.4-alpha.7bbda17 → 1.7.4-alpha.a05d6ab

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.
@@ -29,7 +29,8 @@ class BaseProcessPublishAssortment {
29
29
  let publisher;
30
30
  const assortmentId = event.assortmentId;
31
31
  try {
32
- console.info('process-start!');
32
+ console.info('process-start: assortmentId: ' + assortmentId
33
+ + ', assortmentPublishChangeId: ' + assortmentPublishChangeId);
33
34
  let seasonFed;
34
35
  try {
35
36
  seasonFed = await this.getSeasonFederation(assortmentId);
@@ -74,9 +75,9 @@ class BaseProcessPublishAssortment {
74
75
  e.publishInfo = publishInfo;
75
76
  }
76
77
  catch (e2) {
77
- console.log('catch e2: ' + e2.message);
78
+ console.error('getPublishInfo failed: ' + e2.message);
78
79
  }
79
- console.log('catch e: ' + e.message);
80
+ console.error('process failed: assortmentId: ' + assortmentId + ': ' + e.message);
80
81
  throw e;
81
82
  }
82
83
  }
@@ -315,19 +316,17 @@ class BaseProcessPublishAssortment {
315
316
  }
316
317
  async downloadHydratedChangeDetail(assortmentPublishChange) {
317
318
  try {
318
- console.info('downloadHydratedChangeDetail-start');
319
319
  const link = assortmentPublishChange?.hydratedDetailDownloadLink;
320
320
  const response = await fetch(link);
321
321
  const data = await response.json();
322
322
  return data;
323
323
  }
324
324
  catch (e) {
325
- console.log('Error hydratedDetailDownloadLink: ' + e.message);
325
+ console.error('Error hydratedDetailDownloadLink: ' + e.message);
326
326
  }
327
327
  return undefined;
328
328
  }
329
329
  async downloadAssortmentBaseline(assortmentPublishChange) {
330
- console.info('downloadAssortmentBaseline-start');
331
330
  try {
332
331
  const link = assortmentPublishChange?.assortmentBaselineDownloadLink;
333
332
  const response = await fetch(link);
@@ -335,15 +334,12 @@ class BaseProcessPublishAssortment {
335
334
  return data;
336
335
  }
337
336
  catch (e) {
338
- console.log('Error assortmentBaselineDownloadLink: ' + e.message);
337
+ console.error('Error assortmentBaselineDownloadLink: ' + e.message);
339
338
  }
340
339
  return undefined;
341
340
  }
342
341
  async getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
343
- console.info('getDeleteChanges(): ' + assortmentPublishChange?.id);
344
- console.info(sinceDate);
345
342
  const currentAPCIndex = apcHistory.findIndex(pc => pc?.id === assortmentPublishChange?.id);
346
- console.info(' currentAPCIndex: ' + currentAPCIndex);
347
343
  if (currentAPCIndex == 0) {
348
344
  return [];
349
345
  }
@@ -352,16 +348,13 @@ class BaseProcessPublishAssortment {
352
348
  const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
353
349
  let previousApcDate = Date.parse(apcCreatedOn);
354
350
  const sinceDateMilliseconds = sinceDate.getTime();
355
- if (app_framework_1.Logger.isInfoOn()) {
356
- console.info('apcCreatedOn: ' + apcCreatedOn);
357
- console.info('sinceDate: ' + sinceDate);
358
- console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
359
- console.info('apcDateMilliseconds: ' + previousApcDate);
360
- }
351
+ console.info('getDeleteChanges(): apc: ' + assortmentPublishChange?.id
352
+ + ', index: ' + currentAPCIndex + ', previousApcCreatedOn: ' + apcCreatedOn);
361
353
  //if only 1 apc, no processing needed
362
354
  if (sinceDateMilliseconds !== previousApcDate) {
363
- console.info('sinceDateMilliseconds !== apcDateMilliseconds');
364
355
  const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
356
+ let scannedCount = 0;
357
+ let addedCount = 0;
365
358
  const deleteIds = (apcDeletes.length === 0)
366
359
  ? []
367
360
  : apcDeletes.map(item => item?.itemId);
@@ -376,19 +369,20 @@ class BaseProcessPublishAssortment {
376
369
  ? apcHistory[i - 1]
377
370
  : undefined;
378
371
  const deleteChanges = await this.buildDeleteChanges(workingAPC, previousApc);
379
- console.info('checking deleteChanges');
380
372
  for (const dItem of deleteChanges) {
381
373
  const dItemId = dItem?.itemId;
382
- console.info(dItemId);
374
+ scannedCount++;
383
375
  if (!currentAssortmentItemIds.includes(dItemId) && !deleteIds.includes(dItemId)) {
384
- console.info('adding');
376
+ addedCount++;
385
377
  deleteIds.push(dItemId);
386
378
  apcDeletes.push(dItem);
387
379
  }
388
380
  }
389
381
  }
390
382
  }
391
- console.info('getDeleteChanges()-currentAssortmentItemIds: ' + currentAssortmentItemIds);
383
+ console.info('getDeleteChanges(): baselineItems: ' + currentAssortmentItemIds.length
384
+ + ', scanned ' + scannedCount + ' historical delete changes, added ' + addedCount
385
+ + ', deleteIds: ' + deleteIds.length);
392
386
  console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
393
387
  }
394
388
  return apcDeletes;
@@ -402,7 +396,6 @@ class BaseProcessPublishAssortment {
402
396
  return itemIds;
403
397
  }
404
398
  async downloadDeleteChanges(apc) {
405
- console.info('deleteDataDownloadLink-start');
406
399
  try {
407
400
  const link = apc?.deleteDataDownloadLink;
408
401
  const response = await fetch(link);
@@ -410,19 +403,17 @@ class BaseProcessPublishAssortment {
410
403
  return data;
411
404
  }
412
405
  catch (e) {
413
- console.log('Error deleteDataDownloadLink: ' + e.message);
406
+ console.error('Error deleteDataDownloadLink: ' + e.message);
414
407
  }
415
408
  return undefined;
416
409
  }
417
410
  async buildDeleteChanges(apc, previousApc) {
418
- console.info('buildDeleteChanges()');
419
411
  if (apc?.deleteDataDownloadLink) {
420
412
  const deleteChanges = await this.downloadDeleteChanges(apc);
421
413
  if (deleteChanges) {
422
414
  return deleteChanges;
423
415
  }
424
416
  }
425
- console.info('pulling down full APC');
426
417
  apc = await this.downloadAssortmentPublishChange(apc?.assortmentId, apc?.id);
427
418
  if (apc?.deleteDataDownloadLink) {
428
419
  const deleteChanges = await this.downloadDeleteChanges(apc);
@@ -434,19 +425,18 @@ class BaseProcessPublishAssortment {
434
425
  throw new Error(BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES);
435
426
  }
436
427
  let previousBaseline;
437
- console.info('check previousApc');
438
428
  if (previousApc?.assortmentBaselineDownloadLink) {
439
429
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
440
430
  }
441
431
  else {
442
- console.info('previousApc pulling down full APC');
443
432
  previousApc = await this.downloadAssortmentPublishChange(previousApc?.assortmentId, previousApc?.id);
444
433
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
445
434
  }
446
435
  const deleteIds = apc?.detail?.deletes.map(dItem => dItem?.id);
447
436
  //building deletes based on previous baseline; because some APCs don't have delete data
448
437
  const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
449
- console.info('deleteArray.length: ' + deleteArray.length);
438
+ console.info('buildDeleteChanges(): no deleteDataDownloadLink on apc ' + apc?.id
439
+ + '; rebuilding ' + deleteArray.length + ' deletes from previous baseline');
450
440
  return deleteArray;
451
441
  }
452
442
  async getItemFederatedIds( /*itemIds*/) {
@@ -484,7 +474,6 @@ class BaseProcessPublishAssortment {
484
474
  return deleteItems;
485
475
  }
486
476
  getReleasedForDevelopmentItemAndFamilyIds(fullChange, deleteChanges) {
487
- console.info('getReleasedForDevelopmentItemAndFamilyIds');
488
477
  const releasedForDevelopmentItemIds = [];
489
478
  const itemFamilySet = new Set();
490
479
  const assortmentItemsArray = fullChange?.assortmentItems;
@@ -536,7 +525,6 @@ class BaseProcessPublishAssortment {
536
525
  return !!lifecycleStage && !this.config.itemPreDevelopmentLifecycleStages.includes(lifecycleStage);
537
526
  }
538
527
  async processPublish(pcd, changeDetail, fullChange, deleteChanges) {
539
- console.info('processPublish-start');
540
528
  const event = {
541
529
  assortmentId: pcd.assortmentId,
542
530
  assortmentPublishChangeId: pcd.assortmentPublishChangeId
@@ -720,7 +708,6 @@ class BaseProcessPublishAssortment {
720
708
  + '-' + d.getUTCMilliseconds();
721
709
  }
722
710
  getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap) {
723
- console.info('getItemFamilyChanges-start');
724
711
  const itemFamilyChanges = new Map();
725
712
  const { adds, deletes, updates, familyItemsRemoved } = changeDetail;
726
713
  const addIds = adds.map(item => item.id);
@@ -785,8 +772,7 @@ class BaseProcessPublishAssortment {
785
772
  }
786
773
  else {
787
774
  if (!item) {
788
- console.error('Failed to find deleted item entity');
789
- console.error(' itemFamilyId: ' + itemFamilyId + ' -itemId: ' + itemId);
775
+ console.error('Failed to find deleted item entity: itemFamilyId: ' + itemFamilyId + ', itemId: ' + itemId);
790
776
  continue;
791
777
  }
792
778
  }
@@ -823,7 +809,6 @@ class BaseProcessPublishAssortment {
823
809
  return itemFamilyChanges;
824
810
  }
825
811
  async getEventsForPublishChangeData(publishChangeData) {
826
- console.info('getEventsForPublishChangeData-start');
827
812
  const seasonalPayloads = [];
828
813
  for (const itemFamilyChange of publishChangeData.itemFamilyChanges.values()) {
829
814
  const events = await this.getEventsForItemFamilyChanges(itemFamilyChange, publishChangeData.assortmentId, publishChangeData.seasonFed, publishChangeData.itemToFederatedIdMapping);
@@ -831,6 +816,8 @@ class BaseProcessPublishAssortment {
831
816
  seasonalPayloads.push(...events);
832
817
  }
833
818
  }
819
+ console.info('getEventsForPublishChangeData: itemFamilies: '
820
+ + publishChangeData.itemFamilyChanges.size + ', payloads: ' + seasonalPayloads.length);
834
821
  return seasonalPayloads;
835
822
  }
836
823
  /**Returns the events for a given ItemFamilyChanges object
@@ -850,7 +837,6 @@ class BaseProcessPublishAssortment {
850
837
  * @returns
851
838
  */
852
839
  async getEventsForItemFamilyChanges(itemFamilyChanges, assortmentId, seasonFed, itemToFederatedIdMapping) {
853
- console.info('getEventsForItemFamilyChanges()');
854
840
  const events = [];
855
841
  const LCSSeason = Object.assign({}, seasonFed);
856
842
  const assortment = await this.getAssortment(assortmentId);
@@ -175,18 +175,12 @@ class DataConverter {
175
175
  }
176
176
  async getObjectReferenceValue(prop, newData, inflateObjRef = false) {
177
177
  const slug = prop['slug'];
178
- if (app_framework_1.Logger.isDebugOn()) {
179
- console.debug('getObjectReferenceValue-prop: ' + slug);
180
- }
181
178
  let value = newData[slug];
182
179
  const entityType = prop['referencedTypeRootSlug'];
183
180
  const entityId = newData[slug + 'Id'];
184
181
  if ((!value || typeof value === 'string') && inflateObjRef) {
185
182
  if (entityId) {
186
183
  if (this.objRefCache[entityId]) {
187
- if (app_framework_1.Logger.isDebugOn()) {
188
- console.debug('cache hit: ' + entityId);
189
- }
190
184
  return this.objRefCache[entityId];
191
185
  }
192
186
  const criteria = {
@@ -432,9 +426,6 @@ class DataConverter {
432
426
  return "";
433
427
  }
434
428
  if (this.objRefCache[ctx.cacheKey]) {
435
- if (app_framework_1.Logger.isDebugOn()) {
436
- console.debug(`object reference cache hit: ${ctx.cacheKey}`);
437
- }
438
429
  return this.objRefCache[ctx.cacheKey];
439
430
  }
440
431
  const objectReferenceId = ctx.useIdentityService
@@ -634,9 +625,6 @@ class DataConverter {
634
625
  }
635
626
  let cacheUser = DataConverter.getFromStaticCache(nd.email);
636
627
  if (cacheUser) {
637
- if (app_framework_1.Logger.isDebugOn()) {
638
- console.debug('user cache hit: ' + nd.email);
639
- }
640
628
  await this.processGroupMemberCheck(prop, nd.email);
641
629
  return cacheUser;
642
630
  }
@@ -706,18 +694,12 @@ class DataConverter {
706
694
  */
707
695
  async getUserListValue(prop, newData) {
708
696
  const slug = prop['slug'];
709
- if (app_framework_1.Logger.isDebugOn()) {
710
- console.debug('getUserListValue-prop: ' + slug);
711
- }
712
697
  const entityId = newData[slug + 'Id'];
713
698
  if (!entityId) {
714
699
  return {};
715
700
  }
716
701
  const cacheUser = DataConverter.getFromStaticCache(entityId);
717
702
  if (cacheUser) {
718
- if (app_framework_1.Logger.isDebugOn()) {
719
- console.debug('user cache hit: ' + entityId);
720
- }
721
703
  return Object.assign({}, cacheUser);
722
704
  }
723
705
  const user = await this.getUserById(entityId);
package/package.json CHANGED
@@ -1,67 +1,66 @@
1
- {
2
- "name": "@contrail/flexplm",
3
- "version": "1.7.4-alpha.7bbda17",
4
- "description": "Library used for integration with flexplm.",
5
- "main": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "bin": {
8
- "flexplm-mapping": "lib/cli/index.js",
9
- "flexplm-config": "lib/cli/config-index.js"
10
- },
11
- "files": [
12
- "lib/**/*",
13
- "scripts/copy-template.js"
14
- ],
15
- "scripts": {
16
- "build": "tsc; node scripts/copy-template.js",
17
- "build:win": "tsc && node scripts/copy-template.js",
18
- "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
19
- "lint": "tslint -p tsconfig.json",
20
- "test": "jest",
21
- "test-watch": "jest --watch",
22
- "test-debug": "jest --runInBand",
23
- "test-coverage": "jest --coverage"
24
- },
25
- "keywords": [],
26
- "author": "VibeIQ",
27
- "license": "ISC",
28
- "devDependencies": {
29
- "@types/jest": "^29.5.2",
30
- "jest": "^29.5.0",
31
- "prettier": "^1.19.1",
32
- "ts-jest": "^29.1.1",
33
- "tslint": "^5.11.0",
34
- "tslint-config-prettier": "^1.18.0",
35
- "typescript": "^4.0.0"
36
- },
37
- "peerDependencies": {
38
- "typescript": ">=4.0.0"
39
- },
40
- "peerDependenciesMeta": {
41
- "typescript": {
42
- "optional": true
43
- }
44
- },
45
- "jest": {
46
- "moduleFileExtensions": [
47
- "js",
48
- "json",
49
- "ts"
50
- ],
51
- "rootDir": "src",
52
- "testRegex": ".spec.ts$",
53
- "transform": {
54
- "^.+\\.(t|j)s$": "ts-jest"
55
- },
56
- "coverageDirectory": "../coverage",
57
- "testEnvironment": "node"
58
- },
59
- "dependencies": {
60
- "@contrail/app-framework": "^1.4.3",
61
- "@contrail/sdk": "^1.5.10",
62
- "@contrail/transform-data": "^1.3.2",
63
- "@contrail/util": "^1.3.1",
64
- "axios": "^1.4.0",
65
- "p-limit": "^3.1.0"
66
- }
67
- }
1
+ {
2
+ "name": "@contrail/flexplm",
3
+ "version": "1.7.4-alpha.a05d6ab",
4
+ "description": "Library used for integration with flexplm.",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "bin": {
8
+ "flexplm-mapping": "lib/cli/index.js"
9
+ },
10
+ "files": [
11
+ "lib/**/*",
12
+ "scripts/copy-template.js"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc; node scripts/copy-template.js",
16
+ "build:win": "tsc && node scripts/copy-template.js",
17
+ "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
18
+ "lint": "tslint -p tsconfig.json",
19
+ "test": "jest",
20
+ "test-watch": "jest --watch",
21
+ "test-debug": "jest --runInBand",
22
+ "test-coverage": "jest --coverage"
23
+ },
24
+ "keywords": [],
25
+ "author": "VibeIQ",
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "@types/jest": "^29.5.2",
29
+ "jest": "^29.5.0",
30
+ "prettier": "^1.19.1",
31
+ "ts-jest": "^29.1.1",
32
+ "tslint": "^5.11.0",
33
+ "tslint-config-prettier": "^1.18.0",
34
+ "typescript": "^4.0.0"
35
+ },
36
+ "peerDependencies": {
37
+ "typescript": ">=4.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "typescript": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "jest": {
45
+ "moduleFileExtensions": [
46
+ "js",
47
+ "json",
48
+ "ts"
49
+ ],
50
+ "rootDir": "src",
51
+ "testRegex": ".spec.ts$",
52
+ "transform": {
53
+ "^.+\\.(t|j)s$": "ts-jest"
54
+ },
55
+ "coverageDirectory": "../coverage",
56
+ "testEnvironment": "node"
57
+ },
58
+ "dependencies": {
59
+ "@contrail/app-framework": "^1.4.3",
60
+ "@contrail/sdk": "^1.5.10",
61
+ "@contrail/transform-data": "^1.3.2",
62
+ "@contrail/util": "^1.3.1",
63
+ "axios": "^1.4.0",
64
+ "p-limit": "^3.1.0"
65
+ }
66
+ }
@@ -2,13 +2,9 @@
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
 
5
- const TEMPLATES = ['mapping-template.ts.template', 'config-template.json.template'];
5
+ const SRC = path.join('src', 'cli', 'template', 'mapping-template.ts.template');
6
+ const DST = path.join('lib', 'cli', 'template', 'mapping-template.ts.template');
6
7
 
7
- for (const templateFilename of TEMPLATES) {
8
- const SRC = path.join('src', 'cli', 'template', templateFilename);
9
- const DST = path.join('lib', 'cli', 'template', templateFilename);
10
-
11
- fs.mkdirSync(path.dirname(DST), { recursive: true });
12
- fs.copyFileSync(SRC, DST);
13
- console.log(`Copied ${SRC} -> ${DST}`);
14
- }
8
+ fs.mkdirSync(path.dirname(DST), { recursive: true });
9
+ fs.copyFileSync(SRC, DST);
10
+ console.log(`Copied ${SRC} -> ${DST}`);
@@ -1,5 +0,0 @@
1
- export declare class ConfigCreateCommand {
2
- private prompt;
3
- private findTemplate;
4
- run(): Promise<void>;
5
- }
@@ -1,85 +0,0 @@
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.ConfigCreateCommand = void 0;
27
- const fs = __importStar(require("fs"));
28
- const path = __importStar(require("path"));
29
- const readline = __importStar(require("readline"));
30
- const TEMPLATE_FILENAME = 'config-template.json.template';
31
- const ORG_PLACEHOLDER = '<ORG_NAME>';
32
- const APP_IDENTIFIER_PLACEHOLDER = '<APP_IDENTIFIER>';
33
- const DEFAULT_APP_IDENTIFIER = '@vibeiq/flexplm-connector';
34
- class ConfigCreateCommand {
35
- prompt(rl, question) {
36
- return new Promise((resolve) => {
37
- rl.question(question, (answer) => resolve(answer));
38
- });
39
- }
40
- findTemplate() {
41
- const candidates = [
42
- path.join(__dirname, '..', 'template', TEMPLATE_FILENAME),
43
- path.join(__dirname, '..', '..', '..', 'src', 'cli', 'template', TEMPLATE_FILENAME),
44
- ];
45
- for (const candidate of candidates) {
46
- if (fs.existsSync(candidate)) {
47
- return candidate;
48
- }
49
- }
50
- throw new Error(`Could not locate ${TEMPLATE_FILENAME}. Tried:\n ${candidates.join('\n ')}`);
51
- }
52
- async run() {
53
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
54
- const onSigint = () => {
55
- rl.close();
56
- process.stdout.write('\n');
57
- process.exit(130);
58
- };
59
- process.once('SIGINT', onSigint);
60
- let orgName;
61
- let appIdentifier;
62
- try {
63
- orgName = (await this.prompt(rl, 'orgName: ')).trim();
64
- appIdentifier = (await this.prompt(rl, `appIdentifier (default: ${DEFAULT_APP_IDENTIFIER}): `)).trim() || DEFAULT_APP_IDENTIFIER;
65
- }
66
- finally {
67
- process.removeListener('SIGINT', onSigint);
68
- rl.close();
69
- }
70
- if (!orgName) {
71
- throw new Error('orgName is required');
72
- }
73
- const templatePath = this.findTemplate();
74
- const templateBody = fs.readFileSync(templatePath, 'utf8');
75
- const rendered = templateBody.split(ORG_PLACEHOLDER).join(orgName).split(APP_IDENTIFIER_PLACEHOLDER).join(appIdentifier);
76
- const outPath = path.resolve(process.cwd(), `${orgName}-flexplmConfig.json`);
77
- if (fs.existsSync(outPath)) {
78
- throw new Error(`Refusing to overwrite existing file: ${outPath}`);
79
- }
80
- fs.writeFileSync(outPath, rendered, 'utf8');
81
- console.log(`Created ${outPath}`);
82
- console.log('Only "orgName" and "appIdentifier" are required. See the "_availableAttributes" block in the file for other attributes this org can set (apiHost, identifierAtts, LCSMaterial, etc.) — add the ones you need as real top-level keys, then delete "_availableAttributes"; the connector ignores it and applies defaults at runtime for anything you omit.');
83
- }
84
- }
85
- exports.ConfigCreateCommand = ConfigCreateCommand;
@@ -1 +0,0 @@
1
- export {};
@@ -1,80 +0,0 @@
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
- const fs = __importStar(require("fs"));
27
- const os = __importStar(require("os"));
28
- const path = __importStar(require("path"));
29
- let answers = [];
30
- jest.mock('readline', () => ({
31
- createInterface: () => ({
32
- question: (_q, cb) => cb(answers.shift() || ''),
33
- close: () => { },
34
- }),
35
- }));
36
- const config_create_1 = require("./config-create");
37
- describe('ConfigCreateCommand', () => {
38
- let tempDir;
39
- let originalCwd;
40
- let logSpy;
41
- beforeEach(() => {
42
- originalCwd = process.cwd();
43
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flexplm-config-create-'));
44
- process.chdir(tempDir);
45
- logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
46
- answers = [];
47
- });
48
- afterEach(() => {
49
- logSpy.mockRestore();
50
- process.chdir(originalCwd);
51
- fs.rmSync(tempDir, { recursive: true, force: true });
52
- });
53
- it('writes <orgName>-flexplmConfig.json with orgName and appIdentifier filled in', async () => {
54
- answers = ['acme', '@vibeiq/flexplm-connector'];
55
- await new config_create_1.ConfigCreateCommand().run();
56
- const outPath = path.join(tempDir, 'acme-flexplmConfig.json');
57
- expect(fs.existsSync(outPath)).toBe(true);
58
- const config = JSON.parse(fs.readFileSync(outPath, 'utf8'));
59
- expect(config.orgName).toEqual('acme');
60
- expect(config.appIdentifier).toEqual('@vibeiq/flexplm-connector');
61
- });
62
- it('defaults appIdentifier to @vibeiq/flexplm-connector when left blank', async () => {
63
- answers = ['acme', ''];
64
- await new config_create_1.ConfigCreateCommand().run();
65
- const outPath = path.join(tempDir, 'acme-flexplmConfig.json');
66
- const config = JSON.parse(fs.readFileSync(outPath, 'utf8'));
67
- expect(config.appIdentifier).toEqual('@vibeiq/flexplm-connector');
68
- });
69
- it('throws when orgName is empty', async () => {
70
- answers = [' '];
71
- await expect(new config_create_1.ConfigCreateCommand().run()).rejects.toThrow(/orgName is required/);
72
- });
73
- it('refuses to overwrite an existing file', async () => {
74
- const existing = path.join(tempDir, 'acme-flexplmConfig.json');
75
- fs.writeFileSync(existing, 'do not clobber', 'utf8');
76
- answers = ['acme', '@vibeiq/flexplm-connector'];
77
- await expect(new config_create_1.ConfigCreateCommand().run()).rejects.toThrow(/Refusing to overwrite/);
78
- expect(fs.readFileSync(existing, 'utf8')).toEqual('do not clobber');
79
- });
80
- });
@@ -1,19 +0,0 @@
1
- interface ConfigUploadOptions {
2
- filePath: string;
3
- message?: string;
4
- branch?: string;
5
- skipGit: boolean;
6
- updateConfig: boolean;
7
- }
8
- export declare class ConfigUploadCommand {
9
- static parseArgs(args: string[]): ConfigUploadOptions;
10
- static buildCommitMessage(userMessage: string, fileId: string): string;
11
- private prompt;
12
- private promptHidden;
13
- private runGit;
14
- private tryRunGit;
15
- private commitToGit;
16
- run(args: string[]): Promise<void>;
17
- private setConfigFileOnAppOrg;
18
- }
19
- export {};
@@ -1,249 +0,0 @@
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.ConfigUploadCommand = void 0;
27
- const child_process_1 = require("child_process");
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
- const readline = __importStar(require("readline"));
31
- const sdk_1 = require("@contrail/sdk");
32
- class ConfigUploadCommand {
33
- static parseArgs(args) {
34
- let filePath;
35
- let message;
36
- let branch;
37
- let skipGit = false;
38
- let updateConfig = false;
39
- for (let i = 0; i < args.length; i++) {
40
- const a = args[i];
41
- if (a === '-m') {
42
- message = args[++i];
43
- if (message === undefined) {
44
- throw new Error('-m requires a commit message argument');
45
- }
46
- }
47
- else if (a === '-b') {
48
- branch = args[++i];
49
- if (branch === undefined) {
50
- throw new Error('-b requires a branch name argument');
51
- }
52
- }
53
- else if (a === '--skip-git' || a === '--skipGit') {
54
- skipGit = true;
55
- }
56
- else if (a === '--update-config') {
57
- updateConfig = true;
58
- }
59
- else if (a.startsWith('-')) {
60
- throw new Error(`Unknown option: ${a}`);
61
- }
62
- else if (!filePath) {
63
- filePath = a;
64
- }
65
- else {
66
- throw new Error(`Unexpected argument: ${a}`);
67
- }
68
- }
69
- if (!filePath) {
70
- throw new Error('upload: missing <path.json> argument');
71
- }
72
- return { filePath, message, branch, skipGit, updateConfig };
73
- }
74
- static buildCommitMessage(userMessage, fileId) {
75
- const lines = userMessage.split(/\r?\n/);
76
- lines[0] = `${lines[0]} [fileId: ${fileId}]`;
77
- return lines.join('\n');
78
- }
79
- prompt(question) {
80
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
81
- return new Promise((resolve) => {
82
- rl.question(question, (answer) => {
83
- rl.close();
84
- resolve(answer.trim());
85
- });
86
- });
87
- }
88
- promptHidden(question) {
89
- return new Promise((resolve) => {
90
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
91
- const rlAny = rl;
92
- rlAny._writeToOutput = (str) => {
93
- if (str.includes(question)) {
94
- rlAny.output.write(str);
95
- }
96
- };
97
- rl.question(question, (answer) => {
98
- rl.close();
99
- process.stdout.write('\n');
100
- resolve(answer);
101
- });
102
- });
103
- }
104
- runGit(args, cwd) {
105
- return (0, child_process_1.execFileSync)('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
106
- }
107
- tryRunGit(args, cwd) {
108
- try {
109
- const stdout = this.runGit(args, cwd);
110
- return { ok: true, stdout, stderr: '' };
111
- }
112
- catch (err) {
113
- return {
114
- ok: false,
115
- stdout: err && err.stdout ? err.stdout.toString() : '',
116
- stderr: err && err.stderr ? err.stderr.toString() : (err && err.message) || '',
117
- };
118
- }
119
- }
120
- async commitToGit(absPath, fileId, options) {
121
- const repoDir = path.dirname(absPath);
122
- const relPath = path.basename(absPath);
123
- const versionCheck = this.tryRunGit(['--version'], repoDir);
124
- if (!versionCheck.ok) {
125
- console.log('git command not available; skipping git commit.');
126
- return;
127
- }
128
- const insideRepo = this.tryRunGit(['rev-parse', '--is-inside-work-tree'], repoDir);
129
- if (!insideRepo.ok || insideRepo.stdout.trim() !== 'true') {
130
- console.log(`Not inside a git working tree (${repoDir}); skipping git commit.`);
131
- return;
132
- }
133
- const tracked = this.tryRunGit(['ls-files', '--error-unmatch', relPath], repoDir);
134
- if (!tracked.ok) {
135
- const answer = (await this.prompt(`File is not tracked by git: ${relPath}\nAdd it to git? (Y/n): `)).toLowerCase();
136
- if (answer === 'n' || answer === 'no') {
137
- console.log('Nothing was done in git.');
138
- return;
139
- }
140
- }
141
- if (options.branch) {
142
- const branchResult = this.tryRunGit(['checkout', '-b', options.branch], repoDir);
143
- if (!branchResult.ok) {
144
- throw new Error(`Failed to create branch "${options.branch}": ${branchResult.stderr.trim()}`);
145
- }
146
- console.log(`Created and switched to branch "${options.branch}"`);
147
- }
148
- let message = options.message;
149
- if (!message) {
150
- message = await this.prompt('Commit message: ');
151
- if (!message) {
152
- throw new Error('A commit message is required');
153
- }
154
- }
155
- const finalMessage = ConfigUploadCommand.buildCommitMessage(message, fileId);
156
- const addResult = this.tryRunGit(['add', '--', relPath], repoDir);
157
- if (!addResult.ok) {
158
- throw new Error(`git add failed: ${addResult.stderr.trim()}`);
159
- }
160
- const commitResult = this.tryRunGit(['commit', '-m', finalMessage, '--', relPath], repoDir);
161
- if (!commitResult.ok) {
162
- throw new Error(`git commit failed: ${commitResult.stderr.trim() || commitResult.stdout.trim()}`);
163
- }
164
- console.log(commitResult.stdout.trim());
165
- }
166
- async run(args) {
167
- const options = ConfigUploadCommand.parseArgs(args);
168
- const absPath = path.resolve(process.cwd(), options.filePath);
169
- if (!fs.existsSync(absPath)) {
170
- throw new Error(`File not found: ${absPath}`);
171
- }
172
- if (!absPath.endsWith('.json')) {
173
- throw new Error(`Expected a .json file, got: ${absPath}`);
174
- }
175
- const raw = fs.readFileSync(absPath, 'utf8');
176
- let config;
177
- try {
178
- config = JSON.parse(raw);
179
- }
180
- catch (err) {
181
- throw new Error(`File is not valid JSON: ${absPath}\n${err && err.message ? err.message : err}`);
182
- }
183
- const orgName = config && config.orgName;
184
- const appIdentifier = config && config.appIdentifier;
185
- if (!orgName) {
186
- throw new Error(`Config file is missing "orgName": ${absPath}`);
187
- }
188
- if (!appIdentifier) {
189
- throw new Error(`Config file is missing "appIdentifier": ${absPath}`);
190
- }
191
- let email = process.env.CONTRAIL_CLI_EMAIL;
192
- let password = process.env.CONTRAIL_CLI_PASSWORD;
193
- if (!email) {
194
- email = await this.prompt('Email: ');
195
- }
196
- if (!password) {
197
- password = await this.promptHidden('Password: ');
198
- }
199
- if (!email || !password) {
200
- throw new Error('Email and password are required');
201
- }
202
- await (0, sdk_1.login)({ orgSlug: orgName, email, password });
203
- console.log(`Logged in to org "${orgName}" as ${email}`);
204
- const apps = await new sdk_1.Entities().get({
205
- entityName: 'app',
206
- criteria: { identifier: appIdentifier },
207
- });
208
- if (!apps || apps.length !== 1) {
209
- throw new Error(`Expected exactly one app with identifier "${appIdentifier}" in org "${orgName}", found ${apps ? apps.length : 0}`);
210
- }
211
- const app = apps[0];
212
- const buffer = fs.readFileSync(absPath);
213
- const fileName = path.basename(absPath);
214
- const fileOwner = `app:${app.id}`;
215
- const uploadedFile = await new sdk_1.Files().createAndUploadFileFromBuffer(buffer, 'application/json', fileName, fileOwner);
216
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
217
- const responsePath = `${absPath}.uploaded-${timestamp}.json`;
218
- fs.writeFileSync(responsePath, JSON.stringify(uploadedFile, null, 2), 'utf8');
219
- console.log(`Wrote response to ${responsePath}`);
220
- console.log(`FILE ID: ${uploadedFile.id}`);
221
- if (!options.skipGit) {
222
- await this.commitToGit(absPath, uploadedFile.id, options);
223
- }
224
- if (options.updateConfig) {
225
- await this.setConfigFileOnAppOrg(app.id, appIdentifier, orgName, uploadedFile.id);
226
- }
227
- }
228
- async setConfigFileOnAppOrg(appId, appIdentifier, orgName, fileId) {
229
- const appOrgs = await new sdk_1.Entities().get({
230
- entityName: 'app-org',
231
- criteria: { appId },
232
- });
233
- if (!appOrgs || appOrgs.length === 0) {
234
- throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" because it is not installed in org "${orgName}". Install it via the admin console before using --update-config. You can paste the uploaded file's ID into the app config without needing to re-run this command.`);
235
- }
236
- if (appOrgs.length > 1) {
237
- throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" in org "${orgName}" because ${appOrgs.length} installations were identified. Expected one. Please contact customer support for assistance.`);
238
- }
239
- const appOrg = appOrgs[0];
240
- const nextAppConfig = { ...(appOrg.appConfig || {}), configFile: fileId };
241
- await new sdk_1.Entities().update({
242
- entityName: 'app-org',
243
- id: appOrg.id,
244
- object: { appConfig: nextAppConfig },
245
- });
246
- console.log(`Successfully set "appConfig.configFile" for installed "${appIdentifier}" to new FILE ID: "${fileId}" on org "${orgName}"`);
247
- }
248
- }
249
- exports.ConfigUploadCommand = ConfigUploadCommand;
@@ -1 +0,0 @@
1
- export {};
@@ -1,90 +0,0 @@
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 config_upload_1 = require("./config-upload");
9
- describe('ConfigUploadCommand.parseArgs', () => {
10
- it('parses a bare file path', () => {
11
- const opts = config_upload_1.ConfigUploadCommand.parseArgs(['config.json']);
12
- expect(opts).toEqual({
13
- filePath: 'config.json',
14
- message: undefined,
15
- branch: undefined,
16
- skipGit: false,
17
- updateConfig: false,
18
- });
19
- });
20
- it('parses -m commit message option', () => {
21
- const opts = config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '-m', 'my message']);
22
- expect(opts.message).toEqual('my message');
23
- expect(opts.skipGit).toBe(false);
24
- });
25
- it('parses -b branch option', () => {
26
- const opts = config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '-b', 'feature/x']);
27
- expect(opts.branch).toEqual('feature/x');
28
- });
29
- it('parses --skip-git flag', () => {
30
- const opts = config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '--skip-git']);
31
- expect(opts.skipGit).toBe(true);
32
- });
33
- it('parses --update-config flag', () => {
34
- const opts = config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '--update-config']);
35
- expect(opts.updateConfig).toBe(true);
36
- });
37
- it('throws when -m is missing its value', () => {
38
- expect(() => config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '-m'])).toThrow(/-m requires a commit message/);
39
- });
40
- it('throws on unknown option', () => {
41
- expect(() => config_upload_1.ConfigUploadCommand.parseArgs(['config.json', '--bogus'])).toThrow(/Unknown option: --bogus/);
42
- });
43
- it('throws when no file path is provided', () => {
44
- expect(() => config_upload_1.ConfigUploadCommand.parseArgs([])).toThrow(/missing <path\.json>/);
45
- });
46
- });
47
- describe('ConfigUploadCommand.buildCommitMessage', () => {
48
- it('appends fileId to the first line of a single-line message', () => {
49
- expect(config_upload_1.ConfigUploadCommand.buildCommitMessage('initial commit', 'abc123')).toEqual('initial commit [fileId: abc123]');
50
- });
51
- it('handles CRLF line endings', () => {
52
- const result = config_upload_1.ConfigUploadCommand.buildCommitMessage('header\r\nbody', 'fid');
53
- expect(result).toEqual('header [fileId: fid]\nbody');
54
- });
55
- });
56
- describe('ConfigUploadCommand.run', () => {
57
- const fs = require('fs');
58
- const os = require('os');
59
- const path = require('path');
60
- let tempDir;
61
- beforeEach(() => {
62
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flexplm-config-upload-'));
63
- });
64
- afterEach(() => {
65
- fs.rmSync(tempDir, { recursive: true, force: true });
66
- });
67
- it('throws when the config file is missing orgName', async () => {
68
- const filePath = path.join(tempDir, 'bad.json');
69
- fs.writeFileSync(filePath, JSON.stringify({ appIdentifier: '@vibeiq/flexplm-connector' }), 'utf8');
70
- await expect(new config_upload_1.ConfigUploadCommand().run([filePath, '--skip-git'])).rejects.toThrow(/missing "orgName"/);
71
- });
72
- it('throws when the config file is missing appIdentifier', async () => {
73
- const filePath = path.join(tempDir, 'bad.json');
74
- fs.writeFileSync(filePath, JSON.stringify({ orgName: 'acme' }), 'utf8');
75
- await expect(new config_upload_1.ConfigUploadCommand().run([filePath, '--skip-git'])).rejects.toThrow(/missing "appIdentifier"/);
76
- });
77
- it('throws when the file is not valid JSON', async () => {
78
- const filePath = path.join(tempDir, 'bad.json');
79
- fs.writeFileSync(filePath, '{not json', 'utf8');
80
- await expect(new config_upload_1.ConfigUploadCommand().run([filePath, '--skip-git'])).rejects.toThrow(/not valid JSON/);
81
- });
82
- it('throws when the file does not exist', async () => {
83
- await expect(new config_upload_1.ConfigUploadCommand().run([path.join(tempDir, 'nope.json'), '--skip-git'])).rejects.toThrow(/File not found/);
84
- });
85
- it('throws when the file is not a .json file', async () => {
86
- const filePath = path.join(tempDir, 'config.txt');
87
- fs.writeFileSync(filePath, '{}', 'utf8');
88
- await expect(new config_upload_1.ConfigUploadCommand().run([filePath, '--skip-git'])).rejects.toThrow(/Expected a \.json file/);
89
- });
90
- });
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- export declare class ConfigCli {
3
- main(): Promise<void>;
4
- }
5
- export declare function main(): Promise<void>;
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.main = exports.ConfigCli = void 0;
5
- const config_create_1 = require("./commands/config-create");
6
- const config_upload_1 = require("./commands/config-upload");
7
- const USAGE = `Usage: flexplm-config <command> [args]
8
-
9
- Commands:
10
- create Scaffold a new connector config .json file in the current directory
11
- upload <path.json> [opts] Upload a connector config .json file to VibeIQ
12
-
13
- Upload options:
14
- -m <message> Git commit message (prompted if omitted)
15
- -b <branch> Create a new git branch before committing
16
- --skip-git Skip the post-upload git commit (default: commit)
17
- --update-config Patch the app-org appConfig.configFile with the uploaded file ID without needing to paste into the admin console
18
-
19
- Environment (upload):
20
- CONTRAIL_CLI_EMAIL VibeIQ user email
21
- CONTRAIL_CLI_PASSWORD VibeIQ user password
22
- `;
23
- class ConfigCli {
24
- async main() {
25
- const [, , command, ...rest] = process.argv;
26
- switch (command) {
27
- case 'create':
28
- await new config_create_1.ConfigCreateCommand().run();
29
- return;
30
- case 'upload':
31
- if (!rest[0]) {
32
- console.error('upload: missing <path.json> argument');
33
- console.error(USAGE);
34
- process.exit(1);
35
- }
36
- await new config_upload_1.ConfigUploadCommand().run(rest);
37
- return;
38
- case undefined:
39
- case '-h':
40
- case '--help':
41
- case 'help':
42
- console.log(USAGE);
43
- return;
44
- default:
45
- console.error(`Unknown command: ${command}`);
46
- console.error(USAGE);
47
- process.exit(1);
48
- }
49
- }
50
- }
51
- exports.ConfigCli = ConfigCli;
52
- function main() {
53
- return new ConfigCli().main();
54
- }
55
- exports.main = main;
56
- if (require.main === module) {
57
- main().catch((err) => {
58
- console.error(err && err.message ? err.message : err);
59
- process.exit(1);
60
- });
61
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,68 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const createRunMock = jest.fn().mockResolvedValue(undefined);
4
- const uploadRunMock = jest.fn().mockResolvedValue(undefined);
5
- jest.mock('./commands/config-create', () => ({
6
- ConfigCreateCommand: jest.fn().mockImplementation(() => ({ run: createRunMock })),
7
- }));
8
- jest.mock('./commands/config-upload', () => ({
9
- ConfigUploadCommand: jest.fn().mockImplementation(() => ({ run: uploadRunMock })),
10
- }));
11
- const config_index_1 = require("./config-index");
12
- describe('config cli main dispatcher', () => {
13
- let originalArgv;
14
- let logSpy;
15
- let errorSpy;
16
- let exitSpy;
17
- beforeEach(() => {
18
- originalArgv = process.argv;
19
- createRunMock.mockClear();
20
- uploadRunMock.mockClear();
21
- logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
22
- errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
23
- exitSpy = jest.spyOn(process, 'exit').mockImplementation(((code) => {
24
- throw new Error(`__EXIT__:${code}`);
25
- }));
26
- });
27
- afterEach(() => {
28
- process.argv = originalArgv;
29
- logSpy.mockRestore();
30
- errorSpy.mockRestore();
31
- exitSpy.mockRestore();
32
- });
33
- function setArgv(...args) {
34
- process.argv = ['node', 'cli', ...args];
35
- }
36
- it('dispatches the create command', async () => {
37
- setArgv('create');
38
- await (0, config_index_1.main)();
39
- expect(createRunMock).toHaveBeenCalledTimes(1);
40
- expect(uploadRunMock).not.toHaveBeenCalled();
41
- });
42
- it('dispatches the upload command and forwards remaining args', async () => {
43
- setArgv('upload', 'config.json', '-m', 'msg', '--skip-git');
44
- await (0, config_index_1.main)();
45
- expect(uploadRunMock).toHaveBeenCalledWith(['config.json', '-m', 'msg', '--skip-git']);
46
- });
47
- it('exits when upload is missing its argument', async () => {
48
- setArgv('upload');
49
- await expect((0, config_index_1.main)()).rejects.toThrow('__EXIT__:1');
50
- expect(uploadRunMock).not.toHaveBeenCalled();
51
- expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/missing <path\.json>/));
52
- });
53
- it.each(['help', '-h', '--help'])('prints usage for %s', async (helpFlag) => {
54
- setArgv(helpFlag);
55
- await (0, config_index_1.main)();
56
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm-config/));
57
- });
58
- it('prints usage when no command is provided', async () => {
59
- setArgv();
60
- await (0, config_index_1.main)();
61
- expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/Usage: flexplm-config/));
62
- });
63
- it('exits on an unknown command', async () => {
64
- setArgv('bogus');
65
- await expect((0, config_index_1.main)()).rejects.toThrow('__EXIT__:1');
66
- expect(errorSpy).toHaveBeenCalledWith(expect.stringMatching(/Unknown command: bogus/));
67
- });
68
- });
@@ -1,22 +0,0 @@
1
- {
2
- "orgName": "<ORG_NAME>",
3
- "appIdentifier": "<APP_IDENTIFIER>",
4
-
5
- "_availableAttributes": {
6
- "_note": "Reference only — the connector never reads this key. Add any of the attributes below as real top-level keys if this org needs them, then delete this whole \"_availableAttributes\" block.",
7
- "apiHost": "FlexPLM API host base URL (required for the connector to reach FlexPLM)",
8
- "userName": "FlexPLM user name used for Basic Auth (required)",
9
- "password": "FlexPLM password used for Basic Auth (required)",
10
- "plmEnviornment": "Sent as the PLM_ENV header on every FlexPLM request",
11
- "urlContext": "Path prefix for FlexPLM URLs. Default: '/Windchill'",
12
- "csrfEndpoint": "CSRF token endpoint path. Default: '/servlet/rest/security/csrf'",
13
- "vibeEventEndpoint": "Endpoint VibeIQ posts inbound events to. Default: '/rfa/vibeiq/vibeEvents'",
14
- "itemPreDevelopmentLifecycleStages": "Item lifecycle stages during which items are not synced to FlexPLM. Default: ['concept']",
15
- "identifierAtts": "Map of FlexPLM object class to identifier attribute name(s), e.g. { \"LCSProduct\": [\"itemNumber\"] }",
16
- "LCSMaterial": "{ \"processAsItem\": true } routes LCSMaterial to item:material instead of custom-entity",
17
- "sendMode": "Map of event type to send mode, e.g. { \"ASYNC_PUBLISH_SEASON\": \"vibeiqfile\" }",
18
- "payloadDefaultAsArray": "Whether outbound payload values default to arrays. Default: true",
19
- "flexplmConnect": "{ \"staticHeaders\": { ... } } adds custom static headers to every FlexPLM request",
20
- "propertyMapping": "Reserved for custom property-mapping overrides"
21
- }
22
- }