@izara_project/izara-core-library-service-schemas 1.0.106 → 1.0.108
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 +4 -0
- package/package.json +2 -1
- package/src/GetObjectSchema.js +49 -0
- package/src/IdentifiersObject.js +165 -2
- package/src/Utils.js +73 -1
- package/src/ValidatorSchema.js +120 -20
- package/src/libs/CheckPermission.js +122 -0
- package/src/libs/DataDetailsLib.js +642 -0
package/index.js
CHANGED
|
@@ -25,6 +25,8 @@ import uploadUseCase from './src/libs/UploadUseCase.js';
|
|
|
25
25
|
import relSchemaLib from './src/libs/RelSchemaLib.js';
|
|
26
26
|
import s3Utils from './src/libs/s3Utils.js';
|
|
27
27
|
import createNodeLib from './src/libs/CreateNodeLib.js';
|
|
28
|
+
import dataDetailsLib from './src/libs/DataDetailsLib.js';
|
|
29
|
+
import checkPermission from './src/libs/CheckPermission.js';
|
|
28
30
|
|
|
29
31
|
import consts from './src/Consts.js';
|
|
30
32
|
import getObjectSchema from './src/GetObjectSchema.js';
|
|
@@ -43,6 +45,8 @@ export {
|
|
|
43
45
|
relSchemaLib,
|
|
44
46
|
s3Utils,
|
|
45
47
|
createNodeLib,
|
|
48
|
+
dataDetailsLib,
|
|
49
|
+
checkPermission,
|
|
46
50
|
|
|
47
51
|
// ./src/
|
|
48
52
|
consts,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izara_project/izara-core-library-service-schemas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.108",
|
|
4
4
|
"description": "Schemas for the service and objects it controls",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@izara_project/izara-core-library-lambda": "^1.0.6",
|
|
31
31
|
"@izara_project/izara-core-library-logger": "^1.0.8",
|
|
32
32
|
"@izara_project/izara-core-library-s3": "^1.0.5",
|
|
33
|
+
"@izara_project/izara-core-library-sqs": "^1.0.5",
|
|
33
34
|
"@izara_project/izara-shared-core": "^1.0.8",
|
|
34
35
|
"@izara_project/izara-shared-service-schemas": "^1.0.35",
|
|
35
36
|
"glob": "^13.0.0",
|
package/src/GetObjectSchema.js
CHANGED
|
@@ -473,6 +473,24 @@ async function getObjSchemaS3WithHierarchy(_izContext, objType, bucketName = pro
|
|
|
473
473
|
});
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Retrieves specific object schema from S3, with hierarchical data
|
|
478
|
+
* use this inside share function of frtontend
|
|
479
|
+
*
|
|
480
|
+
* @see {@link getObjSchemaS3}
|
|
481
|
+
* @see {@link getObjSchemaS3WithHierarchy}
|
|
482
|
+
*
|
|
483
|
+
*/
|
|
484
|
+
async function getObjSchemaS3WithHierarchyShared(objType, bucketName = process.env.iz_serviceSchemaBucketName) {
|
|
485
|
+
return await getObjectSchema.getObjSchemaWithHierarchy(s3Utils.getSchemaByNameWithCache, objType, bucketName).then(res => {
|
|
486
|
+
if (res.errorsFound.length && res.errorsFound.length > 0) {
|
|
487
|
+
throw new NoRetryError(res.errorsFound.join(","))
|
|
488
|
+
} else {
|
|
489
|
+
return res.result
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
476
494
|
/**
|
|
477
495
|
* Retrieves specific object schema from S3, without hierarchical data
|
|
478
496
|
*
|
|
@@ -757,6 +775,35 @@ async function getObjectRelationship(
|
|
|
757
775
|
|
|
758
776
|
|
|
759
777
|
|
|
778
|
+
/**
|
|
779
|
+
*
|
|
780
|
+
* get relationshipSchema of objType depend on specific relationshipTags
|
|
781
|
+
*
|
|
782
|
+
* @see {@link getObjectRelationship}
|
|
783
|
+
* @param {Object} objType
|
|
784
|
+
* @param {String} objType.objectType
|
|
785
|
+
* @param {String} objType.serviceTag
|
|
786
|
+
* @param {String[]} [specificRelTags] - optional array of relationshipTags that need to find
|
|
787
|
+
* @returns {Promise<Object>} - reference relationship data of objType
|
|
788
|
+
*/
|
|
789
|
+
async function getObjectLinksShared(
|
|
790
|
+
objType,
|
|
791
|
+
specificRelTags,
|
|
792
|
+
overWriteBaseObjType,
|
|
793
|
+
) {
|
|
794
|
+
|
|
795
|
+
return await getObjectSchema.getObjectRelationship(s3Utils.getSchemaByNameWithCache, objType, specificRelTags, overWriteBaseObjType, process.env.iz_serviceSchemaBucketName).then(res => {
|
|
796
|
+
if (res.errorsFound.length && res.errorsFound.length > 0) {
|
|
797
|
+
throw new NoRetryError(res.errorsFound.join(","))
|
|
798
|
+
} else {
|
|
799
|
+
return res.result
|
|
800
|
+
}
|
|
801
|
+
})
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
760
807
|
/**
|
|
761
808
|
* Caches the getObjectRelationship function
|
|
762
809
|
*
|
|
@@ -1138,6 +1185,7 @@ export default {
|
|
|
1138
1185
|
getObjSchemaS3,
|
|
1139
1186
|
getObjSchemaS3WithCache,
|
|
1140
1187
|
getObjSchemaS3WithHierarchy,
|
|
1188
|
+
getObjSchemaS3WithHierarchyShared,
|
|
1141
1189
|
getObjSchemaS3WithoutHierarchy,
|
|
1142
1190
|
|
|
1143
1191
|
getObjectSchemaCombineFieldNames,
|
|
@@ -1160,6 +1208,7 @@ export default {
|
|
|
1160
1208
|
|
|
1161
1209
|
getObjectRelationship,
|
|
1162
1210
|
getObjectRelationshipWithCache,
|
|
1211
|
+
getObjectLinksShared, // will rename getObjectRelationship to getObjectLinks further
|
|
1163
1212
|
|
|
1164
1213
|
getRequiredOnCreateLinks,
|
|
1165
1214
|
getRequiredOnCreateLinksWithCache,
|
package/src/IdentifiersObject.js
CHANGED
|
@@ -17,7 +17,7 @@ along with this program.If not, see < http://www.gnu.org/licenses/>.
|
|
|
17
17
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
|
-
import { NoRetryError } from '@izara_project/izara-core-library-core';
|
|
20
|
+
import { NoRetryError, inMemoryCacheLib } from '@izara_project/izara-core-library-core';
|
|
21
21
|
import { validateObjType } from '@izara_project/izara-shared-service-schemas';
|
|
22
22
|
|
|
23
23
|
import deliminatorTree from './libs/DeliminatorTree.js';
|
|
@@ -507,6 +507,164 @@ async function explodedNestedIdentifiersConcat(
|
|
|
507
507
|
return concatIdentifier.split(deliminator.repeat(compositeDeliminatorAmount + level));
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
+
/**
|
|
511
|
+
* create dynamoDb identifiers using request param and objectSchema
|
|
512
|
+
*
|
|
513
|
+
* @param {Object} _izContext
|
|
514
|
+
* @param {Object} objectSchema
|
|
515
|
+
* @param {Object} reqIdentifiers - input identifier from calling function
|
|
516
|
+
*/
|
|
517
|
+
async function generateDynamoDbIdentifiers(
|
|
518
|
+
_izContext,
|
|
519
|
+
objectSchema,
|
|
520
|
+
reqIdentifiers,
|
|
521
|
+
bucketName = process.env.iz_serviceSchemaBucketName
|
|
522
|
+
) {
|
|
523
|
+
// start create identifiers for dynamo
|
|
524
|
+
let dynamoIdentifiers = {};
|
|
525
|
+
|
|
526
|
+
for (let identifier of objectSchema.identifiers) {
|
|
527
|
+
if (identifier.name) {
|
|
528
|
+
let deliminator =
|
|
529
|
+
identifier.deliminator || coreConsts.DEFAULT_IDENTIFIER_DELIMINATOR;
|
|
530
|
+
let currentDeliminatorAmount = 0;
|
|
531
|
+
|
|
532
|
+
let concatenateValue = [];
|
|
533
|
+
for (let fieldName of identifier.fieldNames) {
|
|
534
|
+
concatenateValue.push(reqIdentifiers[fieldName]);
|
|
535
|
+
|
|
536
|
+
let deliminatorTreeFieldName =
|
|
537
|
+
await deliminatorTree.generateDeliminatorTreePerFieldName(
|
|
538
|
+
_izContext,
|
|
539
|
+
fieldName,
|
|
540
|
+
objectSchema.fieldNames[fieldName],
|
|
541
|
+
{},
|
|
542
|
+
bucketName
|
|
543
|
+
);
|
|
544
|
+
_izContext.logger.debug(
|
|
545
|
+
'deliminatorTreeFieldName: ',
|
|
546
|
+
deliminatorTreeFieldName
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
if (
|
|
550
|
+
deliminatorTreeFieldName[fieldName]?.deliminatorList?.[deliminator]
|
|
551
|
+
) {
|
|
552
|
+
currentDeliminatorAmount = Math.max(
|
|
553
|
+
currentDeliminatorAmount,
|
|
554
|
+
deliminatorTreeFieldName[fieldName].deliminatorList[deliminator]
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
Object.assign(dynamoIdentifiers, {
|
|
560
|
+
[identifier.name]: concatenateValue.join(
|
|
561
|
+
deliminator.repeat(currentDeliminatorAmount + 1)
|
|
562
|
+
)
|
|
563
|
+
});
|
|
564
|
+
} else {
|
|
565
|
+
Object.assign(dynamoIdentifiers, {
|
|
566
|
+
[identifier.fieldName]: reqIdentifiers[identifier.fieldName]
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
_izContext.logger.debug('dynamoIdentifiers: ', dynamoIdentifiers);
|
|
572
|
+
|
|
573
|
+
return dynamoIdentifiers;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* create dynamoDb identifiers using request param and objectSchema
|
|
578
|
+
*
|
|
579
|
+
* @param {Object} _izContext
|
|
580
|
+
* @param {Object} objectSchema
|
|
581
|
+
* @param {Object} reqIdentifiers - input identifier from calling function
|
|
582
|
+
*/
|
|
583
|
+
const generateDynamoDbIdentifiersWithCache = inMemoryCacheLib(
|
|
584
|
+
generateDynamoDbIdentifiers, // fn
|
|
585
|
+
{
|
|
586
|
+
// setting
|
|
587
|
+
max: 20,
|
|
588
|
+
maxAge: 86400000,
|
|
589
|
+
promise: true,
|
|
590
|
+
profileName: 'generateDynamoDbIdentifiers',
|
|
591
|
+
normalizer: function (args) {
|
|
592
|
+
return hash([args[1], args[2]]);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
*
|
|
600
|
+
* @param {Object} _izContext
|
|
601
|
+
* @param {Object} objectSchema
|
|
602
|
+
* @param {Object} dynamoStorageResource
|
|
603
|
+
* @param {Object} reqIdentifiers
|
|
604
|
+
*/
|
|
605
|
+
async function dynamoDbIdentifiersByStorageResource(
|
|
606
|
+
_izContext,
|
|
607
|
+
objectSchema,
|
|
608
|
+
dynamoStorageResource,
|
|
609
|
+
reqIdentifiers,
|
|
610
|
+
bucketName = process.env.iz_serviceSchemaBucketName
|
|
611
|
+
) {
|
|
612
|
+
let dynamoIdentifiers = await generateDynamoDbIdentifiersWithCache(
|
|
613
|
+
_izContext,
|
|
614
|
+
objectSchema,
|
|
615
|
+
reqIdentifiers,
|
|
616
|
+
bucketName
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
let identifiersByStorageResource = {};
|
|
620
|
+
|
|
621
|
+
if (dynamoStorageResource.groupByPartitionKeyField) {
|
|
622
|
+
let deliminatorTreeIdentifiers =
|
|
623
|
+
await deliminatorTree.generateDeliminatorTreeIdentifier(
|
|
624
|
+
_izContext,
|
|
625
|
+
objectSchema,
|
|
626
|
+
bucketName
|
|
627
|
+
);
|
|
628
|
+
_izContext.logger.debug('deliminatorTree: ', deliminatorTreeIdentifiers);
|
|
629
|
+
|
|
630
|
+
let compositeKeyDeliminator =
|
|
631
|
+
objectSchema.compositeKeyDeliminator ||
|
|
632
|
+
coreConsts.DEFAULT_IDENTIFIER_DELIMINATOR;
|
|
633
|
+
|
|
634
|
+
let currentDeliminatorAmount = 0;
|
|
635
|
+
|
|
636
|
+
for (let currentDeliminator of Object.values(deliminatorTreeIdentifiers)) {
|
|
637
|
+
if (currentDeliminator.deliminator === compositeKeyDeliminator) {
|
|
638
|
+
if (
|
|
639
|
+
currentDeliminatorAmount <
|
|
640
|
+
currentDeliminator.deliminatorList[compositeKeyDeliminator]
|
|
641
|
+
) {
|
|
642
|
+
currentDeliminatorAmount =
|
|
643
|
+
currentDeliminator.deliminatorList[compositeKeyDeliminator];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// sort dynamoDbIdentifiers data by objectSchema
|
|
649
|
+
let sortedIdentifiersValue = [];
|
|
650
|
+
for (let identifier of objectSchema.identifiers) {
|
|
651
|
+
let identifierName = identifier.fieldName || identifier.name;
|
|
652
|
+
sortedIdentifiersValue.push(dynamoIdentifiers[identifierName]);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
let newIdentifierValue = sortedIdentifiersValue.join(
|
|
656
|
+
compositeKeyDeliminator.repeat(currentDeliminatorAmount + 1)
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
identifiersByStorageResource = {
|
|
660
|
+
[dynamoStorageResource.groupByPartitionKeyField]: newIdentifierValue
|
|
661
|
+
};
|
|
662
|
+
} else {
|
|
663
|
+
identifiersByStorageResource = dynamoIdentifiers;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return identifiersByStorageResource;
|
|
667
|
+
}
|
|
510
668
|
|
|
511
669
|
|
|
512
670
|
export default {
|
|
@@ -517,5 +675,10 @@ export default {
|
|
|
517
675
|
identifiersFromIdentifiersConcat, // identifiersObjectFromCompositeIdentifier
|
|
518
676
|
|
|
519
677
|
createNestedIdentifiersConcat, // createConcatIdentifier
|
|
520
|
-
explodedNestedIdentifiersConcat // explodedConcatIdentifier
|
|
678
|
+
explodedNestedIdentifiersConcat, // explodedConcatIdentifier
|
|
679
|
+
|
|
680
|
+
generateDynamoDbIdentifiers,
|
|
681
|
+
generateDynamoDbIdentifiersWithCache,
|
|
682
|
+
|
|
683
|
+
dynamoDbIdentifiersByStorageResource
|
|
521
684
|
}
|
package/src/Utils.js
CHANGED
|
@@ -766,6 +766,75 @@ async function getApiLinksV2(objectTypes) {
|
|
|
766
766
|
}
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
+
|
|
770
|
+
function createFieldForUpdateDynamoDb(
|
|
771
|
+
_izContext,
|
|
772
|
+
objectSchema,
|
|
773
|
+
dynamoDataDetail,
|
|
774
|
+
fields
|
|
775
|
+
) {
|
|
776
|
+
_izContext.logger.debug('createFieldForUpdateDynamoDb', {
|
|
777
|
+
objectSchema,
|
|
778
|
+
dynamoDataDetail,
|
|
779
|
+
fields
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
let fieldsForUpdateDynamo = {};
|
|
783
|
+
|
|
784
|
+
for (let [fieldName, fieldSetting] of Object.entries(
|
|
785
|
+
objectSchema.fieldNames
|
|
786
|
+
)) {
|
|
787
|
+
if (
|
|
788
|
+
fieldSetting.canUpdate == true ||
|
|
789
|
+
!fieldSetting.hasOwnProperty('canUpdate')
|
|
790
|
+
) {
|
|
791
|
+
if (dynamoDataDetail.fieldNames.includes(fieldName)) {
|
|
792
|
+
if (fields[fieldName]) {
|
|
793
|
+
if (!fieldsForUpdateDynamo.hasOwnProperty(fields[fieldName])) {
|
|
794
|
+
_izContext.logger.debug('assign new fieldName', fields[fieldName]);
|
|
795
|
+
Object.assign(fieldsForUpdateDynamo, {
|
|
796
|
+
[fieldName]: fields[fieldName]
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return fieldsForUpdateDynamo;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* @param {object} record - One record from sqs.
|
|
808
|
+
* @param {string} messageFailError - The message error.
|
|
809
|
+
* @param {object} lambdaFunctionName - Queue anme what to send to dlq./name of Lambda being invoked, used to build SQS/DLQ
|
|
810
|
+
*
|
|
811
|
+
*/
|
|
812
|
+
async function messageToDlq(record, messageFailError, queueUrl) {
|
|
813
|
+
let messageBody = record.body;
|
|
814
|
+
|
|
815
|
+
let params = {
|
|
816
|
+
QueueUrl: queueUrl,
|
|
817
|
+
MessageAttributes: {
|
|
818
|
+
messageFailError: {
|
|
819
|
+
DataType: 'String',
|
|
820
|
+
StringValue: messageFailError
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
MessageBody: JSON.stringify(messageBody)
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
record._izContext.logger.debug(
|
|
827
|
+
'messageToDlq, params before sending DLQ',
|
|
828
|
+
params
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
await sqs.sendMessage(record._izContext, params);
|
|
832
|
+
record._izContext.logger.debug(
|
|
833
|
+
'----- messageToDlq sendMessage success -----'
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
|
|
769
838
|
export default {
|
|
770
839
|
createObjType,
|
|
771
840
|
getIdentifierTypeByPriority,
|
|
@@ -792,5 +861,8 @@ export default {
|
|
|
792
861
|
|
|
793
862
|
createLinkTypeId,
|
|
794
863
|
getApiLinksV1,
|
|
795
|
-
getApiLinksV2
|
|
864
|
+
getApiLinksV2,
|
|
865
|
+
|
|
866
|
+
createFieldForUpdateDynamoDb,
|
|
867
|
+
messageToDlq
|
|
796
868
|
}
|
package/src/ValidatorSchema.js
CHANGED
|
@@ -24,31 +24,30 @@ import { objectHash as hash } from '@izara_project/izara-shared-core';
|
|
|
24
24
|
import Logger from '@izara_project/izara-core-library-logger';
|
|
25
25
|
import uploadUseCase from './libs/UploadUseCase.js';
|
|
26
26
|
|
|
27
|
+
import { validateObjType } from '@izara_project/izara-shared-service-schemas';
|
|
28
|
+
import { validator as validateObject } from '@izara_project/izara-core-library-core';
|
|
29
|
+
import sqsSharedLib from '@izara_project/izara-core-library-sqs';
|
|
30
|
+
import explodedReqParams from './libs/ExplodedReqParams.js';
|
|
31
|
+
import getObjectSchema from './GetObjectSchema.js';
|
|
27
32
|
import serviceConfig from './ServiceConfig.js';
|
|
33
|
+
import consts from './Consts.js';
|
|
28
34
|
import utils from './Utils.js';
|
|
29
|
-
import getObjectSchema from './GetObjectSchema.js';
|
|
30
|
-
import consts from './Consts.js'
|
|
31
|
-
import { validateObjType } from '@izara_project/izara-shared-service-schemas';
|
|
32
|
-
|
|
33
|
-
// module.exports.createFieldNameUniqueRequestId = (prefix = 'cache') => {
|
|
34
|
-
// return prefix + "UniqueRequestId";
|
|
35
|
-
// }
|
|
36
35
|
|
|
37
|
-
// module.exports.createFieldNameStatus = (prefix = 'cache') => {
|
|
38
|
-
// return prefix + 'Status';
|
|
39
|
-
// }
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const schemaFunctionPerAction = {
|
|
38
|
+
[consts.ACTIONS.create]: generateValidatorSchemaForCreate,
|
|
39
|
+
[consts.ACTIONS.update]: generateValidatorSchemaForUpdate,
|
|
40
|
+
[consts.ACTIONS.get]: generateValidatorSchemaForIdentifier,
|
|
41
|
+
[consts.ACTIONS.delete]: generateValidatorSchemaForIdentifier
|
|
42
|
+
};
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
const explodeDataPerAction = {
|
|
45
|
+
[consts.ACTIONS.create]: explodedReqParams.explodedDataForCreate,
|
|
46
|
+
[consts.ACTIONS.get]: explodedReqParams.explodedDataForIdentifiers,
|
|
47
|
+
[consts.ACTIONS.update]: explodedReqParams.explodedDataForUpdate,
|
|
48
|
+
[consts.ACTIONS.delete]: explodedReqParams.explodedDataForIdentifiers
|
|
49
|
+
};
|
|
48
50
|
|
|
49
|
-
// module.exports.createFlowParamsFieldName = (prefix = 'cache') => {
|
|
50
|
-
// return prefix + 'FlowParams';
|
|
51
|
-
// }
|
|
52
51
|
|
|
53
52
|
function createSpecificFieldNameForCacheTable(prefix = 'cache', statusType) {
|
|
54
53
|
return {
|
|
@@ -1375,11 +1374,112 @@ async function validateLocalSchema(_izContext, schemasPath, serviceConfigPath) {
|
|
|
1375
1374
|
}
|
|
1376
1375
|
}
|
|
1377
1376
|
|
|
1377
|
+
/**
|
|
1378
|
+
* use to validate record for Lambda handler hdrSqs/hdrDsq
|
|
1379
|
+
* Note!: cannot throw error when use this function outside recordHandler
|
|
1380
|
+
*
|
|
1381
|
+
* @param {object} record - one record from sqs
|
|
1382
|
+
* @param {string} lambdaFunctionName - name of Lambda used to build SQS/DLQ // QueueName
|
|
1383
|
+
* @param {string} objectType - name of ObjectType
|
|
1384
|
+
* @param {string} action - action type of Lambda create | update | get | delete
|
|
1385
|
+
* @param {object} [setting]
|
|
1386
|
+
* @param {string[]} [setting.specificFieldNames] - optional - specific fieldNames use as param in generateValidatorFunction
|
|
1387
|
+
*/
|
|
1388
|
+
async function validateSchemaPerRecord(
|
|
1389
|
+
record,
|
|
1390
|
+
lambdaFunctionName,
|
|
1391
|
+
objType,
|
|
1392
|
+
action,
|
|
1393
|
+
setting = {
|
|
1394
|
+
bucketName: process.env.iz_serviceSchemaBucketName,
|
|
1395
|
+
specificFieldNames: []
|
|
1396
|
+
}
|
|
1397
|
+
) {
|
|
1398
|
+
try {
|
|
1399
|
+
record._izContext.logger.debug(
|
|
1400
|
+
'validateSchemaPerRecord: ',
|
|
1401
|
+
record,
|
|
1402
|
+
lambdaFunctionName,
|
|
1403
|
+
objType,
|
|
1404
|
+
action,
|
|
1405
|
+
setting
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
if (!schemaFunctionPerAction[action] || !explodeDataPerAction[action]) {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
`Not found function for generateValidatorSchema or function for explodeData of action:${action}`
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
const objectSchema = await getObjectSchema.getObjSchemaS3WithHierarchy(
|
|
1415
|
+
record._izContext,
|
|
1416
|
+
objType
|
|
1417
|
+
);
|
|
1418
|
+
|
|
1419
|
+
if (!objectSchema) {
|
|
1420
|
+
throw new Error('not have objectSchema in service');
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
const generateValidatorFunction = schemaFunctionPerAction[action];
|
|
1424
|
+
const explodeDataFunction = explodeDataPerAction[action];
|
|
1425
|
+
|
|
1426
|
+
const generatedSchema = await generateValidatorFunction(
|
|
1427
|
+
record._izContext,
|
|
1428
|
+
objType,
|
|
1429
|
+
setting
|
|
1430
|
+
);
|
|
1431
|
+
record._izContext.logger.debug(
|
|
1432
|
+
`generatedSchema in validateSchemaPerRecord : `,
|
|
1433
|
+
generatedSchema
|
|
1434
|
+
);
|
|
1435
|
+
|
|
1436
|
+
const explodedDataRequestParams = await explodeDataFunction(
|
|
1437
|
+
record._izContext,
|
|
1438
|
+
record.body.Message,
|
|
1439
|
+
objectSchema,
|
|
1440
|
+
setting
|
|
1441
|
+
);
|
|
1442
|
+
|
|
1443
|
+
record._izContext.logger.debug(
|
|
1444
|
+
'explodedDataRequestParams in validateSchemaPerRecord : ',
|
|
1445
|
+
JSON.stringify(explodedDataRequestParams)
|
|
1446
|
+
);
|
|
1447
|
+
|
|
1448
|
+
let validateStatus = validateObject(
|
|
1449
|
+
generatedSchema,
|
|
1450
|
+
explodedDataRequestParams
|
|
1451
|
+
);
|
|
1452
|
+
record._izContext.logger.debug('validateStatus : ', validateStatus);
|
|
1453
|
+
|
|
1454
|
+
// if not pass validate will sent message to dlq and throw NoRetryError
|
|
1455
|
+
if (!validateStatus.pass) {
|
|
1456
|
+
await utils.messageToDlq(
|
|
1457
|
+
record,
|
|
1458
|
+
`Invalid: ${validateStatus.error}`,
|
|
1459
|
+
await sqsSharedLib.sqsQueueUrlDLQ(record._izContext, lambdaFunctionName)
|
|
1460
|
+
);
|
|
1461
|
+
record._izError = new Error(validateStatus.error);
|
|
1462
|
+
}
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
record._izContext.logger.debug('validate PerSchema Record error', error);
|
|
1465
|
+
await utils.messageToDlq(
|
|
1466
|
+
record,
|
|
1467
|
+
error.message,
|
|
1468
|
+
await sqsSharedLib.sqsQueueUrlDLQ(record._izContext, lambdaFunctionName)
|
|
1469
|
+
);
|
|
1470
|
+
record._izError = new Error(error);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
|
|
1475
|
+
|
|
1378
1476
|
export default {
|
|
1379
1477
|
generateValidatorSchemaForCreate,
|
|
1380
1478
|
generateValidatorSchemaForUpdate,
|
|
1381
1479
|
generateValidatorSchemaForIdentifier,
|
|
1382
1480
|
|
|
1383
1481
|
generateValidatorSchemaForExplodedData,
|
|
1384
|
-
validateLocalSchema
|
|
1482
|
+
validateLocalSchema,
|
|
1483
|
+
|
|
1484
|
+
validateSchemaPerRecord,
|
|
1385
1485
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2021 Sven Mason <http://izara.io>
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
|
5
|
+
it under the terms of the GNU Affero General Public License as
|
|
6
|
+
published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful,
|
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import lambdaSharedLib from '@izara_project/izara-core-library-lambda';
|
|
19
|
+
import { lambda } from '@izara_project/izara-core-library-external-request';
|
|
20
|
+
import { consts } from '@izara_project/izara-core-library-core';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks a user's permission by invoking another Lambda function.
|
|
24
|
+
* Adheres to Izara.io backend syntax guidelines.
|
|
25
|
+
*
|
|
26
|
+
* @param {object} _izContext - The context object containing logger, credentials, etc.
|
|
27
|
+
* @param {object} payload - The data payload for the permission check.
|
|
28
|
+
* @param {string} [payload.objectType] - The type of the object (e.g., 'INVOICE'). Must be used with `action`.
|
|
29
|
+
* @param {string} [payload.action] - The action to perform (e.g., 'READ', 'APPROVE'). Must be used with `objectType`.
|
|
30
|
+
* @param {string} [payload.flowTag] - The tag for a flow-based permission check. Used instead of `objectType`/`action`.
|
|
31
|
+
* @param {string} [payload.serviceName] - The name of the calling service (optional).
|
|
32
|
+
* @returns {Promise<{status: boolean, errorFound?: string}>}
|
|
33
|
+
* Resolves with the result of the permission check:
|
|
34
|
+
* - `status`: Whether the user has permission.
|
|
35
|
+
* - `errorFound`: Optional error message if the permission check failed.
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error} If the payload is invalid or Lambda invocation fails.
|
|
38
|
+
*/
|
|
39
|
+
async function checkPermission(_izContext, payload) {
|
|
40
|
+
// Validate that the payload object itself is provided.
|
|
41
|
+
if (!payload || typeof payload !== 'object') {
|
|
42
|
+
const error = new Error('Payload object is required.');
|
|
43
|
+
_izContext.logger.error(error.message);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const userId = _izContext.correlationIds.get(consts.BASE_USER_ID);
|
|
48
|
+
const targetId = _izContext.correlationIds.get(consts.TARGET_ID);
|
|
49
|
+
|
|
50
|
+
// Validate the required 'userId' field.
|
|
51
|
+
if (!userId || typeof userId !== 'string' || userId.trim() === '') {
|
|
52
|
+
const error = new Error(
|
|
53
|
+
'userId is required and must be a non-empty string.'
|
|
54
|
+
);
|
|
55
|
+
_izContext.logger.error(error.message);
|
|
56
|
+
throw error;
|
|
57
|
+
} else {
|
|
58
|
+
payload.userId = userId;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (targetId) {
|
|
62
|
+
payload.targetId = targetId;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Define validation flags for business rules.
|
|
66
|
+
const hasObjectParams = payload.objectType || payload.action;
|
|
67
|
+
const hasFlowTag = payload.flowTag;
|
|
68
|
+
|
|
69
|
+
// Rule: Cannot mix object-based and flow-based parameters.
|
|
70
|
+
if (hasObjectParams && hasFlowTag) {
|
|
71
|
+
const error = new Error(
|
|
72
|
+
'Invalid payload: Cannot provide flowTag together with objectType or action.'
|
|
73
|
+
);
|
|
74
|
+
_izContext.logger.error(error.message, { payload });
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Rule: If using object-based, both objectType and action are required.
|
|
79
|
+
if (hasObjectParams && (!payload.objectType || !payload.action)) {
|
|
80
|
+
const error = new Error(
|
|
81
|
+
'Invalid payload: Both objectType and action must be provided together.'
|
|
82
|
+
);
|
|
83
|
+
_izContext.logger.error(error.message, { payload });
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Rule: Must provide at least one of the two valid schemas.
|
|
88
|
+
if (!hasObjectParams && !hasFlowTag) {
|
|
89
|
+
const error = new Error(
|
|
90
|
+
'Invalid payload: Must provide either (objectType and action) or flowTag.'
|
|
91
|
+
);
|
|
92
|
+
_izContext.logger.error(error.message, { payload });
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Log the payload for debugging before invoking the next service.
|
|
97
|
+
_izContext.logger.debug('Checking permission with payload:', payload);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const lambdaName = lambdaSharedLib.lambdaFunctionName(
|
|
101
|
+
_izContext,
|
|
102
|
+
'CheckPermissionHdrInv',
|
|
103
|
+
'UserAccount'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const result = await lambda.invokeSync(_izContext, lambdaName, payload);
|
|
107
|
+
|
|
108
|
+
_izContext.logger.info('Permission check invoked successfully.');
|
|
109
|
+
|
|
110
|
+
// example result = { status: boolean , errorFound: string }
|
|
111
|
+
return result;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
// Log the full error object for better traceability.
|
|
114
|
+
_izContext.logger.error(
|
|
115
|
+
'Error invoking CheckPermissionHdrInv Lambda: ',
|
|
116
|
+
err
|
|
117
|
+
);
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default checkPermission;
|
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright(C) 2021 Sven Mason < http://izara.io>
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and / or modify
|
|
5
|
+
it under the terms of the GNU Affero General Public License as
|
|
6
|
+
published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
License, or(at your option) any later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful,
|
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
|
12
|
+
GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { NoRetryError } from "@izara_project/izara-core-library-core";
|
|
19
|
+
import serviceConfig from "../ServiceConfig.js";
|
|
20
|
+
import utils from "../Utils.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* helper function for get endpoint
|
|
24
|
+
* create get data detail for each storageTag
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} _izContext
|
|
27
|
+
* @param {Object} objectSchema
|
|
28
|
+
*/
|
|
29
|
+
async function createGetDataDetails(
|
|
30
|
+
_izContext,
|
|
31
|
+
objectSchema,
|
|
32
|
+
settings = { bucketName: process.env.iz_serviceSchemaBucketName }
|
|
33
|
+
) {
|
|
34
|
+
// group versionedData per storageTag
|
|
35
|
+
// or should group versionedData per graph serviceTag
|
|
36
|
+
let versionedDataPerStorageTag = {};
|
|
37
|
+
if (objectSchema?.addOnDataStructure?.length) {
|
|
38
|
+
for (let addOn of objectSchema.addOnDataStructure) {
|
|
39
|
+
if (addOn.type !== 'versionedData') {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (
|
|
44
|
+
!versionedDataPerStorageTag.hasOwnProperty(addOn.storageResourceTag)
|
|
45
|
+
) {
|
|
46
|
+
versionedDataPerStorageTag[addOn.storageResourceTag] = [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
versionedDataPerStorageTag[addOn.storageResourceTag].push(addOn);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_izContext.logger.debug(
|
|
54
|
+
'versionedDataPerStorageTag: ',
|
|
55
|
+
versionedDataPerStorageTag
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
let getGraphDataDetails = {};
|
|
59
|
+
let getDynamoDbDataDetails = {};
|
|
60
|
+
|
|
61
|
+
// reference to graph serviceTag that already used
|
|
62
|
+
let graphStorageTagPerGraphServiceTag = {}; // {serviceTag:storageTag,... }
|
|
63
|
+
|
|
64
|
+
for (let [storageTag, storageData] of Object.entries(
|
|
65
|
+
objectSchema.storageResources
|
|
66
|
+
)) {
|
|
67
|
+
// collect fieldNames per storageTag
|
|
68
|
+
let storageTagFieldNames = [];
|
|
69
|
+
for (let [fieldName, fieldNameData] of Object.entries(
|
|
70
|
+
objectSchema.fieldNames
|
|
71
|
+
)) {
|
|
72
|
+
// if (fieldName.startsWith("versionedDataField")) {
|
|
73
|
+
// continue;
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
if (fieldNameData.storageResourceTags.includes(storageTag)) {
|
|
77
|
+
storageTagFieldNames.push(fieldName);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (storageData.storageType === coreConsts.STORAGE_TYPES.graph) {
|
|
82
|
+
let useStorageTag = storageTag;
|
|
83
|
+
let graphServiceTag = await serviceConfig.getGraphServiceTagWithCache(
|
|
84
|
+
_izContext,
|
|
85
|
+
storageData.graphServerTag,
|
|
86
|
+
settings.bucketName
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// check duplicate graph serviceTag
|
|
90
|
+
if (!graphStorageTagPerGraphServiceTag.hasOwnProperty(graphServiceTag)) {
|
|
91
|
+
graphStorageTagPerGraphServiceTag[graphServiceTag] = storageTag;
|
|
92
|
+
} else {
|
|
93
|
+
// overwrite useStorageTag if graphServiceTag already used
|
|
94
|
+
_izContext.logger.debug('current storageTag: ', storageTag);
|
|
95
|
+
_izContext.logger.debug('use storageTag: ', useStorageTag);
|
|
96
|
+
|
|
97
|
+
useStorageTag = graphStorageTagPerGraphServiceTag[graphServiceTag];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!getGraphDataDetails.hasOwnProperty(useStorageTag)) {
|
|
101
|
+
getGraphDataDetails[useStorageTag] = {
|
|
102
|
+
storageType: storageData.storageType,
|
|
103
|
+
graphServiceTag: graphServiceTag,
|
|
104
|
+
fieldNames: storageTagFieldNames
|
|
105
|
+
};
|
|
106
|
+
} else {
|
|
107
|
+
getGraphDataDetails[useStorageTag].fieldNames = [
|
|
108
|
+
...new Set(
|
|
109
|
+
getGraphDataDetails[useStorageTag].fieldNames.concat(
|
|
110
|
+
storageTagFieldNames
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// add versionedData fieldNames
|
|
117
|
+
if (versionedDataPerStorageTag.hasOwnProperty(storageTag)) {
|
|
118
|
+
if (
|
|
119
|
+
!getGraphDataDetails[useStorageTag].hasOwnProperty('versionedDatas')
|
|
120
|
+
) {
|
|
121
|
+
getGraphDataDetails[useStorageTag].versionedDatas = {};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (let versionedData of versionedDataPerStorageTag[storageTag]) {
|
|
125
|
+
_izContext.logger.debug(
|
|
126
|
+
'versionedData.fieldNames',
|
|
127
|
+
versionedData.fieldNames
|
|
128
|
+
);
|
|
129
|
+
let versionedDataFieldNames = Object.keys(versionedData.fieldNames);
|
|
130
|
+
Object.assign(getGraphDataDetails[useStorageTag].versionedDatas, {
|
|
131
|
+
[versionedData.versionedDataLabel]: versionedDataFieldNames
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else if (storageData.storageType === coreConsts.STORAGE_TYPES.dynamoDB) {
|
|
136
|
+
let serviceTag = storageData.serviceTag || process.env.iz_serviceTag;
|
|
137
|
+
Object.assign(getDynamoDbDataDetails, {
|
|
138
|
+
[storageTag]: {
|
|
139
|
+
storageType: storageData.storageType,
|
|
140
|
+
tableName: storageData.tableName,
|
|
141
|
+
serviceTag: serviceTag,
|
|
142
|
+
fieldNames: storageTagFieldNames
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (storageData.groupByPartitionKeyField) {
|
|
147
|
+
Object.assign(getDynamoDbDataDetails[storageTag], {
|
|
148
|
+
groupByPartitionKeyField: storageData.groupByPartitionKeyField
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
throw new NoRetryError('Unhanled storageType');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_izContext.logger.debug('getGraphDataDetail: ', getGraphDataDetails);
|
|
157
|
+
_izContext.logger.debug('getDynamoDbData: ', getDynamoDbDataDetails);
|
|
158
|
+
|
|
159
|
+
//-------- sort descending order of fieldNames.length --------
|
|
160
|
+
let sortedGetGraphWithVersionedData = Object.entries(getGraphDataDetails)
|
|
161
|
+
.filter(getGraphDetail =>
|
|
162
|
+
getGraphDetail[1].hasOwnProperty('versionedDatas')
|
|
163
|
+
)
|
|
164
|
+
.sort(
|
|
165
|
+
(storageDataA, storageDataB) =>
|
|
166
|
+
storageDataB[1].fieldNames.length - storageDataA[1].fieldNames.length
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
_izContext.logger.debug(
|
|
170
|
+
'sortedGetGraphWithVersionedData: ',
|
|
171
|
+
sortedGetGraphWithVersionedData
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
let sortedGetGraphWithoutVersionedData = Object.entries(getGraphDataDetails)
|
|
175
|
+
.filter(
|
|
176
|
+
getGraphDetail => !getGraphDetail[1].hasOwnProperty('versionedDatas')
|
|
177
|
+
)
|
|
178
|
+
.sort(
|
|
179
|
+
(storageDataA, storageDataB) =>
|
|
180
|
+
storageDataB[1].fieldNames.length - storageDataA[1].fieldNames.length
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
_izContext.logger.debug(
|
|
184
|
+
'sortedGetGraphWithoutVersionedData: ',
|
|
185
|
+
sortedGetGraphWithoutVersionedData
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
let sortedGetDynamoDbData = Object.entries(getDynamoDbDataDetails).sort(
|
|
189
|
+
(storageDataA, storageDataB) =>
|
|
190
|
+
storageDataB[1].fieldNames.length - storageDataA[1].fieldNames.length
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
_izContext.logger.debug('sortedGetDynamoDbData: ', sortedGetDynamoDbData);
|
|
194
|
+
|
|
195
|
+
// after sort data then filter duplicate fieldName
|
|
196
|
+
let allSortedGetDataDetails = [
|
|
197
|
+
...sortedGetGraphWithVersionedData,
|
|
198
|
+
...sortedGetDynamoDbData,
|
|
199
|
+
...sortedGetGraphWithoutVersionedData
|
|
200
|
+
];
|
|
201
|
+
_izContext.logger.debug('allSortedGetDataDetails: ', allSortedGetDataDetails);
|
|
202
|
+
|
|
203
|
+
let usedFieldName = [];
|
|
204
|
+
for (let [storageTag, getDataDetail] of allSortedGetDataDetails) {
|
|
205
|
+
let remainsFieldNames = [];
|
|
206
|
+
while (getDataDetail.fieldNames.length) {
|
|
207
|
+
let checkedFiedName = getDataDetail.fieldNames.pop();
|
|
208
|
+
if (!usedFieldName.includes(checkedFiedName)) {
|
|
209
|
+
usedFieldName.push(checkedFiedName);
|
|
210
|
+
remainsFieldNames.push(checkedFiedName);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getDataDetail.fieldNames = remainsFieldNames;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_izContext.logger.debug('newSortedArray after: ', allSortedGetDataDetails);
|
|
218
|
+
|
|
219
|
+
let getDataDetailResults = Object.fromEntries(
|
|
220
|
+
allSortedGetDataDetails.filter(([storageTag, getDataDetail]) => {
|
|
221
|
+
return (
|
|
222
|
+
getDataDetail.fieldNames.length ||
|
|
223
|
+
getDataDetail.hasOwnProperty('versionedDatas')
|
|
224
|
+
);
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
_izContext.logger.debug('getDataDetailResults: ', getDataDetailResults);
|
|
229
|
+
|
|
230
|
+
for (const getDataDetail of Object.values(getDataDetailResults)) {
|
|
231
|
+
if (getDataDetail.hasOwnProperty('versionedDatas')) {
|
|
232
|
+
getDataDetail.versionedDataLabels = [];
|
|
233
|
+
for (let [versionedDataLabel, fieldNames] of Object.entries(
|
|
234
|
+
getDataDetail.versionedDatas
|
|
235
|
+
)) {
|
|
236
|
+
getDataDetail.versionedDataLabels.push(versionedDataLabel);
|
|
237
|
+
getDataDetail.fieldNames.push(...fieldNames);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return getDataDetailResults;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* receive result from multiple database and create data format depend on returnFormat setting
|
|
247
|
+
*
|
|
248
|
+
* @param {object} _izContext
|
|
249
|
+
* @param {object[]} getResults
|
|
250
|
+
* @param {object} objectSchema
|
|
251
|
+
* @returns
|
|
252
|
+
*/
|
|
253
|
+
function collectGetData(
|
|
254
|
+
_izContext,
|
|
255
|
+
getResults,
|
|
256
|
+
objectSchema,
|
|
257
|
+
returnSystemFieldsName
|
|
258
|
+
) {
|
|
259
|
+
_izContext.logger.debug('collectGetData: ', {
|
|
260
|
+
getResults,
|
|
261
|
+
objectSchema,
|
|
262
|
+
returnSystemFieldsName
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
let collectedData = {
|
|
266
|
+
identifiers: {},
|
|
267
|
+
fields: {}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
if (returnSystemFieldsName) {
|
|
271
|
+
Object.assign(collectedData, { versionDataSystemFields: {} });
|
|
272
|
+
}
|
|
273
|
+
const identifierFieldNames = new Set(
|
|
274
|
+
utils.getUsedFieldNamesOfIdentifiers(_izContext, objectSchema.identifiers)
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
let addedFieldNames = new Set();
|
|
278
|
+
|
|
279
|
+
// if isFound === true then return null from this function
|
|
280
|
+
let isFound = false;
|
|
281
|
+
|
|
282
|
+
for (const [getData, getDataDetail] of getResults) {
|
|
283
|
+
if (!getData || !Object.keys(getData).length) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// this process will add result from graph and dynamo to collectedData object by add identifiers and fieldName from getData
|
|
288
|
+
if (getDataDetail.storageType === coreConsts.STORAGE_TYPES.graph) {
|
|
289
|
+
if (
|
|
290
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull &&
|
|
291
|
+
Object.keys(
|
|
292
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull
|
|
293
|
+
).length
|
|
294
|
+
) {
|
|
295
|
+
for (const getDetailFieldName of getDataDetail.fieldNames) {
|
|
296
|
+
if (addedFieldNames.has(getDetailFieldName)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (
|
|
301
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull.identifiers.hasOwnProperty(
|
|
302
|
+
getDetailFieldName
|
|
303
|
+
)
|
|
304
|
+
) {
|
|
305
|
+
Object.assign(collectedData.identifiers, {
|
|
306
|
+
[getDetailFieldName]:
|
|
307
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull
|
|
308
|
+
.identifiers[getDetailFieldName]
|
|
309
|
+
});
|
|
310
|
+
addedFieldNames.add(getDetailFieldName);
|
|
311
|
+
isFound = true;
|
|
312
|
+
} else if (
|
|
313
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull.fields.hasOwnProperty(
|
|
314
|
+
getDetailFieldName
|
|
315
|
+
)
|
|
316
|
+
) {
|
|
317
|
+
Object.assign(collectedData.fields, {
|
|
318
|
+
[getDetailFieldName]:
|
|
319
|
+
getData.returnValue.queryResults.refactoredNodes.objInstanceFull
|
|
320
|
+
.fields[getDetailFieldName]
|
|
321
|
+
});
|
|
322
|
+
addedFieldNames.add(getDetailFieldName);
|
|
323
|
+
isFound = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (
|
|
328
|
+
getData.returnValue.queryResults.refactoredNodes
|
|
329
|
+
.versionDataSystemFields ||
|
|
330
|
+
returnSystemFieldsName == true
|
|
331
|
+
) {
|
|
332
|
+
Object.assign(
|
|
333
|
+
collectedData.versionDataSystemFields,
|
|
334
|
+
getData.returnValue.queryResults.refactoredNodes
|
|
335
|
+
.versionDataSystemFields
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
} else if (
|
|
339
|
+
getDataDetail.storageType === coreConsts.STORAGE_TYPES.dynamoDB
|
|
340
|
+
) {
|
|
341
|
+
for (const getDetailFieldName of getDataDetail.fieldNames) {
|
|
342
|
+
if (addedFieldNames.has(getDetailFieldName)) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (getData.hasOwnProperty(getDetailFieldName)) {
|
|
347
|
+
if (identifierFieldNames.has(getDetailFieldName)) {
|
|
348
|
+
Object.assign(collectedData.identifiers, {
|
|
349
|
+
[getDetailFieldName]: getData[getDetailFieldName]
|
|
350
|
+
});
|
|
351
|
+
addedFieldNames.add(getDetailFieldName);
|
|
352
|
+
isFound = true;
|
|
353
|
+
} else {
|
|
354
|
+
Object.assign(collectedData.fields, {
|
|
355
|
+
[getDetailFieldName]: getData[getDetailFieldName]
|
|
356
|
+
});
|
|
357
|
+
addedFieldNames.add(getDetailFieldName);
|
|
358
|
+
isFound = true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
throw new NoRetryError('collectGetData unhandled storageType');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (isFound) {
|
|
368
|
+
return collectedData;
|
|
369
|
+
} else {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function createUpdateDataDetail(
|
|
375
|
+
_izContext,
|
|
376
|
+
objectSchema,
|
|
377
|
+
settings = { bucketName: process.env.iz_serviceSchemaBucketName }
|
|
378
|
+
) {
|
|
379
|
+
let getGraphDataDetails = {};
|
|
380
|
+
let getDynamoDbDataDetails = {};
|
|
381
|
+
let allUpdateDataDetail = {};
|
|
382
|
+
for (let [storageTag, storageProperties] of Object.entries(
|
|
383
|
+
objectSchema.storageResources
|
|
384
|
+
)) {
|
|
385
|
+
let storageFieldNames = [];
|
|
386
|
+
for (let [fieldName, fieldNameProperties] of Object.entries(
|
|
387
|
+
objectSchema.fieldNames
|
|
388
|
+
)) {
|
|
389
|
+
if (fieldNameProperties.storageResourceTags.includes(storageTag)) {
|
|
390
|
+
let versionedDataLabel = fieldNameProperties.versionedDataLabel;
|
|
391
|
+
if (!fieldName.includes(versionedDataLabel)) {
|
|
392
|
+
// not get fieldNames VersionedData_VersionedDataLabel_VersionedDataFieldNames
|
|
393
|
+
storageFieldNames.push(fieldName);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (storageProperties.storageType === coreConsts.STORAGE_TYPES.graph) {
|
|
398
|
+
let graphStorageTagPerGraphServiceTag = {};
|
|
399
|
+
let useStorageTag = storageTag;
|
|
400
|
+
let graphServiceName = await serviceConfig.getGraphServiceTagWithCache(
|
|
401
|
+
_izContext,
|
|
402
|
+
storageProperties.graphServerTag,
|
|
403
|
+
settings.bucketName
|
|
404
|
+
);
|
|
405
|
+
if (!graphStorageTagPerGraphServiceTag.hasOwnProperty(graphServiceName)) {
|
|
406
|
+
graphStorageTagPerGraphServiceTag[graphServiceName] = storageTag;
|
|
407
|
+
_izContext.logger.debug(
|
|
408
|
+
'graphStorageTagPerGraphServiceTag',
|
|
409
|
+
graphStorageTagPerGraphServiceTag
|
|
410
|
+
);
|
|
411
|
+
} else {
|
|
412
|
+
_izContext.logger.debug('current storageTag', storageTag);
|
|
413
|
+
_izContext.logger.debug('used storageTag', useStorageTag);
|
|
414
|
+
useStorageTag = graphStorageTagPerGraphServiceTag[graphServiceName];
|
|
415
|
+
}
|
|
416
|
+
if (!getGraphDataDetails.hasOwnProperty(useStorageTag)) {
|
|
417
|
+
getGraphDataDetails[graphServiceName] = {
|
|
418
|
+
storageType: storageProperties.storageType,
|
|
419
|
+
fieldNames: storageFieldNames
|
|
420
|
+
};
|
|
421
|
+
} else {
|
|
422
|
+
getGraphDataDetails[graphServiceName].fieldNames = [
|
|
423
|
+
...new Set(
|
|
424
|
+
getGraphDataDetails[useStorageTag].fieldNames.concat(
|
|
425
|
+
storageFieldNames
|
|
426
|
+
)
|
|
427
|
+
)
|
|
428
|
+
];
|
|
429
|
+
}
|
|
430
|
+
} else if (
|
|
431
|
+
storageProperties.storageType === coreConsts.STORAGE_TYPES.dynamoDB
|
|
432
|
+
) {
|
|
433
|
+
let serviceTag =
|
|
434
|
+
storageProperties.serviceTag || process.env.iz_serviceTag;
|
|
435
|
+
Object.assign(getDynamoDbDataDetails, {
|
|
436
|
+
[storageTag]: {
|
|
437
|
+
storageType: storageProperties.storageType,
|
|
438
|
+
tableName: storageProperties.tableName,
|
|
439
|
+
serviceTag: serviceTag,
|
|
440
|
+
fieldNames: storageFieldNames
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
if (storageTag.groupByPartitionKeyField) {
|
|
445
|
+
Object.assign(getDynamoDbDataDetails[storageTag], {
|
|
446
|
+
groupByPartitionKeyField: storageProperties.groupByPartitionKeyField
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
_izContext.logger.debug('getGraphDataDetails', getGraphDataDetails);
|
|
453
|
+
_izContext.logger.debug('getDynamoDataDetail', getDynamoDbDataDetails);
|
|
454
|
+
|
|
455
|
+
Object.assign(
|
|
456
|
+
allUpdateDataDetail,
|
|
457
|
+
getDynamoDbDataDetails,
|
|
458
|
+
getGraphDataDetails
|
|
459
|
+
);
|
|
460
|
+
return allUpdateDataDetail;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function createDataDetailsLib(
|
|
464
|
+
_izContext,
|
|
465
|
+
objectSchemas,
|
|
466
|
+
settings = { bucketName: process.env.iz_serviceSchemaBucketName }
|
|
467
|
+
) {
|
|
468
|
+
_izContext.logger.debug('Lib: createDataDetailsLib:', {
|
|
469
|
+
objectSchemas: objectSchemas
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
let createDataDetails = {};
|
|
473
|
+
|
|
474
|
+
let storageResources = objectSchemas.storageResources;
|
|
475
|
+
_izContext.logger.debug('storageResources:', storageResources);
|
|
476
|
+
|
|
477
|
+
for (let [keyFieldName, settingFieldName] of Object.entries(
|
|
478
|
+
objectSchemas.fieldNames
|
|
479
|
+
)) {
|
|
480
|
+
// _izContext.logger.debug("Loop fieldNamesObjectSchemas", { keyFieldName });
|
|
481
|
+
|
|
482
|
+
for (let eachStorageResourceTag of settingFieldName.storageResourceTags) {
|
|
483
|
+
// _izContext.logger.debug("Loop eachStorageResourceTags", eachStorageResourceTag);
|
|
484
|
+
|
|
485
|
+
if (!storageResources.hasOwnProperty(eachStorageResourceTag)) {
|
|
486
|
+
throw new Error("storageResources is'n exist"); // should be validate in step upload in s3
|
|
487
|
+
} else {
|
|
488
|
+
if (
|
|
489
|
+
storageResources[eachStorageResourceTag].storageType ==
|
|
490
|
+
coreConsts.STORAGE_TYPES.dynamoDB
|
|
491
|
+
) {
|
|
492
|
+
if (createDataDetails.hasOwnProperty(eachStorageResourceTag)) {
|
|
493
|
+
// _izContext.logger.debug("SAME STG DB", eachStorageResourceTag);
|
|
494
|
+
createDataDetails[eachStorageResourceTag].fieldNames.push(
|
|
495
|
+
keyFieldName
|
|
496
|
+
);
|
|
497
|
+
} else {
|
|
498
|
+
Object.assign(createDataDetails, {
|
|
499
|
+
[eachStorageResourceTag]: {
|
|
500
|
+
storageType: coreConsts.STORAGE_TYPES.dynamoDB,
|
|
501
|
+
tableName: storageResources[eachStorageResourceTag].tableName,
|
|
502
|
+
serviceTag:
|
|
503
|
+
storageResources[eachStorageResourceTag].serviceTag ||
|
|
504
|
+
process.env.iz_serviceTag,
|
|
505
|
+
fieldNames: [keyFieldName]
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
if (
|
|
510
|
+
storageResources[eachStorageResourceTag].hasOwnProperty(
|
|
511
|
+
'groupByPartitionKeyField'
|
|
512
|
+
)
|
|
513
|
+
) {
|
|
514
|
+
Object.assign(createDataDetails[eachStorageResourceTag], {
|
|
515
|
+
groupByPartitionKeyField:
|
|
516
|
+
storageResources[eachStorageResourceTag]
|
|
517
|
+
.groupByPartitionKeyField
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} else if (
|
|
522
|
+
storageResources[eachStorageResourceTag].storageType ==
|
|
523
|
+
coreConsts.STORAGE_TYPES.graph
|
|
524
|
+
) {
|
|
525
|
+
let checkGraphServerTags =
|
|
526
|
+
await serviceConfig.getGraphServiceTagWithCache(
|
|
527
|
+
_izContext,
|
|
528
|
+
storageResources[eachStorageResourceTag].graphServerTag,
|
|
529
|
+
settings.bucketName
|
|
530
|
+
);
|
|
531
|
+
if (checkGraphServerTags) {
|
|
532
|
+
if (createDataDetails.hasOwnProperty(checkGraphServerTags)) {
|
|
533
|
+
// _izContext.logger.debug("SAME STG", checkGraphServerTags);
|
|
534
|
+
createDataDetails[checkGraphServerTags].fieldNames.push(
|
|
535
|
+
keyFieldName
|
|
536
|
+
);
|
|
537
|
+
} else {
|
|
538
|
+
// _izContext.logger.debug("NEW STG", checkGraphServerTags);
|
|
539
|
+
Object.assign(createDataDetails, {
|
|
540
|
+
[checkGraphServerTags]: {
|
|
541
|
+
storageType: coreConsts.STORAGE_TYPES.graph,
|
|
542
|
+
fieldNames: [keyFieldName]
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} else if (
|
|
548
|
+
storageResources[eachStorageResourceTag].storageType ===
|
|
549
|
+
coreConsts.STORAGE_TYPES.externalTopic
|
|
550
|
+
) {
|
|
551
|
+
let topicName =
|
|
552
|
+
storageResources[eachStorageResourceTag].serviceTag +
|
|
553
|
+
storageResources[eachStorageResourceTag].stage +
|
|
554
|
+
storageResources[eachStorageResourceTag].topicName;
|
|
555
|
+
Object.assign(createDataDetails, {
|
|
556
|
+
[topicName]: {
|
|
557
|
+
storageType: coreConsts.STORAGE_TYPES.externalTopic,
|
|
558
|
+
fieldNames: [keyFieldName],
|
|
559
|
+
accountId: storageResources[eachStorageResourceTag].accountId,
|
|
560
|
+
region: storageResources[eachStorageResourceTag].region,
|
|
561
|
+
serviceTag: storageResources[eachStorageResourceTag].serviceTag,
|
|
562
|
+
stage: storageResources[eachStorageResourceTag].stage
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
} // end loop storageResourceTags
|
|
568
|
+
} // end loop
|
|
569
|
+
|
|
570
|
+
return createDataDetails;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function createDeleteDataDetail(
|
|
574
|
+
_izContext,
|
|
575
|
+
objectSchema,
|
|
576
|
+
settings = { bucketName: process.env.iz_serviceSchemaBucketName }
|
|
577
|
+
) {
|
|
578
|
+
let deleteDynamoDataDetail = {};
|
|
579
|
+
let deleteGraphDataDetail = {};
|
|
580
|
+
let allDeleteDataDetail = {};
|
|
581
|
+
|
|
582
|
+
for (let [storageTag, storageProperties] of Object.entries(
|
|
583
|
+
objectSchema.storageResources
|
|
584
|
+
)) {
|
|
585
|
+
if (storageProperties.storageType === coreConsts.STORAGE_TYPES.dynamoDB) {
|
|
586
|
+
let serviceTag =
|
|
587
|
+
storageProperties.serviceTag || process.env.iz_serviceTag;
|
|
588
|
+
Object.assign(deleteDynamoDataDetail, {
|
|
589
|
+
[storageTag]: {
|
|
590
|
+
storageType: storageProperties.storageType,
|
|
591
|
+
serviceTag: serviceTag,
|
|
592
|
+
tableName: storageProperties.tableName
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
if (storageProperties.groupByPartitionKeyField) {
|
|
596
|
+
Object.assign(deleteDynamoDataDetail[storageTag], {
|
|
597
|
+
groupByPartitionKeyField: storageProperties.groupByPartitionKeyField
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
} else if (
|
|
601
|
+
storageProperties.storageType === coreConsts.STORAGE_TYPES.graph
|
|
602
|
+
) {
|
|
603
|
+
let dataDetailPerGraphStorage = {};
|
|
604
|
+
let useStorageTag = storageTag;
|
|
605
|
+
let graphServiceName = await serviceConfig.getGraphServiceTagWithCache(
|
|
606
|
+
_izContext,
|
|
607
|
+
storageProperties.graphServerTag,
|
|
608
|
+
settings.bucketName
|
|
609
|
+
);
|
|
610
|
+
if (!dataDetailPerGraphStorage.hasOwnProperty(graphServiceName)) {
|
|
611
|
+
dataDetailPerGraphStorage[graphServiceName] = storageTag;
|
|
612
|
+
} else {
|
|
613
|
+
useStorageTag = dataDetailPerGraphStorage[graphServiceName];
|
|
614
|
+
}
|
|
615
|
+
if (!deleteGraphDataDetail.hasOwnProperty(useStorageTag)) {
|
|
616
|
+
deleteGraphDataDetail[graphServiceName] = {
|
|
617
|
+
storageType: storageProperties.storageType
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
_izContext.logger.debug('deleteGraphDataDetail', deleteGraphDataDetail);
|
|
623
|
+
_izContext.logger.debug('deleteDynamoDataDetail', deleteDynamoDataDetail);
|
|
624
|
+
Object.assign(
|
|
625
|
+
allDeleteDataDetail,
|
|
626
|
+
deleteGraphDataDetail,
|
|
627
|
+
deleteDynamoDataDetail
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
_izContext.logger.debug('allDeleteDataDetail', allDeleteDataDetail);
|
|
631
|
+
return allDeleteDataDetail;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
export default {
|
|
636
|
+
createUpdateDataDetail,
|
|
637
|
+
createDataDetailsLib,
|
|
638
|
+
createDeleteDataDetail,
|
|
639
|
+
|
|
640
|
+
createGetDataDetails,
|
|
641
|
+
collectGetData,
|
|
642
|
+
}
|