refactor(persist/util): improve calc_patches
This commit is contained in:
@@ -155,7 +155,7 @@ def calc_patches(
|
||||
|
||||
Given its current state and the update to apply.
|
||||
|
||||
Filters out nix-values so it doesnt matter if the anyone sends them.
|
||||
Filters out nix-values so it doesn't matter if the anyone sends them.
|
||||
|
||||
: param persisted: The current state of the inventory.
|
||||
: param update: The update to apply.
|
||||
@@ -165,9 +165,9 @@ def calc_patches(
|
||||
|
||||
Returns a tuple with the SET and DELETE patches.
|
||||
"""
|
||||
persisted_flat = flatten_data(persisted)
|
||||
update_flat = flatten_data(update)
|
||||
all_values_flat = flatten_data(all_values)
|
||||
data_all = flatten_data(all_values)
|
||||
data_all_updated = flatten_data(update)
|
||||
data_dyn = flatten_data(persisted)
|
||||
|
||||
def is_writeable_key(key: str) -> bool:
|
||||
"""
|
||||
@@ -187,49 +187,66 @@ def calc_patches(
|
||||
msg = f"Cannot determine writeability for key '{key}'"
|
||||
raise ClanError(msg, description="F001")
|
||||
|
||||
all_keys = set(data_all) | set(data_all_updated)
|
||||
patchset = {}
|
||||
for update_key, update_data in update_flat.items():
|
||||
if not is_writeable_key(update_key):
|
||||
if update_data != all_values_flat.get(update_key):
|
||||
msg = f"Key '{update_key}' is not writeable."
|
||||
raise ClanError(msg)
|
||||
continue
|
||||
|
||||
if is_writeable_key(update_key):
|
||||
prev_value = all_values_flat.get(update_key)
|
||||
if prev_value and type(update_data) is not type(prev_value):
|
||||
msg = f"Type mismatch for key '{update_key}'. Cannot update {type(all_values_flat.get(update_key))} with {type(update_data)}"
|
||||
delete_set = find_deleted_paths(all_values, update, parent_key="")
|
||||
|
||||
for key in all_keys:
|
||||
# Get the old and new values
|
||||
old = data_all.get(key, None)
|
||||
new = data_all_updated.get(key, None)
|
||||
|
||||
# Some kind of change
|
||||
if old != new:
|
||||
# If there is a change, check if the key is writeable
|
||||
if not is_writeable_key(key):
|
||||
msg = f"Key '{key}' is not writeable."
|
||||
raise ClanError(msg)
|
||||
|
||||
# Handle list separation
|
||||
if isinstance(update_data, list):
|
||||
duplicates = find_duplicates(update_data)
|
||||
if any(key.startswith(d) for d in delete_set):
|
||||
# Skip this key if it or any of its parent paths are marked for deletion
|
||||
continue
|
||||
|
||||
if old is not None and type(old) is not type(new):
|
||||
if new is None:
|
||||
# If this is a deleted key, they are handled by 'find_deleted_paths'
|
||||
continue
|
||||
|
||||
msg = f"Type mismatch for key '{key}'. Cannot update {type(old)} with {type(new)}"
|
||||
description = f"""
|
||||
Previous_value is of type '{type(old)}' this operation would change it to '{type(new)}'.
|
||||
|
||||
Prev: {old}
|
||||
->
|
||||
After: {new}
|
||||
"""
|
||||
raise ClanError(msg, description=description)
|
||||
|
||||
if isinstance(new, list):
|
||||
duplicates = find_duplicates(new)
|
||||
if duplicates:
|
||||
msg = f"Key '{update_key}' contains duplicates: {duplicates}. This not supported yet."
|
||||
msg = f"Key '{key}' contains duplicates: {duplicates}. This not supported yet."
|
||||
raise ClanError(msg)
|
||||
# List of current values
|
||||
persisted_data = persisted_flat.get(update_key, [])
|
||||
persisted_data = data_dyn.get(key, [])
|
||||
# List including nix values
|
||||
all_list = all_values_flat.get(update_key, [])
|
||||
all_list = data_all.get(key, [])
|
||||
nix_list = unmerge_lists(all_list, persisted_data)
|
||||
if update_data != all_list:
|
||||
patchset[update_key] = unmerge_lists(update_data, nix_list)
|
||||
if new != all_list:
|
||||
patchset[key] = unmerge_lists(new, nix_list)
|
||||
else:
|
||||
patchset[key] = new
|
||||
|
||||
elif update_data != persisted_flat.get(update_key, None):
|
||||
patchset[update_key] = update_data
|
||||
|
||||
continue
|
||||
|
||||
msg = f"Cannot determine writeability for key '{update_key}'"
|
||||
# Ensure not inadvertently patching something already marked for deletion
|
||||
conflicts = {key for d in delete_set for key in patchset if key.startswith(d)}
|
||||
if conflicts:
|
||||
conflict_list = ", ".join(sorted(conflicts))
|
||||
msg = (
|
||||
f"The following keys are marked for deletion but also have update values: {conflict_list}. "
|
||||
"You cannot delete and patch the same key and its subkeys."
|
||||
)
|
||||
raise ClanError(msg)
|
||||
|
||||
delete_set = find_deleted_paths(persisted, update)
|
||||
|
||||
for delete_key in delete_set:
|
||||
if not is_writeable_key(delete_key):
|
||||
msg = f"Cannot delete: Key '{delete_key}' is not writeable."
|
||||
raise ClanError(msg)
|
||||
|
||||
return patchset, delete_set
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user