@contrail/flexplm 1.1.57 → 1.1.58
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/.vscode/launch.json +20 -0
- package/lib/entity-processor/base-entity-processor.d.ts +2 -0
- package/lib/entity-processor/base-entity-processor.js +27 -0
- package/lib/entity-processor/base-entity-processor.spec.js +94 -0
- package/lib/util/data-converter.d.ts +1 -1
- package/lib/util/data-converter.js +15 -3
- package/package.json +3 -2
- package/src/entity-processor/base-entity-processor.spec.ts +111 -2
- package/src/entity-processor/base-entity-processor.ts +55 -0
- package/src/util/data-converter.spec.ts +1 -200
- package/src/util/data-converter.ts +15 -3
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "node",
|
|
9
|
+
"request": "launch",
|
|
10
|
+
"name": "Launch Program",
|
|
11
|
+
"skipFiles": [
|
|
12
|
+
"<node_internals>/**"
|
|
13
|
+
],
|
|
14
|
+
"program": "${workspaceFolder}\\lib\\index.js",
|
|
15
|
+
"outFiles": [
|
|
16
|
+
"${workspaceFolder}/**/*.js"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -19,6 +19,8 @@ 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
|
+
queryEntityWithSubTypeCriteria(entityType: string, entityTypePath: string, propertyCriteria: any): Promise<any[]>;
|
|
23
|
+
getRootTypePropertyKeys(rootType: any, propertyCriteria?: any): string[];
|
|
22
24
|
handleIncomingDelete(event: any): Promise<void>;
|
|
23
25
|
getTransformedData(event: any): Promise<any>;
|
|
24
26
|
getUpdatesForEntity(entity: any, inboundData: any): Promise<object>;
|
|
@@ -114,6 +114,33 @@ class BaseEntityProcessor {
|
|
|
114
114
|
+ ', federatedId: ' + statusObject.federatedId
|
|
115
115
|
+ ', orgSlug: ' + this.orgSlug;
|
|
116
116
|
}
|
|
117
|
+
async queryEntityWithSubTypeCriteria(entityType, entityTypePath, propertyCriteria) {
|
|
118
|
+
if (!entityType || !entityTypePath) {
|
|
119
|
+
throw new Error('type and entityTypePath must be defined');
|
|
120
|
+
}
|
|
121
|
+
if (!propertyCriteria || Object.getOwnPropertyNames(propertyCriteria).length == 0) {
|
|
122
|
+
throw new Error('propertyCriteria must be defined and have at least one property');
|
|
123
|
+
}
|
|
124
|
+
const rootType = await this.typeUtil.getByRootAndPath({ root: entityType });
|
|
125
|
+
const rootTypePropertyKeys = await this.getRootTypePropertyKeys(rootType, propertyCriteria);
|
|
126
|
+
const rootTypeCriteria = { typePath: entityTypePath };
|
|
127
|
+
const subTypeCriteria = {};
|
|
128
|
+
for (const key in propertyCriteria) {
|
|
129
|
+
if (rootTypePropertyKeys.includes(key)) {
|
|
130
|
+
rootTypeCriteria[key] = propertyCriteria[key];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
subTypeCriteria[key] = propertyCriteria[key];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const returnedEntities = await this.dc.getAllObjectReferences(entityType, rootTypeCriteria, subTypeCriteria);
|
|
137
|
+
return returnedEntities;
|
|
138
|
+
}
|
|
139
|
+
getRootTypePropertyKeys(rootType, propertyCriteria = null) {
|
|
140
|
+
const props = rootType['typeProperties'];
|
|
141
|
+
const rootTypePropertyKeys = props.map(prop => prop.slug);
|
|
142
|
+
return rootTypePropertyKeys;
|
|
143
|
+
}
|
|
117
144
|
async handleIncomingDelete(event) {
|
|
118
145
|
console.warn('delete is not configured', event);
|
|
119
146
|
}
|
|
@@ -4,6 +4,26 @@ const sdk_1 = require("@contrail/sdk");
|
|
|
4
4
|
const transform_data_1 = require("@contrail/transform-data");
|
|
5
5
|
const data_converter_1 = require("../util/data-converter");
|
|
6
6
|
const base_entity_processor_1 = require("./base-entity-processor");
|
|
7
|
+
const mockRootType = {
|
|
8
|
+
typeProperties: [
|
|
9
|
+
{ slug: 'rootText' }
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
const mockTypeUtilGetByRootAndPath = jest.fn((options) => {
|
|
13
|
+
return Promise.resolve(mockRootType);
|
|
14
|
+
});
|
|
15
|
+
jest.mock('@contrail/sdk', () => {
|
|
16
|
+
const origModule = jest.requireActual('@contrail/sdk');
|
|
17
|
+
return {
|
|
18
|
+
__esModule: true,
|
|
19
|
+
...origModule,
|
|
20
|
+
Types: jest.fn().mockImplementation(() => {
|
|
21
|
+
return {
|
|
22
|
+
getByRootAndPath: mockTypeUtilGetByRootAndPath
|
|
23
|
+
};
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
|
+
});
|
|
7
27
|
class TestBaseEntityProcessor extends base_entity_processor_1.BaseEntityProcessor {
|
|
8
28
|
getIncomingEntity(event, inboundData) {
|
|
9
29
|
throw new Error("Method not implemented.");
|
|
@@ -72,4 +92,78 @@ describe('BaseEntityProcessor', () => {
|
|
|
72
92
|
expect(updates).toBeUndefined();
|
|
73
93
|
});
|
|
74
94
|
});
|
|
95
|
+
describe('queryEntityWithSubType', () => {
|
|
96
|
+
const config = {};
|
|
97
|
+
const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
});
|
|
100
|
+
it('entity parameter not set', async () => {
|
|
101
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
102
|
+
const entity = '';
|
|
103
|
+
const subType = 'test';
|
|
104
|
+
const propertyCriteria = { a: 'val' };
|
|
105
|
+
expect(async () => { await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria); }).rejects.toThrow();
|
|
106
|
+
});
|
|
107
|
+
it('entityTypePath not set', async () => {
|
|
108
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
109
|
+
const entity = 'test';
|
|
110
|
+
const subType = '';
|
|
111
|
+
const propertyCriteria = {};
|
|
112
|
+
expect(async () => { await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria); }).rejects.toThrow();
|
|
113
|
+
});
|
|
114
|
+
it('propertyCriteria not set', async () => {
|
|
115
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
116
|
+
const entity = 'test';
|
|
117
|
+
const subType = 'test';
|
|
118
|
+
const propertyCriteria = {};
|
|
119
|
+
expect(async () => { await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria); }).rejects.toThrow();
|
|
120
|
+
});
|
|
121
|
+
it('basic queryEntityWithSubTypeCriteria test', async () => {
|
|
122
|
+
const dc = new data_converter_1.DataConverter(config, mapFileUtil);
|
|
123
|
+
const btep = new TestBaseEntityProcessor(config, dc, mapFileUtil, 'test');
|
|
124
|
+
const entityType = 'custom-entity';
|
|
125
|
+
const entityTypePath = 'custom-entity:sample';
|
|
126
|
+
const rootCriteria = {
|
|
127
|
+
rootText: 'root'
|
|
128
|
+
};
|
|
129
|
+
const subCriteria = {
|
|
130
|
+
sampleText: 'sample'
|
|
131
|
+
};
|
|
132
|
+
const propertyCriteria = Object.assign({}, rootCriteria, subCriteria);
|
|
133
|
+
const rootCriteriaResults = Object.assign({ typePath: entityTypePath }, rootCriteria);
|
|
134
|
+
const mockGetByRootTypeProperties = jest.spyOn(btep, 'getRootTypePropertyKeys').mockReturnValue(['rootText']);
|
|
135
|
+
const mockDCgetAllObjectReferences = jest.spyOn(dc, 'getAllObjectReferences').mockReturnValue(Promise.resolve([]));
|
|
136
|
+
const results = await btep.queryEntityWithSubTypeCriteria(entityType, entityTypePath, propertyCriteria);
|
|
137
|
+
expect(results).toEqual([]);
|
|
138
|
+
expect(mockTypeUtilGetByRootAndPath).toBeCalledTimes(1);
|
|
139
|
+
expect(mockTypeUtilGetByRootAndPath).toBeCalledWith({ root: entityType });
|
|
140
|
+
expect(mockGetByRootTypeProperties).toBeCalledTimes(1);
|
|
141
|
+
expect(mockGetByRootTypeProperties).toBeCalledWith(mockRootType, propertyCriteria);
|
|
142
|
+
expect(mockDCgetAllObjectReferences).toBeCalledTimes(1);
|
|
143
|
+
expect(mockDCgetAllObjectReferences).toBeCalledWith(entityType, rootCriteriaResults, subCriteria);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('getRootTypePropertyKeys', () => {
|
|
147
|
+
it('rootType parameter not set', async () => {
|
|
148
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
149
|
+
const rootType = '';
|
|
150
|
+
expect(async () => { await btep.getRootTypePropertyKeys(rootType); }).rejects.toThrow();
|
|
151
|
+
});
|
|
152
|
+
it('get keys from rootType.typeProperties', async () => {
|
|
153
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
154
|
+
const rootType = {
|
|
155
|
+
typeProperties: ['a', 'b']
|
|
156
|
+
};
|
|
157
|
+
const keys = await btep.getRootTypePropertyKeys(rootType);
|
|
158
|
+
expect(keys).not.toEqual(['a', 'b']);
|
|
159
|
+
});
|
|
160
|
+
it('get keys from rootType.typeProperties', async () => {
|
|
161
|
+
const btep = new TestBaseEntityProcessor({}, {}, {}, 'test');
|
|
162
|
+
const rootType = {
|
|
163
|
+
typeProperties: [{ slug: 'a' }, { slug: 'b' }]
|
|
164
|
+
};
|
|
165
|
+
const keys = await btep.getRootTypePropertyKeys(rootType);
|
|
166
|
+
expect(keys).toEqual(['a', 'b']);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
75
169
|
});
|
|
@@ -28,7 +28,7 @@ export declare class DataConverter {
|
|
|
28
28
|
setEnumerationKeys(prop: any, nd: any, matchByDisplay: any): any;
|
|
29
29
|
getPersistableChanges(entity: object, changes: object): object;
|
|
30
30
|
setObjectReferenceValue(prop: any, nd: any): Promise<any>;
|
|
31
|
-
getAllObjectReferences(entityType:
|
|
31
|
+
getAllObjectReferences(entityType: string, rootTypeCriteria: any, postProcessCriteria?: any): Promise<any[]>;
|
|
32
32
|
checkKeysAndValues(criteria: any, arrayOfObjects: any, entityTypePath: any): any[];
|
|
33
33
|
setUserListValue(prop: any, nd: any): Promise<any>;
|
|
34
34
|
getUserByEmail(nd: any): Promise<any>;
|
|
@@ -398,22 +398,29 @@ class DataConverter {
|
|
|
398
398
|
this.objRefCache[cacheKey] = objectReferenceId;
|
|
399
399
|
return objectReferenceId;
|
|
400
400
|
}
|
|
401
|
-
async getAllObjectReferences(entityType, rootTypeCriteria) {
|
|
401
|
+
async getAllObjectReferences(entityType, rootTypeCriteria, postProcessCriteria = null) {
|
|
402
402
|
const entities = new sdk_1.Entities();
|
|
403
403
|
let loads = [];
|
|
404
404
|
let nextPageKey;
|
|
405
405
|
let usedV2 = false;
|
|
406
406
|
do {
|
|
407
|
+
const take = 1000;
|
|
407
408
|
const loadPage = await entities.get({
|
|
408
409
|
entityName: entityType,
|
|
409
410
|
criteria: rootTypeCriteria,
|
|
410
411
|
apiVersion: sdk_1.API_VERSION.V2,
|
|
412
|
+
take,
|
|
411
413
|
nextPageKey,
|
|
412
414
|
});
|
|
413
415
|
nextPageKey = loadPage?.nextPageKey;
|
|
414
416
|
if (Object.keys(loadPage).includes('results')) {
|
|
415
417
|
usedV2 = true;
|
|
416
|
-
|
|
418
|
+
let postProcessedResults = loadPage.results;
|
|
419
|
+
if (postProcessCriteria && Object.keys(postProcessCriteria).length !== 0) {
|
|
420
|
+
const subEntityTypePath = rootTypeCriteria.typePath || entityType;
|
|
421
|
+
postProcessedResults = this.checkKeysAndValues(postProcessCriteria, postProcessedResults, subEntityTypePath);
|
|
422
|
+
}
|
|
423
|
+
loads.push(...postProcessedResults);
|
|
417
424
|
}
|
|
418
425
|
else {
|
|
419
426
|
nextPageKey = null;
|
|
@@ -430,7 +437,12 @@ class DataConverter {
|
|
|
430
437
|
take,
|
|
431
438
|
skip,
|
|
432
439
|
});
|
|
433
|
-
|
|
440
|
+
let postProcessedResults = loadPage;
|
|
441
|
+
if (postProcessCriteria && Object.keys(postProcessCriteria).length !== 0) {
|
|
442
|
+
const subEntityTypePath = rootTypeCriteria.typePath || entityType;
|
|
443
|
+
postProcessedResults = this.checkKeysAndValues(postProcessCriteria, postProcessedResults, subEntityTypePath);
|
|
444
|
+
}
|
|
445
|
+
loads.push(...postProcessedResults);
|
|
434
446
|
if (loadPage.length !== take) {
|
|
435
447
|
done = true;
|
|
436
448
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrail/flexplm",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.58",
|
|
4
4
|
"description": "Library used for integration with flexplm.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"lint": "tslint -p tsconfig.json",
|
|
11
11
|
"test": "jest",
|
|
12
12
|
"test-watch": "jest --watch",
|
|
13
|
-
"test-debug": "jest --runInBand"
|
|
13
|
+
"test-debug": "jest --runInBand",
|
|
14
|
+
"test-coverage": "jest --coverage"
|
|
14
15
|
},
|
|
15
16
|
"keywords": [],
|
|
16
17
|
"author": "",
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
import { Entities } from "@contrail/sdk";
|
|
1
|
+
import { Entities, TypeClientOptions } from "@contrail/sdk";
|
|
2
2
|
import { MapFileUtil } from "@contrail/transform-data";
|
|
3
3
|
import { EntityPayloadType, FCConfig } from "../interfaces/interfaces";
|
|
4
4
|
import { DataConverter } from "../util/data-converter";
|
|
5
5
|
import { BaseEntityProcessor, IncomingEntityResponse } from "./base-entity-processor";
|
|
6
6
|
|
|
7
|
+
const mockRootType = {
|
|
8
|
+
typeProperties: [
|
|
9
|
+
{ slug: 'rootText' }
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
const mockTypeUtilGetByRootAndPath = jest.fn((options: TypeClientOptions) => {
|
|
13
|
+
return Promise.resolve(mockRootType);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
jest.mock('@contrail/sdk', () => {
|
|
17
|
+
const origModule = jest.requireActual('@contrail/sdk');
|
|
18
|
+
return {
|
|
19
|
+
__esModule: true,
|
|
20
|
+
...origModule,
|
|
21
|
+
Types: jest.fn().mockImplementation(() => {
|
|
22
|
+
return {
|
|
23
|
+
getByRootAndPath: mockTypeUtilGetByRootAndPath
|
|
24
|
+
};
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
7
29
|
class TestBaseEntityProcessor extends BaseEntityProcessor {
|
|
8
30
|
protected getIncomingEntity(event: any, inboundData: any): Promise<IncomingEntityResponse> {
|
|
9
31
|
throw new Error("Method not implemented.");
|
|
@@ -77,4 +99,91 @@ describe('BaseEntityProcessor', () =>{
|
|
|
77
99
|
expect(updates).toBeUndefined();
|
|
78
100
|
});
|
|
79
101
|
});
|
|
80
|
-
|
|
102
|
+
|
|
103
|
+
describe('queryEntityWithSubType', () =>{
|
|
104
|
+
const config = {} as FCConfig;
|
|
105
|
+
const mapFileUtil = new MapFileUtil(new Entities());
|
|
106
|
+
beforeEach(() =>{
|
|
107
|
+
// jest.clearAllMocks();
|
|
108
|
+
});
|
|
109
|
+
it('entity parameter not set', async () =>{
|
|
110
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
111
|
+
const entity = '';
|
|
112
|
+
const subType = 'test';
|
|
113
|
+
const propertyCriteria = {a:'val'};
|
|
114
|
+
expect(async () => {await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria)}).rejects.toThrow();
|
|
115
|
+
});
|
|
116
|
+
it('entityTypePath not set', async () =>{
|
|
117
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
118
|
+
const entity = 'test';
|
|
119
|
+
const subType = '';
|
|
120
|
+
const propertyCriteria = {};
|
|
121
|
+
expect(async () => {await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria)}).rejects.toThrow();
|
|
122
|
+
|
|
123
|
+
});
|
|
124
|
+
it('propertyCriteria not set', async () =>{
|
|
125
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
126
|
+
const entity = 'test';
|
|
127
|
+
const subType = 'test';
|
|
128
|
+
const propertyCriteria = {};
|
|
129
|
+
expect(async () => {await btep.queryEntityWithSubTypeCriteria(entity, subType, propertyCriteria)}).rejects.toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('basic queryEntityWithSubTypeCriteria test', async () =>{
|
|
133
|
+
const dc = new DataConverter(config, mapFileUtil);
|
|
134
|
+
const btep = new TestBaseEntityProcessor(config, dc, mapFileUtil, 'test');
|
|
135
|
+
const entityType = 'custom-entity';
|
|
136
|
+
const entityTypePath = 'custom-entity:sample';
|
|
137
|
+
const rootCriteria = {
|
|
138
|
+
rootText: 'root'
|
|
139
|
+
};
|
|
140
|
+
const subCriteria = {
|
|
141
|
+
sampleText: 'sample'
|
|
142
|
+
};
|
|
143
|
+
const propertyCriteria = Object.assign({}, rootCriteria, subCriteria);
|
|
144
|
+
const rootCriteriaResults = Object.assign({typePath: entityTypePath}, rootCriteria);
|
|
145
|
+
|
|
146
|
+
const mockGetByRootTypeProperties = jest.spyOn(btep, 'getRootTypePropertyKeys').mockReturnValue(['rootText']);
|
|
147
|
+
const mockDCgetAllObjectReferences = jest.spyOn(dc, 'getAllObjectReferences').mockReturnValue(Promise.resolve([]));
|
|
148
|
+
|
|
149
|
+
const results = await btep.queryEntityWithSubTypeCriteria(entityType, entityTypePath, propertyCriteria);
|
|
150
|
+
expect(results).toEqual([]);
|
|
151
|
+
expect(mockTypeUtilGetByRootAndPath).toBeCalledTimes(1);
|
|
152
|
+
expect(mockTypeUtilGetByRootAndPath).toBeCalledWith({ root: entityType});
|
|
153
|
+
expect(mockGetByRootTypeProperties).toBeCalledTimes(1);
|
|
154
|
+
expect(mockGetByRootTypeProperties).toBeCalledWith(mockRootType, propertyCriteria);
|
|
155
|
+
|
|
156
|
+
expect(mockDCgetAllObjectReferences).toBeCalledTimes(1);
|
|
157
|
+
expect(mockDCgetAllObjectReferences).toBeCalledWith(entityType, rootCriteriaResults, subCriteria);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('getRootTypePropertyKeys', () =>{
|
|
163
|
+
it('rootType parameter not set', async () =>{
|
|
164
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
165
|
+
const rootType = '';
|
|
166
|
+
expect(async () => {await btep.getRootTypePropertyKeys(rootType)}).rejects.toThrow();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('get keys from rootType.typeProperties', async () =>{
|
|
170
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
171
|
+
const rootType = {
|
|
172
|
+
typeProperties: ['a', 'b']
|
|
173
|
+
};
|
|
174
|
+
const keys = await btep.getRootTypePropertyKeys(rootType);
|
|
175
|
+
expect(keys).not.toEqual(['a', 'b']);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('get keys from rootType.typeProperties', async () =>{
|
|
179
|
+
const btep = new TestBaseEntityProcessor({} as FCConfig, {} as DataConverter, {} as MapFileUtil, 'test');
|
|
180
|
+
const rootType = {
|
|
181
|
+
typeProperties: [{ slug: 'a'},{slug: 'b'}]
|
|
182
|
+
};
|
|
183
|
+
const keys = await btep.getRootTypePropertyKeys(rootType);
|
|
184
|
+
expect(keys).toEqual(['a', 'b']);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
});
|
|
@@ -5,6 +5,7 @@ import { FlexPLMConnect,} from '../util/flexplm-connect';
|
|
|
5
5
|
import { MapUtil } from '../util/map-utils';
|
|
6
6
|
import { MapFileUtil } from '@contrail/transform-data';
|
|
7
7
|
import { Entities } from '@contrail/sdk';
|
|
8
|
+
import { TypeProperty } from '@contrail/types';
|
|
8
9
|
import { TypeConversionUtils } from '../util/type-conversion-utils';
|
|
9
10
|
import { EventShortMessageStatus } from '../util/event-short-message-status';
|
|
10
11
|
|
|
@@ -133,6 +134,60 @@ export abstract class BaseEntityProcessor {
|
|
|
133
134
|
+ ', orgSlug: ' + this.orgSlug;
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/**This will query for the entity, and handle post-processing
|
|
138
|
+
* of any critieria that is defined at the sub-type level.
|
|
139
|
+
* Because sub-type criteria can't be used in the search done
|
|
140
|
+
* on the server. This is expected to be called by getIncomingEntity().
|
|
141
|
+
*
|
|
142
|
+
* @param entityType: the root type of the entity
|
|
143
|
+
* @param entityTypePath: the full type path of the entity. Ex: custom-entity:sample
|
|
144
|
+
* @param propertyCriteria: all the criteria to search for the entity
|
|
145
|
+
* @returns the entities that match the criteria
|
|
146
|
+
*/
|
|
147
|
+
async queryEntityWithSubTypeCriteria(entityType: string, entityTypePath: string, propertyCriteria: any): Promise<any[]>{
|
|
148
|
+
//allCriteria; identifierKeys; entityType; entityTypePath
|
|
149
|
+
if(!entityType || !entityTypePath){
|
|
150
|
+
throw new Error('type and entityTypePath must be defined');
|
|
151
|
+
}
|
|
152
|
+
if(!propertyCriteria || Object.getOwnPropertyNames(propertyCriteria).length == 0){
|
|
153
|
+
throw new Error('propertyCriteria must be defined and have at least one property');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const rootType = await this.typeUtil.getByRootAndPath({root: entityType});
|
|
157
|
+
const rootTypePropertyKeys = await this.getRootTypePropertyKeys(rootType, propertyCriteria);
|
|
158
|
+
|
|
159
|
+
const rootTypeCriteria = {typePath:entityTypePath};
|
|
160
|
+
const subTypeCriteria = {};
|
|
161
|
+
for(const key in propertyCriteria){
|
|
162
|
+
if(rootTypePropertyKeys.includes(key)){
|
|
163
|
+
rootTypeCriteria[key] = propertyCriteria[key];
|
|
164
|
+
}else{
|
|
165
|
+
subTypeCriteria[key] = propertyCriteria[key];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const returnedEntities = await this.dc.getAllObjectReferences(entityType, rootTypeCriteria, subTypeCriteria);
|
|
170
|
+
|
|
171
|
+
return returnedEntities;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** This is to get the properties that are owned by the root type
|
|
175
|
+
* This needs to be overridded for multi-level types, such as item
|
|
176
|
+
* and project-item. And for those types, the propertyCriteria
|
|
177
|
+
* will be needed to determine the correct level.
|
|
178
|
+
*
|
|
179
|
+
* @param rootType: the full root type entity for the processed entity
|
|
180
|
+
* @param propertyCriteria: the criteria to determine the correct level (unused for single level types)
|
|
181
|
+
* @returns: string[] of the property keys
|
|
182
|
+
*/
|
|
183
|
+
getRootTypePropertyKeys(rootType, propertyCriteria = null): string[] {
|
|
184
|
+
const props: TypeProperty[] = rootType['typeProperties'];
|
|
185
|
+
const rootTypePropertyKeys = props.map(prop => prop.slug);
|
|
186
|
+
|
|
187
|
+
return rootTypePropertyKeys;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
136
191
|
async handleIncomingDelete(event) {
|
|
137
192
|
console.warn('delete is not configured', event);
|
|
138
193
|
}
|
|
@@ -29,205 +29,6 @@ import { FCConfig } from '../interfaces/interfaces';
|
|
|
29
29
|
import { TypeConversionUtils } from './type-conversion-utils';
|
|
30
30
|
import { MapUtil } from './map-utils';
|
|
31
31
|
|
|
32
|
-
// describe('transformEntity', () =>{
|
|
33
|
-
// const config: FCConfig = {
|
|
34
|
-
// apiHost: 'host',
|
|
35
|
-
// userName: () => 'user',
|
|
36
|
-
// password: () => 'pass',
|
|
37
|
-
// urlContext: 'xxx',
|
|
38
|
-
// vibeEventEndpoint: '/rfa/vibeiq/vibeEvents',
|
|
39
|
-
// itemPreDevelopmentLifecycleStages: ['concept'],
|
|
40
|
-
// propertyMapping: {
|
|
41
|
-
// LCSSeason :{
|
|
42
|
-
// flexPLMSeasonName: 'seasonName'
|
|
43
|
-
// },
|
|
44
|
-
// LCSProduct: {
|
|
45
|
-
// name: 'productName'
|
|
46
|
-
// },
|
|
47
|
-
// LCSSKU: {
|
|
48
|
-
// optionName: 'skuName'
|
|
49
|
-
// }
|
|
50
|
-
// }
|
|
51
|
-
// };
|
|
52
|
-
|
|
53
|
-
// it('LCSSeason-flexPLMSeasonName', () =>{
|
|
54
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
55
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
56
|
-
// const stringAtt = 'some value';
|
|
57
|
-
// const numberAtt = 44;
|
|
58
|
-
// const name = 'productName';
|
|
59
|
-
// const seasonEntity = {
|
|
60
|
-
// objectClass: 'LCSSeason',
|
|
61
|
-
// flexPLMSeasonName,
|
|
62
|
-
// stringAtt,
|
|
63
|
-
// numberAtt,
|
|
64
|
-
// name
|
|
65
|
-
// };
|
|
66
|
-
|
|
67
|
-
// const seasonObj = dc.transformEntity(seasonEntity);
|
|
68
|
-
|
|
69
|
-
// const objectKeys = Object.keys(seasonObj);
|
|
70
|
-
// expect(objectKeys.length).toEqual(5);
|
|
71
|
-
// expect(seasonObj['seasonName']).toEqual(flexPLMSeasonName);
|
|
72
|
-
// expect(seasonObj['numberAtt']).toEqual(numberAtt);
|
|
73
|
-
// expect(seasonObj['stringAtt']).toEqual(stringAtt);
|
|
74
|
-
// expect(seasonObj['name']).toEqual(name);
|
|
75
|
-
|
|
76
|
-
// });
|
|
77
|
-
|
|
78
|
-
// it('LCSProduct-name', () =>{
|
|
79
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
80
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
81
|
-
// const stringAtt = 'some value';
|
|
82
|
-
// const numberAtt = 44;
|
|
83
|
-
// const name = 'productName';
|
|
84
|
-
// const productEntity = {
|
|
85
|
-
// objectClass: 'LCSProduct',
|
|
86
|
-
// flexPLMSeasonName,
|
|
87
|
-
// stringAtt,
|
|
88
|
-
// numberAtt,
|
|
89
|
-
// name
|
|
90
|
-
// };
|
|
91
|
-
|
|
92
|
-
// const productObj = dc.transformEntity(productEntity);
|
|
93
|
-
|
|
94
|
-
// const objectKeys = Object.keys(productObj);
|
|
95
|
-
// expect(objectKeys.length).toEqual(5);
|
|
96
|
-
// expect(productObj['flexPLMSeasonName']).toEqual(flexPLMSeasonName);
|
|
97
|
-
// expect(productObj['numberAtt']).toEqual(numberAtt);
|
|
98
|
-
// expect(productObj['stringAtt']).toEqual(stringAtt);
|
|
99
|
-
// expect(productObj['productName']).toEqual(name);
|
|
100
|
-
|
|
101
|
-
// });
|
|
102
|
-
|
|
103
|
-
// it('LCSSKU-name', () =>{
|
|
104
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
105
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
106
|
-
// const stringAtt = 'some value';
|
|
107
|
-
// const numberAtt = 44;
|
|
108
|
-
// const name = 'productName';
|
|
109
|
-
// const optionName = 'sku name';
|
|
110
|
-
// const skuEntity = {
|
|
111
|
-
// objectClass: 'LCSSKU',
|
|
112
|
-
// flexPLMSeasonName,
|
|
113
|
-
// stringAtt,
|
|
114
|
-
// numberAtt,
|
|
115
|
-
// name,
|
|
116
|
-
// optionName
|
|
117
|
-
// };
|
|
118
|
-
|
|
119
|
-
// const skuObj = dc.transformEntity(skuEntity);
|
|
120
|
-
|
|
121
|
-
// const objectKeys = Object.keys(skuObj);
|
|
122
|
-
// expect(objectKeys.length).toEqual(6);
|
|
123
|
-
// expect(skuObj['flexPLMSeasonName']).toEqual(flexPLMSeasonName);
|
|
124
|
-
// expect(skuObj['numberAtt']).toEqual(numberAtt);
|
|
125
|
-
// expect(skuObj['stringAtt']).toEqual(stringAtt);
|
|
126
|
-
// expect(skuObj['name']).toEqual(name);
|
|
127
|
-
// expect(skuObj['skuName']).toEqual(optionName);
|
|
128
|
-
|
|
129
|
-
// });
|
|
130
|
-
// });
|
|
131
|
-
|
|
132
|
-
// describe('transformObject', () =>{
|
|
133
|
-
// const config: FCConfig = {
|
|
134
|
-
// apiHost: 'host',
|
|
135
|
-
// userName: () => 'user',
|
|
136
|
-
// password: () => 'pass',
|
|
137
|
-
// urlContext: 'xxx',
|
|
138
|
-
// vibeEventEndpoint: '/rfa/vibeiq/vibeEvents',
|
|
139
|
-
// itemPreDevelopmentLifecycleStages: ['concept'],
|
|
140
|
-
// propertyMapping: {
|
|
141
|
-
// LCSSeason :{
|
|
142
|
-
// flexPLMSeasonName: 'seasonName'
|
|
143
|
-
// },
|
|
144
|
-
// LCSProduct: {
|
|
145
|
-
// name: 'productName'
|
|
146
|
-
// },
|
|
147
|
-
// LCSSKU: {
|
|
148
|
-
// optionName: 'skuName'
|
|
149
|
-
// }
|
|
150
|
-
// }
|
|
151
|
-
// };
|
|
152
|
-
|
|
153
|
-
// it('LCSSeason-flexPLMSeasonName', () =>{
|
|
154
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
155
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
156
|
-
// const stringAtt = 'some value';
|
|
157
|
-
// const numberAtt = 44;
|
|
158
|
-
// const name = 'productName';
|
|
159
|
-
// const seasonObj = {
|
|
160
|
-
// objectClass: 'LCSSeason',
|
|
161
|
-
// seasonName: flexPLMSeasonName,
|
|
162
|
-
// stringAtt,
|
|
163
|
-
// numberAtt,
|
|
164
|
-
// name
|
|
165
|
-
// };
|
|
166
|
-
|
|
167
|
-
// const seasonEntity = dc.transformObject(seasonObj);
|
|
168
|
-
// const objectKeys = Object.keys(seasonEntity);
|
|
169
|
-
// expect(objectKeys.length).toEqual(5);
|
|
170
|
-
// expect(seasonEntity['flexPLMSeasonName']).toEqual(flexPLMSeasonName);
|
|
171
|
-
// expect(seasonEntity['numberAtt']).toEqual(numberAtt);
|
|
172
|
-
// expect(seasonEntity['stringAtt']).toEqual(stringAtt);
|
|
173
|
-
// expect(seasonEntity['name']).toEqual(name);
|
|
174
|
-
|
|
175
|
-
// });
|
|
176
|
-
|
|
177
|
-
// it('LCSProduct-name', () =>{
|
|
178
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
179
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
180
|
-
// const stringAtt = 'some value';
|
|
181
|
-
// const numberAtt = 44;
|
|
182
|
-
// const name = 'productName';
|
|
183
|
-
// const productObj = {
|
|
184
|
-
// objectClass: 'LCSProduct',
|
|
185
|
-
// flexPLMSeasonName,
|
|
186
|
-
// stringAtt,
|
|
187
|
-
// numberAtt,
|
|
188
|
-
// productName: name
|
|
189
|
-
// };
|
|
190
|
-
|
|
191
|
-
// const productEntity = dc.transformObject(productObj);
|
|
192
|
-
|
|
193
|
-
// const objectKeys = Object.keys(productEntity);
|
|
194
|
-
// expect(objectKeys.length).toEqual(5);
|
|
195
|
-
// expect(productEntity['flexPLMSeasonName']).toEqual(flexPLMSeasonName);
|
|
196
|
-
// expect(productEntity['numberAtt']).toEqual(numberAtt);
|
|
197
|
-
// expect(productEntity['stringAtt']).toEqual(stringAtt);
|
|
198
|
-
// expect(productEntity['name']).toEqual(name);
|
|
199
|
-
|
|
200
|
-
// });
|
|
201
|
-
|
|
202
|
-
// it('LCSSKU-name', () =>{
|
|
203
|
-
// const dc = new DataConverter(new Logger(config), config);
|
|
204
|
-
// const flexPLMSeasonName = 'Season Name';
|
|
205
|
-
// const stringAtt = 'some value';
|
|
206
|
-
// const numberAtt = 44;
|
|
207
|
-
// const name = 'productName';
|
|
208
|
-
// const optionName = 'sku name';
|
|
209
|
-
// const skuObj = {
|
|
210
|
-
// objectClass: 'LCSSKU',
|
|
211
|
-
// flexPLMSeasonName,
|
|
212
|
-
// stringAtt,
|
|
213
|
-
// numberAtt,
|
|
214
|
-
// name,
|
|
215
|
-
// skuName: optionName
|
|
216
|
-
// };
|
|
217
|
-
|
|
218
|
-
// const skuEntity = dc.transformObject(skuObj);
|
|
219
|
-
|
|
220
|
-
// const objectKeys = Object.keys(skuEntity);
|
|
221
|
-
// expect(objectKeys.length).toEqual(6);
|
|
222
|
-
// expect(skuEntity['flexPLMSeasonName']).toEqual(flexPLMSeasonName);
|
|
223
|
-
// expect(skuEntity['numberAtt']).toEqual(numberAtt);
|
|
224
|
-
// expect(skuEntity['stringAtt']).toEqual(stringAtt);
|
|
225
|
-
// expect(skuEntity['name']).toEqual(name);
|
|
226
|
-
// expect(skuEntity['optionName']).toEqual(optionName);
|
|
227
|
-
|
|
228
|
-
// });
|
|
229
|
-
// });
|
|
230
|
-
|
|
231
32
|
describe('getFlexPLMValue multi_select', () => {
|
|
232
33
|
const config: FCConfig = {
|
|
233
34
|
apiHost: 'host',
|
|
@@ -590,7 +391,7 @@ describe('getObjectReferenceValue cache', () =>{
|
|
|
590
391
|
}
|
|
591
392
|
});
|
|
592
393
|
});
|
|
593
|
-
|
|
394
|
+
describe('getObjectReferenceValue bad value', () =>{
|
|
594
395
|
const config: FCConfig = {
|
|
595
396
|
apiHost: 'host',
|
|
596
397
|
userName: () => 'user',
|
|
@@ -495,22 +495,29 @@ export class DataConverter {
|
|
|
495
495
|
* @param {object} rootTypeCriteria - The criteria used to filter object references.
|
|
496
496
|
* @returns {Promise<Array>} A Promise that resolves to an array containing all object references.
|
|
497
497
|
*/
|
|
498
|
-
async getAllObjectReferences(entityType, rootTypeCriteria) {
|
|
498
|
+
async getAllObjectReferences(entityType: string, rootTypeCriteria, postProcessCriteria = null) {
|
|
499
499
|
const entities = new Entities();
|
|
500
500
|
let loads = [];
|
|
501
501
|
let nextPageKey;
|
|
502
502
|
let usedV2 = false;
|
|
503
503
|
do {
|
|
504
|
+
const take = 1000;
|
|
504
505
|
const loadPage = await entities.get({
|
|
505
506
|
entityName: entityType,
|
|
506
507
|
criteria: rootTypeCriteria,
|
|
507
508
|
apiVersion: API_VERSION.V2,
|
|
509
|
+
take,
|
|
508
510
|
nextPageKey,
|
|
509
511
|
});
|
|
510
512
|
nextPageKey = loadPage?.nextPageKey;
|
|
511
513
|
if (Object.keys(loadPage).includes('results')) {
|
|
512
514
|
usedV2 = true;
|
|
513
|
-
|
|
515
|
+
let postProcessedResults = loadPage.results;
|
|
516
|
+
if(postProcessCriteria && Object.keys(postProcessCriteria).length !== 0) {
|
|
517
|
+
const subEntityTypePath = rootTypeCriteria.typePath || entityType;
|
|
518
|
+
postProcessedResults = this.checkKeysAndValues(postProcessCriteria, postProcessedResults, subEntityTypePath);
|
|
519
|
+
}
|
|
520
|
+
loads.push(...postProcessedResults);
|
|
514
521
|
} else {
|
|
515
522
|
nextPageKey = null;
|
|
516
523
|
}
|
|
@@ -527,7 +534,12 @@ export class DataConverter {
|
|
|
527
534
|
take,
|
|
528
535
|
skip,
|
|
529
536
|
});
|
|
530
|
-
|
|
537
|
+
let postProcessedResults = loadPage;
|
|
538
|
+
if(postProcessCriteria && Object.keys(postProcessCriteria).length !== 0) {
|
|
539
|
+
const subEntityTypePath = rootTypeCriteria.typePath || entityType;
|
|
540
|
+
postProcessedResults = this.checkKeysAndValues(postProcessCriteria, postProcessedResults, subEntityTypePath);
|
|
541
|
+
}
|
|
542
|
+
loads.push(...postProcessedResults);
|
|
531
543
|
|
|
532
544
|
if (loadPage.length !== take) {
|
|
533
545
|
done = true;
|