Skip to content

Commit

Permalink
add subgroups change detect (#374)
Browse files Browse the repository at this point in the history
* add subgroups change detect
  • Loading branch information
AllyW authored May 16, 2023
1 parent 2014bc8 commit 8484a04
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 43 deletions.
98 changes: 70 additions & 28 deletions azdev/operations/command_change/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from azdev.utilities import (CMD_PROPERTY_ADD_BREAK_LIST, CMD_PROPERTY_REMOVE_BREAK_LIST,
CMD_PROPERTY_UPDATE_BREAK_LIST, PARA_PROPERTY_REMOVE_BREAK_LIST,
PARA_PROPERTY_ADD_BREAK_LIST, PARA_PROPERTY_UPDATE_BREAK_LIST)
from .util import extract_cmd_name, extract_cmd_property, ChangeType
from .util import extract_cmd_name, extract_cmd_property, extract_subgroup_name, ChangeType
from .util import get_command_tree
from .meta_changes import (CmdAdd, CmdRemove, CmdPropAdd, CmdPropRemove, CmdPropUpdate, ParaAdd, ParaRemove,
ParaPropAdd, ParaPropRemove, ParaPropUpdate)
ParaPropAdd, ParaPropRemove, ParaPropUpdate, SubgroupAdd, SubgroupRemove)

logger = get_logger(__name__)

Expand All @@ -25,7 +25,7 @@ class DiffExportFormat(Enum):

class MetaChangeDetects:

EXPORTED_META_PROPERTY = ["rule_id", "is_break", "rule_message", "suggest_message", "cmd_name"]
EXPORTED_META_PROPERTY = ["rule_id", "is_break", "rule_message", "suggest_message", "cmd_name", "subgroup_name"]
CHECKED_PARA_PROPERTY = ["name", "options", "required", "choices", "id_part", "nargs", "default", "desc",
"aaz_type", "type", "aaz_default", "aaz_choices"]

Expand Down Expand Up @@ -69,7 +69,14 @@ def __iter_dict_items(self, dict_items, diff_type):
for dict_key in dict_items:
has_cmd, cmd_name = extract_cmd_name(dict_key)
if not has_cmd or not cmd_name:
print("extract cmd failed for " + dict_key)
has_subgroup, subgroup_name = extract_subgroup_name(dict_key)
if not has_subgroup or not subgroup_name:
continue
if diff_type == ChangeType.REMOVE:
diff_obj = SubgroupRemove(subgroup_name)
else:
diff_obj = SubgroupAdd(subgroup_name)
self.diff_objs.append(diff_obj)
continue

has_cmd_key, cmd_property = extract_cmd_property(dict_key, cmd_name)
Expand Down Expand Up @@ -257,6 +264,55 @@ def check_deep_diffs(self):
self.check_value_change()
self.check_cmds_parameter_diff()

@staticmethod
def fill_subgroup_rules(obj, ret_mod, rule):
command_group_info = ret_mod
group_name_arr = obj.subgroup_name.split(" ")
start_level = 1
while start_level < len(group_name_arr):
group_name = " ".join(group_name_arr[:start_level])
if group_name not in command_group_info["sub_groups"]:
command_group_info["sub_groups"][group_name] = {
"name": group_name,
"commands": {},
"sub_groups": {}
}
start_level += 1
command_group_info = command_group_info["sub_groups"][group_name]
group_name = obj.subgroup_name
group_rules = []
if group_name not in command_group_info["sub_groups"]:
command_group_info["sub_groups"] = {group_name: group_rules}
group_rules = command_group_info["sub_groups"][group_name]
group_rules.append(rule)
command_group_info["sub_groups"][group_name] = group_rules

@staticmethod
def fill_cmd_rules(obj, ret_mod, rule):
command_tree = get_command_tree(obj.cmd_name)
command_group_info = ret_mod
while True:
if "is_group" not in command_tree:
break
if command_tree["is_group"]:
group_name = command_tree["group_name"]
if group_name not in command_group_info["sub_groups"]:
command_group_info["sub_groups"][group_name] = {
"name": group_name,
"commands": {},
"sub_groups": {}
}
command_tree = command_tree["sub_info"]
command_group_info = command_group_info["sub_groups"][group_name]
else:
cmd_name = command_tree["cmd_name"]
command_rules = []
if cmd_name in command_group_info["commands"]:
command_rules = command_group_info["commands"][cmd_name]
command_rules.append(rule)
command_group_info["commands"][cmd_name] = command_rules
break

def export_meta_changes(self, only_break, output_type="text"):
ret_objs = []
ret_mod = {
Expand All @@ -270,36 +326,22 @@ def export_meta_changes(self, only_break, output_type="text"):
continue
ret = {}
for prop in self.EXPORTED_META_PROPERTY:
ret[prop] = getattr(obj, prop)
if hasattr(obj, prop):
ret[prop] = getattr(obj, prop)
ret["rule_name"] = obj.__class__.__name__
if output_type == "dict":
ret_objs.append(ret)
elif output_type == "text":
ret_objs.append(str(obj))
if output_type != "tree":
continue
command_tree = get_command_tree(obj.cmd_name)
command_group_info = ret_mod
while True:
if "is_group" not in command_tree:
break
if command_tree["is_group"]:
group_name = command_tree["group_name"]
if group_name not in command_group_info["sub_groups"]:
command_group_info["sub_groups"][group_name] = {
"name": group_name,
"commands": {},
"sub_groups": {}
}
command_tree = command_tree["sub_info"]
command_group_info = command_group_info["sub_groups"][group_name]
else:
cmd_name = command_tree["cmd_name"]
command_rules = []
if cmd_name in command_group_info["commands"]:
command_rules = command_group_info["commands"][cmd_name]
command_rules.append(ret)
command_group_info["commands"][cmd_name] = command_rules
break
if not hasattr(obj, "cmd_name") and not hasattr(obj, "subgroup_name"):
logger.info("unsupported rule ignored")
elif not hasattr(obj, "cmd_name") and hasattr(obj, "subgroup_name"):
self.fill_subgroup_rules(obj, ret_mod, ret)
elif not hasattr(obj, "subgroup_name") and hasattr(obj, "cmd_name"):
self.fill_cmd_rules(obj, ret_mod, ret)
else:
logger.info("unsupported rule ignored")

return ret_objs if output_type in ["text", "dict"] else ret_mod
26 changes: 26 additions & 0 deletions azdev/operations/command_change/meta_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ def __str__(self):
return " | ".join([str(a) for a in res])


class SubgroupAdd(MetaChange):
def __init__(self, subgroup_name, is_break=False):
if not subgroup_name:
raise Exception("sub group name needed")
self.rule_id = "1011"
self.subgroup_name = subgroup_name
self.is_break = is_break
self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name)
self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_name) \
if self.is_break else ""
super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)


class SubgroupRemove(MetaChange):
def __init__(self, subgroup_name, is_break=True):
if not subgroup_name:
raise Exception("sub group name needed")
self.rule_id = "1012"
self.subgroup_name = subgroup_name
self.is_break = is_break
self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name)
self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_name) \
if self.is_break else ""
super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)


class CmdAdd(MetaChange):
def __init__(self, cmd_name, is_break=False):
if not cmd_name:
Expand Down
18 changes: 12 additions & 6 deletions azdev/operations/command_change/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

logger = get_logger(__name__)

SUBGROUP_NAME_PATTERN = re.compile(r"\[\'sub_groups\'\]\[\'([a-zA-Z0-9\-\s]+)\'\]")
CMD_NAME_PATTERN = re.compile(r"\[\'commands\'\]\[\'([a-zA-Z0-9\-\s]+)\'\]")
CMD_PARAMETER_PROPERTY_PATTERN = re.compile(r"\[(.*?)\]")

Expand Down Expand Up @@ -183,19 +184,24 @@ def gen_commands_meta(commands_meta, meta_output_path=None):
f_out.write(jsbeautifier.beautify(json.dumps(module_info), options))


def extract_subgroup_name(key):
subgroup_ame_res = re.findall(SUBGROUP_NAME_PATTERN, key)
if not subgroup_ame_res or len(subgroup_ame_res) == 0:
return False, None
return True, subgroup_ame_res[-1]


def extract_cmd_name(key):
cmd_name_res = re.finditer(CMD_NAME_PATTERN, key)
if not cmd_name_res:
cmd_name_res = re.findall(CMD_NAME_PATTERN, key)
if not cmd_name_res or len(cmd_name_res) == 0:
return False, None
for cmd_match in cmd_name_res:
cmd_name = cmd_match.group(1)
return True, cmd_name
return True, cmd_name_res[0]


def extract_cmd_property(key, cmd_name):
cmd_key_pattern = re.compile(cmd_name + r"\'\]\[\'([a-zA-Z0-9\-\_]+)\'\]")
cmd_key_res = re.findall(cmd_key_pattern, key)
if not cmd_key_res:
if not cmd_key_res or len(cmd_key_res) == 0:
return False, None
return True, cmd_key_res[0]

Expand Down
13 changes: 12 additions & 1 deletion azdev/operations/tests/jsons/az_monitor_meta_before.json
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,18 @@
}]
}
},
"sub_groups": {}
"sub_groups": {
"monitor private-link-scope private-endpoint-connection cust": {
"name": "monitor private-link-scope private-endpoint-connection cust",
"commands": {
"monitor private-link-scope private-endpoint-connection cust show": {
"name": "monitor private-link-scope private-endpoint-connection cust show",
"parameters": []
}
},
"sub_groups": {}
}
}
}
}
}
Expand Down
28 changes: 20 additions & 8 deletions azdev/operations/tests/test_break_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import os
from azdev.operations.command_change import export_command_meta, cmp_command_meta
from azdev.operations.command_change.util import get_command_tree, extract_cmd_name, \
extract_cmd_property, extract_para_info
extract_cmd_property, extract_para_info, extract_subgroup_name


class MyTestCase(unittest.TestCase):
Expand Down Expand Up @@ -41,20 +41,32 @@ def test_diff_dict_key_parse(self):
para_res = extract_para_info(test_key)
self.assertEqual(para_res[0], "0", "cmd parameter parse failed")

def test_diff_dict_key_for_subgroups(self):
test_key = "root['sub_groups']['monitor']['sub_groups']['monitor account']"
has_cmd, _ = extract_cmd_name(test_key)
self.assertFalse(has_cmd, "cmd parse error from diff dict key")
has_subgroup, subgroup_name = extract_subgroup_name(test_key)
self.assertTrue(has_subgroup, "sub group parse failed from diff dict key")
self.assertEqual(subgroup_name, "monitor account", "sub group name extract failed")

def test_diff_meta(self):
if not os.path.exists("./jsons/az_monitor_meta_before.json") \
or not os.path.exists("./jsons/az_monitor_meta_after.json"):
return
result = cmp_command_meta(base_meta_file="./jsons/az_monitor_meta_before.json",
diff_meta_file="./jsons/az_monitor_meta_after.json",
output_type="text")
target_message = "please confirm cmd `monitor private-link-scope scoped-resource show` removed"
found = False
for line in result:
if line.find(target_message) > -1:
found = True
break
self.assertTrue(found, "target message not found")
target_message = [
"please confirm cmd `monitor private-link-scope scoped-resource show` removed",
"sub group `monitor private-link-scope private-endpoint-connection cust` removed",
]
for mes in target_message:
found = False
for line in result:
if line.find(mes) > -1:
found = True
break
self.assertTrue(found, "target message not found")


if __name__ == '__main__':
Expand Down
4 changes: 4 additions & 0 deletions azdev/utilities/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"1008": "cmd `{0}` update parameter `{1}`: added property `{2}`",
"1009": "cmd `{0}` update parameter `{1}`: removed property `{2}`",
"1010": "cmd `{0}` update parameter `{1}`: updated property `{2}` from `{3}` to `{4}`",
"1011": "sub group `{0}` added",
"1012": "sub group `{0}` removed",
}

CHANGE_SUGGEST_MESSAGE_MAPPING = {
Expand All @@ -49,4 +51,6 @@
"1008": "please remove property `{0}` for parameter `{1}` for cmd `{2}`",
"1009": "please add back property `{0}` for parameter {1}` for cmd `{2}`",
"1010": "please change property `{0}` from `{1}` to `{2}` for parameter `{3}` of cmd `{4}`",
"1011": "please confirm sub group `{0}` added",
"1012": "please confirm sub group `{0}` removed",
}

0 comments on commit 8484a04

Please sign in to comment.