diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 3b124a36..2ea6e972 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -95,7 +95,7 @@ hesitate to open an issue in GitHub](https://github.com/balena-io/balena-sdk-pyt - [get_directly_accessible(slug_or_uuid_or_id, options)](#application.get_directly_accessible) ⇒ [TypeApplication](#typeapplication) - [get_id(slug_or_uuid_or_id)](#application.get_id) ⇒ int - [get_target_release_hash(slug_or_uuid_or_id)](#application.get_target_release_hash) ⇒ Union[str, None] - - [get_with_device_service_details(slug_or_uuid_or_id, options)](#application.get_with_device_service_details) ⇒ [TypeApplication](#typeapplication) + - [get_with_device_service_details(slug_or_uuid_or_id, options)](#application.get_with_device_service_details) ⇒ [TypeApplicationWithDeviceServiceDetails](#typeapplicationwithdeviceservicedetails) - [grant_support_access(slug_or_uuid_or_id, expiry_timestamp)](#application.grant_support_access) ⇒ None - [has(slug_or_uuid_or_id)](#application.has) ⇒ bool - [has_any()](#application.has_any) ⇒ bool @@ -171,7 +171,7 @@ hesitate to open an issue in GitHub](https://github.com/balena-io/balena-sdk-pyt - [get_supervisor_state(uuid_or_id)](#device.get_supervisor_state) ⇒ SupervisorStateType - [get_supervisor_target_state(uuid_or_id)](#device.get_supervisor_target_state) ⇒ Any - [get_supervisor_target_state_for_app(slug_or_uuid_or_id, release)](#device.get_supervisor_target_state_for_app) ⇒ Any - - [get_with_service_details(uuid_or_id, options)](#device.get_with_service_details) ⇒ [TypeDevice](#typedevice) + - [get_with_service_details(uuid_or_id, options)](#device.get_with_service_details) ⇒ [TypeDeviceWithServices](#typedevicewithservices) - [grant_support_access(uuid_or_id_or_ids, expiry_timestamp)](#device.grant_support_access) ⇒ None - [has(uuid_or_id)](#device.has) ⇒ bool - [has_device_url(uuid_or_id)](#device.has_device_url) ⇒ bool @@ -569,7 +569,7 @@ Get the hash of the current release for a specific application. ``` -### Function: get_with_device_service_details(slug_or_uuid_or_id, options) ⇒ [TypeApplication](#typeapplication) +### Function: get_with_device_service_details(slug_or_uuid_or_id, options) ⇒ [TypeApplicationWithDeviceServiceDetails](#typeapplicationwithdeviceservicedetails) This method does not map exactly to the underlying model: it runs a larger prebuilt query, and reformats it into an easy to use and @@ -1614,7 +1614,7 @@ Get the supervisor target state on a device ``` -### Function: get_with_service_details(uuid_or_id, options) ⇒ [TypeDevice](#typedevice) +### Function: get_with_service_details(uuid_or_id, options) ⇒ [TypeDeviceWithServices](#typedevicewithservices) This method does not map exactly to the underlying model: it runs a larger prebuilt query, and reformats it into an easy to use and @@ -4540,7 +4540,49 @@ The name must be a string; the optional doc argument can have any type. "application_environment_variable": Union[List[EnvironmentVariableBase], None], "build_environment_variable": Union[List[EnvironmentVariableBase], None], "application_tag": Union[List[BaseTagType], None], - "owns__device": Union[List[DeviceTypeType], None], + "owns__device": Union[List[TypeDevice], None], + "owns__public_device": Union[List[PublicDeviceType], None], + "owns__release": Union[List[ReleaseType], None], + "service": Union[List[ServiceType], None], + "is_depended_on_by__application": Union[List[ApplicationType], None], + "is_directly_accessible_by__user": Union[List[UserType], None], + "user_application_membership": Union[List[ApplicationMembershipType], None], + "team_application_access": Union[List[TeamApplicationAccessType], None], + "can_use__application_as_host": Union[List[ApplicationHostedOnApplication], None] +} +``` + + +### TypeApplicationWithDeviceServiceDetails + + +```python +{ + "id": int, + "created_at": str, + "app_name": str, + "actor": Union[List[ActorType], int], + "slug": str, + "uuid": str, + "is_accessible_by_support_until__date": str, + "is_host": bool, + "should_track_latest_release": bool, + "is_public": bool, + "is_of__class": Literal["fleet", "block", "app"], + "is_archived": bool, + "is_discoverable": bool, + "is_stored_at__repository_url": str, + "public_organization": Union[List[PublicOrganizationType], PineDeferred, None], + "application_type": Union[List[ApplicationType], PineDeferred], + "is_for__device_type": Union[List[DeviceTypeType], PineDeferred], + "depends_on__application": Union[List[ApplicationType], PineDeferred, None], + "organization": Union[List[OrganizationType], PineDeferred], + "should_be_running__release": Union[List[ReleaseType], PineDeferred, None], + "application_config_variable": Union[List[EnvironmentVariableBase], None], + "application_environment_variable": Union[List[EnvironmentVariableBase], None], + "build_environment_variable": Union[List[EnvironmentVariableBase], None], + "application_tag": Union[List[BaseTagType], None], + "owns__device": List[TypeDeviceWithServices], "owns__public_device": Union[List[PublicDeviceType], None], "owns__release": Union[List[ReleaseType], None], "service": Union[List[ServiceType], None], @@ -4632,6 +4674,75 @@ The name must be a string; the optional doc argument can have any type. ``` +### TypeDeviceWithServices + + +```python +{ + "id": int, + "actor": Union[List[ActorType], int], + "created_at": str, + "modified_at": str, + "custom_latitude": str, + "custom_longitude": str, + "device_name": str, + "download_progress": int, + "ip_address": str, + "public_address": str, + "mac_address": str, + "is_accessible_by_support_until__date": str, + "is_connected_to_vpn": bool, + "is_locked_until__date": str, + "is_web_accessible": bool, + "is_active": bool, + "is_frozen": bool, + "is_online": bool, + "last_connectivity_event": str, + "last_vpn_event": str, + "latitude": str, + "local_id": str, + "location": str, + "longitude": str, + "note": str, + "os_variant": str, + "os_version": str, + "provisioning_progress": int, + "provisioning_state": str, + "state": TypeDeviceState, + "status": str, + "status_sort_index": int, + "supervisor_version": str, + "uuid": str, + "vpn_address": str, + "api_heartbeat_state": Literal["online", "offline", "timeout", "unknown"], + "memory_usage": int, + "memory_total": int, + "storage_block_device": str, + "storage_usage": int, + "storage_total": int, + "cpu_usage": int, + "cpu_temp": int, + "cpu_id": str, + "is_undervolted": bool, + "overall_status": Any, + "overall_progress": int, + "is_of__device_type": Union[List[DeviceTypeType], PineDeferred], + "belongs_to__application": Union[List[TypeApplication], PineDeferred], + "belongs_to__user": Union[List[UserType], PineDeferred, None], + "is_running__release": Union[List[ReleaseType], PineDeferred, None], + "should_be_running__release": Union[List[ReleaseType], PineDeferred, None], + "is_managed_by__service_instance": Union[List[ServiceInstanceType], PineDeferred, None], + "should_be_managed_by__supervisor_release": Union[List[SupervisorReleaseType], PineDeferred, None], + "device_config_variable": Union[List[EnvironmentVariableBase], None], + "device_environment_variable": Union[List[EnvironmentVariableBase], None], + "device_tag": Union[List[BaseTagType], None], + "service_install": Union[List[ServiceInstanceType], None], + "image_install": Union[List[ImageInstallType], None], + "current_services": Dict[str, Any] +} +``` + + ### TypeVar diff --git a/balena/models/application.py b/balena/models/application.py index b90693e1..7c0d0f67 100644 --- a/balena/models/application.py +++ b/balena/models/application.py @@ -15,6 +15,7 @@ TypeApplication, BaseTagType, EnvironmentVariableBase, + TypeApplicationWithDeviceServiceDetails, ) from ..utils import ( generate_current_service_details, @@ -292,7 +293,7 @@ def get_with_device_service_details( self, slug_or_uuid_or_id: Union[str, int], options: AnyObject = {}, - ) -> TypeApplication: + ) -> TypeApplicationWithDeviceServiceDetails: """ This method does not map exactly to the underlying model: it runs a larger prebuilt query, and reformats it into an easy to use and diff --git a/balena/models/device.py b/balena/models/device.py index 20e89b7e..34a659c0 100644 --- a/balena/models/device.py +++ b/balena/models/device.py @@ -18,7 +18,7 @@ from ..resources import Message from ..settings import Settings from ..types import AnyObject -from ..types.models import BaseTagType, DeviceMetricsType, EnvironmentVariableBase, TypeDevice +from ..types.models import BaseTagType, DeviceMetricsType, EnvironmentVariableBase, TypeDevice, TypeDeviceWithServices from ..utils import ( ensure_version_compatibility, generate_current_service_details, @@ -404,7 +404,7 @@ def get(self, uuid_or_id: Union[str, int], options: AnyObject = {}) -> TypeDevic return device - def get_with_service_details(self, uuid_or_id: Union[str, int], options: AnyObject = {}) -> TypeDevice: + def get_with_service_details(self, uuid_or_id: Union[str, int], options: AnyObject = {}) -> TypeDeviceWithServices: """ This method does not map exactly to the underlying model: it runs a larger prebuilt query, and reformats it into an easy to use and diff --git a/balena/types/models.py b/balena/types/models.py index f14e0518..181aef69 100644 --- a/balena/types/models.py +++ b/balena/types/models.py @@ -1,4 +1,4 @@ -from typing import Any, List, Literal, Optional, TypedDict, Union, TypeVar +from typing import Any, List, Literal, Optional, TypedDict, Union, TypeVar, Dict __T = TypeVar("__T") @@ -146,7 +146,7 @@ class TypeApplication(TypedDict): application_environment_variable: ReverseNavigationResource["EnvironmentVariableBase"] build_environment_variable: ReverseNavigationResource["EnvironmentVariableBase"] application_tag: ReverseNavigationResource["BaseTagType"] - owns__device: ReverseNavigationResource["DeviceTypeType"] + owns__device: ReverseNavigationResource["TypeDevice"] owns__public_device: ReverseNavigationResource[PublicDeviceType] owns__release: ReverseNavigationResource["ReleaseType"] service: ReverseNavigationResource["ServiceType"] @@ -157,6 +157,10 @@ class TypeApplication(TypedDict): can_use__application_as_host: ReverseNavigationResource[ApplicationHostedOnApplication] +class TypeApplicationWithDeviceServiceDetails(TypeApplication): + owns__device: List["TypeDeviceWithServices"] # type: ignore + + class APIKeyInfoType(TypedDict, total=False): name: str description: Optional[str] @@ -331,6 +335,19 @@ class TypeDevice(TypedDict): image_install: ReverseNavigationResource[ImageInstallType] +class TypeCurrentService(TypedDict): + id: int + image_id: int + service_id: int + download_progress: int + status: str + install_date: str + + +class TypeDeviceWithServices(TypeDevice): + current_services: Dict[str, List[TypeCurrentService]] + + class DeviceMetricsType(TypedDict): memory_usage: int memory_total: int diff --git a/balena/utils.py b/balena/utils.py index 0def449d..dd0dd833 100644 --- a/balena/utils.py +++ b/balena/utils.py @@ -2,6 +2,7 @@ import re from collections import defaultdict from typing import Any, Callable, Dict, Literal, Optional, TypeVar +from .types.models import TypeDevice, TypeDeviceWithServices from semver.version import Version @@ -198,21 +199,21 @@ def get_single_install_summary(raw_data: Any) -> Any: return install -def generate_current_service_details(raw_device: Any) -> Any: +def generate_current_service_details(raw_device: TypeDevice) -> TypeDeviceWithServices: # TODO: Please compare me to node-sdk version grouped_services = defaultdict(list) - for obj in [get_single_install_summary(i) for i in raw_device.get("image_install", [])]: + for obj in [get_single_install_summary(i) for i in raw_device.get("image_install", [])]: # type: ignore grouped_services[obj.pop("service_name", None)].append(obj) - raw_device["current_services"] = dict(grouped_services) - raw_device["current_gateway_downloads"] = [ + raw_device["current_services"] = dict(grouped_services) # type: ignore + raw_device["current_gateway_downloads"] = [ # type: ignore get_single_install_summary(i) for i in raw_device.get("gateway_download", []) ] - raw_device.pop("image_install", None) - raw_device.pop("gateway_download", None) + raw_device.pop("image_install", None) # type: ignore + raw_device.pop("gateway_download", None) # type: ignore - return raw_device + return raw_device # type: ignore def is_provisioned(device: Any) -> bool: