@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.
- package/index.js +14 -0
- package/package.json +14 -17
- package/src/endpoint.js +121 -0
- package/src/getObjectSchema.js +1341 -0
|
@@ -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
|
+
}
|