@contrail/flexplm 1.3.0-alpha.4 → 1.3.0-alpha.5
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/CHANGELOG.md +10 -0
- package/lib/entity-processor/base-entity-processor.js +18 -10
- package/lib/entity-processor/base-entity-processor.spec.js +3 -32
- package/lib/util/event-short-message-status.d.ts +1 -0
- package/lib/util/event-short-message-status.js +1 -0
- package/lib/util/thumbnail-util.js +7 -4
- package/lib/util/thumbnail-util.spec.js +20 -24
- package/lib/util/type-conversion-utils.d.ts +1 -1
- package/lib/util/type-conversion-utils.js +0 -1
- package/package.json +1 -1
- package/src/entity-processor/base-entity-processor.spec.ts +3 -38
- package/src/entity-processor/base-entity-processor.ts +20 -10
- package/src/util/event-short-message-status.ts +1 -0
- package/src/util/thumbnail-util.spec.ts +27 -28
- package/src/util/thumbnail-util.ts +17 -4
- package/src/util/type-conversion-utils.ts +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-04-12
|
|
11
|
+
### Added
|
|
12
|
+
- Added inbound thumbnail/primary content syncing from FlexPLM to VibeIQ via `ThumbnailUtil.syncThumbnailToVibeIQ`.
|
|
13
|
+
- Added `syncInboundImages` and `syncOutboundImages` methods to `TypeConversionUtils` for controlling image sync per map file configuration.
|
|
14
|
+
- Added `PRIMARY_CONTENT_UPDATED` status to `EventShortMessageStatus` for when only primary content changes are detected.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- `BaseEntityProcessor` update flow now distinguishes between primary-content-only changes and no changes, returning the updated entity when only the thumbnail was synced.
|
|
18
|
+
- Improved `FlexPLMConnect.getRequest` function and added unit tests.
|
|
19
|
+
|
|
10
20
|
## [1.2.1] - 2026-04-08
|
|
11
21
|
|
|
12
22
|
### Added
|
|
@@ -68,13 +68,10 @@ class BaseEntityProcessor {
|
|
|
68
68
|
console.log(statusMsg);
|
|
69
69
|
return createEntityResponse.earlyReturn;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
let createdEntity = await this.createEntity(this.baseType, createEntityResponse.entity);
|
|
72
72
|
const shouldSyncThumbnail = await type_conversion_utils_1.TypeConversionUtils.syncInboundImages(this.transformMapFile, this.mapFileUtil, event.data);
|
|
73
73
|
if (shouldSyncThumbnail) {
|
|
74
|
-
|
|
75
|
-
if (thumbnailUpdates) {
|
|
76
|
-
await this.updateEntity(this.baseType, createdEntity, thumbnailUpdates);
|
|
77
|
-
}
|
|
74
|
+
createdEntity = await new thumbnail_util_1.ThumbnailUtil(this.config).syncThumbnailToVibeIQ({ entityId: createdEntity.id, primaryViewableId: createdEntity.primaryViewableId, event, entityName: this.baseType }) || createdEntity;
|
|
78
75
|
}
|
|
79
76
|
const statusMsg = this.getInboundStatusMessage({
|
|
80
77
|
status: event_short_message_status_1.EventShortMessageStatus.SUCCESS,
|
|
@@ -88,12 +85,23 @@ class BaseEntityProcessor {
|
|
|
88
85
|
}
|
|
89
86
|
const diffs = await this.getUpdatesForEntity(entity, inboundData);
|
|
90
87
|
const shouldSyncThumbnail = await type_conversion_utils_1.TypeConversionUtils.syncInboundImages(this.transformMapFile, this.mapFileUtil, event.data);
|
|
91
|
-
let
|
|
88
|
+
let thumbnailEntity;
|
|
92
89
|
if (shouldSyncThumbnail) {
|
|
93
|
-
|
|
90
|
+
thumbnailEntity = await new thumbnail_util_1.ThumbnailUtil(this.config).syncThumbnailToVibeIQ({ entityId: entity.id, primaryViewableId: entity.primaryViewableId, event, entityName: this.baseType });
|
|
94
91
|
}
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
92
|
+
const hasPropertyChanges = Object.getOwnPropertyNames(diffs).length > 0;
|
|
93
|
+
if (!hasPropertyChanges && thumbnailEntity) {
|
|
94
|
+
const statusMsg = this.getInboundStatusMessage({
|
|
95
|
+
status: event_short_message_status_1.EventShortMessageStatus.SUCCESS,
|
|
96
|
+
statusMessage: event_short_message_status_1.EventShortMessageStatus.PRIMARY_CONTENT_UPDATED,
|
|
97
|
+
objectClass: event.objectClass,
|
|
98
|
+
entityId: entity.id,
|
|
99
|
+
federatedId: event.federatedId
|
|
100
|
+
});
|
|
101
|
+
console.log(statusMsg);
|
|
102
|
+
return thumbnailEntity;
|
|
103
|
+
}
|
|
104
|
+
if (!hasPropertyChanges) {
|
|
97
105
|
const statusMsg = this.getInboundStatusMessage({
|
|
98
106
|
status: event_short_message_status_1.EventShortMessageStatus.SUCCESS,
|
|
99
107
|
statusMessage: event_short_message_status_1.EventShortMessageStatus.NO_CHANGES,
|
|
@@ -108,7 +116,7 @@ class BaseEntityProcessor {
|
|
|
108
116
|
data: { message }
|
|
109
117
|
};
|
|
110
118
|
}
|
|
111
|
-
const updatedEntity = await this.updateEntity(this.baseType, entity,
|
|
119
|
+
const updatedEntity = await this.updateEntity(this.baseType, entity, diffs);
|
|
112
120
|
const statusMsg = this.getInboundStatusMessage({
|
|
113
121
|
status: event_short_message_status_1.EventShortMessageStatus.SUCCESS,
|
|
114
122
|
statusMessage: event_short_message_status_1.EventShortMessageStatus.UPDATED,
|
|
@@ -320,30 +320,14 @@ describe('BaseEntityProcessor', () => {
|
|
|
320
320
|
syncInboundImagesSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'syncInboundImages');
|
|
321
321
|
syncThumbnailSpy = jest.spyOn(thumbnail_util_1.ThumbnailUtil.prototype, 'syncThumbnailToVibeIQ').mockResolvedValue(undefined);
|
|
322
322
|
});
|
|
323
|
-
it('should call
|
|
323
|
+
it('should call syncThumbnailToVibeIQ with entity after create when syncInboundImages returns true', async () => {
|
|
324
324
|
jest.spyOn(btep, 'getIncomingEntity').mockResolvedValue({ entity: null });
|
|
325
325
|
jest.spyOn(btep, 'getCreateEntity').mockResolvedValue({ entity: { name: 'new' } });
|
|
326
326
|
jest.spyOn(btep, 'createEntity').mockResolvedValue(mockCreatedEntity);
|
|
327
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockCreatedEntity);
|
|
328
327
|
syncInboundImagesSpy.mockResolvedValue(true);
|
|
329
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new', primaryFileUrl: 'https://files/new.png' };
|
|
330
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
331
328
|
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
332
329
|
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
333
330
|
expect(syncThumbnailSpy).toBeCalledWith({ entityId: 'created-1', primaryViewableId: 'pv-created', event: mockEvent, entityName: 'test' });
|
|
334
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockCreatedEntity, thumbnailUpdates);
|
|
335
|
-
expect(result).toBe(mockCreatedEntity);
|
|
336
|
-
});
|
|
337
|
-
it('should not call updateEntity after create when syncThumbnailToVibeIQ returns undefined', async () => {
|
|
338
|
-
jest.spyOn(btep, 'getIncomingEntity').mockResolvedValue({ entity: null });
|
|
339
|
-
jest.spyOn(btep, 'getCreateEntity').mockResolvedValue({ entity: { name: 'new' } });
|
|
340
|
-
jest.spyOn(btep, 'createEntity').mockResolvedValue(mockCreatedEntity);
|
|
341
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity');
|
|
342
|
-
syncInboundImagesSpy.mockResolvedValue(true);
|
|
343
|
-
syncThumbnailSpy.mockResolvedValue(undefined);
|
|
344
|
-
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
345
|
-
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
346
|
-
expect(updateEntitySpy).not.toHaveBeenCalled();
|
|
347
331
|
expect(result).toBe(mockCreatedEntity);
|
|
348
332
|
});
|
|
349
333
|
it('should not sync images after create when syncInboundImages returns false', async () => {
|
|
@@ -356,28 +340,15 @@ describe('BaseEntityProcessor', () => {
|
|
|
356
340
|
expect(syncThumbnailSpy).toBeCalledTimes(0);
|
|
357
341
|
expect(result).toBe(mockCreatedEntity);
|
|
358
342
|
});
|
|
359
|
-
it('should
|
|
343
|
+
it('should call syncThumbnailToVibeIQ with entity and update with property diffs on update', async () => {
|
|
360
344
|
jest.spyOn(btep, 'getIncomingEntity').mockResolvedValue({ entity: mockExistingEntity });
|
|
361
345
|
jest.spyOn(btep, 'getUpdatesForEntity').mockResolvedValue({ name: 'changed' });
|
|
362
346
|
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockUpdatedEntity);
|
|
363
347
|
syncInboundImagesSpy.mockResolvedValue(true);
|
|
364
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new' };
|
|
365
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
366
348
|
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
367
349
|
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
368
350
|
expect(syncThumbnailSpy).toBeCalledWith({ entityId: 'existing-1', primaryViewableId: undefined, event: mockEvent, entityName: 'test' });
|
|
369
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { name: 'changed'
|
|
370
|
-
expect(result).toBe(mockUpdatedEntity);
|
|
371
|
-
});
|
|
372
|
-
it('should update entity with only thumbnail changes when no property diffs', async () => {
|
|
373
|
-
jest.spyOn(btep, 'getIncomingEntity').mockResolvedValue({ entity: mockExistingEntity });
|
|
374
|
-
jest.spyOn(btep, 'getUpdatesForEntity').mockResolvedValue({});
|
|
375
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockUpdatedEntity);
|
|
376
|
-
syncInboundImagesSpy.mockResolvedValue(true);
|
|
377
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new' };
|
|
378
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
379
|
-
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
380
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { primaryViewableId: 'pv-new' });
|
|
351
|
+
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { name: 'changed' });
|
|
381
352
|
expect(result).toBe(mockUpdatedEntity);
|
|
382
353
|
});
|
|
383
354
|
it('should not sync images on update when syncInboundImages returns false', async () => {
|
|
@@ -9,6 +9,7 @@ export declare enum EventShortMessageStatus {
|
|
|
9
9
|
MISSING_INPUT = "Missing_input",
|
|
10
10
|
NOT_CREATABLE = "Not_creatable",
|
|
11
11
|
NO_CHANGES = "No_Changes",
|
|
12
|
+
PRIMARY_CONTENT_UPDATED = "Primary_Content_Updated",
|
|
12
13
|
TOO_MANY_ENTITIES_FOUND = "Too_Many_Entities_Found",
|
|
13
14
|
UPDATED = "Updated",
|
|
14
15
|
NOT_PUBLISHABLE = "Not_Publishable",
|
|
@@ -13,6 +13,7 @@ var EventShortMessageStatus;
|
|
|
13
13
|
EventShortMessageStatus["MISSING_INPUT"] = "Missing_input";
|
|
14
14
|
EventShortMessageStatus["NOT_CREATABLE"] = "Not_creatable";
|
|
15
15
|
EventShortMessageStatus["NO_CHANGES"] = "No_Changes";
|
|
16
|
+
EventShortMessageStatus["PRIMARY_CONTENT_UPDATED"] = "Primary_Content_Updated";
|
|
16
17
|
EventShortMessageStatus["TOO_MANY_ENTITIES_FOUND"] = "Too_Many_Entities_Found";
|
|
17
18
|
EventShortMessageStatus["UPDATED"] = "Updated";
|
|
18
19
|
EventShortMessageStatus["NOT_PUBLISHABLE"] = "Not_Publishable";
|
|
@@ -119,8 +119,9 @@ class ThumbnailUtil {
|
|
|
119
119
|
await this.entities.delete({ entityName: 'content', id: primaryViewableId });
|
|
120
120
|
}
|
|
121
121
|
const clearUpdates = await this.getClearPrimaryViewableUpdates();
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: clearUpdates });
|
|
123
|
+
console.debug(`syncThumbnailToVibeIQ: applied clear updates for entityId=${entityId}`);
|
|
124
|
+
return updatedEntity;
|
|
124
125
|
}
|
|
125
126
|
if (!thumbnailUrl) {
|
|
126
127
|
console.debug(`syncThumbnailToVibeIQ: no thumbnail URL for entityId=${entityId}`);
|
|
@@ -130,8 +131,9 @@ class ThumbnailUtil {
|
|
|
130
131
|
const content = await this.createContentFromFlexPLM(thumbnailUrl, entityId, entityName);
|
|
131
132
|
await this.entities.update({ entityName: 'content', id: content.id, object: { flexplmThumbnailUrl: thumbnailUrl } });
|
|
132
133
|
const primaryUpdates = await this.getPrimaryViewableUpdates(content);
|
|
134
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: primaryUpdates });
|
|
133
135
|
console.debug(`syncThumbnailToVibeIQ: created new content ${content.id} for entityId=${entityId}`);
|
|
134
|
-
return
|
|
136
|
+
return updatedEntity;
|
|
135
137
|
}
|
|
136
138
|
const primaryViewable = await this.entities.get({ entityName: 'content', id: primaryViewableId });
|
|
137
139
|
if (primaryViewable?.flexplmThumbnailUrl === thumbnailUrl) {
|
|
@@ -141,9 +143,10 @@ class ThumbnailUtil {
|
|
|
141
143
|
const content = await this.createContentFromFlexPLM(thumbnailUrl, entityId, entityName);
|
|
142
144
|
await this.entities.update({ entityName: 'content', id: content.id, object: { flexplmThumbnailUrl: thumbnailUrl } });
|
|
143
145
|
const primaryUpdates = await this.getPrimaryViewableUpdates(content);
|
|
146
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: primaryUpdates });
|
|
144
147
|
await this.entities.delete({ entityName: 'content', id: primaryViewableId });
|
|
145
148
|
console.debug(`syncThumbnailToVibeIQ: replaced content ${primaryViewableId} with ${content.id} for entityId=${entityId}`);
|
|
146
|
-
return
|
|
149
|
+
return updatedEntity;
|
|
147
150
|
}
|
|
148
151
|
async createContentFromFlexPLM(thumbnailUrl, entityId, entityName) {
|
|
149
152
|
const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
|
|
@@ -276,34 +276,32 @@ describe('ThumbnailUtil Tests', () => {
|
|
|
276
276
|
mockEntitiesUpdate.mockImplementation((opts) => Promise.resolve({ id: opts.id }));
|
|
277
277
|
mockEntitiesDelete.mockImplementation((opts) => Promise.resolve({ id: opts.id }));
|
|
278
278
|
});
|
|
279
|
-
it('
|
|
279
|
+
it('does not update when no thumbnail IDs in event data', async () => {
|
|
280
280
|
const event = { data: {} };
|
|
281
|
-
|
|
282
|
-
expect(result).toBeUndefined();
|
|
281
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
283
282
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
284
283
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
285
284
|
expect(mockContentCreate).not.toHaveBeenCalled();
|
|
286
285
|
});
|
|
287
|
-
it('
|
|
286
|
+
it('does not update when event.data is undefined', async () => {
|
|
288
287
|
const event = {};
|
|
289
|
-
|
|
290
|
-
expect(result).toBeUndefined();
|
|
288
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
291
289
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
292
290
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
293
291
|
});
|
|
294
|
-
it('REMOVE_THUMBNAIL with existing primaryViewableId deletes content and
|
|
292
|
+
it('REMOVE_THUMBNAIL with existing primaryViewableId deletes content and updates entity', async () => {
|
|
295
293
|
const event = { data: { [thumbnail_util_1.ThumbnailUtil.NEW_THUMBNAIL_ID]: thumbnail_util_1.ThumbnailUtil.REMOVE_THUMBNAIL } };
|
|
296
|
-
|
|
294
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'pv1', event, entityName: 'color' });
|
|
297
295
|
expect(mockEntitiesDelete).toHaveBeenCalledWith({ entityName: 'content', id: 'pv1' });
|
|
298
|
-
expect(
|
|
296
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({ entityName: 'color', id: 'entity1' }));
|
|
299
297
|
});
|
|
300
|
-
it('REMOVE_THUMBNAIL with no primaryViewableId
|
|
298
|
+
it('REMOVE_THUMBNAIL with no primaryViewableId updates entity without deleting', async () => {
|
|
301
299
|
const event = { data: { [thumbnail_util_1.ThumbnailUtil.NEW_THUMBNAIL_ID]: thumbnail_util_1.ThumbnailUtil.REMOVE_THUMBNAIL } };
|
|
302
|
-
|
|
300
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'item' });
|
|
303
301
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
304
|
-
expect(
|
|
302
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({ entityName: 'item', id: 'entity1' }));
|
|
305
303
|
});
|
|
306
|
-
it('creates new content when no primaryViewableId exists and
|
|
304
|
+
it('creates new content when no primaryViewableId exists and updates entity', async () => {
|
|
307
305
|
const mockResponse = {
|
|
308
306
|
arrayBuffer: jest.fn().mockResolvedValue(new ArrayBuffer(8)),
|
|
309
307
|
headers: { get: jest.fn().mockReturnValue('image/png') },
|
|
@@ -322,7 +320,7 @@ describe('ThumbnailUtil Tests', () => {
|
|
|
322
320
|
};
|
|
323
321
|
mockContentCreate.mockResolvedValue(createdContent);
|
|
324
322
|
const event = { data: { [thumbnail_util_1.ThumbnailUtil.NEW_THUMBNAIL_ID]: '/rest/thumbnail/thumb.png' } };
|
|
325
|
-
|
|
323
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
326
324
|
expect(mockGetRequest).toHaveBeenCalledWith({ urlPath: '/rest/thumbnail/thumb.png', includeUrlContext: false, returnFullResponse: true });
|
|
327
325
|
expect(mockContentCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
328
326
|
fileName: 'thumb.png',
|
|
@@ -334,13 +332,12 @@ describe('ThumbnailUtil Tests', () => {
|
|
|
334
332
|
id: 'newContent1',
|
|
335
333
|
object: { flexplmThumbnailUrl: '/rest/thumbnail/thumb.png' },
|
|
336
334
|
}));
|
|
337
|
-
expect(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
largeViewableDownloadUrl: 'https://files/large.png',
|
|
335
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
336
|
+
entityName: 'color',
|
|
337
|
+
id: 'entity1',
|
|
341
338
|
}));
|
|
342
339
|
});
|
|
343
|
-
it('replaces content when primaryViewable.flexplmThumbnailUrl differs and
|
|
340
|
+
it('replaces content when primaryViewable.flexplmThumbnailUrl differs and updates entity', async () => {
|
|
344
341
|
mockEntitiesGet.mockImplementation((opts) => {
|
|
345
342
|
if (opts.entityName === 'content-custom-size')
|
|
346
343
|
return Promise.resolve([]);
|
|
@@ -367,17 +364,17 @@ describe('ThumbnailUtil Tests', () => {
|
|
|
367
364
|
};
|
|
368
365
|
mockContentCreate.mockResolvedValue(createdContent);
|
|
369
366
|
const event = { data: { [thumbnail_util_1.ThumbnailUtil.NEW_THUMBNAIL_ID]: '/rest/thumbnail/new.jpg' } };
|
|
370
|
-
|
|
367
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'oldPv', event, entityName: 'item' });
|
|
371
368
|
expect(mockContentCreate).toHaveBeenCalled();
|
|
372
369
|
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({
|
|
373
370
|
entityName: 'content',
|
|
374
371
|
id: 'newContent2',
|
|
375
372
|
object: { flexplmThumbnailUrl: '/rest/thumbnail/new.jpg' },
|
|
376
373
|
}));
|
|
377
|
-
expect(
|
|
374
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({ entityName: 'item', id: 'entity1' }));
|
|
378
375
|
expect(mockEntitiesDelete).toHaveBeenCalledWith({ entityName: 'content', id: 'oldPv' });
|
|
379
376
|
});
|
|
380
|
-
it('
|
|
377
|
+
it('does not update when primaryViewable.flexplmThumbnailUrl matches', async () => {
|
|
381
378
|
const thumbnailUrl = '/rest/thumbnail/same.png';
|
|
382
379
|
mockEntitiesGet.mockImplementation((opts) => {
|
|
383
380
|
if (opts.entityName === 'content-custom-size')
|
|
@@ -388,8 +385,7 @@ describe('ThumbnailUtil Tests', () => {
|
|
|
388
385
|
return Promise.resolve({});
|
|
389
386
|
});
|
|
390
387
|
const event = { data: { [thumbnail_util_1.ThumbnailUtil.EXISTING_THUMBNAIL_ID]: thumbnailUrl } };
|
|
391
|
-
|
|
392
|
-
expect(result).toBeUndefined();
|
|
388
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'pv1', event, entityName: 'color' });
|
|
393
389
|
expect(mockContentCreate).not.toHaveBeenCalled();
|
|
394
390
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
395
391
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
@@ -14,7 +14,7 @@ export declare class TypeConversionUtils {
|
|
|
14
14
|
static getEntityTypePathFromOjbect(fileId: any, mapFileUtil: any, object: any): Promise<string>;
|
|
15
15
|
static getIdentifierPropertiesFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any): Promise<string[]>;
|
|
16
16
|
static getInformationalPropertiesFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any): Promise<string[]>;
|
|
17
|
-
static getMapKeyFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any, direction: string): Promise<string>;
|
|
17
|
+
static getMapKeyFromObject(fileId: any, mapFileUtil: MapFileUtil, object: any, direction: string): Promise<string | undefined>;
|
|
18
18
|
static isInboundCreatableFromObject(fileId: string, mapFileUtil: MapFileUtil, object: any, context?: any): Promise<boolean>;
|
|
19
19
|
static isOutboundCreatableFromEntity(fileId: string, mapFileUtil: MapFileUtil, entity: any, context?: any): Promise<boolean>;
|
|
20
20
|
static syncInboundImages(fileId: string, mapFileUtil: MapFileUtil, object: any, context?: any): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -359,35 +359,16 @@ describe('BaseEntityProcessor', () =>{
|
|
|
359
359
|
syncThumbnailSpy = jest.spyOn(ThumbnailUtil.prototype, 'syncThumbnailToVibeIQ').mockResolvedValue(undefined);
|
|
360
360
|
});
|
|
361
361
|
|
|
362
|
-
it('should call
|
|
362
|
+
it('should call syncThumbnailToVibeIQ with entity after create when syncInboundImages returns true', async () =>{
|
|
363
363
|
jest.spyOn(btep, 'getIncomingEntity' as any).mockResolvedValue({ entity: null });
|
|
364
364
|
jest.spyOn(btep, 'getCreateEntity' as any).mockResolvedValue({ entity: { name: 'new' } });
|
|
365
365
|
jest.spyOn(btep, 'createEntity').mockResolvedValue(mockCreatedEntity);
|
|
366
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockCreatedEntity);
|
|
367
366
|
syncInboundImagesSpy.mockResolvedValue(true);
|
|
368
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new', primaryFileUrl: 'https://files/new.png' };
|
|
369
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
370
367
|
|
|
371
368
|
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
372
369
|
|
|
373
370
|
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
374
371
|
expect(syncThumbnailSpy).toBeCalledWith({ entityId: 'created-1', primaryViewableId: 'pv-created', event: mockEvent, entityName: 'test' });
|
|
375
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockCreatedEntity, thumbnailUpdates);
|
|
376
|
-
expect(result).toBe(mockCreatedEntity);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('should not call updateEntity after create when syncThumbnailToVibeIQ returns undefined', async () =>{
|
|
380
|
-
jest.spyOn(btep, 'getIncomingEntity' as any).mockResolvedValue({ entity: null });
|
|
381
|
-
jest.spyOn(btep, 'getCreateEntity' as any).mockResolvedValue({ entity: { name: 'new' } });
|
|
382
|
-
jest.spyOn(btep, 'createEntity').mockResolvedValue(mockCreatedEntity);
|
|
383
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity');
|
|
384
|
-
syncInboundImagesSpy.mockResolvedValue(true);
|
|
385
|
-
syncThumbnailSpy.mockResolvedValue(undefined);
|
|
386
|
-
|
|
387
|
-
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
388
|
-
|
|
389
|
-
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
390
|
-
expect(updateEntitySpy).not.toHaveBeenCalled();
|
|
391
372
|
expect(result).toBe(mockCreatedEntity);
|
|
392
373
|
});
|
|
393
374
|
|
|
@@ -404,33 +385,17 @@ describe('BaseEntityProcessor', () =>{
|
|
|
404
385
|
expect(result).toBe(mockCreatedEntity);
|
|
405
386
|
});
|
|
406
387
|
|
|
407
|
-
it('should
|
|
388
|
+
it('should call syncThumbnailToVibeIQ with entity and update with property diffs on update', async () =>{
|
|
408
389
|
jest.spyOn(btep, 'getIncomingEntity' as any).mockResolvedValue({ entity: mockExistingEntity });
|
|
409
390
|
jest.spyOn(btep, 'getUpdatesForEntity').mockResolvedValue({ name: 'changed' });
|
|
410
391
|
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockUpdatedEntity);
|
|
411
392
|
syncInboundImagesSpy.mockResolvedValue(true);
|
|
412
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new' };
|
|
413
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
414
393
|
|
|
415
394
|
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
416
395
|
|
|
417
396
|
expect(syncThumbnailSpy).toBeCalledTimes(1);
|
|
418
397
|
expect(syncThumbnailSpy).toBeCalledWith({ entityId: 'existing-1', primaryViewableId: undefined, event: mockEvent, entityName: 'test' });
|
|
419
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { name: 'changed'
|
|
420
|
-
expect(result).toBe(mockUpdatedEntity);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it('should update entity with only thumbnail changes when no property diffs', async () =>{
|
|
424
|
-
jest.spyOn(btep, 'getIncomingEntity' as any).mockResolvedValue({ entity: mockExistingEntity });
|
|
425
|
-
jest.spyOn(btep, 'getUpdatesForEntity').mockResolvedValue({});
|
|
426
|
-
const updateEntitySpy = jest.spyOn(btep, 'updateEntity').mockResolvedValue(mockUpdatedEntity);
|
|
427
|
-
syncInboundImagesSpy.mockResolvedValue(true);
|
|
428
|
-
const thumbnailUpdates = { primaryViewableId: 'pv-new' };
|
|
429
|
-
syncThumbnailSpy.mockResolvedValue(thumbnailUpdates);
|
|
430
|
-
|
|
431
|
-
const result = await btep.handleIncomingUpsert(mockEvent);
|
|
432
|
-
|
|
433
|
-
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { primaryViewableId: 'pv-new' });
|
|
398
|
+
expect(updateEntitySpy).toBeCalledWith('test', mockExistingEntity, { name: 'changed' });
|
|
434
399
|
expect(result).toBe(mockUpdatedEntity);
|
|
435
400
|
});
|
|
436
401
|
|
|
@@ -84,15 +84,12 @@ export abstract class BaseEntityProcessor {
|
|
|
84
84
|
console.log(statusMsg);
|
|
85
85
|
return createEntityResponse.earlyReturn;
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
let createdEntity = await this.createEntity(this.baseType, createEntityResponse.entity);
|
|
88
88
|
const shouldSyncThumbnail = await TypeConversionUtils.syncInboundImages(
|
|
89
89
|
this.transformMapFile, this.mapFileUtil, event.data
|
|
90
90
|
);
|
|
91
91
|
if (shouldSyncThumbnail) {
|
|
92
|
-
|
|
93
|
-
if (thumbnailUpdates) {
|
|
94
|
-
await this.updateEntity(this.baseType, createdEntity, thumbnailUpdates);
|
|
95
|
-
}
|
|
92
|
+
createdEntity = await new ThumbnailUtil(this.config).syncThumbnailToVibeIQ({ entityId: createdEntity.id, primaryViewableId: createdEntity.primaryViewableId, event, entityName: this.baseType }) || createdEntity;
|
|
96
93
|
}
|
|
97
94
|
const statusMsg = this.getInboundStatusMessage({
|
|
98
95
|
status: EventShortMessageStatus.SUCCESS,
|
|
@@ -109,13 +106,26 @@ export abstract class BaseEntityProcessor {
|
|
|
109
106
|
const shouldSyncThumbnail = await TypeConversionUtils.syncInboundImages(
|
|
110
107
|
this.transformMapFile, this.mapFileUtil, event.data
|
|
111
108
|
);
|
|
112
|
-
let
|
|
109
|
+
let thumbnailEntity;
|
|
113
110
|
if (shouldSyncThumbnail) {
|
|
114
|
-
|
|
111
|
+
thumbnailEntity = await new ThumbnailUtil(this.config).syncThumbnailToVibeIQ({ entityId: entity.id, primaryViewableId: entity.primaryViewableId, event, entityName: this.baseType });
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
const
|
|
118
|
-
|
|
114
|
+
const hasPropertyChanges = Object.getOwnPropertyNames(diffs).length > 0;
|
|
115
|
+
|
|
116
|
+
if (!hasPropertyChanges && thumbnailEntity) {
|
|
117
|
+
const statusMsg = this.getInboundStatusMessage({
|
|
118
|
+
status: EventShortMessageStatus.SUCCESS,
|
|
119
|
+
statusMessage: EventShortMessageStatus.PRIMARY_CONTENT_UPDATED,
|
|
120
|
+
objectClass: event.objectClass,
|
|
121
|
+
entityId: entity.id,
|
|
122
|
+
federatedId: event.federatedId
|
|
123
|
+
});
|
|
124
|
+
console.log(statusMsg);
|
|
125
|
+
return thumbnailEntity;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!hasPropertyChanges) {
|
|
119
129
|
const statusMsg = this.getInboundStatusMessage({
|
|
120
130
|
status: EventShortMessageStatus.SUCCESS,
|
|
121
131
|
statusMessage: EventShortMessageStatus.NO_CHANGES,
|
|
@@ -131,7 +141,7 @@ export abstract class BaseEntityProcessor {
|
|
|
131
141
|
};
|
|
132
142
|
}
|
|
133
143
|
|
|
134
|
-
const updatedEntity = await this.updateEntity(this.baseType, entity,
|
|
144
|
+
const updatedEntity = await this.updateEntity(this.baseType, entity, diffs);
|
|
135
145
|
const statusMsg = this.getInboundStatusMessage({
|
|
136
146
|
status: EventShortMessageStatus.SUCCESS,
|
|
137
147
|
statusMessage: EventShortMessageStatus.UPDATED,
|
|
@@ -9,6 +9,7 @@ export enum EventShortMessageStatus {
|
|
|
9
9
|
MISSING_INPUT = 'Missing_input',
|
|
10
10
|
NOT_CREATABLE = 'Not_creatable',
|
|
11
11
|
NO_CHANGES = 'No_Changes',
|
|
12
|
+
PRIMARY_CONTENT_UPDATED = 'Primary_Content_Updated',
|
|
12
13
|
TOO_MANY_ENTITIES_FOUND = 'Too_Many_Entities_Found',
|
|
13
14
|
UPDATED = 'Updated',
|
|
14
15
|
|
|
@@ -308,42 +308,40 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
308
308
|
mockEntitiesDelete.mockImplementation((opts) => Promise.resolve({ id: opts.id }));
|
|
309
309
|
});
|
|
310
310
|
|
|
311
|
-
it('
|
|
311
|
+
it('does not update when no thumbnail IDs in event data', async () => {
|
|
312
312
|
const event = { data: {} };
|
|
313
|
-
|
|
313
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
314
314
|
|
|
315
|
-
expect(result).toBeUndefined();
|
|
316
315
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
317
316
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
318
317
|
expect(mockContentCreate).not.toHaveBeenCalled();
|
|
319
318
|
});
|
|
320
319
|
|
|
321
|
-
it('
|
|
320
|
+
it('does not update when event.data is undefined', async () => {
|
|
322
321
|
const event = {};
|
|
323
|
-
|
|
322
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
324
323
|
|
|
325
|
-
expect(result).toBeUndefined();
|
|
326
324
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
327
325
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
328
326
|
});
|
|
329
327
|
|
|
330
|
-
it('REMOVE_THUMBNAIL with existing primaryViewableId deletes content and
|
|
328
|
+
it('REMOVE_THUMBNAIL with existing primaryViewableId deletes content and updates entity', async () => {
|
|
331
329
|
const event = { data: { [ThumbnailUtil.NEW_THUMBNAIL_ID]: ThumbnailUtil.REMOVE_THUMBNAIL } };
|
|
332
|
-
|
|
330
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'pv1', event, entityName: 'color' });
|
|
333
331
|
|
|
334
332
|
expect(mockEntitiesDelete).toHaveBeenCalledWith({ entityName: 'content', id: 'pv1' });
|
|
335
|
-
expect(
|
|
333
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({ entityName: 'color', id: 'entity1' }));
|
|
336
334
|
});
|
|
337
335
|
|
|
338
|
-
it('REMOVE_THUMBNAIL with no primaryViewableId
|
|
336
|
+
it('REMOVE_THUMBNAIL with no primaryViewableId updates entity without deleting', async () => {
|
|
339
337
|
const event = { data: { [ThumbnailUtil.NEW_THUMBNAIL_ID]: ThumbnailUtil.REMOVE_THUMBNAIL } };
|
|
340
|
-
|
|
338
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'item' });
|
|
341
339
|
|
|
342
340
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
343
|
-
expect(
|
|
341
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(expect.objectContaining({ entityName: 'item', id: 'entity1' }));
|
|
344
342
|
});
|
|
345
343
|
|
|
346
|
-
it('creates new content when no primaryViewableId exists and
|
|
344
|
+
it('creates new content when no primaryViewableId exists and updates entity', async () => {
|
|
347
345
|
const mockResponse = {
|
|
348
346
|
arrayBuffer: jest.fn().mockResolvedValue(new ArrayBuffer(8)),
|
|
349
347
|
headers: { get: jest.fn().mockReturnValue('image/png') },
|
|
@@ -364,7 +362,7 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
364
362
|
mockContentCreate.mockResolvedValue(createdContent);
|
|
365
363
|
|
|
366
364
|
const event = { data: { [ThumbnailUtil.NEW_THUMBNAIL_ID]: '/rest/thumbnail/thumb.png' } };
|
|
367
|
-
|
|
365
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', event, entityName: 'color' });
|
|
368
366
|
|
|
369
367
|
expect(mockGetRequest).toHaveBeenCalledWith({ urlPath: '/rest/thumbnail/thumb.png', includeUrlContext: false, returnFullResponse: true });
|
|
370
368
|
expect(mockContentCreate).toHaveBeenCalledWith(
|
|
@@ -382,15 +380,16 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
382
380
|
object: { flexplmThumbnailUrl: '/rest/thumbnail/thumb.png' },
|
|
383
381
|
}),
|
|
384
382
|
);
|
|
385
|
-
//
|
|
386
|
-
expect(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
383
|
+
// Updates the main entity
|
|
384
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(
|
|
385
|
+
expect.objectContaining({
|
|
386
|
+
entityName: 'color',
|
|
387
|
+
id: 'entity1',
|
|
388
|
+
}),
|
|
389
|
+
);
|
|
391
390
|
});
|
|
392
391
|
|
|
393
|
-
it('replaces content when primaryViewable.flexplmThumbnailUrl differs and
|
|
392
|
+
it('replaces content when primaryViewable.flexplmThumbnailUrl differs and updates entity', async () => {
|
|
394
393
|
mockEntitiesGet.mockImplementation((opts) => {
|
|
395
394
|
if (opts.entityName === 'content-custom-size') return Promise.resolve([]);
|
|
396
395
|
if (opts.entityName === 'content' && opts.id === 'oldPv') {
|
|
@@ -419,7 +418,7 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
419
418
|
mockContentCreate.mockResolvedValue(createdContent);
|
|
420
419
|
|
|
421
420
|
const event = { data: { [ThumbnailUtil.NEW_THUMBNAIL_ID]: '/rest/thumbnail/new.jpg' } };
|
|
422
|
-
|
|
421
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'oldPv', event, entityName: 'item' });
|
|
423
422
|
|
|
424
423
|
// Creates new content
|
|
425
424
|
expect(mockContentCreate).toHaveBeenCalled();
|
|
@@ -431,13 +430,15 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
431
430
|
object: { flexplmThumbnailUrl: '/rest/thumbnail/new.jpg' },
|
|
432
431
|
}),
|
|
433
432
|
);
|
|
434
|
-
//
|
|
435
|
-
expect(
|
|
433
|
+
// Updates the main entity
|
|
434
|
+
expect(mockEntitiesUpdate).toHaveBeenCalledWith(
|
|
435
|
+
expect.objectContaining({ entityName: 'item', id: 'entity1' }),
|
|
436
|
+
);
|
|
436
437
|
// Deletes old content
|
|
437
438
|
expect(mockEntitiesDelete).toHaveBeenCalledWith({ entityName: 'content', id: 'oldPv' });
|
|
438
439
|
});
|
|
439
440
|
|
|
440
|
-
it('
|
|
441
|
+
it('does not update when primaryViewable.flexplmThumbnailUrl matches', async () => {
|
|
441
442
|
const thumbnailUrl = '/rest/thumbnail/same.png';
|
|
442
443
|
mockEntitiesGet.mockImplementation((opts) => {
|
|
443
444
|
if (opts.entityName === 'content-custom-size') return Promise.resolve([]);
|
|
@@ -448,9 +449,7 @@ describe('ThumbnailUtil Tests', () =>{
|
|
|
448
449
|
});
|
|
449
450
|
|
|
450
451
|
const event = { data: { [ThumbnailUtil.EXISTING_THUMBNAIL_ID]: thumbnailUrl } };
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
expect(result).toBeUndefined();
|
|
452
|
+
await tu.syncThumbnailToVibeIQ({ entityId: 'entity1', primaryViewableId: 'pv1', event, entityName: 'color' });
|
|
454
453
|
expect(mockContentCreate).not.toHaveBeenCalled();
|
|
455
454
|
expect(mockEntitiesUpdate).not.toHaveBeenCalled();
|
|
456
455
|
expect(mockEntitiesDelete).not.toHaveBeenCalled();
|
|
@@ -9,6 +9,7 @@ interface ContentCustomSize {
|
|
|
9
9
|
name: string;
|
|
10
10
|
};
|
|
11
11
|
export class ThumbnailUtil {
|
|
12
|
+
/** The max_thumbnail_size is for limiting the size of the thumbnail being sent to FlexPLM. It is used when checking the size of the auto generated thumbnails (smallViewable, tinyViewable, etc.). */
|
|
12
13
|
private max_thumbnail_size = 5 * 1_024 * 1_024;
|
|
13
14
|
private entities: Entities;
|
|
14
15
|
static NEW_THUMBNAIL_ID = 'NEW_THUMBNAIL_ID';
|
|
@@ -140,6 +141,15 @@ export class ThumbnailUtil {
|
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
/** Syncs the thumbnail from FlexPLM to VibeIQ. Handles creating, replacing, or removing
|
|
145
|
+
* the primary viewable content and persists the updates directly to the entity.
|
|
146
|
+
*
|
|
147
|
+
* @param entityId - The ID of the entity to update with thumbnail properties.
|
|
148
|
+
* @param primaryViewableId - The existing primary viewable content ID, if any.
|
|
149
|
+
* @param event - The inbound event containing thumbnail data (NEW_THUMBNAIL_ID / EXISTING_THUMBNAIL_ID).
|
|
150
|
+
* @param entityName - The entity type name (e.g. 'item', 'color') used for API calls.
|
|
151
|
+
* @returns The updated entity, or undefined if no thumbnail changes were needed.
|
|
152
|
+
*/
|
|
143
153
|
async syncThumbnailToVibeIQ({ entityId, primaryViewableId, event, entityName }: { entityId: string; primaryViewableId?: string; event: any; entityName: string }): Promise<any> {
|
|
144
154
|
console.debug(`syncThumbnailToVibeIQ: entityId=${entityId}, primaryViewableId=${primaryViewableId}, entityName=${entityName}`);
|
|
145
155
|
const eventData = event.data || {};
|
|
@@ -153,8 +163,9 @@ export class ThumbnailUtil {
|
|
|
153
163
|
await this.entities.delete({ entityName: 'content', id: primaryViewableId });
|
|
154
164
|
}
|
|
155
165
|
const clearUpdates = await this.getClearPrimaryViewableUpdates();
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: clearUpdates });
|
|
167
|
+
console.debug(`syncThumbnailToVibeIQ: applied clear updates for entityId=${entityId}`);
|
|
168
|
+
return updatedEntity;
|
|
158
169
|
}
|
|
159
170
|
|
|
160
171
|
// Early return if no thumbnail URL
|
|
@@ -168,8 +179,9 @@ export class ThumbnailUtil {
|
|
|
168
179
|
const content = await this.createContentFromFlexPLM(thumbnailUrl, entityId, entityName);
|
|
169
180
|
await this.entities.update({ entityName: 'content', id: content.id, object: { flexplmThumbnailUrl: thumbnailUrl } });
|
|
170
181
|
const primaryUpdates = await this.getPrimaryViewableUpdates(content);
|
|
182
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: primaryUpdates });
|
|
171
183
|
console.debug(`syncThumbnailToVibeIQ: created new content ${content.id} for entityId=${entityId}`);
|
|
172
|
-
return
|
|
184
|
+
return updatedEntity;
|
|
173
185
|
}
|
|
174
186
|
|
|
175
187
|
// Case 3: Has primaryViewableId — check if thumbnail changed
|
|
@@ -182,9 +194,10 @@ export class ThumbnailUtil {
|
|
|
182
194
|
const content = await this.createContentFromFlexPLM(thumbnailUrl, entityId, entityName);
|
|
183
195
|
await this.entities.update({ entityName: 'content', id: content.id, object: { flexplmThumbnailUrl: thumbnailUrl } });
|
|
184
196
|
const primaryUpdates = await this.getPrimaryViewableUpdates(content);
|
|
197
|
+
const updatedEntity = await this.entities.update({ entityName, id: entityId, object: primaryUpdates });
|
|
185
198
|
await this.entities.delete({ entityName: 'content', id: primaryViewableId });
|
|
186
199
|
console.debug(`syncThumbnailToVibeIQ: replaced content ${primaryViewableId} with ${content.id} for entityId=${entityId}`);
|
|
187
|
-
return
|
|
200
|
+
return updatedEntity;
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
private async createContentFromFlexPLM(thumbnailUrl: string, entityId: string, entityName: string): Promise<any> {
|
|
@@ -323,7 +323,7 @@ export class TypeConversionUtils {
|
|
|
323
323
|
return TypeDefaults.getDefaultInformationalPropertiesFromObject(object);
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
static async getMapKeyFromObject(fileId, mapFileUtil: MapFileUtil, object: any, direction:string): Promise<string> {
|
|
326
|
+
static async getMapKeyFromObject(fileId, mapFileUtil: MapFileUtil, object: any, direction:string): Promise<string | undefined> {
|
|
327
327
|
const type = this.getObjectType(object);
|
|
328
328
|
if(fileId){
|
|
329
329
|
const mappingData = await mapFileUtil.getMappingSection(fileId, 'typeConversion', direction);
|
|
@@ -334,7 +334,6 @@ export class TypeConversionUtils {
|
|
|
334
334
|
return type;
|
|
335
335
|
}
|
|
336
336
|
//TODO use TypeDefaults?
|
|
337
|
-
return '';
|
|
338
337
|
}
|
|
339
338
|
|
|
340
339
|
static async isInboundCreatableFromObject(fileId: string, mapFileUtil: MapFileUtil, object: any, context?: any): Promise<boolean> {
|