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
« 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
3import jmespath
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
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
25@no_type_check
26def is_valid(document: dict) -> bool:
27 """Complete validation of all mandatory rules.
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
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
38 if not is_valid_unique_product_ids(document):
39 return False
41 if not is_valid_unique_group_ids(document):
42 return False
44 if not is_valid_defined_product_ids(document):
45 return False
47 if not is_valid_defined_group_ids(document):
48 return False
50 return NotImplemented
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
73 return True
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
84 return True
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
101 return True
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
117 return True
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 )
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)
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))
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))