Merge pull request 'API/Machine: refactor api returns readonly' (#4627) from readonly into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4627
This commit is contained in:
hsjobeki
2025-08-08 08:54:49 +00:00
2 changed files with 25 additions and 16 deletions

View File

@@ -98,23 +98,23 @@ def set_machine(machine: Machine, update: InventoryMachine) -> None:
) )
class Writeability(TypedDict): class FieldSchema(TypedDict):
writable: bool readonly: bool
reason: str | None reason: str | None
@API.register @API.register
def get_machine_writeability(machine: Machine) -> dict[str, Writeability]: def get_machine_fields_schema(machine: Machine) -> dict[str, FieldSchema]:
""" """
Get writeability information for the fields of a machine. Get attributes for each field of the machine.
This function checks which fields of the 'machine' resource are writable and provides a reason for each field's writability. This function checks which fields of the 'machine' resource are readonly and provides a reason if so.
Args: Args:
machine (Machine): The machine object for which to retrieve writeability. machine (Machine): The machine object for which to retrieve fields.
Returns: Returns:
dict[str, Writeability]: A map from field-names to { 'writable' (bool) and 'reason' (str or None ) } dict[str, FieldSchema]: A map from field-names to { 'readonly' (bool) and 'reason' (str or None ) }
""" """
inventory_store = InventoryStore(machine.flake) inventory_store = InventoryStore(machine.flake)
@@ -122,15 +122,24 @@ def get_machine_writeability(machine: Machine) -> dict[str, Writeability]:
field_names = retrieve_typed_field_names(InventoryMachine) field_names = retrieve_typed_field_names(InventoryMachine)
# TODO: handle this more generically protected_fields = {
"name", # name is always readonly
"machineClass", # machineClass can only be set during create
}
# TODO: handle this more generically. I.e via json schema
# persisted_data = inventory_store._get_persisted() # # persisted_data = inventory_store._get_persisted() #
# unmerge_lists(all_list, persisted_data) # unmerge_lists(all_list, persisted_data)
return { return {
field: { field: {
"writable": False "readonly": (
if field == "name" True
else is_writeable_key(f"machines.{machine.name}.{field}", write_info), if field in protected_fields
else not is_writeable_key(
f"machines.{machine.name}.{field}", write_info
)
),
# TODO: Provide a meaningful reason # TODO: Provide a meaningful reason
"reason": None, "reason": None,
} }

View File

@@ -10,7 +10,7 @@ from clan_lib.machines import actions as actions_module
from clan_lib.machines.machines import Machine from clan_lib.machines.machines import Machine
from clan_lib.nix_models.clan import Clan, InventoryMachine, Unknown from clan_lib.nix_models.clan import Clan, InventoryMachine, Unknown
from .actions import get_machine, get_machine_writeability, list_machines, set_machine from .actions import get_machine, get_machine_fields_schema, list_machines, set_machine
@pytest.mark.with_core @pytest.mark.with_core
@@ -191,13 +191,13 @@ def test_get_machine_writeability(clan_flake: Callable[..., Flake]) -> None:
}, },
) )
write_info = get_machine_writeability(Machine("jon", flake)) write_info = get_machine_fields_schema(Machine("jon", flake))
# {'tags': {'writable': True, 'reason': None}, 'machineClass': {'writable': False, 'reason': None}, 'name': {'writable': False, 'reason': None}, 'description': {'writable': True, 'reason': None}, 'deploy.buildHost': {'writable': True, 'reason': None}, 'icon': {'writable': True, 'reason': None}, 'deploy.targetHost': {'writable': True, 'reason': None}} # {'tags': {'writable': True, 'reason': None}, 'machineClass': {'writable': False, 'reason': None}, 'name': {'writable': False, 'reason': None}, 'description': {'writable': True, 'reason': None}, 'deploy.buildHost': {'writable': True, 'reason': None}, 'icon': {'writable': True, 'reason': None}, 'deploy.targetHost': {'writable': True, 'reason': None}}
writeable_fields = {field for field, info in write_info.items() if info["writable"]} writeable_fields = {
read_only_fields = { field for field, info in write_info.items() if not info["readonly"]
field for field, info in write_info.items() if not info["writable"]
} }
read_only_fields = {field for field, info in write_info.items() if info["readonly"]}
assert writeable_fields == { assert writeable_fields == {
"tags", "tags",