From b5363d84bb67390f1af5fb92ea067373df3f5a6a Mon Sep 17 00:00:00 2001 From: Nathan O'Sullivan Date: Thu, 26 Feb 2026 17:43:20 +1000 Subject: [PATCH] refactor: read x-cli-entity-* extensions from OpenApi spec Replace hardcoded lookup_map/entity_map in templates/lookups.jinja with spec-driven logic that reads x-cli-entity-lookup (on parameters), x-cli-entity-list (on response schemas), and x-cli-entity-ref (on entity schemas) directly from the OpenAPI specification. --- ..._v_2_servers_server_id_actions_add_disk.py | 4 ++ ...2_servers_server_id_actions_delete_disk.py | 4 ++ lib/binarylane/models/size_options.py | 3 +- templates/lookups.jinja | 69 ++++++++++--------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_add_disk.py b/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_add_disk.py index 1b1883bd..3098d70e 100644 --- a/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_add_disk.py +++ b/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_add_disk.py @@ -86,6 +86,7 @@ def sync_detailed( This is used to add an additional disk in unallocated storage space. This does not alter the total disk space available to your server: to add additional disk space for your server use the 'Resize' action. + **This action may require the server to be rebooted.** Args: @@ -125,6 +126,7 @@ def sync( This is used to add an additional disk in unallocated storage space. This does not alter the total disk space available to your server: to add additional disk space for your server use the 'Resize' action. + **This action may require the server to be rebooted.** Args: @@ -157,6 +159,7 @@ async def asyncio_detailed( This is used to add an additional disk in unallocated storage space. This does not alter the total disk space available to your server: to add additional disk space for your server use the 'Resize' action. + **This action may require the server to be rebooted.** Args: @@ -194,6 +197,7 @@ async def asyncio( This is used to add an additional disk in unallocated storage space. This does not alter the total disk space available to your server: to add additional disk space for your server use the 'Resize' action. + **This action may require the server to be rebooted.** Args: diff --git a/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_delete_disk.py b/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_delete_disk.py index 606d8cf1..4afb55fa 100644 --- a/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_delete_disk.py +++ b/lib/binarylane/api/server_actions/post_v_2_servers_server_id_actions_delete_disk.py @@ -85,6 +85,7 @@ def sync_detailed( This is used to delete a disk added using the 'AddDisk' action. **NB: This is a destructive operation and no further confirmation will be requested.** + **This action may require the server to be rebooted.** Args: @@ -123,6 +124,7 @@ def sync( This is used to delete a disk added using the 'AddDisk' action. **NB: This is a destructive operation and no further confirmation will be requested.** + **This action may require the server to be rebooted.** Args: @@ -154,6 +156,7 @@ async def asyncio_detailed( This is used to delete a disk added using the 'AddDisk' action. **NB: This is a destructive operation and no further confirmation will be requested.** + **This action may require the server to be rebooted.** Args: @@ -190,6 +193,7 @@ async def asyncio( This is used to delete a disk added using the 'AddDisk' action. **NB: This is a destructive operation and no further confirmation will be requested.** + **This action may require the server to be rebooted.** Args: diff --git a/lib/binarylane/models/size_options.py b/lib/binarylane/models/size_options.py index 0944174d..a1819288 100644 --- a/lib/binarylane/models/size_options.py +++ b/lib/binarylane/models/size_options.py @@ -21,7 +21,8 @@ class SizeOptions: disk_cost_per_additional_gigabyte (float): The additional cost per GB per month for additional storage space. memory_max (int): The maximum memory in MB permitted on this size. memory_cost_per_additional_megabyte (float): The additional cost per MB per month for additional memory. - transfer_max (float): The maximum transfer in TB permitted for this size. + transfer_max (float): The maximum transfer in TB permitted for this size. If this is the same as Size.Transfer + no additional transfer is supported. transfer_cost_per_additional_gigabyte (float): The additional cost per GB per month for additional included transfer. ipv4_addresses_max (int): The maximum number of IPv4 addresses permitted on this size. diff --git a/templates/lookups.jinja b/templates/lookups.jinja index 2b95d283..03ea0251 100644 --- a/templates/lookups.jinja +++ b/templates/lookups.jinja @@ -1,46 +1,49 @@ {% filter trim %}{# Discard whitespace #} {# --- -This template synthesises "x-cli-lookup" and "x-cli-entity" extension properties -on path parameter and request schema definitions as the required data is not -available in OpenAPI Specification published by BinaryLane. +This template reads x-cli-entity-lookup, x-cli-entity-list, and x-cli-entity-ref +extension properties from the OpenAPI specification and adapts them to the +"x-cli-lookup" and "x-cli-entity" format used by downstream templates. --- #} -{# Map of attribute name to command that can resolve attribute reference. #} -{% set lookup_map = { - "load_balancer_id": "load-balancer list", - "server_id": "server list", - "vpc_id": "vpc list", -} %} - -{# Map of command to {id,ref} obect specifying the attributes within -x-cli-command's response that provide the reference (input) and id (output). #} -{% set entity_map = { - "load-balancer list": {"id": "id", "ref": "name"}, - "server list": {"id": "id", "ref": "name"}, - "vpc list": {"id": "id", "ref": "name"}, -} %} +{# Build global lookup map on first invocation by scanning all endpoints. #} +{% if openapi.cli_entity_lookup_map is not defined %} + {% set _ = openapi.__setattr__("cli_entity_lookup_map", {}) %} + {% for tag_key, tag_value in endpoint_collections_by_tag | dictsort %} + {% for ep in tag_value.endpoints if ep.data["x-cli-command"] %} + {% for param in (ep.data.parameters or []) if param.param_in == "path" and param["x-cli-entity-lookup"] %} + {% set _ = openapi.cli_entity_lookup_map.__setitem__(param.name, param["x-cli-entity-lookup"]) %} + {% endfor %} + {% endfor %} + {% endfor %} +{% endif %} -{# Add x-cli-lookup to attributes that support lookup. #} -{% for parameter_name in lookup_map %} +{# Copy x-cli-entity-lookup to x-cli-lookup on path parameters. #} +{% for param in (endpoint.data.parameters or []) if param.param_in == "path" and param["x-cli-entity-lookup"] %} + {% set _ = param.__setattr__("x-cli-lookup", param["x-cli-entity-lookup"]) %} +{% endfor %} - {# Examine path parameters #} - {% for param in (endpoint.data.parameters or []) if param.param_in == "path" and param.name == parameter_name %} - {% set _ = param.__setattr__("x-cli-lookup", lookup_map[parameter_name]) %} +{# Copy x-cli-entity-lookup to x-cli-lookup on matching request body properties. #} +{% if endpoint.json_body and endpoint.json_body.get_instance_type_string() != 'list' %} + {% for name, prop in openapi.models_by_class[endpoint.json_body.class_info].data.properties.items() %} + {% for parameter_name, lookup_command in openapi.cli_entity_lookup_map.items() if name in ["%s","%ss"] | map("format", parameter_name) %} + {% set _ = prop.__setattr__("x-cli-lookup", lookup_command) %} + {% endfor %} {% endfor %} - - {# Examine request body schema #} - {% if endpoint.json_body and endpoint.json_body.get_instance_type_string() != 'list' %} - {% for name, prop in openapi.models_by_class[endpoint.json_body.class_info].data.properties.items() if name in ["%s","%ss"] | map("format", parameter_name) %} - {% set _ = prop.__setattr__("x-cli-lookup", lookup_map[parameter_name]) %} +{% endif %} + +{# Build x-cli-entity on list endpoints from x-cli-entity-list and x-cli-entity-ref. #} +{% for response_type in endpoint.responses if response_type.status_code == 200 and response_type.prop.class_info %} + {% set response_model = openapi.models_by_class[response_type.prop.class_info] %} + {% if response_model.data["x-cli-entity-list"] %} + {% set list_prop_name = response_model.data["x-cli-entity-list"] %} + {% for prop in response_type.prop.required_properties if prop.name == list_prop_name and prop.inner_property.class_info %} + {% set entity_model = openapi.models_by_class[prop.inner_property.class_info] %} + {% if entity_model.data["x-cli-entity-ref"] %} + {% set _ = endpoint.data.__setattr__("x-cli-entity", {"id": "id", "ref": entity_model.data["x-cli-entity-ref"]}) %} + {% endif %} {% endfor %} {% endif %} - -{% endfor %} - -{# Add x-cli-entity if endpoint is is used to perform lookups. #} -{% for command_name in entity_map if endpoint.data["x-cli-command"] == command_name %} - {% set _ = endpoint.data.__setattr__("x-cli-entity", entity_map[command_name]) %} {% endfor %} {% endfilter %}