stand_socotra_policy_transformer 3.0.1 → 3.0.3
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/__tests__/__utils__/payloads/sample_socotra_with_additional_insured.json +764 -0
- package/__tests__/package_version.test.js +1 -1
- package/__tests__/retool_utils/socotra_group_update.test.js +563 -0
- package/__tests__/retool_utils/socotra_structure_helper.test.js +104 -1
- package/dist/stand_underwriter.js +1 -1
- package/package.json +1 -1
- package/src/retool_utils/socotra_payloads.js +7 -3
- package/src/retool_utils/socotra_structure_helper.js +242 -1
- package/src/retool_utils/versions/stand_v3_entries.js +42 -6
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {SocotraEntry} = require("./socotra_structure_helper");
|
|
1
|
+
const {SocotraEntry, SocotraGroupEntry} = require("./socotra_structure_helper");
|
|
2
2
|
class SocotraPayloadConverter {
|
|
3
3
|
constructor(entries) {
|
|
4
4
|
this.entries = entries
|
|
@@ -79,7 +79,11 @@ class SocotraPayloadConverter {
|
|
|
79
79
|
let payload = SocotraEntry.socotra_create_update_template(dwelling_exposure_locator)
|
|
80
80
|
this.entries.forEach(entry => {
|
|
81
81
|
if (Object.keys(retool_payload).includes(entry.retool_id)) {
|
|
82
|
-
entry
|
|
82
|
+
if(entry instanceof SocotraGroupEntry){
|
|
83
|
+
entry.socotra_update(retool_payload, payload, old_payload)
|
|
84
|
+
} else{
|
|
85
|
+
entry.socotra_update(retool_payload, payload)
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
88
|
})
|
|
85
89
|
|
|
@@ -133,4 +137,4 @@ class SocotraPayloadConverter {
|
|
|
133
137
|
}
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
module.exports = {SocotraPayloadConverter}
|
|
140
|
+
module.exports = {SocotraPayloadConverter}
|
|
@@ -3,6 +3,7 @@ const { DateTime } = require('luxon');
|
|
|
3
3
|
const socotra_locations = Object.freeze([
|
|
4
4
|
'policy.fields',
|
|
5
5
|
'policy.fields.array',
|
|
6
|
+
'policy.fields.group',
|
|
6
7
|
'exposure.dwelling.fields',
|
|
7
8
|
'policy.group',
|
|
8
9
|
'policy',
|
|
@@ -236,6 +237,246 @@ class SocotraEntry {
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
class SocotraGroupEntry extends SocotraEntry {
|
|
241
|
+
constructor(retool_id, socotra_id, socotra_location, socotra_schema = {}, retool_schema = {}) {
|
|
242
|
+
super(retool_id, socotra_id, socotra_location);
|
|
243
|
+
this.socotra_schema = socotra_schema;
|
|
244
|
+
this.retool_schema = retool_schema;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
socotra_create_response(json_payload, socotra_response) {
|
|
248
|
+
// Handle nested data structure
|
|
249
|
+
const path = this.retool_id.split('.');
|
|
250
|
+
let data = json_payload;
|
|
251
|
+
|
|
252
|
+
// Navigate through the nested structure
|
|
253
|
+
for (const key of path) {
|
|
254
|
+
if (data && data[key] !== undefined) {
|
|
255
|
+
data = data[key];
|
|
256
|
+
} else {
|
|
257
|
+
// If the path doesn't exist, return
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// If data is an array, process each item
|
|
263
|
+
if (Array.isArray(data)) {
|
|
264
|
+
// Initialize fieldGroups if it doesn't exist
|
|
265
|
+
if (!socotra_response.fieldGroups) {
|
|
266
|
+
socotra_response.fieldGroups = [];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Transform each item according to the schemas
|
|
270
|
+
const transformedData = data.map(item => {
|
|
271
|
+
const fieldValues = {};
|
|
272
|
+
|
|
273
|
+
// Map fields from retool schema to socotra schema
|
|
274
|
+
for (const [socotraField, retoolField] of Object.entries(this.socotra_schema)) {
|
|
275
|
+
// If the retool field starts with ':', it's a direct mapping
|
|
276
|
+
if (retoolField.startsWith(':')) {
|
|
277
|
+
const actualRetoolField = retoolField.substring(1);
|
|
278
|
+
if (item[actualRetoolField] !== undefined) {
|
|
279
|
+
fieldValues[socotraField] = item[actualRetoolField];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
fieldName: this.socotra_id,
|
|
286
|
+
fieldValues: fieldValues
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Add the transformed data to the response
|
|
291
|
+
socotra_response.fieldGroups = transformedData;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
retool_response(socotra_payload, retool_response) {
|
|
296
|
+
// Handle group entities for Socotra to Retool transformation
|
|
297
|
+
if (this.socotra_location === 'policy.fields.group') {
|
|
298
|
+
// Get the field values from the Socotra payload
|
|
299
|
+
const fieldValues = socotra_payload.characteristics.fieldValues;
|
|
300
|
+
const fieldGroupsByLocator = socotra_payload.characteristics.fieldGroupsByLocator;
|
|
301
|
+
|
|
302
|
+
// Check if the field exists and has values
|
|
303
|
+
if (fieldValues && fieldValues[this.socotra_id] && fieldGroupsByLocator) {
|
|
304
|
+
// Initialize the nested structure in the retool response
|
|
305
|
+
const path = this.retool_id.split('.');
|
|
306
|
+
let current = retool_response;
|
|
307
|
+
|
|
308
|
+
// Create the nested structure
|
|
309
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
310
|
+
if (!current[path[i]]) {
|
|
311
|
+
current[path[i]] = {};
|
|
312
|
+
}
|
|
313
|
+
current = current[path[i]];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Get the locators for the group entities
|
|
317
|
+
const locators = fieldValues[this.socotra_id];
|
|
318
|
+
|
|
319
|
+
// Initialize the array for the group entities
|
|
320
|
+
current[path[path.length - 1]] = [];
|
|
321
|
+
|
|
322
|
+
// Transform each group entity
|
|
323
|
+
for (const locator of locators) {
|
|
324
|
+
if (fieldGroupsByLocator[locator]) {
|
|
325
|
+
const groupData = fieldGroupsByLocator[locator];
|
|
326
|
+
const retoolItem = {};
|
|
327
|
+
|
|
328
|
+
// Map fields from socotra schema to retool schema
|
|
329
|
+
for (const [socotraField, retoolField] of Object.entries(this.retool_schema)) {
|
|
330
|
+
// If the retool field starts with ':', it's a direct mapping
|
|
331
|
+
if (retoolField.startsWith(':')) {
|
|
332
|
+
const actualRetoolField = retoolField.substring(1);
|
|
333
|
+
if (groupData[socotraField] && groupData[socotraField][0] !== undefined) {
|
|
334
|
+
retoolItem[actualRetoolField] = groupData[socotraField][0];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Add the socotra field locator
|
|
340
|
+
retoolItem.socotra_field_locator = locator;
|
|
341
|
+
|
|
342
|
+
// Add the transformed item to the array
|
|
343
|
+
current[path[path.length - 1]].push(retoolItem);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Fall back to the parent class implementation for non-group fields
|
|
351
|
+
return super.retool_response(socotra_payload, retool_response);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Helper function to navigate through nested structure
|
|
355
|
+
_navigateNestedStructure(obj, path) {
|
|
356
|
+
let result = obj;
|
|
357
|
+
for (const key of path) {
|
|
358
|
+
if (result && result[key] !== undefined) {
|
|
359
|
+
result = result[key];
|
|
360
|
+
} else {
|
|
361
|
+
// If the path doesn't exist, return empty array
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Helper function to create field values from an item
|
|
369
|
+
_createFieldValues(item) {
|
|
370
|
+
const fieldValues = {};
|
|
239
371
|
|
|
372
|
+
// Map fields from retool schema to socotra schema
|
|
373
|
+
for (const [socotraField, retoolField] of Object.entries(this.socotra_schema)) {
|
|
374
|
+
// If the retool field starts with ':', it's a direct mapping
|
|
375
|
+
if (retoolField.startsWith(':')) {
|
|
376
|
+
const actualRetoolField = retoolField.substring(1);
|
|
377
|
+
if (item[actualRetoolField] !== undefined) {
|
|
378
|
+
fieldValues[socotraField] = item[actualRetoolField];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return fieldValues;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Helper function to add an item to addFieldGroups
|
|
387
|
+
_addItemToFieldGroups(item, socotra_response) {
|
|
388
|
+
// Skip explicit remove operations
|
|
389
|
+
if (item.remove === true) return;
|
|
390
|
+
|
|
391
|
+
// Transform the item for add
|
|
392
|
+
const fieldValues = this._createFieldValues(item);
|
|
393
|
+
|
|
394
|
+
// Add to addFieldGroups
|
|
395
|
+
socotra_response.addFieldGroups.push({
|
|
396
|
+
fieldName: this.socotra_id,
|
|
397
|
+
fieldValues: fieldValues
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Helper function to process explicit remove operations
|
|
402
|
+
_processRemoveOperations(data, socotra_response) {
|
|
403
|
+
for (const item of data) {
|
|
404
|
+
if (item.remove === true && item.locator) {
|
|
405
|
+
// Add to removeFieldGroups
|
|
406
|
+
socotra_response.removeFieldGroups.push(item.locator);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Helper function to create a comparison key for an item
|
|
412
|
+
_createComparisonKey(item) {
|
|
413
|
+
const itemCopy = { ...item };
|
|
414
|
+
delete itemCopy.socotra_field_locator;
|
|
415
|
+
return JSON.stringify(itemCopy);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
socotra_update(retool_payload, socotra_response, old_payload) {
|
|
419
|
+
// Handle nested data structure
|
|
420
|
+
const path = this.retool_id.split('.');
|
|
421
|
+
let data = this._navigateNestedStructure(retool_payload, path);
|
|
422
|
+
let oldData = old_payload ? this._navigateNestedStructure(old_payload, path) : [];
|
|
423
|
+
|
|
424
|
+
// Initialize arrays for different operations if they don't exist
|
|
425
|
+
if (!socotra_response.addFieldGroups) {
|
|
426
|
+
socotra_response.addFieldGroups = [];
|
|
427
|
+
}
|
|
428
|
+
if (!socotra_response.removeFieldGroups) {
|
|
429
|
+
socotra_response.removeFieldGroups = [];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Process explicit remove operations first
|
|
433
|
+
this._processRemoveOperations(data, socotra_response);
|
|
434
|
+
|
|
435
|
+
// If old_payload is provided, compare new and old data
|
|
436
|
+
if (old_payload && Array.isArray(oldData) && Array.isArray(data)) {
|
|
437
|
+
// Create a map of new items for easier comparison
|
|
438
|
+
const newItemsMap = new Map();
|
|
439
|
+
for (const item of data) {
|
|
440
|
+
// Create a key for comparison (excluding socotra_field_locator)
|
|
441
|
+
const key = this._createComparisonKey(item);
|
|
442
|
+
newItemsMap.set(key, item);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Check old items
|
|
446
|
+
for (const oldItem of oldData) {
|
|
447
|
+
// Create a key for comparison (excluding socotra_field_locator)
|
|
448
|
+
const key = this._createComparisonKey(oldItem);
|
|
449
|
+
|
|
450
|
+
// If the item exists in both old and new, it's a match - skip it
|
|
451
|
+
if (newItemsMap.has(key)) {
|
|
452
|
+
newItemsMap.delete(key);
|
|
453
|
+
}
|
|
454
|
+
// If the item only exists in old, remove it
|
|
455
|
+
else if (oldItem.socotra_field_locator) {
|
|
456
|
+
socotra_response.removeFieldGroups.push(oldItem.socotra_field_locator);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Add items that only exist in new
|
|
461
|
+
for (const item of newItemsMap.values()) {
|
|
462
|
+
this._addItemToFieldGroups(item, socotra_response);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// If no old_payload, process as before
|
|
466
|
+
else {
|
|
467
|
+
for (const item of data) {
|
|
468
|
+
this._addItemToFieldGroups(item, socotra_response);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Remove empty arrays
|
|
473
|
+
if (socotra_response.addFieldGroups.length === 0) {
|
|
474
|
+
delete socotra_response.addFieldGroups;
|
|
475
|
+
}
|
|
476
|
+
if (socotra_response.removeFieldGroups.length === 0) {
|
|
477
|
+
delete socotra_response.removeFieldGroups;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
240
481
|
|
|
241
|
-
module.exports = {SocotraEntry}
|
|
482
|
+
module.exports = {SocotraEntry, SocotraGroupEntry}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {SocotraEntry} = require("../socotra_structure_helper");
|
|
1
|
+
const {SocotraEntry, SocotraGroupEntry} = require("../socotra_structure_helper");
|
|
2
2
|
|
|
3
3
|
function solar_panel_functions() {
|
|
4
4
|
let to_socotra_map = {0: "No", "1+": "Yes"}
|
|
@@ -204,8 +204,44 @@ entries_v3 = [
|
|
|
204
204
|
|
|
205
205
|
]
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
// Define schemas for Groups
|
|
208
|
+
const additional_insured_socotra_schema = {
|
|
209
|
+
type: ":type",
|
|
210
|
+
name: ":name",
|
|
211
|
+
street_address: ":street_address",
|
|
212
|
+
street_address2: ":street_address2",
|
|
213
|
+
city: ":city",
|
|
214
|
+
state: ":state",
|
|
215
|
+
zip: ":zip",
|
|
216
|
+
description: ":description",
|
|
217
|
+
loan_number: ":loan_number"
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const additional_insured_retool_schema = {
|
|
221
|
+
zip: ":zip",
|
|
222
|
+
street_address: ":street_address",
|
|
223
|
+
loan_number: ":loan_number",
|
|
224
|
+
city: ":city",
|
|
225
|
+
street_address2: ":street_address2",
|
|
226
|
+
state: ":state",
|
|
227
|
+
type: ":type",
|
|
228
|
+
name: ":name",
|
|
229
|
+
description: ":description"
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Add additional insured to entries_v3
|
|
233
|
+
entries_v3.push(
|
|
234
|
+
new SocotraGroupEntry(
|
|
235
|
+
"additional_insured_data.additionalInterest",
|
|
236
|
+
"additional_insured",
|
|
237
|
+
"policy.fields.group",
|
|
238
|
+
additional_insured_socotra_schema,
|
|
239
|
+
additional_insured_retool_schema
|
|
240
|
+
)
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
module.exports = {entries_v3}
|