hydroserverpy 1.0.0__py3-none-any.whl → 1.1.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hydroserverpy might be problematic. Click here for more details.

hydroserverpy/api/main.py CHANGED
@@ -62,7 +62,7 @@ class HydroServer:
62
62
  auth = auth or self.auth
63
63
 
64
64
  if auth and auth[0] == "__key__":
65
- self._session.headers.update({"key": auth[1]})
65
+ self._session.headers.update({"X-API-Key": auth[1]})
66
66
  elif auth:
67
67
  self._session.headers.update(
68
68
  {"Authorization": f"Bearer {self._authenticate(auth[0], auth[1])}"}
@@ -2,6 +2,7 @@ from .iam.account import Account
2
2
  from .iam.workspace import Workspace
3
3
  from .iam.role import Role
4
4
  from .iam.collaborator import Collaborator
5
+ from .iam.apikey import APIKey
5
6
  from .iam.account import Account
6
7
  from .sta.datastream import Datastream
7
8
  from .sta.observed_property import ObservedProperty
@@ -17,5 +18,6 @@ from .etl.data_archive import DataArchive
17
18
  Workspace.model_rebuild()
18
19
  Role.model_rebuild()
19
20
  Collaborator.model_rebuild()
21
+ APIKey.model_rebuild()
20
22
 
21
23
  Unit.model_rebuild()
@@ -0,0 +1,77 @@
1
+ from typing import Optional, Union, TYPE_CHECKING
2
+ from uuid import UUID
3
+ from datetime import datetime
4
+ from pydantic import BaseModel
5
+ from ..base import HydroServerModel
6
+
7
+ if TYPE_CHECKING:
8
+ from hydroserverpy import HydroServer
9
+ from hydroserverpy.api.models import Workspace, Role
10
+
11
+
12
+ class APIKeyFields(BaseModel):
13
+ name: str
14
+ description: Optional[str] = None
15
+ is_active: bool
16
+ expires_at: Optional[datetime] = None
17
+ role: "Role"
18
+
19
+
20
+ class APIKey(HydroServerModel, APIKeyFields):
21
+ def __init__(self, _connection: "HydroServer", _uid: Union[UUID, str], **data):
22
+ super().__init__(
23
+ _connection=_connection, _model_ref="apikeys", _uid=_uid, **data
24
+ )
25
+
26
+ self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
27
+ self._workspace = None
28
+
29
+ @property
30
+ def workspace(self) -> "Workspace":
31
+ """The workspace this data source belongs to."""
32
+
33
+ if self._workspace is None and self._workspace_id:
34
+ self._workspace = self._connection.workspaces.get(uid=self._workspace_id)
35
+
36
+ return self._workspace
37
+
38
+ def refresh(self):
39
+ """Refresh this data source from HydroServer."""
40
+
41
+ self._original_data = (
42
+ self._connection.workspaces.get_api_key(
43
+ uid=self._workspace_id, api_key_id=self.uid
44
+ ).model_dump(exclude=["uid"])
45
+ )
46
+ self.__dict__.update(self._original_data)
47
+ self._workspace = None
48
+
49
+ def save(self):
50
+ """Save changes to this data source to HydroServer."""
51
+
52
+ if self._patch_data:
53
+ api_key = self._connection.workspaces.update_api_key(
54
+ uid=self._workspace_id, api_key_id=self.uid, **self._patch_data
55
+ )
56
+ self._original_data = api_key.dict(by_alias=False, exclude=["uid"])
57
+ self.__dict__.update(self._original_data)
58
+
59
+ def delete(self):
60
+ """Delete this data source from HydroServer."""
61
+
62
+ if not self._uid:
63
+ raise AttributeError("This resource cannot be deleted: UID is not set.")
64
+
65
+ self._connection.workspaces.delete_api_key(
66
+ uid=self._workspace_id, api_key_id=self.uid
67
+ )
68
+ self._uid = None
69
+
70
+ def regenerate(self):
71
+ """Regenerates this API key. WARNING: Previous key will be invalidated."""
72
+
73
+ _, key = self._connection.workspaces.regenerate_api_key(
74
+ uid=self._workspace_id, api_key_id=self.uid
75
+ )
76
+
77
+ return key
@@ -1,5 +1,6 @@
1
1
  from typing import List, Union, Optional, TYPE_CHECKING
2
2
  from uuid import UUID
3
+ from datetime import datetime
3
4
  from pydantic import BaseModel, Field, EmailStr
4
5
  from ..base import HydroServerModel
5
6
 
@@ -8,6 +9,7 @@ if TYPE_CHECKING:
8
9
  from hydroserverpy.api.models import (
9
10
  Role,
10
11
  Collaborator,
12
+ APIKey,
11
13
  Account,
12
14
  Thing,
13
15
  ObservedProperty,
@@ -189,6 +191,79 @@ class Workspace(HydroServerModel, WorkspaceFields):
189
191
 
190
192
  super()._delete()
191
193
 
194
+ def list_api_keys(self) -> List["APIKey"]:
195
+ """Get all API keys associated with this workspace."""
196
+
197
+ return self._connection.workspaces.list_api_keys(uid=self.uid)
198
+
199
+ def get_api_key(self, api_key: Union["APIKey", UUID, str]) -> "APIKey":
200
+ """Get an API key associated with this workspace."""
201
+
202
+ return self._connection.workspaces.get_api_key(
203
+ uid=self.uid,
204
+ api_key_id=str(getattr(api_key, "uid", api_key))
205
+ )
206
+
207
+ def create_api_key(
208
+ self,
209
+ role: Union["Role", UUID, str],
210
+ name: str,
211
+ description: Optional[str] = None,
212
+ is_active: bool = True,
213
+ expires_at: Optional[datetime] = None
214
+ ):
215
+ """Create an API key associated with this workspace."""
216
+
217
+ api_key, key = self._connection.workspaces.create_api_key(
218
+ uid=self.uid,
219
+ role=role,
220
+ name=name,
221
+ description=description,
222
+ is_active=is_active,
223
+ expires_at=expires_at
224
+ )
225
+
226
+ return api_key, key
227
+
228
+ def update_api_key(
229
+ self,
230
+ api_key_id: Union[UUID, str],
231
+ role: Union["Role", UUID, str] = ...,
232
+ name: str = ...,
233
+ description: Optional[str] = ...,
234
+ is_active: bool = ...,
235
+ expires_at: Optional[datetime] = ...
236
+ ):
237
+ """Create an API key associated with this workspace."""
238
+
239
+ return self._connection.workspaces.update_api_key(
240
+ uid=self.uid,
241
+ api_key_id=api_key_id,
242
+ role=role,
243
+ name=name,
244
+ description=description,
245
+ is_active=is_active,
246
+ expires_at=expires_at
247
+ )
248
+
249
+ def delete_api_key(self, api_key_id: Union[UUID, str]):
250
+ """Delete an API key associated with this workspace."""
251
+
252
+ return self._connection.workspaces.delete_api_key(
253
+ uid=self.uid,
254
+ api_key_id=api_key_id
255
+ )
256
+
257
+ def regenerate_api_key(self, api_key_id: Union[UUID, str]):
258
+ """Regenerate an API key associated with this workspace."""
259
+
260
+ api_key, key = self._connection.workspaces.regenerate_api_key(
261
+ uid=self.uid,
262
+ api_key_id=api_key_id
263
+ )
264
+
265
+ return api_key, key
266
+
192
267
  def add_collaborator(
193
268
  self, email: EmailStr, role: Union["Role", UUID, str]
194
269
  ) -> "Collaborator":
@@ -1,7 +1,8 @@
1
- from typing import TYPE_CHECKING, Union, List
1
+ from typing import TYPE_CHECKING, Union, List, Tuple, Optional
2
2
  from pydantic import EmailStr
3
3
  from uuid import UUID
4
- from hydroserverpy.api.models import Workspace, Role, Collaborator
4
+ from datetime import datetime
5
+ from hydroserverpy.api.models import Workspace, Role, Collaborator, APIKey
5
6
  from ..base import EndpointService
6
7
 
7
8
 
@@ -107,6 +108,114 @@ class WorkspaceService(EndpointService):
107
108
  path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/collaborators"
108
109
  self._connection.request("delete", path, json={"email": email})
109
110
 
111
+ def list_api_keys(self, uid: Union[UUID, str]) -> List["APIKey"]:
112
+ """Get all API keys associated with a workspace."""
113
+
114
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys"
115
+ response = self._connection.request("get", path)
116
+
117
+ return [APIKey(_connection=self._connection, _uid=UUID(str(obj.pop("id"))), **obj) for obj in response.json()]
118
+
119
+ def get_api_key(self, uid: Union[UUID, str], api_key_id: Union[UUID, str]) -> "APIKey":
120
+ """Get an API key associated with a workspace."""
121
+
122
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys/{api_key_id}"
123
+ response = self._connection.request("get", path).json()
124
+
125
+ return APIKey(_connection=self._connection, _uid=UUID(str(response.pop("id"))), **response)
126
+
127
+ def create_api_key(
128
+ self,
129
+ uid: Union[UUID, str],
130
+ role: Union["Role", UUID, str],
131
+ name: str,
132
+ description: Optional[str] = None,
133
+ is_active: bool = True,
134
+ expires_at: Optional[datetime] = None
135
+ ) -> Tuple["APIKey", str]:
136
+ """Create an API key for a workspace."""
137
+
138
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys"
139
+ kwargs = {
140
+ "roleId": str(getattr(role, "uid", role)),
141
+ "name": name,
142
+ "description": description,
143
+ "isActive": is_active,
144
+ "expiresAt": expires_at
145
+ }
146
+ headers = {"Content-type": "application/json"}
147
+
148
+ response = self._connection.request(
149
+ "post", path, headers=headers, json=self._to_iso_time(kwargs)
150
+ ).json()
151
+
152
+ return APIKey(
153
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
154
+ ), response["key"]
155
+
156
+ def update_api_key(
157
+ self,
158
+ uid: Union[UUID, str],
159
+ api_key_id: Union[UUID, str],
160
+ role: Union["Role", UUID, str] = ...,
161
+ name: str = ...,
162
+ description: Optional[str] = ...,
163
+ is_active: bool = ...,
164
+ expires_at: Optional[datetime] = ...
165
+ ) -> "APIKey":
166
+ """Update an existing API key."""
167
+
168
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys/{str(api_key_id)}"
169
+ kwargs = {
170
+ "roleId": ... if role is ... else str(getattr(role, "uid", role)),
171
+ "name": name,
172
+ "description": description,
173
+ "isActive": is_active,
174
+ "expiresAt": (
175
+ expires_at.isoformat()
176
+ if expires_at
177
+ not in (
178
+ None,
179
+ ...,
180
+ )
181
+ else None
182
+ )
183
+ }
184
+ headers = {"Content-type": "application/json"}
185
+
186
+ response = self._connection.request(
187
+ "patch", path, headers=headers,
188
+ json={k: v for k, v in kwargs.items() if v is not ...}
189
+ ).json()
190
+
191
+ return APIKey(
192
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
193
+ )
194
+
195
+ def delete_api_key(
196
+ self,
197
+ uid: Union[UUID, str],
198
+ api_key_id: Union[UUID, str]
199
+ ):
200
+ """Delete an existing API key."""
201
+
202
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys/{str(api_key_id)}"
203
+ self._connection.request("delete", path)
204
+
205
+ def regenerate_api_key(
206
+ self,
207
+ uid: Union[UUID, str],
208
+ api_key_id: Union[UUID, str]
209
+ ):
210
+ """Regenerate an existing API key."""
211
+
212
+ path = f"/{self._api_route}/{self._endpoint_route}/{str(uid)}/api-keys/{str(api_key_id)}/regenerate"
213
+ response = self._connection.request("put", path).json()
214
+
215
+ return APIKey(
216
+ _connection=self._connection, _uid=UUID(str(response.pop("id"))), **response
217
+ ), response["key"]
218
+
110
219
  def transfer_ownership(self, uid: Union[UUID, str], email: str) -> None:
111
220
  """Transfer ownership of a workspace to another HydroServer user."""
112
221
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydroserverpy
3
- Version: 1.0.0
3
+ Version: 1.1.0b1
4
4
  Requires-Python: <4,>=3.9
5
5
  License-File: LICENSE
6
6
  Requires-Dist: requests>=2
@@ -1,8 +1,8 @@
1
1
  hydroserverpy/__init__.py,sha256=FgaGFyhCjwmpJYEKNzOZxvfRx2neWMaOybj1z02_VSE,218
2
2
  hydroserverpy/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  hydroserverpy/api/http.py,sha256=C5DgvEeiu54RaL-9oouFPSKosC8Uy5Qdwm5hYh2Ps-s,620
4
- hydroserverpy/api/main.py,sha256=OWJpCIuheBgWQA7R33BiG89-upMS-a3K_AScbVeVjxQ,4760
5
- hydroserverpy/api/models/__init__.py,sha256=ELrf3b7Aix7YcVF__Q_8e_G_FF8GYlX0J5l7fkGcHnY,690
4
+ hydroserverpy/api/main.py,sha256=r-KD1Xyufzyt4YvRRhZbAngrj5h9yWLXiznx1Yda_8E,4766
5
+ hydroserverpy/api/models/__init__.py,sha256=buOhJ2Bf9yI0GftSyulpR74A5IhyyKJrXY1xANicohw,744
6
6
  hydroserverpy/api/models/base.py,sha256=dc2tfMSgizymxAAOVURfy7Jzeh6xIiiq7hfWZI7l1_Q,2280
7
7
  hydroserverpy/api/models/etl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  hydroserverpy/api/models/etl/data_archive.py,sha256=u-gpvUsaWaw0kyF3bPMm2e55Jx2yhvSV9ufXXaNtrTc,3429
@@ -11,9 +11,10 @@ hydroserverpy/api/models/etl/orchestration_configuration.py,sha256=ElSrgi7ioFZJF
11
11
  hydroserverpy/api/models/etl/orchestration_system.py,sha256=25En2G0z1gQzN-RW3UlrEGgkC952QDW21oYnawCX8hY,2357
12
12
  hydroserverpy/api/models/iam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  hydroserverpy/api/models/iam/account.py,sha256=7COk_CPYFlthg1uFWTBlJESfnuqMW90TSjZoIcBb-_8,439
14
+ hydroserverpy/api/models/iam/apikey.py,sha256=FLppIWqoBlFYqumecF1cKVm4R2-ZacyKrpObMW1g50s,2465
14
15
  hydroserverpy/api/models/iam/collaborator.py,sha256=jp661DKDCwk8c8HFPAV-YVhEc80F5eGDKaSHmH62Q8Q,1007
15
16
  hydroserverpy/api/models/iam/role.py,sha256=8FVTj_1QwtPF9tk7baliMVg000kjc5N8oP6eYo8vTDY,275
16
- hydroserverpy/api/models/iam/workspace.py,sha256=s9u1oZyOdxM7txjJARFcIBrWMHQSDxODdreiatFsXJs,7331
17
+ hydroserverpy/api/models/iam/workspace.py,sha256=rlRL52fAQrCZqmqMnCUNvBIWrlUGoYtQd16zCg-esWU,9630
17
18
  hydroserverpy/api/models/sta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
19
  hydroserverpy/api/models/sta/datastream.py,sha256=vRjgwAKaoBJEtgUXrZjIS-VuIZsCilm7FRwbvLS8Y8o,11186
19
20
  hydroserverpy/api/models/sta/observed_property.py,sha256=ThTg8aPMHPxbk9Hzpxw3AwM16gE1xvYpRK8UkiOdGeA,2180
@@ -29,7 +30,7 @@ hydroserverpy/api/services/etl/data_archive.py,sha256=hlNJOHJSZ1kV2n2xivWIBtT1Eo
29
30
  hydroserverpy/api/services/etl/data_source.py,sha256=DCgTyh8lF2iwh4uszePFg9UupXxJCN7Ww9Ut1MQKHis,6491
30
31
  hydroserverpy/api/services/etl/orchestration_system.py,sha256=JFuSJJUq4JJUt8KlZ-Ga0ktyQIe2U0Sa7ogd4oLjex4,2166
31
32
  hydroserverpy/api/services/iam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- hydroserverpy/api/services/iam/workspace.py,sha256=jJiqkMxFEp9REjR4LXyVp2o45CGBrrEadGelPPCuRJs,4547
33
+ hydroserverpy/api/services/iam/workspace.py,sha256=KQ1zH7EnEcBrpeF-2APAo3aMiiSGCaUgOy_GwRQtyRI,8395
33
34
  hydroserverpy/api/services/sta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
35
  hydroserverpy/api/services/sta/datastream.py,sha256=_m-xFom3z8wo5-1_q8NjWUpcw36wYv1brIG7xeGGadk,12402
35
36
  hydroserverpy/api/services/sta/observed_property.py,sha256=nRlqBldJpXlj8VOZ4EwNOs4ZgmBw5w-EqAChfM3Z0Z0,2908
@@ -58,9 +59,9 @@ hydroserverpy/etl_csv/exceptions.py,sha256=0UY8YUlNepG0y6FfH36hJyR1bOhwYHSZIdUSS
58
59
  hydroserverpy/etl_csv/hydroserver_etl_csv.py,sha256=0ueBphEaAAlsb0cn71Ihgd5zOD8Zdu4Ts_yGwvXW53M,14544
59
60
  hydroserverpy/quality/__init__.py,sha256=GGBMkFSXciJLYrbV-NraFrj_mXWCy_GTcy9KKrKXU4c,84
60
61
  hydroserverpy/quality/service.py,sha256=U02UfLKVmFvr5ySiH0n0JYzUIabq5uprrHIiwcqBlqY,13879
61
- hydroserverpy-1.0.0.dist-info/licenses/LICENSE,sha256=xVqFxDw3QOEJukakL7gQCqIMTQ1dlSCTo6Oc1otNW80,1508
62
- hydroserverpy-1.0.0.dist-info/METADATA,sha256=Da0zrvryIMT9-eYhmsNES05Ebu7SwqYQIAs0qIe_19c,530
63
- hydroserverpy-1.0.0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
64
- hydroserverpy-1.0.0.dist-info/top_level.txt,sha256=Zf37hrncXLOYvXhgCrf5mZdeq81G9fShdE2LfYbtb7w,14
65
- hydroserverpy-1.0.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
66
- hydroserverpy-1.0.0.dist-info/RECORD,,
62
+ hydroserverpy-1.1.0b1.dist-info/licenses/LICENSE,sha256=xVqFxDw3QOEJukakL7gQCqIMTQ1dlSCTo6Oc1otNW80,1508
63
+ hydroserverpy-1.1.0b1.dist-info/METADATA,sha256=SF2m2AtxqgHEz0XO7ozpwAimryavdn5Hap9rdYEdSIA,532
64
+ hydroserverpy-1.1.0b1.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
65
+ hydroserverpy-1.1.0b1.dist-info/top_level.txt,sha256=Zf37hrncXLOYvXhgCrf5mZdeq81G9fShdE2LfYbtb7w,14
66
+ hydroserverpy-1.1.0b1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
67
+ hydroserverpy-1.1.0b1.dist-info/RECORD,,