Coverage for csaf/mandatory/rules.py: 27.91%

96 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 16:28:45 +00:00

1from typing import Dict, List, Tuple, no_type_check 

2 

3import jmespath 

4 

5# import csaf.mandatory.acyclic_product_ids as acy_product_ids 

6# import csaf.mandatory.consistent_product_status as con_pro_sta 

7import csaf.mandatory.defined_group_ids as def_gro_ids 

8import csaf.mandatory.defined_product_ids as def_pro_ids 

9import csaf.mandatory.translator_and_source_lang as tra_and_sou_lan 

10import csaf.mandatory.unique_group_ids as uni_gro_ids 

11import csaf.mandatory.unique_product_ids as uni_pro_ids 

12import csaf.mandatory.valid_category_name as val_cat_nam 

13 

14 

15@no_type_check 

16def guess_max_depth(map_or_seq): 

17 """HACK A DID ACK - please delete me when cleaning up.""" 

18 if isinstance(map_or_seq, dict): 

19 return 1 + max(map(guess_max_depth, map_or_seq.values()), default=0) 

20 elif isinstance(map_or_seq, list): 

21 return 1 + max(map(guess_max_depth, map_or_seq[0].values()), default=0) 

22 return 0 

23 

24 

25@no_type_check 

26def is_valid(document: dict) -> bool: 

27 """Complete validation of all mandatory rules. 

28 

29 This is a spike - we throw it away when all rules are in and back comes something maintainable. 

30 """ 

31 if not is_valid_category(document): 

32 return False 

33 

34 if jmespath.search(tra_and_sou_lan.TRIGGER_JMES_PATH, document) == tra_and_sou_lan.TRIGGER_VALUE: 

35 if not is_valid_translator(document): 

36 return False 

37 

38 if not is_valid_unique_product_ids(document): 

39 return False 

40 

41 if not is_valid_unique_group_ids(document): 

42 return False 

43 

44 if not is_valid_defined_product_ids(document): 

45 return False 

46 

47 if not is_valid_defined_group_ids(document): 

48 return False 

49 

50 return NotImplemented 

51 

52 

53@no_type_check 

54def is_valid_unique_product_ids(document: dict) -> bool: 

55 """Temporary implementation of rule for unique product ids.""" 

56 prod_ids = [] 

57 for path in uni_pro_ids.CONDITION_JMES_PATHS: 

58 pids = jmespath.search(path, document) 

59 if pids is not None: 

60 prod_ids.extend(pids) 

61 probe = jmespath.search('product_tree.branches[]', document) 

62 trials = guess_max_depth(probe) + 1 

63 ipath = 'product_tree.branches[].product.product_id' 

64 inject = 'branches[].product.' 

65 for trial in range(trials): 

66 harvest = jmespath.search(ipath, document) 

67 if harvest: 

68 prod_ids.extend(harvest) 

69 ipath = ipath.replace('product.', inject) 

70 if len(prod_ids) > len(set(prod_ids)): 

71 return False 

72 

73 return True 

74 

75 

76@no_type_check 

77def is_valid_unique_group_ids(document: dict) -> bool: 

78 """Temporary implementation of rule for unique group ids.""" 

79 group_ids = jmespath.search(uni_gro_ids.CONDITION_JMES_PATH, document) 

80 if group_ids is not None: 

81 if len(group_ids) > len(set(group_ids)): 

82 return False 

83 

84 return True 

85 

86 

87@no_type_check 

88def is_valid_defined_product_ids(document: dict) -> bool: 

89 """Temporary implementation of rule for defined product ids.""" 

90 defined_prod_ids = jmespath.search(def_pro_ids.TRIGGER_JMES_PATH, document) 

91 # TODO(sthagen) too shallow tree visiting # print("DEBUG>>>", defined_prod_ids) 

92 if defined_prod_ids is None: 

93 defined_prod_ids = [] 

94 known_prod_ids = set(defined_prod_ids) 

95 for path in def_pro_ids.CONDITION_JMES_PATHS: 

96 claim_prod_ids = jmespath.search(path, document) 

97 if claim_prod_ids is not None: 

98 if any(claim_prod_id not in known_prod_ids for claim_prod_id in claim_prod_ids): 

99 return False 

100 

101 return True 

102 

103 

104@no_type_check 

105def is_valid_defined_group_ids(document: dict) -> bool: 

106 """Temporary implementation of rule for defined group ids.""" 

107 defined_group_ids = jmespath.search(def_gro_ids.TRIGGER_JMES_PATH, document) 

108 if defined_group_ids is None: 

109 defined_group_ids = [] 

110 known_group_ids = set(defined_group_ids) 

111 for path in def_gro_ids.CONDITION_JMES_PATHS: 

112 claim_group_ids = jmespath.search(path, document) 

113 if claim_group_ids is not None: 

114 if any(claim_group_id not in known_group_ids for cl_seq in claim_group_ids for claim_group_id in cl_seq): 

115 return False 

116 

117 return True 

118 

119 

120@no_type_check 

121def exists(document: dict, claims: Dict[str, List[str]]) -> Tuple[Tuple[str, str, bool]]: 

122 """Verify the existence and return tuple of triplets with claim, path and result.""" 

123 return tuple( 

124 (claim, path, bool(jmespath.search(path, document))) for claim, paths in claims.items() for path in paths 

125 ) 

126 

127 

128@no_type_check 

129def must_skip(document: dict, path: str, skip_these: Tuple[str, ...]) -> Tuple[str, str, bool]: 

130 """Verify any skips and return tuple of triplets with claim, path and result.""" 

131 value = jmespath.search(path, document) 

132 return value, path, any(value == skip for skip in skip_these) 

133 

134 

135@no_type_check 

136def is_valid_category(document: dict) -> bool: 

137 """Verify category value.""" 

138 return val_cat_nam.is_valid(jmespath.search(val_cat_nam.CONDITION_JMES_PATH, document)) 

139 

140 

141@no_type_check 

142def is_valid_translator(document: dict) -> bool: 

143 """Verify source_lang value is present for translator.""" 

144 if jmespath.search(tra_and_sou_lan.TRIGGER_JMES_PATH, document) != tra_and_sou_lan.TRIGGER_VALUE: 

145 return False 

146 return bool(jmespath.search(tra_and_sou_lan.CONDITION_JMES_PATH, document))