@contrail/flexplm 1.1.13 → 1.1.14
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/.github/pull_request_template.md +30 -0
- package/.github/workflows/flexplm-lib.yml +27 -0
- package/lib/flexplm-utils.spec.d.ts +1 -0
- package/lib/flexplm-utils.spec.js +26 -0
- package/lib/publish/base-process-publish-assortment.spec.d.ts +1 -0
- package/lib/publish/base-process-publish-assortment.spec.js +1053 -0
- package/lib/util/config-defaults.spec.d.ts +1 -0
- package/lib/util/config-defaults.spec.js +264 -0
- package/lib/util/data-converter.spec.d.ts +1 -0
- package/lib/util/data-converter.spec.js +591 -0
- package/lib/util/map-utils.d.ts +2 -2
- package/lib/util/map-utils.js +5 -28
- package/lib/util/map-utils.spec.d.ts +1 -0
- package/lib/util/map-utils.spec.js +89 -0
- package/lib/util/thumbnail-util.spec.d.ts +1 -0
- package/lib/util/thumbnail-util.spec.js +132 -0
- package/lib/util/type-conversion-utils-spec-mockData.js +21 -0
- package/lib/util/type-conversion-utils.d.ts +7 -0
- package/lib/util/type-conversion-utils.js +88 -4
- package/lib/util/type-conversion-utils.spec.d.ts +1 -0
- package/lib/util/type-conversion-utils.spec.js +547 -0
- package/lib/util/type-defaults.d.ts +5 -0
- package/lib/util/type-defaults.js +66 -0
- package/lib/util/type-defaults.spec.d.ts +1 -0
- package/lib/util/type-defaults.spec.js +460 -0
- package/lib/util/type-utils.spec.d.ts +1 -0
- package/lib/util/type-utils.spec.js +190 -0
- package/package.json +2 -2
- package/publish.bat +5 -0
- package/publish.sh +5 -0
- package/src/entity-processor/base-entity-processor.ts +183 -0
- package/src/flexplm-request.ts +28 -0
- package/src/flexplm-utils.spec.ts +27 -0
- package/src/flexplm-utils.ts +29 -0
- package/src/index.ts +20 -0
- package/src/interfaces/interfaces.ts +120 -0
- package/src/interfaces/item-family-changes.ts +67 -0
- package/src/interfaces/publish-change-data.ts +43 -0
- package/src/publish/base-process-publish-assortment-callback.ts +23 -0
- package/src/publish/base-process-publish-assortment.spec.ts +1239 -0
- package/src/publish/base-process-publish-assortment.ts +1024 -0
- package/src/publish/mockData.ts +4561 -0
- package/src/transform/identifier-conversion.ts +226 -0
- package/src/util/config-defaults.spec.ts +315 -0
- package/src/util/config-defaults.ts +79 -0
- package/src/util/data-converter-spec-mockData.ts +231 -0
- package/src/util/data-converter.spec.ts +872 -0
- package/src/util/data-converter.ts +389 -0
- package/src/util/federation.ts +172 -0
- package/src/util/flexplm-connect.ts +169 -0
- package/src/util/logger-config.ts +20 -0
- package/src/util/map-util-spec-mockData.ts +231 -0
- package/src/util/map-utils.spec.ts +103 -0
- package/src/util/map-utils.ts +40 -0
- package/src/util/mockData.ts +98 -0
- package/src/util/thumbnail-util.spec.ts +152 -0
- package/src/util/thumbnail-util.ts +128 -0
- package/src/util/type-conversion-utils-spec-mockData.ts +238 -0
- package/src/util/type-conversion-utils.spec.ts +601 -0
- package/src/util/type-conversion-utils.ts +345 -0
- package/src/util/type-defaults.spec.ts +592 -0
- package/src/util/type-defaults.ts +261 -0
- package/src/util/type-utils.spec.ts +227 -0
- package/src/util/type-utils.ts +124 -0
- package/tsconfig.json +27 -0
- package/tslint.json +57 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { Entities, TypeClientOptions } from '@contrail/sdk';
|
|
2
|
+
import { TypeUtils } from './type-utils';
|
|
3
|
+
import { FCConfig } from '../interfaces/interfaces';
|
|
4
|
+
import { MapFileUtil, TransformProcessor, TransformTask } from '@contrail/transform-data';
|
|
5
|
+
import { Logger } from '@contrail/app-framework';
|
|
6
|
+
import { ObjectDiff, ObjectUtil } from '@contrail/util';
|
|
7
|
+
import { TypeConversionUtils } from './type-conversion-utils';
|
|
8
|
+
import { MapUtil } from './map-utils';
|
|
9
|
+
|
|
10
|
+
export class DataConverter {
|
|
11
|
+
private typeUtils: TypeUtils;
|
|
12
|
+
private transformMapFile: string;
|
|
13
|
+
private useDisplayForEnumerationMatching = false;
|
|
14
|
+
private objRefCache = {};
|
|
15
|
+
constructor(private config: FCConfig, private mapFileUtil: MapFileUtil){
|
|
16
|
+
this.typeUtils = new TypeUtils();
|
|
17
|
+
this.transformMapFile = this.config['transformMapFile'];
|
|
18
|
+
this.useDisplayForEnumerationMatching = this.config['dataConverter']
|
|
19
|
+
&& this.config['dataConverter']['useDisplayForEnumerationMatching'];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getFlexPLMObjectDataFromEvent(event, dataToSkip: string[]) {
|
|
23
|
+
return this.getFlexPLMObjectData(event.newData, dataToSkip, true);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getFlexPLMObjectData(newData, dataToSkip: string[], inflateObjRef: boolean) {
|
|
27
|
+
if(Logger.isDebugOn()) {
|
|
28
|
+
console.debug('newData: ' + JSON.stringify(newData));
|
|
29
|
+
}
|
|
30
|
+
//Using event to get propertyDiffs to find emptied values
|
|
31
|
+
//Add standard atts to skip
|
|
32
|
+
dataToSkip = dataToSkip.concat(['updatedOn', 'updatedById', 'createdOn', 'createdById', 'modifiedAt', 'orgId', 'createdAt', 'id', 'typeId', 'workspaceId']);
|
|
33
|
+
// const oldData = event.oldData;
|
|
34
|
+
const data = {};
|
|
35
|
+
const typeId = newData?.typeId;
|
|
36
|
+
if (!typeId) {
|
|
37
|
+
return;// Don't have a type; so can't process
|
|
38
|
+
}
|
|
39
|
+
data['typePath'] = newData['typePath'];
|
|
40
|
+
const type = await this.typeUtils.getTypeById(typeId);
|
|
41
|
+
const typeProps = this.typeUtils.filterTypeProperties(type, newData);
|
|
42
|
+
for(const prop of typeProps){
|
|
43
|
+
// if(this.logger.isTraceOn()){
|
|
44
|
+
// this.logger.log('prop: ' + JSON.stringify(prop));
|
|
45
|
+
// }
|
|
46
|
+
const slug = prop['slug'];
|
|
47
|
+
if (dataToSkip.includes(slug)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
data[slug] = await this.getFlexPLMValue(prop, newData, inflateObjRef);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if(Logger.isDebugOn()) {
|
|
54
|
+
console.debug('getFlexPLMObjectData-data: ' + JSON.stringify(data));
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getFlexPLMValue(prop, newData, inflateObjRef: boolean) {
|
|
61
|
+
const propertyType = prop['propertyType'];
|
|
62
|
+
const slug = prop['slug'];
|
|
63
|
+
const nd = newData[slug];
|
|
64
|
+
// console.log('getFlexPLMValue: ' + propertyType + ', ' +slug + ', ' + nd + ', ' + od);
|
|
65
|
+
let value;
|
|
66
|
+
if(['string', 'text'].includes(propertyType)){
|
|
67
|
+
value = nd || '';
|
|
68
|
+
}else if(['number', 'currency', 'percent', 'sequence'].includes(propertyType)){
|
|
69
|
+
value = nd || 0;
|
|
70
|
+
}else if('date' === propertyType){
|
|
71
|
+
if(nd){
|
|
72
|
+
value = nd;
|
|
73
|
+
// const d = new Date(nd);
|
|
74
|
+
// console.log('Date.getTimezoneOffset(): ' + d.getTimezoneOffset());
|
|
75
|
+
// value = d.toISOString();
|
|
76
|
+
// console.log('date: ' + nd + ' -- ' + value);
|
|
77
|
+
} else {
|
|
78
|
+
value = null;
|
|
79
|
+
}
|
|
80
|
+
}else if('boolean' === propertyType){
|
|
81
|
+
value =(nd)? true: false;
|
|
82
|
+
}else if('choice' === propertyType){
|
|
83
|
+
value = this.getEnumerationValue(prop, nd);
|
|
84
|
+
}else if('multi_select' === propertyType){
|
|
85
|
+
value = this.getEnumerationValue(prop, nd);
|
|
86
|
+
}else if('object_reference' === propertyType){
|
|
87
|
+
value = await this.getObjectReferenceValue(prop, newData, inflateObjRef);
|
|
88
|
+
if(Logger.isDebugOn()){
|
|
89
|
+
console.debug('object_reference: ' + JSON.stringify(value));
|
|
90
|
+
}
|
|
91
|
+
} else if ('image' === propertyType) {
|
|
92
|
+
// console.log('image-TODO');
|
|
93
|
+
}else if('formula' === propertyType){
|
|
94
|
+
value = nd;
|
|
95
|
+
}else if('json' === propertyType){
|
|
96
|
+
// console.log('json-TODO');
|
|
97
|
+
value = nd;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return value;
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
/** Returns the display values for list properties (choice & multi_select)
|
|
104
|
+
*
|
|
105
|
+
* @param prop
|
|
106
|
+
* @param newData
|
|
107
|
+
* @returns
|
|
108
|
+
*/
|
|
109
|
+
getEnumerationValue(prop, nd){
|
|
110
|
+
const propertyType = prop['propertyType'];
|
|
111
|
+
let value;
|
|
112
|
+
if(['choice', 'multi_select'].includes(propertyType)){
|
|
113
|
+
const options: [{value:string, display:string}] = prop['options'] || [];
|
|
114
|
+
if('choice' === propertyType){
|
|
115
|
+
if(nd){
|
|
116
|
+
const option = options.find(option => option.value == nd );
|
|
117
|
+
if(option){
|
|
118
|
+
value = Object.assign({},option);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if(!value) {
|
|
122
|
+
value = {};
|
|
123
|
+
}
|
|
124
|
+
} else if ('multi_select' === propertyType) {
|
|
125
|
+
value = [];
|
|
126
|
+
if (nd instanceof Array) {
|
|
127
|
+
nd.forEach(key => {
|
|
128
|
+
const optionObject = options.find(option => option.value == key);
|
|
129
|
+
if(optionObject){
|
|
130
|
+
const option = Object.assign({},optionObject);
|
|
131
|
+
value.push(option);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
} else if( typeof nd === 'string' && '' !== nd) {
|
|
135
|
+
const optionObject = options.find(option => option.value == nd);
|
|
136
|
+
if(optionObject){
|
|
137
|
+
const option = Object.assign({},optionObject);
|
|
138
|
+
value.push(option);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public async getObjectReferenceValue(prop: any, newData: any, inflateObjRef = false) {
|
|
147
|
+
const slug = prop['slug'];
|
|
148
|
+
if(Logger.isDebugOn()) {
|
|
149
|
+
console.debug('getObjectReferenceValue-prop: ' + slug);
|
|
150
|
+
}
|
|
151
|
+
let value = newData[slug];
|
|
152
|
+
const entityType = prop['referencedTypeRootSlug'];
|
|
153
|
+
const entityId = newData[slug + 'Id'];
|
|
154
|
+
if ((!value || typeof value === 'string' )&& inflateObjRef) {
|
|
155
|
+
if (entityId) {
|
|
156
|
+
if(this.objRefCache[entityId]){
|
|
157
|
+
if(Logger.isDebugOn()) {
|
|
158
|
+
console.debug('cache hit: ' + entityId);
|
|
159
|
+
}
|
|
160
|
+
return this.objRefCache[entityId];
|
|
161
|
+
}
|
|
162
|
+
const criteria = {
|
|
163
|
+
id: entityId
|
|
164
|
+
};
|
|
165
|
+
const entities = await new Entities().get({
|
|
166
|
+
entityName: entityType,
|
|
167
|
+
criteria
|
|
168
|
+
});
|
|
169
|
+
value = (entities && entities[0]) ? entities[0] : undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if(value && !(typeof value === 'string')){
|
|
174
|
+
const unprocessedValue = value;
|
|
175
|
+
const objectClass = await TypeConversionUtils.getObjectClass(this.transformMapFile, this.mapFileUtil, unprocessedValue);
|
|
176
|
+
const flexPLMTypePath = await TypeConversionUtils.getObjectTypePath(this.transformMapFile, this.mapFileUtil, unprocessedValue);
|
|
177
|
+
value = await this.getFlexPLMObjectData(value, [], false);
|
|
178
|
+
value['entityReference'] = entityType + ':' + entityId;
|
|
179
|
+
value['objectClass'] = objectClass;
|
|
180
|
+
value['flexPLMTypePath'] = flexPLMTypePath;
|
|
181
|
+
|
|
182
|
+
if(this.transformMapFile){
|
|
183
|
+
const mapKey = await TypeConversionUtils.getMapKey(this.transformMapFile, this.mapFileUtil, unprocessedValue, TypeConversionUtils.VIBE2FLEX_DIRECTION);
|
|
184
|
+
value = await MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, value, mapKey, TypeConversionUtils.VIBE2FLEX_DIRECTION)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
} else {
|
|
188
|
+
value = {};
|
|
189
|
+
}
|
|
190
|
+
this.objRefCache[entityId] = value;
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** (Deprecated) Use TypeConversionUtils.getMapKey()
|
|
195
|
+
* Will return the class to use to get mapping.
|
|
196
|
+
* This is needed because mappings will be different for different sub types
|
|
197
|
+
* of custom-entity
|
|
198
|
+
*
|
|
199
|
+
* @param obj: Entity being checked
|
|
200
|
+
* @param mapping: The whole mapping file
|
|
201
|
+
*/
|
|
202
|
+
getMappingClass(entity: object, mapping: any): string{
|
|
203
|
+
const entityTypePath = entity['typePath'];
|
|
204
|
+
const typeMapKey = mapping['typeMapKey'];
|
|
205
|
+
let objClass = typeMapKey[entityTypePath];
|
|
206
|
+
const entityType = entity['entityType'];
|
|
207
|
+
|
|
208
|
+
if(!objClass){
|
|
209
|
+
objClass = this.typeUtils.getEventObjectClass(entityType, entity);
|
|
210
|
+
}
|
|
211
|
+
if(!objClass){
|
|
212
|
+
objClass = entityType;
|
|
213
|
+
}
|
|
214
|
+
return objClass;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Sets entity values from FlexPLM data passed in
|
|
218
|
+
* Assumes the entity has a VibeIQ typeId and 'roles' value to filter if necessary.
|
|
219
|
+
* @param entity the entity to update
|
|
220
|
+
* @param data the FlexPLM data
|
|
221
|
+
* @param keysToSkip properties to skip
|
|
222
|
+
* @returns the modified entity with VibeIQ values
|
|
223
|
+
*/
|
|
224
|
+
async setEntityValues(entity, data, keysToSkip: string[] = []){
|
|
225
|
+
// this.logger.log('setEntityValues: ' + JSON.stringify(entity));
|
|
226
|
+
// this.logger.log('data: ' + JSON.stringify(data));
|
|
227
|
+
const type = await this.typeUtils.getTypeById(entity.typeId);
|
|
228
|
+
keysToSkip = keysToSkip.concat(['updatedOn', 'updatedById', 'createdOn', 'createdById', 'modifiedAt', 'orgId', 'createdAt', 'id', 'typeId', 'workspaceId']);
|
|
229
|
+
let typeProps = this.typeUtils.filterTypeProperties(type, entity);
|
|
230
|
+
typeProps = typeProps.filter( prop => !keysToSkip.includes(prop['slug']));
|
|
231
|
+
//Only process properties that had a value sent; to not accidentally clear out values
|
|
232
|
+
//for properties that weren't sent.
|
|
233
|
+
const dataKeys = Object.getOwnPropertyNames(data);
|
|
234
|
+
typeProps = typeProps.filter( prop => dataKeys.includes(prop['slug']));
|
|
235
|
+
|
|
236
|
+
for(const prop of typeProps){
|
|
237
|
+
// const propertyType = prop['propertyType'];
|
|
238
|
+
const slug = prop['slug'];
|
|
239
|
+
entity[slug] = await this.getEntityValue(prop, data);
|
|
240
|
+
// console.log('entity[slug]: ' + entity[slug]);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return entity;
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Gets the entity values from FlexPLM data
|
|
248
|
+
* Assumes there isn't a VibeIQ typeId, so uses FlexPLM data to determine type
|
|
249
|
+
* @param objectClass FlexPLM object class
|
|
250
|
+
* @param data object data
|
|
251
|
+
* @param keysToSkip type properties to not process
|
|
252
|
+
* @returns object with VibeIQ values
|
|
253
|
+
*/
|
|
254
|
+
async getEntityValues(objectClass: string, data: any, keysToSkip: string[] = []){
|
|
255
|
+
const entityValues = {};
|
|
256
|
+
const tco: TypeClientOptions = this.typeUtils.getEntityTypeClientOptions(objectClass,data);
|
|
257
|
+
const type = await this.typeUtils.getByRootAndPath(tco);
|
|
258
|
+
const typePath = type['typePath'];
|
|
259
|
+
if(typePath && (typePath.startsWith('item') || typePath.startsWith('project-item'))){
|
|
260
|
+
if(['LCSProduct', 'LCSProductSeasonLink'].includes(objectClass)){
|
|
261
|
+
entityValues['roles'] = ['family'];
|
|
262
|
+
}else{
|
|
263
|
+
entityValues['roles'] = ['color', 'option'];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let typeProps = this.typeUtils.filterTypeProperties(type, entityValues);
|
|
268
|
+
typeProps = typeProps.filter( prop => !keysToSkip.includes(prop['slug']));
|
|
269
|
+
|
|
270
|
+
for(const prop of typeProps){
|
|
271
|
+
const slug = prop['slug'];
|
|
272
|
+
const value = await this.getEntityValue(prop, data);
|
|
273
|
+
if(value){
|
|
274
|
+
entityValues[slug] = value;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return entityValues;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Gets the VibeIQ value for the property from data
|
|
282
|
+
*
|
|
283
|
+
* @param prop the VibeIQ property
|
|
284
|
+
* @param data the FlexPLM data
|
|
285
|
+
* @returns value to be set in VibeIQ
|
|
286
|
+
*/
|
|
287
|
+
async getEntityValue(prop, data) {
|
|
288
|
+
const propertyType = prop['propertyType'];
|
|
289
|
+
const slug = prop['slug'];
|
|
290
|
+
const nd = data[slug];
|
|
291
|
+
// this.logger.log('getValue: ' + propertyType + ', ' +slug + ', ' + nd);
|
|
292
|
+
|
|
293
|
+
let value;
|
|
294
|
+
if (['string', 'text'].includes(propertyType)) {
|
|
295
|
+
value = nd;
|
|
296
|
+
}else if(['number', 'currency', 'percent', 'sequence'].includes(propertyType)){
|
|
297
|
+
value = (null === nd)? 0 : nd;
|
|
298
|
+
}else if('date' === propertyType){
|
|
299
|
+
if(nd){
|
|
300
|
+
// const offset = new Date(nd).getTimezoneOffset() * 60 * 1_000;
|
|
301
|
+
// this.logger.log('nd: ' + nd);
|
|
302
|
+
// this.logger.log(offset);
|
|
303
|
+
// this.logger.log('No Offset: ' + new Date(nd).toISOString());
|
|
304
|
+
// const d = new Date(nd + offset);// Take system Timezone into account.
|
|
305
|
+
const d = new Date(nd);
|
|
306
|
+
value = d.toISOString();
|
|
307
|
+
|
|
308
|
+
} else {
|
|
309
|
+
value = null;
|
|
310
|
+
}
|
|
311
|
+
}else if('boolean' === propertyType){
|
|
312
|
+
value =(nd)? true: false;
|
|
313
|
+
}else if('choice' === propertyType){
|
|
314
|
+
value = this.setEnumerationKeys(prop, nd, this.useDisplayForEnumerationMatching);
|
|
315
|
+
}else if('multi_select' === propertyType){
|
|
316
|
+
value = this.setEnumerationKeys(prop, nd, this.useDisplayForEnumerationMatching);
|
|
317
|
+
|
|
318
|
+
} else if ('object_reference' === propertyType) {
|
|
319
|
+
//TODO write code
|
|
320
|
+
//use referencedTypeRootSlug to get the entity type
|
|
321
|
+
} else if ('image' === propertyType) {
|
|
322
|
+
// console.log('TODO-image');
|
|
323
|
+
} else if ('formula' === propertyType) {
|
|
324
|
+
// console.log('TODO-formula');
|
|
325
|
+
} else if ('json' === propertyType) {
|
|
326
|
+
// console.log('TODO-json');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// console.log(value);
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
setEnumerationKeys(prop, nd, matchByDisplay) {
|
|
334
|
+
const propertyType = prop['propertyType'];
|
|
335
|
+
let value;
|
|
336
|
+
if(['choice', 'multi_select'].includes(propertyType)){
|
|
337
|
+
//If there are no options, use empty array to not error out and don't set anything
|
|
338
|
+
const options: [{value:string, display:string}] = prop['options'] || [];
|
|
339
|
+
if('choice' === propertyType){
|
|
340
|
+
if(nd){
|
|
341
|
+
const matchKey = (matchByDisplay)? 'display' : 'value';
|
|
342
|
+
const option = options.find(option => option[matchKey] == nd[matchKey] );
|
|
343
|
+
if(option){
|
|
344
|
+
value = option.value;
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
value = undefined;
|
|
348
|
+
}
|
|
349
|
+
}else if('multi_select' === propertyType){
|
|
350
|
+
value = [];
|
|
351
|
+
const matchKey = (matchByDisplay)? 'display' : 'value';
|
|
352
|
+
if(nd instanceof Array){
|
|
353
|
+
nd.forEach(selectedOpt =>{
|
|
354
|
+
const optionObject = options.find(option => option[matchKey] == selectedOpt[matchKey]);
|
|
355
|
+
if(optionObject){
|
|
356
|
+
const option = optionObject.value;
|
|
357
|
+
value.push(option);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return value;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Compares the potential changes to the entity and returns only the actual differences.
|
|
368
|
+
*
|
|
369
|
+
* @param entity the full entity
|
|
370
|
+
* @param changes the potential changes
|
|
371
|
+
* @returns only the change values that are different from the entity's value
|
|
372
|
+
*/
|
|
373
|
+
getPersistableChanges(entity: object, changes: object): object {
|
|
374
|
+
const entityCompareValues = {};
|
|
375
|
+
for(const key of (Object.getOwnPropertyNames(changes))){
|
|
376
|
+
entityCompareValues[key] = entity[key];
|
|
377
|
+
}
|
|
378
|
+
const diffs: ObjectDiff[] = ObjectUtil.compareDeep(entityCompareValues, changes, '');
|
|
379
|
+
const diffValues = {};
|
|
380
|
+
if(diffs && diffs.length > 0){
|
|
381
|
+
for(const diff of diffs){
|
|
382
|
+
diffValues[diff.propertyName] = diff.newValue;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return diffValues;
|
|
386
|
+
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Entities } from '@contrail/sdk';
|
|
2
|
+
|
|
3
|
+
import { FederationRecord } from '../interfaces/interfaces';
|
|
4
|
+
import pLimit from 'p-limit';
|
|
5
|
+
const limit = pLimit(30);
|
|
6
|
+
|
|
7
|
+
interface fedConfigDef {
|
|
8
|
+
appIdentifier: string,
|
|
9
|
+
federationSchema: string
|
|
10
|
+
}
|
|
11
|
+
const FED_CONFIG: fedConfigDef = {
|
|
12
|
+
appIdentifier: '@vibeiq/flexplm-connector',
|
|
13
|
+
federationSchema: 'DEFAULT'
|
|
14
|
+
};
|
|
15
|
+
export class Federation {
|
|
16
|
+
|
|
17
|
+
private CHUNK_SIZE = 50;
|
|
18
|
+
|
|
19
|
+
async getFederatedMappedRefId(entityType: string, entityId: string) {
|
|
20
|
+
const criteria = {
|
|
21
|
+
reference: entityType + ':' + entityId,
|
|
22
|
+
appIdentifier: FED_CONFIG.appIdentifier,
|
|
23
|
+
federationSchema: FED_CONFIG.federationSchema
|
|
24
|
+
};
|
|
25
|
+
console.log('getFederatedMappedRefId: ' + JSON.stringify(criteria));
|
|
26
|
+
const fedRecords: FederationRecord[] = await new Entities().get({
|
|
27
|
+
entityName: 'federation',
|
|
28
|
+
criteria
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const federatedId = (fedRecords[0])
|
|
32
|
+
? fedRecords[0]['mappedReference']
|
|
33
|
+
: '';
|
|
34
|
+
|
|
35
|
+
return federatedId;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async createFederatedRecord(eventBody) {
|
|
39
|
+
const itemResults = eventBody.payload;
|
|
40
|
+
for (let i = 0; i < itemResults.length; i++) {
|
|
41
|
+
if (!itemResults[i].federatedId) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const payload: FederationRecord = {
|
|
45
|
+
appIdentifier: FED_CONFIG.appIdentifier,
|
|
46
|
+
reference: itemResults[i].entityReference,
|
|
47
|
+
mappedReference: itemResults[i].federatedId,
|
|
48
|
+
federationSchema: FED_CONFIG.federationSchema
|
|
49
|
+
};
|
|
50
|
+
// console.log('createFederatedRecord: ' + JSON.stringify(payload));
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const results = await new Entities().create({ entityName: 'federation', object: payload });
|
|
54
|
+
return results;
|
|
55
|
+
// console.log(JSON.stringify(results));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.log('createFederatedRecord-error: ', error);
|
|
58
|
+
throw new Error('Error creating federation record: ' + error.message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async getFederationRecordFromMappedRefId(mappedRefId: string) {
|
|
64
|
+
const criteria = {
|
|
65
|
+
appIdentifier: FED_CONFIG.appIdentifier,
|
|
66
|
+
mappedReference: mappedRefId,
|
|
67
|
+
federationSchema: FED_CONFIG.federationSchema
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
const fedRecords: FederationRecord[] = await new Entities().get({
|
|
71
|
+
entityName: 'federation',
|
|
72
|
+
criteria
|
|
73
|
+
});
|
|
74
|
+
return (fedRecords && fedRecords[0]) ? fedRecords[0] : undefined;
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.log('getFederationData-error: ' + e.message);
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static getEntityId(fedRecord: FederationRecord) {
|
|
82
|
+
const entityString = fedRecord['reference'];
|
|
83
|
+
const entitySplit = entityString.split(':');
|
|
84
|
+
const entityType = entitySplit[0];
|
|
85
|
+
const entityId = entitySplit[1];
|
|
86
|
+
|
|
87
|
+
return { entityType, entityId };
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getEntityFromMappedRefId(mappedRefId: string) {
|
|
92
|
+
// console.log('!---getEntityFromMappedRefId: ' + mappedRefId);
|
|
93
|
+
const fedRecord = await this.getFederationRecordFromMappedRefId(mappedRefId);
|
|
94
|
+
console.log('fedRecord: ' + JSON.stringify(fedRecord));
|
|
95
|
+
if (!fedRecord) {
|
|
96
|
+
//Not creating from FlexPLM at this time.
|
|
97
|
+
console.log('Federation Record doesnt exist. Cant get entity!');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { entityType, entityId } = Federation.getEntityId(fedRecord);
|
|
102
|
+
// console.log(entityType + ':' + entityId);
|
|
103
|
+
const criteria = {
|
|
104
|
+
id: entityId
|
|
105
|
+
};
|
|
106
|
+
const entities = await new Entities().get({
|
|
107
|
+
entityName: entityType,
|
|
108
|
+
criteria
|
|
109
|
+
});
|
|
110
|
+
const entity = (entities && entities[0]) ? entities[0] : undefined;
|
|
111
|
+
// console.log(' entities: ' +JSON.stringify(entities));
|
|
112
|
+
return entity;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getFederationRecordsFromIds(ids: string[]): Promise<FederationRecord[]> {
|
|
116
|
+
const fedRecords = [];
|
|
117
|
+
for (let i = 0; i < ids.length; i++) {
|
|
118
|
+
const criteria = {
|
|
119
|
+
reference: ids[i],
|
|
120
|
+
appIdentifier: FED_CONFIG.appIdentifier,
|
|
121
|
+
federationSchema: FED_CONFIG.federationSchema
|
|
122
|
+
};
|
|
123
|
+
// this.logger.log('getFederatedMappedRefId: ' + JSON.stringify(criteria));
|
|
124
|
+
const recs: FederationRecord[] = await new Entities().get({
|
|
125
|
+
entityName: 'federation',
|
|
126
|
+
criteria
|
|
127
|
+
});
|
|
128
|
+
if (recs[0]) {
|
|
129
|
+
fedRecords.push(recs[0]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return fedRecords;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getFederationRecordsFromIdsBulk(ids: string[]): Promise<FederationRecord[]> {
|
|
137
|
+
|
|
138
|
+
const chunks: string[][] = this.splitIntoChunksByLen(ids, this.CHUNK_SIZE);
|
|
139
|
+
const fedRecords = [];
|
|
140
|
+
const federatedPromises = [];
|
|
141
|
+
const entities: Entities = new Entities();
|
|
142
|
+
for (const chunk of chunks) {
|
|
143
|
+
const fedPromise = limit(async () => {
|
|
144
|
+
const criteria = {
|
|
145
|
+
references: chunk,
|
|
146
|
+
appIdentifier: FED_CONFIG.appIdentifier,
|
|
147
|
+
federationSchema: FED_CONFIG.federationSchema
|
|
148
|
+
};
|
|
149
|
+
// this.logger.log('getFederatedMappedRefId: ' + JSON.stringify(criteria));
|
|
150
|
+
const records: FederationRecord[] = await entities.get({
|
|
151
|
+
entityName: 'federation',
|
|
152
|
+
criteria
|
|
153
|
+
});
|
|
154
|
+
fedRecords.push(...records);
|
|
155
|
+
|
|
156
|
+
});
|
|
157
|
+
federatedPromises.push(fedPromise);
|
|
158
|
+
}
|
|
159
|
+
await Promise.all(federatedPromises);
|
|
160
|
+
|
|
161
|
+
return fedRecords;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
splitIntoChunksByLen(arr, len) {
|
|
165
|
+
const chunks = [], n = arr?.length;
|
|
166
|
+
let i = 0;
|
|
167
|
+
while (i < n) {
|
|
168
|
+
chunks.push(arr.slice(i, i += len));
|
|
169
|
+
}
|
|
170
|
+
return chunks;
|
|
171
|
+
}
|
|
172
|
+
}
|