@contrail/flexplm 1.3.2-alpha.328325d → 1.3.2-alpha.3ffe557
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/compile.d.ts +4 -0
- package/lib/cli/commands/compile.js +73 -0
- package/lib/cli/commands/compile.spec.d.ts +1 -0
- package/lib/cli/commands/compile.spec.js +80 -0
- package/lib/cli/commands/create.d.ts +5 -0
- package/lib/cli/commands/create.js +77 -0
- package/lib/cli/commands/create.spec.d.ts +1 -0
- package/lib/cli/commands/create.spec.js +78 -0
- package/lib/cli/commands/upload.d.ts +17 -0
- package/lib/cli/commands/upload.js +228 -0
- package/lib/cli/commands/upload.spec.d.ts +1 -0
- package/lib/cli/commands/upload.spec.js +88 -0
- package/lib/cli/index.d.ts +5 -0
- package/lib/cli/index.js +70 -0
- package/lib/cli/index.spec.d.ts +1 -0
- package/lib/cli/index.spec.js +85 -0
- package/lib/cli/template/mapping-template.ts.template +62 -0
- package/lib/entity-processor/base-entity-processor.d.ts +47 -5
- package/lib/entity-processor/base-entity-processor.js +53 -28
- package/lib/entity-processor/base-entity-processor.spec.js +1 -103
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/interfaces/mapping-file.d.ts +460 -0
- package/lib/interfaces/mapping-file.js +2 -0
- package/lib/publish/base-process-publish-assortment.d.ts +25 -0
- package/lib/publish/base-process-publish-assortment.js +60 -6
- package/lib/publish/base-process-publish-assortment.spec.js +22 -4
- package/lib/publish/mockData.js +5 -0
- package/lib/transform/identifier-conversion-spec-mockData.js +34 -6
- package/lib/transform/identifier-conversion.d.ts +36 -0
- package/lib/transform/identifier-conversion.js +36 -0
- package/lib/transform/identifier-conversion.spec.js +4 -0
- package/lib/util/config-defaults.js +3 -0
- package/lib/util/config-defaults.spec.js +9 -0
- package/lib/util/data-converter-spec-mockData.js +17 -3
- package/lib/util/data-converter.d.ts +97 -0
- package/lib/util/data-converter.js +133 -1
- package/lib/util/data-converter.spec.js +68 -0
- package/lib/util/error-response-object.d.ts +5 -0
- package/lib/util/error-response-object.js +7 -0
- package/lib/util/event-short-message-status.js +1 -0
- package/lib/util/federation.js +8 -0
- package/lib/util/flexplm-connect.d.ts +7 -0
- package/lib/util/flexplm-connect.js +14 -0
- package/lib/util/logger-config.js +1 -0
- package/lib/util/map-util-spec-mockData.js +17 -3
- package/lib/util/map-utils.d.ts +27 -0
- package/lib/util/map-utils.js +27 -0
- package/lib/util/thumbnail-util.d.ts +21 -0
- package/lib/util/thumbnail-util.js +28 -1
- package/lib/util/thumbnail-util.spec.js +6 -0
- package/lib/util/type-conversion-utils-spec-mockData.js +3 -3
- package/lib/util/type-conversion-utils.d.ts +140 -0
- package/lib/util/type-conversion-utils.js +143 -0
- package/lib/util/type-defaults.d.ts +58 -0
- package/lib/util/type-defaults.js +58 -0
- package/lib/util/type-defaults.spec.js +5 -5
- package/lib/util/type-utils.d.ts +21 -0
- package/lib/util/type-utils.js +23 -0
- package/lib/util/type-utils.spec.js +2 -0
- package/package.json +21 -6
- package/scripts/copy-template.js +10 -0
- package/.github/pull_request_template.md +0 -31
- package/.github/workflows/flexplm-lib.yml +0 -27
- package/.github/workflows/publish-to-npm.yml +0 -121
- package/CHANGELOG.md +0 -35
- package/publish.bat +0 -5
- package/publish.sh +0 -5
- package/src/entity-processor/base-entity-processor.spec.ts +0 -582
- package/src/entity-processor/base-entity-processor.ts +0 -565
- package/src/flexplm-request.ts +0 -28
- package/src/flexplm-utils.spec.ts +0 -27
- package/src/flexplm-utils.ts +0 -29
- package/src/index.ts +0 -22
- package/src/interfaces/interfaces.ts +0 -122
- package/src/interfaces/item-family-changes.ts +0 -67
- package/src/interfaces/publish-change-data.ts +0 -43
- package/src/publish/base-process-publish-assortment-callback.ts +0 -50
- package/src/publish/base-process-publish-assortment.spec.ts +0 -1992
- package/src/publish/base-process-publish-assortment.ts +0 -1134
- package/src/publish/mockData.ts +0 -4561
- package/src/transform/identifier-conversion-spec-mockData.ts +0 -496
- package/src/transform/identifier-conversion.spec.ts +0 -354
- package/src/transform/identifier-conversion.ts +0 -282
- package/src/util/config-defaults.spec.ts +0 -350
- package/src/util/config-defaults.ts +0 -93
- package/src/util/data-converter-spec-mockData.ts +0 -231
- package/src/util/data-converter.spec.ts +0 -1041
- package/src/util/data-converter.ts +0 -762
- package/src/util/error-response-object.spec.ts +0 -116
- package/src/util/error-response-object.ts +0 -50
- package/src/util/event-short-message-status.ts +0 -22
- package/src/util/federation.ts +0 -172
- package/src/util/flexplm-connect.spec.ts +0 -132
- package/src/util/flexplm-connect.ts +0 -208
- package/src/util/logger-config.ts +0 -20
- package/src/util/map-util-spec-mockData.ts +0 -231
- package/src/util/map-utils.spec.ts +0 -103
- package/src/util/map-utils.ts +0 -41
- package/src/util/mockData.ts +0 -101
- package/src/util/thumbnail-util.spec.ts +0 -508
- package/src/util/thumbnail-util.ts +0 -272
- package/src/util/type-conversion-utils-spec-mockData.ts +0 -271
- package/src/util/type-conversion-utils.spec.ts +0 -968
- package/src/util/type-conversion-utils.ts +0 -460
- package/src/util/type-defaults.spec.ts +0 -669
- package/src/util/type-defaults.ts +0 -281
- package/src/util/type-utils.spec.ts +0 -227
- package/src/util/type-utils.ts +0 -144
- package/tsconfig.json +0 -24
- package/tslint.json +0 -57
package/lib/cli/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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]
|
|
9
|
+
|
|
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
|
|
14
|
+
|
|
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
|
+
|
|
20
|
+
Environment (upload):
|
|
21
|
+
CONTRAIL_CLI_EMAIL VibeIQ user email
|
|
22
|
+
CONTRAIL_CLI_PASSWORD VibeIQ user password
|
|
23
|
+
`;
|
|
24
|
+
class Cli {
|
|
25
|
+
async main() {
|
|
26
|
+
const [, , command, ...rest] = process.argv;
|
|
27
|
+
switch (command) {
|
|
28
|
+
case 'create':
|
|
29
|
+
await new create_1.CreateCommand().run();
|
|
30
|
+
return;
|
|
31
|
+
case 'compile':
|
|
32
|
+
if (!rest[0]) {
|
|
33
|
+
console.error('compile: missing <path.ts> argument');
|
|
34
|
+
console.error(USAGE);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
await new compile_1.CompileCommand().run(rest[0]);
|
|
38
|
+
return;
|
|
39
|
+
case 'upload':
|
|
40
|
+
if (!rest[0]) {
|
|
41
|
+
console.error('upload: missing <path.ts> argument');
|
|
42
|
+
console.error(USAGE);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
await new upload_1.UploadCommand().run(rest);
|
|
46
|
+
return;
|
|
47
|
+
case undefined:
|
|
48
|
+
case '-h':
|
|
49
|
+
case '--help':
|
|
50
|
+
case 'help':
|
|
51
|
+
console.log(USAGE);
|
|
52
|
+
return;
|
|
53
|
+
default:
|
|
54
|
+
console.error(`Unknown command: ${command}`);
|
|
55
|
+
console.error(USAGE);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.Cli = Cli;
|
|
61
|
+
function main() {
|
|
62
|
+
return new Cli().main();
|
|
63
|
+
}
|
|
64
|
+
exports.main = main;
|
|
65
|
+
if (require.main === module) {
|
|
66
|
+
main().catch((err) => {
|
|
67
|
+
console.error(err && err.message ? err.message : err);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
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 })),
|
|
8
|
+
}));
|
|
9
|
+
jest.mock('./commands/compile', () => ({
|
|
10
|
+
CompileCommand: jest.fn().mockImplementation(() => ({ run: compileRunMock })),
|
|
11
|
+
}));
|
|
12
|
+
jest.mock('./commands/upload', () => ({
|
|
13
|
+
UploadCommand: jest.fn().mockImplementation(() => ({ run: uploadRunMock })),
|
|
14
|
+
}));
|
|
15
|
+
const index_1 = require("./index");
|
|
16
|
+
describe('cli main dispatcher', () => {
|
|
17
|
+
let originalArgv;
|
|
18
|
+
let logSpy;
|
|
19
|
+
let errorSpy;
|
|
20
|
+
let exitSpy;
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
originalArgv = process.argv;
|
|
23
|
+
createRunMock.mockClear();
|
|
24
|
+
compileRunMock.mockClear();
|
|
25
|
+
uploadRunMock.mockClear();
|
|
26
|
+
logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
27
|
+
errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
28
|
+
exitSpy = jest.spyOn(process, 'exit').mockImplementation(((code) => {
|
|
29
|
+
throw new Error(`__EXIT__:${code}`);
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
process.argv = originalArgv;
|
|
34
|
+
logSpy.mockRestore();
|
|
35
|
+
errorSpy.mockRestore();
|
|
36
|
+
exitSpy.mockRestore();
|
|
37
|
+
});
|
|
38
|
+
function setArgv(...args) {
|
|
39
|
+
process.argv = ['node', 'cli', ...args];
|
|
40
|
+
}
|
|
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/));
|
|
74
|
+
});
|
|
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/));
|
|
79
|
+
});
|
|
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/));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { MappingFile } from '@contrail/flexplm';
|
|
2
|
+
|
|
3
|
+
export const mapping: MappingFile = {
|
|
4
|
+
orgInfo: {
|
|
5
|
+
appIdentifier: '@vibeiq/flexplm-connector',
|
|
6
|
+
orgName: '<ORG_NAME>',
|
|
7
|
+
},
|
|
8
|
+
typeConversion: {
|
|
9
|
+
vibe2flex: {},
|
|
10
|
+
flex2vibe: {},
|
|
11
|
+
},
|
|
12
|
+
LCSSeason: {
|
|
13
|
+
vibeOwningKeys: ['flexPLMSeasonName'],
|
|
14
|
+
vibe2flex: {
|
|
15
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
16
|
+
rekey: {
|
|
17
|
+
seasonName: 'flexPLMSeasonName',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
flex2vibe: {
|
|
21
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
22
|
+
rekey: {
|
|
23
|
+
flexPLMSeasonName: 'seasonName',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
LCSProduct: {
|
|
28
|
+
vibeOwningKeys: ['itemNumber'],
|
|
29
|
+
vibe2flex: {
|
|
30
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
31
|
+
rekey: {
|
|
32
|
+
vibeIQIdentifier: 'itemNumber',
|
|
33
|
+
productName: 'name',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
flex2vibe: {
|
|
37
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
38
|
+
rekey: {
|
|
39
|
+
itemNumber: 'vibeIQIdentifier',
|
|
40
|
+
name: 'productName',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
LCSSKU: {
|
|
45
|
+
vibeOwningKeys: ['itemNumber'],
|
|
46
|
+
vibe2flex: {
|
|
47
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
48
|
+
rekey: {
|
|
49
|
+
vibeIQIdentifier: 'itemNumber',
|
|
50
|
+
skuName: 'optionName',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
flex2vibe: {
|
|
54
|
+
transformOrder: [{ processor: 'REKEY', rekeyDelete: true, rekeyTransformersKey: 'rekey' }],
|
|
55
|
+
rekey: {
|
|
56
|
+
itemNumber: 'vibeIQIdentifier',
|
|
57
|
+
optionName: 'skuName',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
};
|
|
@@ -19,13 +19,36 @@ export declare abstract class BaseEntityProcessor {
|
|
|
19
19
|
inbound(event: EntityPayloadType): Promise<any>;
|
|
20
20
|
handleIncomingUpsert(event: EntityPayloadType): Promise<any>;
|
|
21
21
|
getInboundStatusMessage(statusObject: any): string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
/**This will query for the entity, and handle post-processing
|
|
23
|
+
* of any critieria that is defined at the sub-type level.
|
|
24
|
+
* Because sub-type criteria can't be used in the search done
|
|
25
|
+
* on the server. This is expected to be called by getIncomingEntity().
|
|
26
|
+
*
|
|
27
|
+
* @param entityType: the root type of the entity
|
|
28
|
+
* @param entityTypePath: the full type path of the entity. Ex: custom-entity:sample
|
|
29
|
+
* @param propertyCriteria: all the criteria to search for the entity
|
|
30
|
+
* @returns the entities that match the criteria
|
|
31
|
+
*/
|
|
27
32
|
queryEntityWithSubTypeCriteria(entityType: string, entityTypePath: string, propertyCriteria: any): Promise<any[]>;
|
|
33
|
+
/** This is to get the criteria for the entity that is being processed.
|
|
34
|
+
* This is to be overridden for item & project-item because of the need for
|
|
35
|
+
* setting the roles criteria.
|
|
36
|
+
*
|
|
37
|
+
* @param entityType: the root type of the entity
|
|
38
|
+
* @param entityTypePath: the full type path of the entity. Ex: custom-entity:sample
|
|
39
|
+
* @param propertyCriteria: all the criteria to search for the entity
|
|
40
|
+
* @returns the criteria for the entity
|
|
41
|
+
*/
|
|
28
42
|
getCriteriaForEntity(entityType: string, entityTypePath: string, propertyCriteria: any): Promise<any>;
|
|
43
|
+
/** This is to get the properties that are owned by the root type
|
|
44
|
+
* This needs to be overridded for multi-level types, such as item
|
|
45
|
+
* and project-item. And for those types, the propertyCriteria
|
|
46
|
+
* will be needed to determine the correct level.
|
|
47
|
+
*
|
|
48
|
+
* @param rootType: the full root type entity for the processed entity
|
|
49
|
+
* @param propertyCriteria: the criteria to determine the correct level (unused for single level types)
|
|
50
|
+
* @returns: string[] of the property keys
|
|
51
|
+
*/
|
|
29
52
|
getRootTypePropertyKeys(rootType: any, propertyCriteria?: any): string[];
|
|
30
53
|
handleIncomingDelete(event: any): Promise<void>;
|
|
31
54
|
getTransformedData(event: any): Promise<any>;
|
|
@@ -41,7 +64,26 @@ export declare abstract class BaseEntityProcessor {
|
|
|
41
64
|
getOutboundEntityUpdates(event: any, flexResponse: any): Promise<any>;
|
|
42
65
|
handleOutgoingDelete(entityType: any, event: any): Promise<void>;
|
|
43
66
|
protected abstract getOutgoingUpsertPayload(entityType: any, event: any): Promise<EntityPayloadType>;
|
|
67
|
+
/** Create a new event-workflow-request to rerun sending the entity to FlexPLM
|
|
68
|
+
* The event must contain any information needed to ensure it is put in the correct queue for the entity
|
|
69
|
+
*
|
|
70
|
+
* @param triggerKey Ex: event.entityType + '|sendUpsertToFlexPLM'
|
|
71
|
+
* @param event
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
44
74
|
protected triggerNewEvent(triggerKey: string, event: any): Promise<any>;
|
|
75
|
+
/** Sends the current state of the entity to FlexPLM.
|
|
76
|
+
* So any changes made in Vibe between the event being generated and the event being processed are sent to FlexPLM.
|
|
77
|
+
*
|
|
78
|
+
* @param event must contain entityType, id; which are used to query for the entity
|
|
79
|
+
* @returns results of sending the entity to FlexPLM
|
|
80
|
+
*/
|
|
45
81
|
protected sendUpsertToFlexPLM(event: any): Promise<any>;
|
|
82
|
+
/** Generates the payload to send to FlexPLM, based on the current state of the entity.
|
|
83
|
+
* The current state of the entity are used as the newData and oldData; which is passed
|
|
84
|
+
* to getOutgoingUpsertPayload to generate the payload.
|
|
85
|
+
* @param event information about the item to send to FlexPLM
|
|
86
|
+
* @returns The payload to send to FlexPLM
|
|
87
|
+
*/
|
|
46
88
|
protected getEntityCurrentStateUpsertPayload(event: any): Promise<EntityPayloadType>;
|
|
47
89
|
}
|
|
@@ -23,6 +23,7 @@ class BaseEntityProcessor {
|
|
|
23
23
|
this.entities = new sdk_1.Entities();
|
|
24
24
|
this.orgSlug = this.config?.orgSlug || 'unset-orgSlug';
|
|
25
25
|
}
|
|
26
|
+
// inbound
|
|
26
27
|
async inbound(event) {
|
|
27
28
|
const eventType = event.eventType;
|
|
28
29
|
console.log(`inbound entity: ${eventType}:${event.objectClass}`);
|
|
@@ -42,6 +43,7 @@ class BaseEntityProcessor {
|
|
|
42
43
|
async handleIncomingUpsert(event) {
|
|
43
44
|
const inboundData = await this.getTransformedData(event);
|
|
44
45
|
const incomingEntityResponse = await this.getIncomingEntity(event, inboundData);
|
|
46
|
+
// This case means there was an early return in the getIncomingEntity method
|
|
45
47
|
if (incomingEntityResponse.earlyReturn) {
|
|
46
48
|
const statusMsg = this.getInboundStatusMessage({
|
|
47
49
|
status: event_short_message_status_1.EventShortMessageStatus.FAILURE,
|
|
@@ -136,35 +138,18 @@ class BaseEntityProcessor {
|
|
|
136
138
|
+ ', federatedId: ' + statusObject.federatedId
|
|
137
139
|
+ ', orgSlug: ' + this.orgSlug;
|
|
138
140
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
propertyValue
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
if (!identityEntities || (Array.isArray(identityEntities) && identityEntities.length === 0)) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
if (Array.isArray(identityEntities) && identityEntities.length > 1) {
|
|
156
|
-
throw new Error('Multiple identity entities found for poolKey: ' + poolKey + ', ' + propertyName + ': ' + propertyValue);
|
|
157
|
-
}
|
|
158
|
-
const identityEntity = Array.isArray(identityEntities) ? identityEntities[0] : identityEntities;
|
|
159
|
-
const entityReference = identityEntity.entityReference;
|
|
160
|
-
const [entityName, id] = entityReference.split(':');
|
|
161
|
-
const entity = await this.entities.get({
|
|
162
|
-
entityName,
|
|
163
|
-
id
|
|
164
|
-
});
|
|
165
|
-
return entity;
|
|
166
|
-
}
|
|
141
|
+
/**This will query for the entity, and handle post-processing
|
|
142
|
+
* of any critieria that is defined at the sub-type level.
|
|
143
|
+
* Because sub-type criteria can't be used in the search done
|
|
144
|
+
* on the server. This is expected to be called by getIncomingEntity().
|
|
145
|
+
*
|
|
146
|
+
* @param entityType: the root type of the entity
|
|
147
|
+
* @param entityTypePath: the full type path of the entity. Ex: custom-entity:sample
|
|
148
|
+
* @param propertyCriteria: all the criteria to search for the entity
|
|
149
|
+
* @returns the entities that match the criteria
|
|
150
|
+
*/
|
|
167
151
|
async queryEntityWithSubTypeCriteria(entityType, entityTypePath, propertyCriteria) {
|
|
152
|
+
//allCriteria; identifierKeys; entityType; entityTypePath
|
|
168
153
|
if (!entityType || !entityTypePath) {
|
|
169
154
|
throw new Error('type and entityTypePath must be defined');
|
|
170
155
|
}
|
|
@@ -175,6 +160,15 @@ class BaseEntityProcessor {
|
|
|
175
160
|
const returnedEntities = await this.dc.getAllObjectReferences(entityType, rootTypeCriteria, subTypeCriteria);
|
|
176
161
|
return returnedEntities;
|
|
177
162
|
}
|
|
163
|
+
/** This is to get the criteria for the entity that is being processed.
|
|
164
|
+
* This is to be overridden for item & project-item because of the need for
|
|
165
|
+
* setting the roles criteria.
|
|
166
|
+
*
|
|
167
|
+
* @param entityType: the root type of the entity
|
|
168
|
+
* @param entityTypePath: the full type path of the entity. Ex: custom-entity:sample
|
|
169
|
+
* @param propertyCriteria: all the criteria to search for the entity
|
|
170
|
+
* @returns the criteria for the entity
|
|
171
|
+
*/
|
|
178
172
|
async getCriteriaForEntity(entityType, entityTypePath, propertyCriteria) {
|
|
179
173
|
if (!entityType || !entityTypePath) {
|
|
180
174
|
throw new Error('type and entityTypePath must be defined');
|
|
@@ -199,6 +193,15 @@ class BaseEntityProcessor {
|
|
|
199
193
|
}
|
|
200
194
|
return { rootTypeCriteria, subTypeCriteria };
|
|
201
195
|
}
|
|
196
|
+
/** This is to get the properties that are owned by the root type
|
|
197
|
+
* This needs to be overridded for multi-level types, such as item
|
|
198
|
+
* and project-item. And for those types, the propertyCriteria
|
|
199
|
+
* will be needed to determine the correct level.
|
|
200
|
+
*
|
|
201
|
+
* @param rootType: the full root type entity for the processed entity
|
|
202
|
+
* @param propertyCriteria: the criteria to determine the correct level (unused for single level types)
|
|
203
|
+
* @returns: string[] of the property keys
|
|
204
|
+
*/
|
|
202
205
|
getRootTypePropertyKeys(rootType, propertyCriteria = null) {
|
|
203
206
|
const props = rootType['typeProperties'];
|
|
204
207
|
const rootTypePropertyKeys = props.map(prop => prop.slug);
|
|
@@ -231,6 +234,8 @@ class BaseEntityProcessor {
|
|
|
231
234
|
async getVibeOwningKeys(entity) {
|
|
232
235
|
let vibeOwningKeys = [];
|
|
233
236
|
if (this.transformMapFile && entity) {
|
|
237
|
+
//Technically the transform is flex->vibe. But the vibe entity being updated was passed in,
|
|
238
|
+
// so we use VIBE2FLEX_DIRECTION to get the mapKey
|
|
234
239
|
const mapKey = await type_conversion_utils_1.TypeConversionUtils.getMapKey(this.transformMapFile, this.mapFileUtil, entity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION);
|
|
235
240
|
const mapSection = await map_utils_1.MapUtil.getFullMapSection(this.transformMapFile, this.mapFileUtil, mapKey);
|
|
236
241
|
vibeOwningKeys = mapSection?.vibeOwningKeys || [];
|
|
@@ -265,6 +270,7 @@ class BaseEntityProcessor {
|
|
|
265
270
|
console.log('updateEntity: ' + JSON.stringify(options));
|
|
266
271
|
return await new sdk_1.Entities().update(options);
|
|
267
272
|
}
|
|
273
|
+
// outbound
|
|
268
274
|
async outbound(event) {
|
|
269
275
|
const entityType = event.entityType;
|
|
270
276
|
const eventType = event.eventType;
|
|
@@ -341,6 +347,13 @@ class BaseEntityProcessor {
|
|
|
341
347
|
async handleOutgoingDelete(entityType, event) {
|
|
342
348
|
console.warn('delete is not configured', entityType, event.oldData);
|
|
343
349
|
}
|
|
350
|
+
/** Create a new event-workflow-request to rerun sending the entity to FlexPLM
|
|
351
|
+
* The event must contain any information needed to ensure it is put in the correct queue for the entity
|
|
352
|
+
*
|
|
353
|
+
* @param triggerKey Ex: event.entityType + '|sendUpsertToFlexPLM'
|
|
354
|
+
* @param event
|
|
355
|
+
* @returns
|
|
356
|
+
*/
|
|
344
357
|
async triggerNewEvent(triggerKey, event) {
|
|
345
358
|
const newEvent = {
|
|
346
359
|
entityName: 'event-workflow-request',
|
|
@@ -352,6 +365,12 @@ class BaseEntityProcessor {
|
|
|
352
365
|
const response = await this.entities.create(newEvent);
|
|
353
366
|
return response;
|
|
354
367
|
}
|
|
368
|
+
/** Sends the current state of the entity to FlexPLM.
|
|
369
|
+
* So any changes made in Vibe between the event being generated and the event being processed are sent to FlexPLM.
|
|
370
|
+
*
|
|
371
|
+
* @param event must contain entityType, id; which are used to query for the entity
|
|
372
|
+
* @returns results of sending the entity to FlexPLM
|
|
373
|
+
*/
|
|
355
374
|
async sendUpsertToFlexPLM(event) {
|
|
356
375
|
const payload = await this.getEntityCurrentStateUpsertPayload(event);
|
|
357
376
|
if (!payload) {
|
|
@@ -392,6 +411,12 @@ class BaseEntityProcessor {
|
|
|
392
411
|
throw e;
|
|
393
412
|
}
|
|
394
413
|
}
|
|
414
|
+
/** Generates the payload to send to FlexPLM, based on the current state of the entity.
|
|
415
|
+
* The current state of the entity are used as the newData and oldData; which is passed
|
|
416
|
+
* to getOutgoingUpsertPayload to generate the payload.
|
|
417
|
+
* @param event information about the item to send to FlexPLM
|
|
418
|
+
* @returns The payload to send to FlexPLM
|
|
419
|
+
*/
|
|
395
420
|
async getEntityCurrentStateUpsertPayload(event) {
|
|
396
421
|
const id = event.id;
|
|
397
422
|
if (!id) {
|
|
@@ -269,6 +269,7 @@ describe('BaseEntityProcessor', () => {
|
|
|
269
269
|
const rootCriteriaResults = Object.assign({}, rootCriteria);
|
|
270
270
|
const subCriteriaResults = Object.assign({}, subCriteria);
|
|
271
271
|
const mockGetByRootTypeProperties = jest.spyOn(btep, 'getRootTypePropertyKeys').mockReturnValue(['rootText']);
|
|
272
|
+
// const mockDCgetAllObjectReferences = jest.spyOn(dc, 'getAllObjectReferences').mockReturnValue(Promise.resolve([]));
|
|
272
273
|
const { rootTypeCriteria, subTypeCriteria } = await btep.getCriteriaForEntity(entityType, entityTypePath, propertyCriteria);
|
|
273
274
|
expect(mockTypeUtilGetByRootAndPath).toBeCalledTimes(1);
|
|
274
275
|
expect(mockTypeUtilGetByRootAndPath).toBeCalledWith({ root: entityType });
|
|
@@ -394,107 +395,4 @@ describe('BaseEntityProcessor', () => {
|
|
|
394
395
|
expect(result).toEqual({ status: 200, data: { message: 'No Changes to persist for entity: existing-1' } });
|
|
395
396
|
});
|
|
396
397
|
});
|
|
397
|
-
describe('getEntityUsingIdentityService', () => {
|
|
398
|
-
const config = {};
|
|
399
|
-
const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
|
|
400
|
-
const dc = new data_converter_1.DataConverter(config, mapFileUtil);
|
|
401
|
-
let btep;
|
|
402
|
-
let mockEntitiesGet;
|
|
403
|
-
beforeEach(() => {
|
|
404
|
-
btep = new TestBaseEntityProcessor(config, dc, mapFileUtil, 'item');
|
|
405
|
-
mockEntitiesGet = jest.fn();
|
|
406
|
-
btep.entities = { get: mockEntitiesGet };
|
|
407
|
-
});
|
|
408
|
-
it('should throw error when poolKey is missing', async () => {
|
|
409
|
-
await expect(btep.getEntityUsingIdentityService({
|
|
410
|
-
poolKey: '',
|
|
411
|
-
propertyName: 'itemNumber',
|
|
412
|
-
propertyValue: '12345'
|
|
413
|
-
})).rejects.toThrow('poolKey, propertyName, and propertyValue must be defined');
|
|
414
|
-
});
|
|
415
|
-
it('should throw error when propertyName is missing', async () => {
|
|
416
|
-
await expect(btep.getEntityUsingIdentityService({
|
|
417
|
-
poolKey: 'item',
|
|
418
|
-
propertyName: '',
|
|
419
|
-
propertyValue: '12345'
|
|
420
|
-
})).rejects.toThrow('poolKey, propertyName, and propertyValue must be defined');
|
|
421
|
-
});
|
|
422
|
-
it('should throw error when propertyValue is missing', async () => {
|
|
423
|
-
await expect(btep.getEntityUsingIdentityService({
|
|
424
|
-
poolKey: 'item',
|
|
425
|
-
propertyName: 'itemNumber',
|
|
426
|
-
propertyValue: ''
|
|
427
|
-
})).rejects.toThrow('poolKey, propertyName, and propertyValue must be defined');
|
|
428
|
-
});
|
|
429
|
-
it('should return undefined when no identity entities are found (empty array)', async () => {
|
|
430
|
-
mockEntitiesGet.mockResolvedValue([]);
|
|
431
|
-
const result = await btep.getEntityUsingIdentityService({
|
|
432
|
-
poolKey: 'item',
|
|
433
|
-
propertyName: 'itemNumber',
|
|
434
|
-
propertyValue: '12345'
|
|
435
|
-
});
|
|
436
|
-
expect(result).toBeUndefined();
|
|
437
|
-
expect(mockEntitiesGet).toHaveBeenCalledWith({
|
|
438
|
-
entityName: 'identity',
|
|
439
|
-
criteria: { poolKey: 'item', propertyName: 'itemNumber', propertyValue: '12345' }
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
it('should return undefined when identity entities result is null', async () => {
|
|
443
|
-
mockEntitiesGet.mockResolvedValue(null);
|
|
444
|
-
const result = await btep.getEntityUsingIdentityService({
|
|
445
|
-
poolKey: 'item',
|
|
446
|
-
propertyName: 'itemNumber',
|
|
447
|
-
propertyValue: '12345'
|
|
448
|
-
});
|
|
449
|
-
expect(result).toBeUndefined();
|
|
450
|
-
});
|
|
451
|
-
it('should throw error when multiple identity entities are found', async () => {
|
|
452
|
-
mockEntitiesGet.mockResolvedValue([
|
|
453
|
-
{ entityReference: 'item:1' },
|
|
454
|
-
{ entityReference: 'item:2' }
|
|
455
|
-
]);
|
|
456
|
-
await expect(btep.getEntityUsingIdentityService({
|
|
457
|
-
poolKey: 'item',
|
|
458
|
-
propertyName: 'itemNumber',
|
|
459
|
-
propertyValue: '12345'
|
|
460
|
-
})).rejects.toThrow('Multiple identity entities found for poolKey: item, itemNumber: 12345');
|
|
461
|
-
});
|
|
462
|
-
it('should return the entity when one identity entity is found (array)', async () => {
|
|
463
|
-
const mockEntity = { id: '1', name: 'Test Item' };
|
|
464
|
-
mockEntitiesGet
|
|
465
|
-
.mockResolvedValueOnce([{ entityReference: 'item:1' }])
|
|
466
|
-
.mockResolvedValueOnce(mockEntity);
|
|
467
|
-
const result = await btep.getEntityUsingIdentityService({
|
|
468
|
-
poolKey: 'item',
|
|
469
|
-
propertyName: 'itemNumber',
|
|
470
|
-
propertyValue: '12345'
|
|
471
|
-
});
|
|
472
|
-
expect(result).toEqual(mockEntity);
|
|
473
|
-
expect(mockEntitiesGet).toHaveBeenCalledTimes(2);
|
|
474
|
-
expect(mockEntitiesGet).toHaveBeenNthCalledWith(1, {
|
|
475
|
-
entityName: 'identity',
|
|
476
|
-
criteria: { poolKey: 'item', propertyName: 'itemNumber', propertyValue: '12345' }
|
|
477
|
-
});
|
|
478
|
-
expect(mockEntitiesGet).toHaveBeenNthCalledWith(2, {
|
|
479
|
-
entityName: 'item',
|
|
480
|
-
id: '1'
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
it('should return the entity when identity result is a single object (not array)', async () => {
|
|
484
|
-
const mockEntity = { id: '5', name: 'Test Material' };
|
|
485
|
-
mockEntitiesGet
|
|
486
|
-
.mockResolvedValueOnce({ entityReference: 'item:5' })
|
|
487
|
-
.mockResolvedValueOnce(mockEntity);
|
|
488
|
-
const result = await btep.getEntityUsingIdentityService({
|
|
489
|
-
poolKey: 'item:material',
|
|
490
|
-
propertyName: 'materialNumber',
|
|
491
|
-
propertyValue: 'MAT-001'
|
|
492
|
-
});
|
|
493
|
-
expect(result).toEqual(mockEntity);
|
|
494
|
-
expect(mockEntitiesGet).toHaveBeenNthCalledWith(2, {
|
|
495
|
-
entityName: 'item',
|
|
496
|
-
id: '5'
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
398
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './util/map-utils';
|
|
|
16
16
|
export * from './interfaces/interfaces';
|
|
17
17
|
export * from './interfaces/item-family-changes';
|
|
18
18
|
export * from './interfaces/publish-change-data';
|
|
19
|
+
export * from './interfaces/mapping-file';
|
|
19
20
|
export * from './publish/base-process-publish-assortment';
|
|
20
21
|
export * from './publish/base-process-publish-assortment-callback';
|
|
21
22
|
export * from './entity-processor/base-entity-processor';
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __exportStar(require("./util/map-utils"), exports);
|
|
|
32
32
|
__exportStar(require("./interfaces/interfaces"), exports);
|
|
33
33
|
__exportStar(require("./interfaces/item-family-changes"), exports);
|
|
34
34
|
__exportStar(require("./interfaces/publish-change-data"), exports);
|
|
35
|
+
__exportStar(require("./interfaces/mapping-file"), exports);
|
|
35
36
|
__exportStar(require("./publish/base-process-publish-assortment"), exports);
|
|
36
37
|
__exportStar(require("./publish/base-process-publish-assortment-callback"), exports);
|
|
37
38
|
__exportStar(require("./entity-processor/base-entity-processor"), exports);
|