Skip to content

Commit 57900e6

Browse files
committed
feat: implementation
1 parent 8112a49 commit 57900e6

File tree

3 files changed

+389
-1
lines changed

3 files changed

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

0 commit comments

Comments
 (0)