@izara_frontend/service-schemas 1.0.1 → 1.0.2

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.
@@ -0,0 +1,1341 @@
1
+ import { useCallback, useEffect, useState, useRef } from "react";
2
+ import {
3
+ validateObjType as validateObjTypeLib,
4
+ consts as schemaConsts,
5
+ utils as sharedSchemaUtils,
6
+ reformatObjectSchema
7
+ } from "@izara_project/izara-shared-service-schemas"
8
+
9
+ import lodash from "lodash";
10
+ import { objectHash as hash } from "@izara_project/izara-shared-core";
11
+ const { validateObjType, validateRelType } = validateObjTypeLib;
12
+
13
+
14
+ import {
15
+ getObjectSchemaAllEndpoint,
16
+ getObjectLinksEndpoint,
17
+ getRefObjectRelationshipEndpoint,
18
+ getRelationshipSchemaEndpoint,
19
+ getLinkConfigEndpoint
20
+ } from './endpoint'
21
+
22
+ let schemaInitTools = null;
23
+ let cachedEndpoints = {};
24
+
25
+
26
+ export function initGetSchemaTools(initTools) {
27
+ cachedEndpoints = {};
28
+ schemaInitTools = initTools;
29
+
30
+ if (!schemaInitTools || !schemaInitTools.injectReducer || !schemaInitTools.store || !schemaInitTools.createPostsApi) {
31
+ } else {
32
+ const { injectReducer, store, createPostsApi } = schemaInitTools;
33
+
34
+ const endpoints = [
35
+ getObjectSchemaAllEndpoint,
36
+ getObjectLinksEndpoint,
37
+ getRefObjectRelationshipEndpoint,
38
+ getRelationshipSchemaEndpoint,
39
+ getLinkConfigEndpoint
40
+ ];
41
+
42
+
43
+ const postApi = createPostsApi(endpoints);
44
+ injectReducer(null, endpoints);
45
+
46
+ for (const endpoint of endpoints) {
47
+ const getSchemaFn = {
48
+ [endpoint.name]: async (params) => {
49
+ const result = await store.dispatch(
50
+ postApi.endpoints[endpoint.name].initiate(params)
51
+ );
52
+
53
+ if (result?.data) {
54
+ return result.data;
55
+ } else {
56
+ return null;
57
+ }
58
+ }
59
+ };
60
+
61
+ Object.assign(cachedEndpoints, getSchemaFn);
62
+ }
63
+ }
64
+
65
+ }
66
+
67
+
68
+
69
+ function createObjTypeConcat(objType) {
70
+ const validateObjTypeResult = validateObjType(objType);
71
+ if (validateObjTypeResult.errorsFound.length > 0) {
72
+ return [null, [`Invalid objType:${JSON.stringify(objType)}, ${validateObjTypeResult.errorsFound.join(", ")}`]]
73
+ }
74
+ return [`${objType.serviceTag}_${objType.objectType}`, []]
75
+ }
76
+
77
+ function explodedObjTypeConcat(objTypeConcat) {
78
+ return objTypeConcat.split("_");
79
+ }
80
+
81
+ // ...existing code...
82
+ export function getObjectSchemaWithAllHierarchy() {
83
+ const { getObjectSchemaAll } = cachedEndpoints;
84
+
85
+ if (!getObjectSchemaAll) {
86
+ return [null, () => { }, { errorsFound: ["need to initialize initGetSchemaTools before use"] }];
87
+ }
88
+
89
+ const [result, setResult] = useState(null);
90
+ const [errorsFound, setErrorsFound] = useState([]);
91
+
92
+ // core executor that does the actual work and optionally updates hook state
93
+ // defaults to update the hook state; supports clearing state with `clearState`
94
+ const execute = useCallback(async (objType, { updateState = true, clearState = false } = {}) => {
95
+
96
+ // allow caller to clear current state before running
97
+ if (updateState && clearState) {
98
+ setResult(null);
99
+ setErrorsFound([]);
100
+ }
101
+ const seenObjTypeConcat = new Set();
102
+
103
+ async function getObjectSchemaWithAllHierarchyMain(currentObjType, seenSet, iter = 1) {
104
+ // validate depth
105
+ if (iter > 20) {
106
+ return [null, [`getObjectSchemaWithAllHierarchy Error objectType:${currentObjType?.objectType} Have more than ${20} hierarchy.`]];
107
+ }
108
+
109
+ // validate objType (some validators throw, some return errors)
110
+ try {
111
+ validateObjType(currentObjType);
112
+ } catch (err) {
113
+ return [null, [err.message]];
114
+ }
115
+
116
+ // create unique key for this objType
117
+ const [objTypeConcat, objKeyErr] = createObjTypeConcat(currentObjType);
118
+ if (objKeyErr.length) {
119
+ return [null, objKeyErr];
120
+ }
121
+
122
+ if (seenSet.has(objTypeConcat)) {
123
+ return [null, [`found duplicate objType in hierarchy ${objTypeConcat}, possible infinite loop`]];
124
+ } else {
125
+ seenSet.add(objTypeConcat);
126
+ }
127
+
128
+ // fetch schema
129
+ const objSchema = await getObjectSchemaAll(currentObjType);
130
+ if (!objSchema) {
131
+ if (iter === 1) {
132
+ return [null, [`not found objectSchema, Not found { serviceTag:${currentObjType.serviceTag}, objectType:${currentObjType.objectType} }`]];
133
+ }
134
+ return [null, [`not found objectSchema in hierarchy, Not found { serviceTag:${currentObjType.serviceTag}, objectType:${currentObjType.objectType} }`]];
135
+ }
136
+
137
+ // collect current
138
+ const collectedSchema = { [objTypeConcat]: objSchema };
139
+
140
+ // if extend, recurse and merge maps
141
+ if (objSchema.hasOwnProperty("extendObjType")) {
142
+ const [parentsObjSchema, parentObjSchemaErrors] = await getObjectSchemaWithAllHierarchyMain(
143
+ objSchema.extendObjType,
144
+ seenSet,
145
+ iter + 1,
146
+ );
147
+
148
+ if (parentObjSchemaErrors && parentObjSchemaErrors.length) {
149
+ return [null, parentObjSchemaErrors];
150
+ }
151
+
152
+ // merge parentMap into collected (parent entries may be deeper)
153
+ Object.assign(collectedSchema, parentsObjSchema);
154
+ }
155
+
156
+ return [collectedSchema, []];
157
+ }
158
+
159
+ try {
160
+
161
+ const [schema, errorsFound] = await getObjectSchemaWithAllHierarchyMain(objType, seenObjTypeConcat, 1);
162
+
163
+ if (updateState) {
164
+ if (errorsFound && errorsFound.length) {
165
+ setErrorsFound(errorsFound);
166
+ setResult(null);
167
+ } else {
168
+ setResult(schema);
169
+ setErrorsFound([]);
170
+ }
171
+ }
172
+ return [schema, errorsFound];
173
+ } catch (err) {
174
+ const msg = err?.message || String(err);
175
+
176
+ if (updateState) {
177
+ setErrorsFound([msg]);
178
+ setResult(null);
179
+ }
180
+ return [null, [msg]];
181
+ }
182
+ }, [cachedEndpoints]);
183
+
184
+ // run can be used two ways:
185
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
186
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
187
+ const run = useCallback((objType) => {
188
+ if (typeof objType === "undefined") {
189
+ return {
190
+ initiate: (objType) => execute(objType, { updateState: false })
191
+ };
192
+ }
193
+ // direct call: update state
194
+ return execute(objType, { updateState: true });
195
+ }, [execute]);
196
+
197
+
198
+ return [result, run, { errorsFound }];
199
+ }
200
+
201
+
202
+ export function getObjectLinks() {
203
+ const {
204
+ getObjectSchemaAll,
205
+ getObjectLinks: getObjectLinksSchema,
206
+ getRefObjectRelationship: getRefRelSchema,
207
+ getRelationshipSchema: getRelSchema
208
+ } = cachedEndpoints;
209
+
210
+ if (!getObjectSchemaAll || !getObjectLinksSchema || !getRefRelSchema || !getRelSchema) {
211
+ return [null, () => { }, { errorsFound: ["need to initialize initGetSchemaTools before use"] }];
212
+ }
213
+ const [, getObjectSchemaWithAllHierarchyFn] = getObjectSchemaWithAllHierarchy();
214
+
215
+
216
+ const [result, setResult] = useState(null);
217
+ const [errorsFound, setErrorsFound] = useState([]);
218
+
219
+
220
+ // core executor that does the actual work and optionally updates hook state
221
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
222
+
223
+ async function getObjectLinksMain(objType) {
224
+
225
+ // validate objType (some validators throw, some return errors)
226
+ try {
227
+ validateObjType(objType);
228
+ } catch (err) {
229
+ return [null, [err.message]];
230
+ }
231
+
232
+ let mainErrorsFound = [];
233
+
234
+ const mainObjTypeConcat = createObjTypeConcat(objType);
235
+
236
+ const [objectSchemaHierarChy, getSchemaErrors] = await getObjectSchemaWithAllHierarchyFn().initiate(objType);
237
+
238
+
239
+ if (getSchemaErrors.length || !objectSchemaHierarChy || !Object.keys(objectSchemaHierarChy).length) {
240
+ return [null, getSchemaErrors];
241
+ }
242
+
243
+ let returnLinks = [];
244
+ const linkTypeIds = new Set();
245
+
246
+ await Promise.all(
247
+ Object.keys(objectSchemaHierarChy).map(async (objTypeConcat) => {
248
+
249
+ const [serviceTag, objectType] = explodedObjTypeConcat(objTypeConcat);
250
+
251
+ let getObjLinksAndRefPromises = [];
252
+
253
+ getObjLinksAndRefPromises.push(getObjectLinksSchema({ serviceTag, objectType }));
254
+ getObjLinksAndRefPromises.push(getRefRelSchema({ serviceTag, objectType }));
255
+
256
+ const [objectLinks, refRelationshipsResult] = await Promise.all(getObjLinksAndRefPromises);
257
+
258
+ if (objectLinks && objectLinks?.length) {
259
+ for (const objectLink of objectLinks) {
260
+ let usedLink = objectLink;
261
+
262
+ if (objTypeConcat !== mainObjTypeConcat) {
263
+ usedLink = {
264
+ ...objectLink,
265
+ base: { ...objectLink.base, objType }
266
+ };
267
+ }
268
+
269
+ const usedLinkTypeId = hash([usedLink.base.objType, usedLink.other.objType, usedLink.relType, usedLink.base.direction]);
270
+
271
+ if (!linkTypeIds.has(usedLinkTypeId)) {
272
+ linkTypeIds.add(usedLinkTypeId);
273
+ returnLinks.push(usedLink);
274
+ }
275
+ }
276
+ }
277
+
278
+ if (refRelationshipsResult && Object.keys(refRelationshipsResult).length) {
279
+ await Promise.all(
280
+ Object.entries(refRelationshipsResult).map(async ([relTag, refData]) => {
281
+
282
+ let refRelSchemaResult = await getRelSchema(
283
+ {
284
+ serviceTag: refData.relationshipServiceTag,
285
+ relationshipTag: relTag
286
+ },
287
+ );
288
+
289
+
290
+ if (refRelSchemaResult) {
291
+ const linkRecordsResult = sharedSchemaUtils.findLinksByObjType(
292
+ { serviceTag, objectType },
293
+ {
294
+ serviceTag: refData.relationshipServiceTag,
295
+ relationshipTag: relTag
296
+ },
297
+ refRelSchemaResult
298
+ );
299
+
300
+
301
+
302
+ if (linkRecordsResult.errorsFound.length > 0) {
303
+ mainErrorsFound.push(...linkRecordsResult.errorsFound);
304
+ }
305
+
306
+ if (linkRecordsResult.result.length) {
307
+ for (const linkRecord of linkRecordsResult.result) {
308
+ let usedLink = linkRecord;
309
+ if (objTypeConcat !== mainObjTypeConcat) {
310
+ usedLink = {
311
+ ...linkRecord,
312
+ base: { ...linkRecord.base, objType }
313
+ };
314
+ }
315
+ const usedLinkTypeId = hash([usedLink.base.objType, usedLink.other.objType, usedLink.relType, usedLink.base.direction]);
316
+
317
+ if (!linkTypeIds.has(usedLinkTypeId)) {
318
+ linkTypeIds.add(usedLinkTypeId);
319
+ returnLinks.push(usedLink);
320
+ }
321
+ }
322
+ } else {
323
+ mainErrorsFound.push(`relationshipTag:'${relTag}' in service:'${refData.relationshipServiceTag}' not have objectType:'${objType.objectType}' in links`);
324
+ }
325
+ } else {
326
+ mainErrorsFound.push(`Not found relationshipTag:${relTag} in service:${refData.relationshipServiceTag}`);
327
+ }
328
+ }));
329
+ }
330
+ })
331
+ );
332
+
333
+ return [returnLinks, mainErrorsFound];
334
+ }
335
+
336
+ try {
337
+
338
+ const [schema, errorsFound] = await getObjectLinksMain(objType);
339
+
340
+ if (updateState) {
341
+ if (errorsFound && errorsFound.length) {
342
+ setErrorsFound(errorsFound);
343
+ setResult(null);
344
+ } else {
345
+ setResult(schema);
346
+ setErrorsFound([]);
347
+ }
348
+ }
349
+
350
+ return [schema, errorsFound];
351
+ } catch (err) {
352
+ const msg = err?.message || String(err);
353
+ if (updateState) {
354
+ setErrorsFound([msg]);
355
+ setResult(null);
356
+ }
357
+ return [null, [msg]];
358
+ }
359
+ }, [cachedEndpoints]);
360
+
361
+
362
+ // run can be used two ways:
363
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
364
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
365
+ const run = useCallback((objType) => {
366
+ if (typeof objType === "undefined") {
367
+ return {
368
+ initiate: (objType) => execute(objType, { updateState: false })
369
+ };
370
+ }
371
+ // direct call: update state
372
+ return execute(objType, { updateState: true });
373
+ }, [execute]);
374
+
375
+ return [result, run, { errorsFound }];
376
+ }
377
+
378
+
379
+
380
+ export function getRequiredOnCreateLinks() {
381
+ const [, getObjectLinksFn] = getObjectLinks();
382
+
383
+
384
+ const [result, setResult] = useState(null);
385
+ const [errorsFound, setErrorsFound] = useState([]);
386
+
387
+ // core executor that does the actual work and optionally updates hook state
388
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
389
+
390
+ async function getObjectLinksMain(objType) {
391
+ let returnLinks = [];
392
+
393
+ const [objectLinks, objectLinksErrors] = await getObjectLinksFn().initiate(objType);
394
+
395
+ if (objectLinksErrors?.length) {
396
+ return [[], objectLinksErrors];
397
+ }
398
+
399
+ if (objectLinks?.length) {
400
+ for (const objectLink of objectLinks) {
401
+ if (objectLink.base?.requiredOnCreate) {
402
+ returnLinks.push(objectLink);
403
+ }
404
+ }
405
+ }
406
+
407
+ return [returnLinks, []];
408
+ }
409
+
410
+ try {
411
+
412
+ const [schema, errorsFound] = await getObjectLinksMain(objType);
413
+
414
+ if (updateState) {
415
+ if (errorsFound && errorsFound.length) {
416
+ setErrorsFound(errorsFound);
417
+ setResult(null);
418
+ } else {
419
+ setResult(schema);
420
+ setErrorsFound([]);
421
+ }
422
+ }
423
+
424
+ return [schema, errorsFound];
425
+ } catch (err) {
426
+ const msg = err?.message || String(err);
427
+ if (updateState) {
428
+ setErrorsFound([msg]);
429
+ setResult(null);
430
+ }
431
+ return [null, [msg]];
432
+ }
433
+ }, [getObjectLinksFn]);
434
+
435
+
436
+
437
+ // run can be used two ways:
438
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
439
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
440
+ const run = useCallback((objType) => {
441
+ if (typeof objType === "undefined") {
442
+ return {
443
+ initiate: (objType) => execute(objType, { updateState: false })
444
+ };
445
+ }
446
+ // direct call: update state
447
+ return execute(objType, { updateState: true });
448
+ }, [execute]);
449
+
450
+ return [result, run, { errorsFound }];
451
+ }
452
+
453
+
454
+ export function getRelationshipSchema() {
455
+ const { getRelationshipSchema: getRelationshipSchemaEndpoint } = cachedEndpoints;
456
+
457
+ if (!getRelationshipSchemaEndpoint) {
458
+ return [null, () => { }, { errorsFound: ["need to initialize initGetSchemaTools before use"] }];
459
+ }
460
+
461
+ const [result, setResult] = useState(null);
462
+ const [errorsFound, setErrorsFound] = useState([]);
463
+
464
+
465
+ // core executor that does the actual work and optionally updates hook state
466
+ const execute = useCallback(async (relType, { updateState = true } = {}) => {
467
+
468
+ async function getRelationshipSchemaMain(relType) {
469
+
470
+ const relationshipSchema = await getRelationshipSchemaEndpoint(relType);
471
+ return [relationshipSchema, []];
472
+ }
473
+
474
+ try {
475
+
476
+ const [schema, errorsFound] = await getRelationshipSchemaMain(relType);
477
+
478
+ if (updateState) {
479
+ if (errorsFound && errorsFound.length) {
480
+ setErrorsFound(errorsFound);
481
+ setResult(null);
482
+ } else {
483
+ setResult(schema);
484
+ setErrorsFound([]);
485
+ }
486
+ }
487
+
488
+ return [schema, errorsFound];
489
+ } catch (err) {
490
+ const msg = err?.message || String(err);
491
+ if (updateState) {
492
+ setErrorsFound([msg]);
493
+ setResult(null);
494
+ }
495
+ return [null, [msg]];
496
+ }
497
+ }, [cachedEndpoints]);
498
+
499
+
500
+
501
+ // run can be used two ways:
502
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
503
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
504
+ const run = useCallback((objType) => {
505
+ if (typeof objType === "undefined") {
506
+ return {
507
+ initiate: (objType) => execute(objType, { updateState: false })
508
+ };
509
+ }
510
+ // direct call: update state
511
+ return execute(objType, { updateState: true });
512
+ }, [execute]);
513
+
514
+ return [result, run, { errorsFound }];
515
+ }
516
+
517
+
518
+ export function getObjSchemaWithOutHierarchy() {
519
+ const { getObjectSchemaAll } = cachedEndpoints;
520
+
521
+ if (!getObjectSchemaAll) {
522
+ return [null, () => { }, { errorsFound: ["need to initialize initGetSchemaTools before use"] }];
523
+ }
524
+
525
+ const [result, setResult] = useState(null);
526
+ const [errorsFound, setErrorsFound] = useState([]);
527
+
528
+
529
+ // core executor that does the actual work and optionally updates hook state
530
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
531
+
532
+ async function getObjSchemaWithOutHierarchyMain(objType) {
533
+ const objSchema = await getObjectSchemaAll(objType);
534
+ return [objSchema, []];
535
+ }
536
+
537
+ try {
538
+
539
+ const [schema, errorsFound] = await getObjSchemaWithOutHierarchyMain(objType);
540
+
541
+ if (updateState) {
542
+ if (errorsFound && errorsFound.length) {
543
+ setErrorsFound(errorsFound);
544
+ setResult(null);
545
+ } else {
546
+ setResult(schema);
547
+ setErrorsFound([]);
548
+ }
549
+ }
550
+
551
+ return [schema, errorsFound];
552
+ } catch (err) {
553
+ const msg = err?.message || String(err);
554
+ if (updateState) {
555
+ setErrorsFound([msg]);
556
+ setResult(null);
557
+ }
558
+ return [null, [msg]];
559
+ }
560
+ }, [cachedEndpoints]);
561
+
562
+
563
+
564
+ // run can be used two ways:
565
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
566
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
567
+ const run = useCallback((objType) => {
568
+ if (typeof objType === "undefined") {
569
+ return {
570
+ initiate: (objType) => execute(objType, { updateState: false })
571
+ };
572
+ }
573
+ // direct call: update state
574
+ return execute(objType, { updateState: true });
575
+ }, [execute]);
576
+
577
+ return [result, run, { errorsFound }];
578
+ }
579
+
580
+ export function getObjSchemaWithHierarchy() {
581
+
582
+ const [, getObjectSchemaWithAllHierarchyFn] = getObjectSchemaWithAllHierarchy();
583
+
584
+ const [result, setResult] = useState(null);
585
+ const [errorsFound, setErrorsFound] = useState([]);
586
+
587
+
588
+ // core executor that does the actual work and optionally updates hook state
589
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
590
+
591
+ async function getObjSchemaWithHierarchyMain(objType) {
592
+ const [objectSchemaHierarChy, getSchemaErrors] = await getObjectSchemaWithAllHierarchyFn().initiate(objType);
593
+
594
+ if (getSchemaErrors.length) {
595
+ return [null, getSchemaErrors];
596
+ } else {
597
+
598
+ const mergeCount = Object.keys(objectSchemaHierarChy).length;
599
+
600
+ if (mergeCount === 1) {
601
+ return [Object.values(objectSchemaHierarChy)[0], getSchemaErrors];
602
+ } else if (mergeCount > 1) {
603
+
604
+ let mainObjType = objType;
605
+ let mergeObjSchema = {};
606
+
607
+ // iterate to merge all objectSchema in hierarchy
608
+ for (let i = 0; i < mergeCount; i++) {
609
+
610
+ const [objTypeConcat] = createObjTypeConcat(mainObjType);
611
+ const objectSchema = objectSchemaHierarChy[objTypeConcat];
612
+
613
+
614
+ if (i === 0) {
615
+ mergeObjSchema = lodash.cloneDeep(objectSchema);
616
+
617
+ } else {
618
+ // change mainObjType every time in loop
619
+ if (mergeObjSchema.hasOwnProperty("extendObjType")) {
620
+ mainObjType = mergeObjSchema.extendObjType;
621
+ const [parentObjTypeConcat, errorsCreateObjTypeConcat] = createObjTypeConcat(mergeObjSchema.extendObjType);
622
+
623
+ if (errorsCreateObjTypeConcat.length) {
624
+ return [null, errorsCreateObjTypeConcat];
625
+ }
626
+
627
+ const parentObjSchema = objectSchemaHierarChy[parentObjTypeConcat];
628
+
629
+ if (!parentObjSchema) {
630
+ return [null, [`extendObjType:${objType.objectType} Not found parent. => ${JSON.stringify(mergeObjSchema.extendObjType)}`]]
631
+ }
632
+
633
+ // merge canCreate if not exists in child objectSchema
634
+ if (!mergeObjSchema.canDelete && parentObjSchema.hasOwnProperty("canDelete")) {
635
+ mergeObjSchema.canDelete = parentObjSchema.canDelete;
636
+ }
637
+
638
+ // validate and merge identifiers
639
+ if (parentObjSchema.hasOwnProperty("identifiers") && parentObjSchema.hasOwnProperty("extendObjType")) {
640
+ return [null, ["mergeExtendObjSchema:parent objectSchema should not have identifiers because have extendObjType."]];
641
+ } else if (!parentObjSchema.hasOwnProperty("identifiers") && !parentObjSchema.hasOwnProperty("extendObjType")) {
642
+ return [null, ["mergeExtendObjSchema:invalid parent objectSchema missing identifiers."]];
643
+ } else {
644
+ mergeObjSchema.identifiers = parentObjSchema.identifiers;
645
+ }
646
+
647
+ for (const [parentFieldName, parentFieldNameProp] of Object.entries(parentObjSchema.fieldNames)) {
648
+ const existingFieldNamePropInParent = mergeObjSchema.fieldNames[parentFieldName];
649
+
650
+ if (existingFieldNamePropInParent) {
651
+ // Validate that child fieldName only has storageResourceTags if it exists
652
+ const isValidChildFieldName =
653
+ Object.keys(existingFieldNamePropInParent).length === 0 ||
654
+ (Object.keys(existingFieldNamePropInParent).length === 1 && existingFieldNamePropInParent.hasOwnProperty("storageResourceTags"));
655
+
656
+ if (!isValidChildFieldName) {
657
+ return [null, [`objectSchema:${mergeObjSchema.objectType} merge fieldName:${parentFieldName} invalid, child fieldName can only have storageResourceTags when merging.`]];
658
+ }
659
+
660
+ // Merge parent fieldName properties into child, concatenating storageResourceTags
661
+ for (const [parentFieldNameKey, parentFieldNameValue] of Object.entries(parentFieldNameProp)) {
662
+ if (parentFieldNameKey === "storageResourceTags") {
663
+ // merge storageResourceTags from parent and child, preserving order and uniqueness
664
+ const childStorageTags = existingFieldNamePropInParent.storageResourceTags || [];
665
+ const parentStorageTags = parentFieldNameValue || [];
666
+ existingFieldNamePropInParent.storageResourceTags = [...new Set([...childStorageTags, ...parentStorageTags])];
667
+ } else {
668
+ existingFieldNamePropInParent[parentFieldNameKey] = parentFieldNameValue;
669
+ }
670
+ }
671
+ } else {
672
+ // Add parent fieldName to child if it doesn't exist
673
+ mergeObjSchema.fieldNames[parentFieldName] = parentFieldNameProp;
674
+ }
675
+ }
676
+
677
+
678
+ // Merge addOnDataStructure from parent to child objectSchema
679
+ if (parentObjSchema.hasOwnProperty("addOnDataStructure") && parentObjSchema.addOnDataStructure?.length) {
680
+ for (let parentAddOnData of parentObjSchema.addOnDataStructure) {
681
+ // Merge versionedData from parent to child objectSchema
682
+ if (parentAddOnData.type === "versionedData") {
683
+
684
+ // check duplicate versionedDataLabel
685
+ let duplicateVersionedData = mergeObjSchema.addOnDataStructure?.find(
686
+ (childAddOnData) =>
687
+ childAddOnData.type === "versionedData" &&
688
+ childAddOnData.versionedDataLabel === parentAddOnData.versionedDataLabel
689
+ );
690
+
691
+ if (duplicateVersionedData) {
692
+ return [null, [`objectSchema:${mergeObjSchema.objectType} invalid. Found duplicate versionedDataLabel ${parentAddOnData.versionedDataLabel} between parent and child objectSchema`]];
693
+ }
694
+
695
+ // Add parent versionedData to child if not exists
696
+ if (!mergeObjSchema.addOnDataStructure) {
697
+ mergeObjSchema.addOnDataStructure = [];
698
+ }
699
+ mergeObjSchema.addOnDataStructure.push(lodash.cloneDeep(parentAddOnData));
700
+ }
701
+ }
702
+ }
703
+
704
+ // after merge versionedData will validate duplicate fieldName bwt main fieldNames and versionedData fieldNames
705
+ let fieldNameList = new Set(Object.keys(mergeObjSchema.fieldNames));
706
+ if (mergeObjSchema.addOnDataStructure?.length) {
707
+ for (let addOnData of mergeObjSchema.addOnDataStructure) {
708
+ if (addOnData.type === "versionedData") {
709
+ for (let fieldName in addOnData.fieldNames) {
710
+ if (fieldNameList.has(fieldName)) {
711
+ return [null, [`objectSchema:${mergeObjSchema.objectType} invalid while merge extend. Found duplicate fieldName ${fieldName} between main fieldNames and versionedData fieldNames`]];
712
+ } else {
713
+ fieldNameList.add(fieldName);
714
+ }
715
+ }
716
+ }
717
+ }
718
+ }
719
+
720
+ // add storageResources from parent if not exists in child objectSchema
721
+ let currentStorageResourcesList = Object.keys(mergeObjSchema.storageResources);
722
+ for (let [parentStorageResourceName, parentStorageResourceProp] of Object.entries(parentObjSchema.storageResources)) {
723
+ if (!currentStorageResourcesList.includes(parentStorageResourceName)) {
724
+ Object.assign(mergeObjSchema.storageResources, { [parentStorageResourceName]: parentStorageResourceProp });
725
+ } else {
726
+ // if found storageResource in both parent and child will validate that storageResource will have same data
727
+
728
+ if (hash(mergeObjSchema.storageResources[parentStorageResourceName]) !== hash(parentStorageResourceProp)) {
729
+ return [null, [`objectSchema:${mergeObjSchema.objectType} storageResources.${parentStorageResourceName} not match with parent objectSchema`]];
730
+ }
731
+ }
732
+ }
733
+
734
+ }
735
+ }
736
+ }
737
+
738
+ // validate duplicate storageResources value data and dynamoDB tableName
739
+ const storageHashList = new Set();
740
+ const dynamoDBHashList = new Set();
741
+ const mergedErrors = [];
742
+
743
+ if (mergeObjSchema?.storageResources) {
744
+ for (const storageResource of Object.values(mergeObjSchema.storageResources)) {
745
+ const storageResourceHash = hash(storageResource);
746
+
747
+ if (storageHashList.has(storageResourceHash)) {
748
+ mergedErrors.push(`Should not have duplicate storageResources value data`);
749
+ break;
750
+ } else {
751
+ storageHashList.add(storageResourceHash);
752
+ }
753
+
754
+ if (storageResource.storageType === schemaConsts.STORAGE_TYPES.dynamoDB) {
755
+ const dynamoDBTableNameAndServiceTag = `${storageResource.tableName}_${storageResource.serviceTag}`
756
+ if (dynamoDBHashList.has(dynamoDBTableNameAndServiceTag)) {
757
+ mergedErrors.push(`Should not have duplicate tableName inside storageResources`);
758
+ break;
759
+ } else {
760
+ dynamoDBHashList.add(dynamoDBTableNameAndServiceTag);
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ if (mergedErrors.length) {
767
+ return [null, mergedErrors];
768
+ } else {
769
+ // merge all schema
770
+ return [mergeObjSchema, []];
771
+ }
772
+
773
+ } else {
774
+ return [null, []];
775
+ }
776
+ }
777
+ }
778
+
779
+ try {
780
+
781
+ const [schema, errorsFound] = await getObjSchemaWithHierarchyMain(objType);
782
+
783
+ if (updateState) {
784
+ if (errorsFound && errorsFound.length) {
785
+ setErrorsFound(errorsFound);
786
+ setResult(null);
787
+ } else {
788
+ setResult(schema);
789
+ setErrorsFound([]);
790
+ }
791
+ }
792
+
793
+ return [schema, errorsFound];
794
+ } catch (err) {
795
+ const msg = err?.message || String(err);
796
+ if (updateState) {
797
+ setErrorsFound([msg]);
798
+ setResult(null);
799
+ }
800
+ return [null, [msg]];
801
+ }
802
+ }, [getObjectSchemaWithAllHierarchyFn]);
803
+
804
+
805
+
806
+ // run can be used two ways:
807
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
808
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
809
+ const run = useCallback((objType) => {
810
+ if (typeof objType === "undefined") {
811
+ return {
812
+ initiate: (objType) => execute(objType, { updateState: false })
813
+ };
814
+ }
815
+ // direct call: update state
816
+ return execute(objType, { updateState: true });
817
+ }, [execute]);
818
+
819
+ return [result, run, { errorsFound }];
820
+ }
821
+
822
+ export function getObjSchemaCombineFieldNames() {
823
+
824
+ const [, getObjSchemaWithHierarchyFn] = getObjSchemaWithHierarchy();
825
+
826
+ const [result, setResult] = useState(null);
827
+ const [errorsFound, setErrorsFound] = useState([]);
828
+
829
+
830
+ // core executor that does the actual work and optionally updates hook state
831
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
832
+
833
+ async function getObjSchemaCombineFieldNamesMain(objType) {
834
+ const [objSchema, objSchemaErrors] = await getObjSchemaWithHierarchyFn().initiate(objType);
835
+
836
+ if (objSchemaErrors.length) {
837
+ return [null, objSchemaErrors];
838
+ }
839
+
840
+ const reformatedObjSchema = generateObjectSchemaForCombineFieldNames(objSchema)
841
+ return [reformatedObjSchema, []];
842
+ }
843
+
844
+ try {
845
+
846
+ const [schema, errorsFound] = await getObjSchemaCombineFieldNamesMain(objType);
847
+
848
+ if (updateState) {
849
+ if (errorsFound && errorsFound.length) {
850
+ setErrorsFound(errorsFound);
851
+ setResult(null);
852
+ } else {
853
+ setResult(schema);
854
+ setErrorsFound([]);
855
+ }
856
+ }
857
+
858
+ return [schema, errorsFound];
859
+ } catch (err) {
860
+ const msg = err?.message || String(err);
861
+ if (updateState) {
862
+ setErrorsFound([msg]);
863
+ setResult(null);
864
+ }
865
+ return [null, [msg]];
866
+ }
867
+ }, [getObjSchemaWithHierarchyFn]);
868
+
869
+
870
+
871
+ // run can be used two ways:
872
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
873
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
874
+ const run = useCallback((objType) => {
875
+ if (typeof objType === "undefined") {
876
+ return {
877
+ initiate: (objType) => execute(objType, { updateState: false })
878
+ };
879
+ }
880
+ // direct call: update state
881
+ return execute(objType, { updateState: true });
882
+ }, [execute]);
883
+
884
+ return [result, run, { errorsFound }];
885
+ }
886
+
887
+
888
+
889
+ export function getLinkConfig() {
890
+ const { getLinkConfig: getLinkConfigEndpoint } = cachedEndpoints;
891
+
892
+ if (!getLinkConfigEndpoint) {
893
+ return [null, () => { }, { errorsFound: ["need to initialize initGetSchemaTools before use"] }];
894
+ }
895
+
896
+ const [, getSchemaAllHierarchyFn] = getObjectSchemaWithAllHierarchy();
897
+
898
+ const [result, setResult] = useState(null);
899
+ const [errorsFound, setErrorsFound] = useState([]);
900
+
901
+
902
+ // core executor that does the actual work and optionally updates hook state
903
+ const execute = useCallback(async (firstObjType, secondObjType, relType, direction, settings = { getAllLinks: false, getRawLink: false, getOriginalLink: true }, { updateState = true } = {}) => {
904
+
905
+ async function getLinkConfigMain(firstObjType, secondObjType, relType, direction, settings = { getAllLinks: false, getRawLink: false, getOriginalLink: true }) {
906
+ let errorsFound = [];
907
+ if (!direction || typeof direction !== "string" || (direction !== "from" && direction !== "to")) {
908
+ return [null, ['getLinkConfigByLinkTypeId invalid direction']];
909
+ }
910
+
911
+ const firstObjTypeValidation = validateObjType(firstObjType);
912
+ if (firstObjTypeValidation.errorsFound.length > 0) {
913
+ errorsFound.push(...firstObjTypeValidation.errorsFound);
914
+ }
915
+
916
+ const secondObjTypeValidation = validateObjType(secondObjType);
917
+ if (secondObjTypeValidation.errorsFound.length > 0) {
918
+ errorsFound.push(...secondObjTypeValidation.errorsFound);
919
+ }
920
+
921
+ const relTypeValidation = validateRelType(relType);
922
+ if (relTypeValidation.errorsFound.length > 0) {
923
+ errorsFound.push(...relTypeValidation.errorsFound);
924
+ }
925
+
926
+ if (errorsFound.length) {
927
+ return [null, errorsFound];
928
+ }
929
+
930
+
931
+ let linkConfigs = [];
932
+
933
+ // contain hierarchy of each objType
934
+ let firstObjTypeConcatHierarchy = [];
935
+ let secondObjTypeConcatHierarchy = [];
936
+
937
+ if (settings.getOriginalLink) {
938
+
939
+ const linkConfig = await getLinkConfigEndpoint({ firstObjType, secondObjType, relType, direction });
940
+ if (linkConfig) {
941
+ return [linkConfig, []];
942
+ } else {
943
+ return [null, []];
944
+ }
945
+
946
+ } else {
947
+ const [firstObjSchemaHie, secondObjSchemaHie] = await Promise.all([
948
+ getSchemaAllHierarchyFn().initiate(firstObjType),
949
+ getSchemaAllHierarchyFn().initiate(secondObjType)
950
+ ]);
951
+
952
+ if (!firstObjSchemaHie[0] || !secondObjSchemaHie[0]
953
+ || (typeof (firstObjSchemaHie[0]) !== 'object') || (typeof (secondObjSchemaHie[0]) !== 'object')
954
+ || !Object.keys(firstObjSchemaHie[0]).length || !Object.keys(secondObjSchemaHie[0]).length
955
+ ) {
956
+
957
+ return [null, ['getLinkConfig not found objSchema of some objType']];
958
+ }
959
+
960
+
961
+ firstObjTypeConcatHierarchy = Object.keys(firstObjSchemaHie[0]);
962
+ secondObjTypeConcatHierarchy = Object.keys(secondObjSchemaHie[0]);
963
+
964
+ const firstObjtTypeHie = firstObjTypeConcatHierarchy.map(objTypeConcat => {
965
+ const [firstServiceTag, firstObjectType] = explodedObjTypeConcat(objTypeConcat);
966
+ return { serviceTag: firstServiceTag, objectType: firstObjectType };
967
+ });
968
+
969
+ const secondObjTypeHie = secondObjTypeConcatHierarchy.map(objTypeConcat => {
970
+ const [firstServiceTag, firstObjectType] = explodedObjTypeConcat(objTypeConcat);
971
+ return { serviceTag: firstServiceTag, objectType: firstObjectType };
972
+ });
973
+
974
+
975
+ const linkConfigPromises = [];
976
+ for (const firstObject of firstObjtTypeHie) {
977
+ for (const secondObject of secondObjTypeHie) {
978
+ linkConfigPromises.push(getLinkConfigEndpoint({ firstObjType: firstObject, secondObjType: secondObject, relType, direction }));
979
+ }
980
+ }
981
+
982
+ const linkConfigPromiseResult = await Promise.all(linkConfigPromises);
983
+ linkConfigs = linkConfigPromiseResult.filter(Boolean);
984
+ }
985
+
986
+ let overWriteBaseObjTypeLinkConfigs = [];
987
+ // if getRawLink === true then will return raw link if false will refactor objType of link
988
+ if (settings.getRawLink === false) {
989
+
990
+ for (const linkConfig of linkConfigs) {
991
+ const useLinkConfig = lodash.cloneDeep(linkConfig);
992
+
993
+ const [fromObjTypeConcat] = createObjTypeConcat(linkConfig.from.objType);
994
+ const [toObjTypeConcat] = createObjTypeConcat(linkConfig.to.objType);
995
+
996
+ if (direction === 'from') {
997
+ // if direction === from compare with first - from, second - to
998
+ if (firstObjTypeConcatHierarchy.includes(fromObjTypeConcat) && secondObjTypeConcatHierarchy.includes(toObjTypeConcat)) {
999
+ useLinkConfig.from.objType = firstObjType;
1000
+ useLinkConfig.to.objType = secondObjType;
1001
+ }
1002
+ } else {
1003
+ // if direction === to compare with first - to, second - from
1004
+ if (firstObjTypeConcatHierarchy.includes(toObjTypeConcat) && secondObjTypeConcatHierarchy.includes(fromObjTypeConcat)) {
1005
+ useLinkConfig.from.objType = secondObjType;
1006
+ useLinkConfig.to.objType = firstObjType;
1007
+ }
1008
+ }
1009
+ overWriteBaseObjTypeLinkConfigs.push(useLinkConfig);
1010
+ }
1011
+ linkConfigs = overWriteBaseObjTypeLinkConfigs;
1012
+ }
1013
+
1014
+ if (settings.getAllLinks) {
1015
+ if (linkConfigs.length) {
1016
+ return [linkConfigs, errorsFound];
1017
+ } else {
1018
+ return [null, errorsFound];
1019
+ }
1020
+ } else {
1021
+ if (linkConfigs.length) {
1022
+ return [linkConfigs[0], errorsFound];
1023
+ } else {
1024
+ return [null, errorsFound];
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ try {
1030
+ const [schema, errorsFound] = await getLinkConfigMain(firstObjType, secondObjType, relType, direction, settings);
1031
+ if (updateState) {
1032
+ if (errorsFound && errorsFound.length) {
1033
+ setErrorsFound(errorsFound);
1034
+ setResult(null);
1035
+ } else {
1036
+ setResult(schema);
1037
+ setErrorsFound([]);
1038
+ }
1039
+ }
1040
+
1041
+ return [schema, errorsFound];
1042
+ } catch (err) {
1043
+ const msg = err?.message || String(err);
1044
+ if (updateState) {
1045
+ setErrorsFound([msg]);
1046
+ setResult(null);
1047
+ }
1048
+ return [null, [msg]];
1049
+ }
1050
+ }, [cachedEndpoints]);
1051
+
1052
+
1053
+
1054
+ // run can be used two ways:
1055
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
1056
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
1057
+ const run = useCallback((...params) => {
1058
+ // when called with no args return the initiate helper
1059
+ if (params.length === 0) {
1060
+ return {
1061
+ initiate: (...initParams) => execute(...initParams, { updateState: false })
1062
+ };
1063
+ }
1064
+ // direct call: update state
1065
+ return execute(...params, { updateState: true });
1066
+ }, [execute]);
1067
+
1068
+ return [result, run, { errorsFound }];
1069
+ }
1070
+
1071
+
1072
+ export function getObjectLinksWithRequestProperties() {
1073
+
1074
+ const [, getObjectLinksFnRaw] = getObjectLinks();
1075
+ const [, getLinkConfigFnRaw] = getLinkConfig();
1076
+
1077
+ const getObjectLinksFn = useCallback(() => getObjectLinksFnRaw(), [getObjectLinksFnRaw]);
1078
+ const getLinkConfigFn = useCallback(() => getLinkConfigFnRaw(), [getLinkConfigFnRaw]);
1079
+
1080
+
1081
+ const [result, setResult] = useState(null);
1082
+ const [errorsFound, setErrorsFound] = useState([]);
1083
+
1084
+
1085
+ // core executor that does the actual work and optionally updates hook state
1086
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
1087
+
1088
+ async function getObjectLinksWithRequestPropertiesMain(objType) {
1089
+
1090
+ // validate objType (some validators throw, some return errors)
1091
+ try {
1092
+ validateObjType(objType);
1093
+ } catch (err) {
1094
+ return [null, [err.message]];
1095
+ }
1096
+ let returnLinks = [];
1097
+ let mainErrorsFound = [];
1098
+
1099
+ const mainObjTypeConcat = createObjTypeConcat(objType);
1100
+
1101
+ const [objectLinks, getSchemaErrors] = await getObjectLinksFn().initiate(objType);
1102
+
1103
+ if (getSchemaErrors?.length) {
1104
+ return [[], getSchemaErrors];
1105
+ }
1106
+
1107
+ if (objectLinks?.length) {
1108
+ await Promise.all(objectLinks.map(async (objectLink) => {
1109
+ let newLink = { ...objectLink };
1110
+
1111
+
1112
+ const [objectLinkConfig] = await getLinkConfigFn().initiate(
1113
+ objectLink.base.objType,
1114
+ objectLink.other.objType,
1115
+ objectLink.relType,
1116
+ objectLink.base.direction
1117
+ );
1118
+
1119
+ if (objectLinkConfig && objectLinkConfig.requestProperties) {
1120
+ Object.assign(newLink, { requestProperties: objectLinkConfig.requestProperties });
1121
+ }
1122
+
1123
+ returnLinks.push(newLink);
1124
+ }));
1125
+ }
1126
+
1127
+
1128
+
1129
+ return [{ [mainObjTypeConcat]: returnLinks }, mainErrorsFound];
1130
+ }
1131
+
1132
+ try {
1133
+
1134
+ const [schema, errorsFound] = await getObjectLinksWithRequestPropertiesMain(objType);
1135
+
1136
+ if (updateState) {
1137
+ if (errorsFound && errorsFound.length) {
1138
+ setErrorsFound(errorsFound);
1139
+ setResult(null);
1140
+ } else {
1141
+ setResult(schema);
1142
+ setErrorsFound([]);
1143
+ }
1144
+ }
1145
+
1146
+ return [schema, errorsFound];
1147
+ } catch (err) {
1148
+ const msg = err?.message || String(err);
1149
+ if (updateState) {
1150
+ setErrorsFound([msg]);
1151
+ setResult(null);
1152
+ }
1153
+ return [null, [msg]];
1154
+ }
1155
+ }, [getLinkConfigFnRaw, getObjectLinksFnRaw]);
1156
+
1157
+
1158
+ // run can be used two ways:
1159
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
1160
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
1161
+ const run = useCallback((objType) => {
1162
+ if (typeof objType === "undefined") {
1163
+ return {
1164
+ initiate: (objType) => execute(objType, { updateState: false })
1165
+ };
1166
+ }
1167
+ // direct call: update state
1168
+ return execute(objType, { updateState: true });
1169
+ }, [execute]);
1170
+
1171
+ return [result, run, { errorsFound }];
1172
+ }
1173
+
1174
+ export function collectObjectLinksWithRequestProperties() {
1175
+
1176
+ const [, getObjectLinksWithRequestPropertiesFn] = getObjectLinksWithRequestProperties();
1177
+
1178
+ const [result, setResult] = useState(null);
1179
+ const [errorsFound, setErrorsFound] = useState([]);
1180
+
1181
+
1182
+
1183
+ // ref to always read latest result inside async functions (avoids stale closures)
1184
+ const resultRef = useRef(result);
1185
+ useEffect(() => { resultRef.current = result; }, [result]);
1186
+
1187
+ // core executor that does the actual work and optionally updates hook state
1188
+ const execute = useCallback(async (objType, { updateState = true } = {}) => {
1189
+
1190
+ async function collectObjectLinksWithRequestPropertiesMain(objType) {
1191
+
1192
+ // validate objType (some validators throw, some return errors)
1193
+ try {
1194
+ validateObjType(objType);
1195
+ } catch (err) {
1196
+ return [null, [err.message]];
1197
+ }
1198
+
1199
+ // compute mainObjTypeConcat and validate
1200
+ const [mainObjTypeConcat, mainObjTypeErrors] = createObjTypeConcat(objType);
1201
+ if (mainObjTypeErrors && mainObjTypeErrors.length) {
1202
+ return [null, mainObjTypeErrors];
1203
+ }
1204
+
1205
+ // short-circuit: if updateState and we already have this objType in result, return cached value
1206
+ if (updateState && resultRef.current && resultRef.current.hasOwnProperty(mainObjTypeConcat)) {
1207
+ return [{ [mainObjTypeConcat]: resultRef.current[mainObjTypeConcat] }, []];
1208
+ }
1209
+
1210
+
1211
+
1212
+ const [objectLinks, getSchemaErrors] = await getObjectLinksWithRequestPropertiesFn().initiate(objType);
1213
+
1214
+
1215
+ return [objectLinks, getSchemaErrors];
1216
+ }
1217
+
1218
+ try {
1219
+
1220
+
1221
+ const [schema, errorsFound] = await collectObjectLinksWithRequestPropertiesMain(objType);
1222
+
1223
+ if (updateState) {
1224
+ if (errorsFound && errorsFound.length) {
1225
+ setErrorsFound(errorsFound);
1226
+ setResult(null);
1227
+ } else {
1228
+ const [currentObjTypeConcat] = createObjTypeConcat(objType);
1229
+
1230
+ setResult(prev => {
1231
+ // nothing to add
1232
+ if (!schema || !Object.keys(schema).length) return prev;
1233
+
1234
+ // first-time fill
1235
+ if (!prev) return schema;
1236
+
1237
+ // already have this objType => no update
1238
+ if (prev.hasOwnProperty(currentObjTypeConcat)) return prev;
1239
+
1240
+ // merge but return a NEW object to trigger React update (do not mutate prev)
1241
+ return { ...prev, ...schema };
1242
+ });
1243
+
1244
+ setErrorsFound([]);
1245
+ }
1246
+ }
1247
+
1248
+ return [schema, errorsFound];
1249
+ } catch (err) {
1250
+ const msg = err?.message || String(err);
1251
+ if (updateState) {
1252
+ setErrorsFound([msg]);
1253
+ setResult(null);
1254
+ }
1255
+ return [null, [msg]];
1256
+ }
1257
+ }, [getObjectLinksWithRequestPropertiesFn]);
1258
+
1259
+
1260
+ // run can be used two ways:
1261
+ // 1) run(objType) -> updates hook state and returns Promise<[collected, errors]>
1262
+ // 2) run().initiate(objType) -> does NOT touch hook state, returns Promise<[collected, errors]>
1263
+ const run = useCallback((objType) => {
1264
+ if (typeof objType === "undefined") {
1265
+ return {
1266
+ initiate: (objType) => execute(objType, { updateState: false }),
1267
+ clear: () => { setResult(null); setErrorsFound([]); }
1268
+ };
1269
+ }
1270
+ // direct call: update state
1271
+ return execute(objType, { updateState: true });
1272
+ }, [execute]);
1273
+
1274
+ return [result, run, { errorsFound }];
1275
+ }
1276
+
1277
+
1278
+
1279
+
1280
+ /**
1281
+ * TODO: need to use function inside reformatObjectSchema lib instead. create in this cause need to fix bug first
1282
+ * create object schema that already combine fieldNames between main objectSchema and addOnDataStructure.type=versionedData
1283
+ *
1284
+ * @param {Object} objSchema
1285
+ * @returns {Object} - objectSchema that has property for case update
1286
+ */
1287
+ function generateObjectSchemaForCombineFieldNames(useObjectSchema) {
1288
+
1289
+ // generate data for case update
1290
+ let initReturnObj = {
1291
+ objectType: useObjectSchema.objectType,
1292
+ fieldNames: { ...useObjectSchema.fieldNames },
1293
+ canDelete: useObjectSchema.canDelete,
1294
+ storageResources: { ...useObjectSchema.storageResources }
1295
+ };
1296
+
1297
+ if (useObjectSchema.extendObjType) {
1298
+ Object.assign(initReturnObj, { extendObjType: useObjectSchema.extendObjType });
1299
+ }
1300
+
1301
+ if (useObjectSchema.identifiers) {
1302
+ Object.assign(initReturnObj, { identifiers: useObjectSchema.identifiers });
1303
+ Object.assign(
1304
+ initReturnObj,
1305
+ { identifierFieldNames: reformatObjectSchema.getUsedFieldNamesOfIdentifiers(useObjectSchema.identifiers) }
1306
+ );
1307
+ }
1308
+
1309
+ // add fieldNames from versionedData
1310
+ if (useObjectSchema.addOnDataStructure?.length) {
1311
+ let versionedDataLabels = [];
1312
+ // versionedDataField_ceo_versionedDataId
1313
+ for (let addOn of useObjectSchema.addOnDataStructure) {
1314
+ if (addOn.type === "versionedData") {
1315
+ // add default fieldNames of versionedData to main fieldNames
1316
+ Object.assign(
1317
+ initReturnObj.fieldNames,
1318
+ reformatObjectSchema.defaultVersionedDataFieldNames(addOn.versionedDataLabel, addOn.storageResourceTag)
1319
+ );
1320
+
1321
+ // add each versionedData fieldName to main fieldNames
1322
+ for (let [versionedFieldName, fieldNameData] of Object.entries(addOn.fieldNames)) {
1323
+ Object.assign(initReturnObj.fieldNames, {
1324
+ [versionedFieldName]: {
1325
+ ...fieldNameData,
1326
+ versionedDataLabel: addOn.versionedDataLabel,
1327
+ storageResourceTags: [addOn.storageResourceTag]
1328
+ }
1329
+ });
1330
+ }
1331
+ versionedDataLabels.push(addOn.versionedDataLabel);
1332
+ }
1333
+ }
1334
+
1335
+ if (versionedDataLabels.length) {
1336
+ initReturnObj.versionedDataLabels = versionedDataLabels;
1337
+ }
1338
+ }
1339
+
1340
+ return initReturnObj;
1341
+ }