Skip to content

Commit 9e3f9ce

Browse files
committed
feat: implementation
1 parent f2582aa commit 9e3f9ce

File tree

5 files changed

+681
-2
lines changed

5 files changed

+681
-2
lines changed

hcloud/storage_boxes/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,39 @@
33
from .client import (
44
BoundStorageBox,
55
BoundStorageBoxSnapshot,
6+
BoundStorageBoxSubaccount,
67
StorageBoxesClient,
78
StorageBoxesPageResult,
89
StorageBoxSnapshotsPageResult,
10+
StorageBoxSubaccountsPageResult,
911
)
1012
from .domain import (
1113
CreateStorageBoxResponse,
14+
CreateStorageBoxSnapshotResponse,
15+
CreateStorageBoxSubaccountResponse,
1216
DeleteStorageBoxResponse,
17+
DeleteStorageBoxSnapshotResponse,
18+
DeleteStorageBoxSubaccountResponse,
1319
StorageBox,
1420
StorageBoxAccessSettings,
1521
StorageBoxFoldersResponse,
1622
StorageBoxSnapshot,
1723
StorageBoxSnapshotPlan,
1824
StorageBoxStats,
25+
StorageBoxSubaccount,
26+
StorageBoxSubaccountAccessSettings,
1927
)
2028

2129
__all__ = [
2230
"BoundStorageBox",
2331
"BoundStorageBoxSnapshot",
32+
"BoundStorageBoxSubaccount",
2433
"CreateStorageBoxResponse",
34+
"CreateStorageBoxSnapshotResponse",
35+
"CreateStorageBoxSubaccountResponse",
2536
"DeleteStorageBoxResponse",
37+
"DeleteStorageBoxSnapshotResponse",
38+
"DeleteStorageBoxSubaccountResponse",
2639
"StorageBox",
2740
"StorageBoxAccessSettings",
2841
"StorageBoxesClient",
@@ -32,4 +45,7 @@
3245
"StorageBoxSnapshotPlan",
3346
"StorageBoxSnapshotsPageResult",
3447
"StorageBoxStats",
48+
"StorageBoxSubaccount",
49+
"StorageBoxSubaccountAccessSettings",
50+
"StorageBoxSubaccountsPageResult",
3551
]

hcloud/storage_boxes/client.py

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99
from .domain import (
1010
CreateStorageBoxResponse,
1111
CreateStorageBoxSnapshotResponse,
12+
CreateStorageBoxSubaccountResponse,
1213
DeleteStorageBoxResponse,
1314
DeleteStorageBoxSnapshotResponse,
15+
DeleteStorageBoxSubaccountResponse,
1416
StorageBox,
1517
StorageBoxAccessSettings,
1618
StorageBoxFoldersResponse,
1719
StorageBoxSnapshot,
1820
StorageBoxSnapshotPlan,
1921
StorageBoxSnapshotStats,
2022
StorageBoxStats,
23+
StorageBoxSubaccount,
24+
StorageBoxSubaccountAccessSettings,
2125
)
2226

2327
if TYPE_CHECKING:
@@ -134,6 +138,32 @@ def __init__(
134138
# TODO: implement bound methods
135139

136140

141+
class BoundStorageBoxSubaccount(BoundModelBase, StorageBoxSubaccount):
142+
_client: StorageBoxesClient
143+
144+
model = StorageBoxSubaccount
145+
146+
def __init__(
147+
self,
148+
client: StorageBoxesClient,
149+
data: dict[str, Any],
150+
complete: bool = True,
151+
):
152+
raw = data.get("storage_box")
153+
if raw is not None:
154+
data["storage_box"] = BoundStorageBox(
155+
client, data={"id": raw}, complete=False
156+
)
157+
158+
raw = data.get("access_settings")
159+
if raw is not None:
160+
data["access_settings"] = StorageBoxAccessSettings.from_dict(raw)
161+
162+
super().__init__(client, data, complete)
163+
164+
# TODO: implement bound methods
165+
166+
137167
class StorageBoxesPageResult(NamedTuple):
138168
storage_boxes: list[BoundStorageBox]
139169
meta: Meta
@@ -144,6 +174,11 @@ class StorageBoxSnapshotsPageResult(NamedTuple):
144174
meta: Meta
145175

146176

177+
class StorageBoxSubaccountsPageResult(NamedTuple):
178+
subaccounts: list[BoundStorageBoxSubaccount]
179+
meta: Meta
180+
181+
147182
class StorageBoxesClient(ResourceClientBase):
148183
"""
149184
A client for the Storage Boxes API.
@@ -790,3 +825,212 @@ def delete_snapshot(
790825
return DeleteStorageBoxSnapshotResponse(
791826
action=BoundAction(self._parent.actions, response["action"]),
792827
)
828+
829+
# Subaccounts
830+
###########################################################################
831+
832+
def get_subaccount_by_id(
833+
self,
834+
storage_box: StorageBox | BoundStorageBox,
835+
id: int,
836+
) -> BoundStorageBoxSubaccount:
837+
"""
838+
Returns a single Subaccount from a Storage Box.
839+
840+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-get-a-subaccount
841+
842+
:param storage_box: Storage Box to get the Subaccount from.
843+
:param id: ID of the Subaccount.
844+
"""
845+
response = self._client.request(
846+
method="GET",
847+
url=f"{self._base_url}/{storage_box.id}/subaccounts/{id}",
848+
)
849+
return BoundStorageBoxSubaccount(self, response["subaccount"])
850+
851+
def get_subaccount_by_username(
852+
self,
853+
storage_box: StorageBox | BoundStorageBox,
854+
username: str,
855+
) -> BoundStorageBoxSubaccount:
856+
"""
857+
Returns a single Subaccount from a Storage Box.
858+
859+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts
860+
861+
:param storage_box: Storage Box to get the Subaccount from.
862+
:param name: User name of the Subaccount.
863+
"""
864+
return self._get_first_by(
865+
self.get_subaccount_list,
866+
storage_box,
867+
username=username,
868+
)
869+
870+
def get_subaccount_list(
871+
self,
872+
storage_box: StorageBox | BoundStorageBox,
873+
*,
874+
username: str | None = None,
875+
label_selector: str | None = None,
876+
sort: list[str] | None = None,
877+
) -> StorageBoxSubaccountsPageResult:
878+
"""
879+
Returns all Subaccounts for a Storage Box.
880+
881+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts
882+
883+
:param storage_box: Storage Box to get the Subaccount from.
884+
:param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username.
885+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
886+
:param sort: Sort resources by field and direction.
887+
"""
888+
params: dict[str, Any] = {}
889+
if username is not None:
890+
params["username"] = username
891+
if label_selector is not None:
892+
params["label_selector"] = label_selector
893+
if sort is not None:
894+
params["sort"] = sort
895+
896+
response = self._client.request(
897+
method="GET",
898+
url=f"{self._base_url}/{storage_box.id}/subaccounts",
899+
params=params,
900+
)
901+
return StorageBoxSubaccountsPageResult(
902+
subaccounts=[
903+
BoundStorageBoxSubaccount(self, item)
904+
for item in response["subaccounts"]
905+
],
906+
meta=Meta.parse_meta(response),
907+
)
908+
909+
def get_subaccount_all(
910+
self,
911+
storage_box: StorageBox | BoundStorageBox,
912+
*,
913+
username: str | None = None,
914+
label_selector: str | None = None,
915+
sort: list[str] | None = None,
916+
) -> list[BoundStorageBoxSubaccount]:
917+
"""
918+
Returns all Subaccounts for a Storage Box.
919+
920+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-list-subaccounts
921+
922+
:param storage_box: Storage Box to get the Subaccount from.
923+
:param username: Filter resources by their username. The response will only contain the resources matching exactly the specified username.
924+
:param label_selector: Filter resources by labels. The response will only contain resources matching the label selector.
925+
:param sort: Sort resources by field and direction.
926+
"""
927+
# The endpoint does not have pagination, forward to the list method.
928+
result, _ = self.get_subaccount_list(
929+
storage_box,
930+
username=username,
931+
label_selector=label_selector,
932+
sort=sort,
933+
)
934+
return result
935+
936+
def create_subaccount(
937+
self,
938+
storage_box: StorageBox | BoundStorageBox,
939+
*,
940+
home_directory: str,
941+
password: str,
942+
access_settings: StorageBoxSubaccountAccessSettings | None = None,
943+
description: str | None = None,
944+
labels: dict[str, str] | None = None,
945+
) -> CreateStorageBoxSubaccountResponse:
946+
"""
947+
Creates a Subaccount for the Storage Box.
948+
949+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-create-a-subaccount
950+
951+
:param storage_box: Storage Box to create a Subaccount for.
952+
:param home_directory: Home directory of the Subaccount.
953+
:param password: Password of the Subaccount.
954+
:param access_settings: Access Settings of the Subaccount.
955+
:param description: Description of the Subaccount.
956+
:param labels: User-defined labels (key/value pairs) for the Resource.
957+
"""
958+
data: dict[str, Any] = {
959+
"home_directory": home_directory,
960+
"password": password,
961+
}
962+
if access_settings is not None:
963+
data["access_settings"] = access_settings.to_payload()
964+
if description is not None:
965+
data["description"] = description
966+
if labels is not None:
967+
data["labels"] = labels
968+
969+
response = self._client.request(
970+
method="POST",
971+
url=f"{self._base_url}/{storage_box.id}/subaccounts",
972+
json=data,
973+
)
974+
return CreateStorageBoxSubaccountResponse(
975+
subaccount=BoundStorageBoxSubaccount(
976+
self,
977+
response["subaccount"],
978+
# API only returns a partial object.
979+
complete=False,
980+
),
981+
action=BoundAction(self._parent.actions, response["action"]),
982+
)
983+
984+
def update_subaccount(
985+
self,
986+
subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount,
987+
*,
988+
description: str | None = None,
989+
labels: dict[str, str] | None = None,
990+
) -> BoundStorageBoxSubaccount:
991+
"""
992+
Updates a Storage Box Subaccount.
993+
994+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-update-a-subaccount
995+
996+
:param subaccount: Storage Box Subaccount to update.
997+
:param description: Description of the Subaccount.
998+
:param labels: User-defined labels (key/value pairs) for the Resource.
999+
"""
1000+
if subaccount.storage_box is None:
1001+
raise ValueError("subaccount storage_box property is none")
1002+
1003+
data: dict[str, Any] = {}
1004+
if description is not None:
1005+
data["description"] = description
1006+
if labels is not None:
1007+
data["labels"] = labels
1008+
1009+
response = self._client.request(
1010+
method="PUT",
1011+
url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}",
1012+
json=data,
1013+
)
1014+
return BoundStorageBoxSubaccount(self, response["subaccount"])
1015+
1016+
def delete_subaccount(
1017+
self,
1018+
subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount,
1019+
) -> DeleteStorageBoxSubaccountResponse:
1020+
"""
1021+
Deletes a Storage Box Subaccount.
1022+
1023+
See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccounts-delete-a-subaccount
1024+
1025+
:param subaccount: Storage Box Subaccount to delete.
1026+
"""
1027+
if subaccount.storage_box is None:
1028+
raise ValueError("subaccount storage_box property is none")
1029+
1030+
response = self._client.request(
1031+
method="DELETE",
1032+
url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}",
1033+
)
1034+
return DeleteStorageBoxSubaccountResponse(
1035+
action=BoundAction(self._parent.actions, response["action"]),
1036+
)

0 commit comments

Comments
 (0)