hydroserverpy 1.2.1__py3-none-any.whl → 1.3.0__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/__init__.py +1 -1
- hydroserverpy/api/{main.py → client.py} +52 -22
- hydroserverpy/api/models/__init__.py +1 -2
- hydroserverpy/api/models/base.py +180 -47
- hydroserverpy/api/models/etl/data_archive.py +31 -59
- hydroserverpy/api/models/etl/data_source.py +34 -76
- hydroserverpy/api/models/etl/orchestration_system.py +23 -38
- hydroserverpy/api/models/iam/apikey.py +57 -38
- hydroserverpy/api/models/iam/collaborator.py +55 -19
- hydroserverpy/api/models/iam/role.py +32 -4
- hydroserverpy/api/models/iam/workspace.py +58 -86
- hydroserverpy/api/models/sta/datastream.py +122 -214
- hydroserverpy/api/models/sta/observation.py +101 -0
- hydroserverpy/api/models/sta/observed_property.py +18 -53
- hydroserverpy/api/models/sta/processing_level.py +16 -31
- hydroserverpy/api/models/sta/result_qualifier.py +16 -31
- hydroserverpy/api/models/sta/sensor.py +27 -88
- hydroserverpy/api/models/sta/thing.py +48 -152
- hydroserverpy/api/models/sta/unit.py +16 -29
- hydroserverpy/api/services/__init__.py +1 -0
- hydroserverpy/api/services/base.py +92 -76
- hydroserverpy/api/services/etl/data_archive.py +42 -72
- hydroserverpy/api/services/etl/data_source.py +42 -72
- hydroserverpy/api/services/etl/orchestration_system.py +25 -33
- hydroserverpy/api/services/iam/role.py +38 -0
- hydroserverpy/api/services/iam/workspace.py +96 -99
- hydroserverpy/api/services/sta/datastream.py +150 -211
- hydroserverpy/api/services/sta/observed_property.py +31 -49
- hydroserverpy/api/services/sta/processing_level.py +30 -36
- hydroserverpy/api/services/sta/result_qualifier.py +24 -34
- hydroserverpy/api/services/sta/sensor.py +34 -48
- hydroserverpy/api/services/sta/thing.py +96 -89
- hydroserverpy/api/services/sta/unit.py +30 -34
- hydroserverpy/api/utils.py +22 -0
- hydroserverpy/etl/extractors/base.py +2 -4
- hydroserverpy/etl/loaders/hydroserver_loader.py +1 -0
- hydroserverpy/etl/timestamp_parser.py +82 -48
- hydroserverpy/etl/transformers/base.py +5 -9
- hydroserverpy/etl_csv/hydroserver_etl_csv.py +18 -24
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0.dist-info}/METADATA +1 -1
- hydroserverpy-1.3.0.dist-info/RECORD +70 -0
- hydroserverpy/api/http.py +0 -22
- hydroserverpy-1.2.1.dist-info/RECORD +0 -68
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0.dist-info}/WHEEL +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0.dist-info}/top_level.txt +0 -0
- {hydroserverpy-1.2.1.dist-info → hydroserverpy-1.3.0.dist-info}/zip-safe +0 -0
|
@@ -1,60 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import uuid
|
|
2
2
|
import tempfile
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
from pydantic import
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Union, ClassVar, Optional, TYPE_CHECKING, List
|
|
5
|
+
from pydantic import Field
|
|
6
6
|
from hydroserverpy.etl_csv.hydroserver_etl_csv import HydroServerETLCSV
|
|
7
7
|
from .orchestration_system import OrchestrationSystem
|
|
8
8
|
from .orchestration_configuration import OrchestrationConfigurationFields
|
|
9
9
|
from ..sta.datastream import Datastream
|
|
10
|
-
from ..base import
|
|
10
|
+
from ..base import HydroServerBaseModel
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from hydroserverpy import HydroServer
|
|
14
14
|
from hydroserverpy.api.models import Workspace
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class DataSource(
|
|
18
|
+
HydroServerBaseModel, OrchestrationConfigurationFields
|
|
19
|
+
):
|
|
18
20
|
name: str = Field(..., max_length=255)
|
|
19
21
|
settings: Optional[dict] = None
|
|
22
|
+
orchestration_system_id: uuid.UUID
|
|
23
|
+
workspace_id: uuid.UUID
|
|
20
24
|
|
|
25
|
+
_editable_fields: ClassVar[set[str]] = {
|
|
26
|
+
"name", "settings", "interval", "interval_units", "crontab", "start_time", "end_time", "last_run_successful",
|
|
27
|
+
"last_run_message", "last_run", "next_run", "paused"
|
|
28
|
+
}
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
super().__init__(
|
|
25
|
-
_connection=_connection, _model_ref="datasources", _uid=_uid, **data
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
|
|
29
|
-
self._orchestration_system_id = str(
|
|
30
|
-
data.get("orchestration_system_id") or data["orchestrationSystem"]["id"]
|
|
31
|
-
)
|
|
30
|
+
def __init__(self, client: "HydroServer", **data):
|
|
31
|
+
super().__init__(client=client, service=client.datasources, **data)
|
|
32
32
|
|
|
33
33
|
self._workspace = None
|
|
34
|
+
self._orchestration_system = None
|
|
35
|
+
self._datastreams = None
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
_uid=self._orchestration_system_id,
|
|
39
|
-
**data["orchestrationSystem"]
|
|
40
|
-
)
|
|
41
|
-
else:
|
|
42
|
-
self._orchestration_system = None
|
|
43
|
-
|
|
44
|
-
if data.get("datastreams"):
|
|
45
|
-
self._datastreams = [
|
|
46
|
-
Datastream(_connection=_connection, _uid=datastream["id"], **datastream)
|
|
47
|
-
for datastream in data["datastreams"]
|
|
48
|
-
]
|
|
49
|
-
else:
|
|
50
|
-
self._datastreams = []
|
|
37
|
+
@classmethod
|
|
38
|
+
def get_route(cls):
|
|
39
|
+
return "data-sources"
|
|
51
40
|
|
|
52
41
|
@property
|
|
53
42
|
def workspace(self) -> "Workspace":
|
|
54
43
|
"""The workspace this data source belongs to."""
|
|
55
44
|
|
|
56
|
-
if self._workspace is None
|
|
57
|
-
self._workspace = self.
|
|
45
|
+
if self._workspace is None:
|
|
46
|
+
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
58
47
|
|
|
59
48
|
return self._workspace
|
|
60
49
|
|
|
@@ -62,62 +51,31 @@ class DataSource(HydroServerModel, DataSourceFields, OrchestrationConfigurationF
|
|
|
62
51
|
def orchestration_system(self) -> "OrchestrationSystem":
|
|
63
52
|
"""The orchestration system that manages this data source."""
|
|
64
53
|
|
|
65
|
-
if self._orchestration_system is None
|
|
66
|
-
self._orchestration_system = self.
|
|
67
|
-
uid=self._orchestration_system_id
|
|
68
|
-
)
|
|
54
|
+
if self._orchestration_system is None:
|
|
55
|
+
self._orchestration_system = self.client.orchestrationsystems.get(uid=self.orchestration_system_id)
|
|
69
56
|
|
|
70
57
|
return self._orchestration_system
|
|
71
58
|
|
|
72
|
-
@orchestration_system.setter
|
|
73
|
-
def orchestration_system(
|
|
74
|
-
self, orchestration_system: Union["OrchestrationSystem", UUID, str]
|
|
75
|
-
):
|
|
76
|
-
if not orchestration_system:
|
|
77
|
-
raise ValueError("Orchestration system of data source cannot be None.")
|
|
78
|
-
if str(getattr(orchestration_system, "uid", orchestration_system)) != str(
|
|
79
|
-
self.orchestration_system.uid
|
|
80
|
-
):
|
|
81
|
-
self._orchestration_system = self._connection.orchestrationsystems.get(
|
|
82
|
-
uid=str(getattr(orchestration_system, "uid", orchestration_system))
|
|
83
|
-
)
|
|
84
|
-
|
|
85
59
|
@property
|
|
86
60
|
def datastreams(self) -> List["Datastream"]:
|
|
87
61
|
"""The datastreams this data source provides data for."""
|
|
88
62
|
|
|
89
63
|
if self._datastreams is None:
|
|
90
|
-
|
|
91
|
-
self._datastreams = data_source.datastreams
|
|
64
|
+
self._datastreams = self.client.datastreams.list(data_source=self.uid, fetch_all=True).items
|
|
92
65
|
|
|
93
66
|
return self._datastreams
|
|
94
67
|
|
|
95
|
-
def
|
|
96
|
-
"""Refresh this data source from HydroServer."""
|
|
97
|
-
|
|
98
|
-
super()._refresh()
|
|
99
|
-
self._workspace = None
|
|
100
|
-
self._datastreams = None
|
|
101
|
-
|
|
102
|
-
def save(self):
|
|
103
|
-
"""Save changes to this data source to HydroServer."""
|
|
104
|
-
|
|
105
|
-
super()._save()
|
|
106
|
-
|
|
107
|
-
def delete(self):
|
|
108
|
-
"""Delete this data source from HydroServer."""
|
|
109
|
-
|
|
110
|
-
super()._delete()
|
|
111
|
-
|
|
112
|
-
def add_datastream(self, datastream: Union["Datastream", UUID, str]):
|
|
68
|
+
def add_datastream(self, datastream: Union["Datastream", uuid.UUID, str]):
|
|
113
69
|
"""Add a datastream to this data source."""
|
|
114
70
|
|
|
115
|
-
self.
|
|
71
|
+
self.client.datasources.add_datastream(
|
|
72
|
+
uid=self.uid, datastream=datastream
|
|
73
|
+
)
|
|
116
74
|
|
|
117
|
-
def remove_datastream(self, datastream: Union["Datastream", UUID, str]):
|
|
75
|
+
def remove_datastream(self, datastream: Union["Datastream", uuid.UUID, str]):
|
|
118
76
|
"""Remove a datastream from this data source."""
|
|
119
77
|
|
|
120
|
-
self.
|
|
78
|
+
self.client.datasources.remove_datastream(
|
|
121
79
|
uid=self.uid, datastream=datastream
|
|
122
80
|
)
|
|
123
81
|
|
|
@@ -131,7 +89,7 @@ class DataSource(HydroServerModel, DataSourceFields, OrchestrationConfigurationF
|
|
|
131
89
|
if self.settings["extractor"]["type"] == "local":
|
|
132
90
|
with open(self.settings["extractor"]["sourceUri"]) as data_file:
|
|
133
91
|
loader = HydroServerETLCSV(
|
|
134
|
-
self.
|
|
92
|
+
self.client, data_file=data_file, data_source=self
|
|
135
93
|
)
|
|
136
94
|
loader.run()
|
|
137
95
|
elif self.settings["extractor"]["type"] == "HTTP":
|
|
@@ -148,6 +106,6 @@ class DataSource(HydroServerModel, DataSourceFields, OrchestrationConfigurationF
|
|
|
148
106
|
temp_file.write(chunk.decode("utf-8"))
|
|
149
107
|
temp_file.seek(0)
|
|
150
108
|
loader = HydroServerETLCSV(
|
|
151
|
-
self.
|
|
109
|
+
self.client, data_file=temp_file, data_source=self
|
|
152
110
|
)
|
|
153
111
|
loader.run()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Optional, ClassVar, List, TYPE_CHECKING
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
|
-
from ..base import
|
|
4
|
+
from ..base import HydroServerBaseModel
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
7
|
from hydroserverpy import HydroServer
|
|
@@ -13,27 +13,30 @@ class OrchestrationSystemFields(BaseModel):
|
|
|
13
13
|
orchestration_system_type: str = Field(..., max_length=255, alias="type")
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class OrchestrationSystem(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
_model_ref="orchestrationsystems",
|
|
21
|
-
_uid=_uid,
|
|
22
|
-
**data
|
|
23
|
-
)
|
|
16
|
+
class OrchestrationSystem(HydroServerBaseModel):
|
|
17
|
+
name: str = Field(..., max_length=255)
|
|
18
|
+
orchestration_system_type: str = Field(..., max_length=255, alias="type")
|
|
19
|
+
workspace_id: Optional[uuid.UUID] = None
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
_editable_fields: ClassVar[set[str]] = {"name", "orchestration_system_type"}
|
|
22
|
+
|
|
23
|
+
def __init__(self, client: "HydroServer", **data):
|
|
24
|
+
super().__init__(client=client, service=client.orchestrationsystems, **data)
|
|
26
25
|
|
|
27
26
|
self._workspace = None
|
|
28
27
|
self._datasources = None
|
|
29
28
|
self._dataarchives = None
|
|
30
29
|
|
|
30
|
+
@classmethod
|
|
31
|
+
def get_route(cls):
|
|
32
|
+
return "orchestration-systems"
|
|
33
|
+
|
|
31
34
|
@property
|
|
32
35
|
def workspace(self) -> "Workspace":
|
|
33
36
|
"""The workspace this orchestration system belongs to."""
|
|
34
37
|
|
|
35
|
-
if self._workspace is None and self.
|
|
36
|
-
self._workspace = self.
|
|
38
|
+
if self._workspace is None and self.workspace_id:
|
|
39
|
+
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
37
40
|
|
|
38
41
|
return self._workspace
|
|
39
42
|
|
|
@@ -42,9 +45,9 @@ class OrchestrationSystem(HydroServerModel, OrchestrationSystemFields):
|
|
|
42
45
|
"""The data sources associated with this workspace."""
|
|
43
46
|
|
|
44
47
|
if self._datasources is None:
|
|
45
|
-
self._datasources = self.
|
|
46
|
-
orchestration_system=self.uid
|
|
47
|
-
)
|
|
48
|
+
self._datasources = self.client.datasources.list(
|
|
49
|
+
orchestration_system=self.uid, fetch_all=True
|
|
50
|
+
).items
|
|
48
51
|
|
|
49
52
|
return self._datasources
|
|
50
53
|
|
|
@@ -53,26 +56,8 @@ class OrchestrationSystem(HydroServerModel, OrchestrationSystemFields):
|
|
|
53
56
|
"""The data archives associated with this workspace."""
|
|
54
57
|
|
|
55
58
|
if self._dataarchives is None:
|
|
56
|
-
self._dataarchives = self.
|
|
57
|
-
orchestration_system=self.uid
|
|
58
|
-
)
|
|
59
|
+
self._dataarchives = self.client.dataarchives.list(
|
|
60
|
+
orchestration_system=self.uid, fetch_all=True
|
|
61
|
+
).items
|
|
59
62
|
|
|
60
63
|
return self._dataarchives
|
|
61
|
-
|
|
62
|
-
def refresh(self):
|
|
63
|
-
"""Refresh this orchestration system from HydroServer."""
|
|
64
|
-
|
|
65
|
-
super()._refresh()
|
|
66
|
-
self._workspace = None
|
|
67
|
-
self._datasources = None
|
|
68
|
-
self._dataarchives = None
|
|
69
|
-
|
|
70
|
-
def save(self):
|
|
71
|
-
"""Save changes to this orchestration system to HydroServer."""
|
|
72
|
-
|
|
73
|
-
super()._save()
|
|
74
|
-
|
|
75
|
-
def delete(self):
|
|
76
|
-
"""Delete this orchestration system from HydroServer."""
|
|
77
|
-
|
|
78
|
-
super()._delete()
|
|
@@ -1,77 +1,96 @@
|
|
|
1
|
-
from typing import Optional, Union, TYPE_CHECKING
|
|
1
|
+
from typing import Optional, Union, ClassVar, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from hydroserverpy.api.models.iam.role import Role
|
|
5
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
6
|
+
from ..base import HydroServerBaseModel
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from hydroserverpy import HydroServer
|
|
9
|
-
from hydroserverpy.api.models import Workspace
|
|
10
|
+
from hydroserverpy.api.models import Workspace
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
class
|
|
13
|
+
class APIKey(HydroServerBaseModel):
|
|
13
14
|
name: str
|
|
15
|
+
role_id: Union[UUID, str]
|
|
16
|
+
workspace_id: Union[UUID, str]
|
|
14
17
|
description: Optional[str] = None
|
|
15
18
|
is_active: bool
|
|
16
19
|
expires_at: Optional[datetime] = None
|
|
17
|
-
role: "Role"
|
|
18
20
|
|
|
21
|
+
_editable_fields: ClassVar[set[str]] = {"name", "description", "role_id", "is_active", "expires_at"}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
super().__init__(
|
|
23
|
-
_connection=_connection, _model_ref="apikeys", _uid=_uid, **data
|
|
24
|
-
)
|
|
23
|
+
def __init__(self, client: "HydroServer", **data):
|
|
24
|
+
super().__init__(client=client, service=None, **data)
|
|
25
25
|
|
|
26
|
-
self._workspace_id = str(data.get("workspace_id") or data["workspaceId"])
|
|
27
26
|
self._workspace = None
|
|
27
|
+
self._role = None
|
|
28
28
|
|
|
29
29
|
@property
|
|
30
30
|
def workspace(self) -> "Workspace":
|
|
31
|
-
"""The workspace this
|
|
31
|
+
"""The workspace this API key belongs to."""
|
|
32
32
|
|
|
33
|
-
if self._workspace is None
|
|
34
|
-
self._workspace = self.
|
|
33
|
+
if self._workspace is None:
|
|
34
|
+
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
35
35
|
|
|
36
36
|
return self._workspace
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
@property
|
|
39
|
+
def role(self) -> "Role":
|
|
40
|
+
"""The role this API key is assigned."""
|
|
40
41
|
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
if self._role is None:
|
|
43
|
+
self._role = self.client.roles.get(uid=self.role_id)
|
|
44
|
+
|
|
45
|
+
return self._role
|
|
46
|
+
|
|
47
|
+
@role.setter
|
|
48
|
+
def role(self, role: Union["Role", UUID, str] = ...):
|
|
49
|
+
if not role:
|
|
50
|
+
raise ValueError("Role of API key cannot be None.")
|
|
51
|
+
if normalize_uuid(role) != str(self.role_id):
|
|
52
|
+
self.role_id = normalize_uuid(role)
|
|
53
|
+
self._role = None
|
|
48
54
|
|
|
49
55
|
def save(self):
|
|
50
|
-
"""
|
|
56
|
+
"""Saves changes to this resource to HydroServer."""
|
|
57
|
+
|
|
58
|
+
if not self.uid:
|
|
59
|
+
raise AttributeError("Data cannot be saved: UID is not set.")
|
|
51
60
|
|
|
52
|
-
if self.
|
|
53
|
-
|
|
54
|
-
uid=self.
|
|
61
|
+
if self.unsaved_changes:
|
|
62
|
+
saved_resource = self.client.workspaces.update_api_key(
|
|
63
|
+
uid=self.workspace_id, api_key_id=self.uid, **self.unsaved_changes
|
|
55
64
|
)
|
|
56
|
-
self.
|
|
57
|
-
self.__dict__.update(
|
|
65
|
+
self._server_data = saved_resource.dict(by_alias=False).copy()
|
|
66
|
+
self.__dict__.update(saved_resource.__dict__)
|
|
67
|
+
|
|
68
|
+
def refresh(self):
|
|
69
|
+
"""Refreshes this resource from HydroServer."""
|
|
70
|
+
|
|
71
|
+
if self.uid is None:
|
|
72
|
+
raise ValueError("Cannot refresh data without a valid ID.")
|
|
73
|
+
|
|
74
|
+
refreshed_resource = self.client.workspaces.get_api_key(uid=self.workspace_id, api_key_id=self.uid)
|
|
75
|
+
self._server_data = refreshed_resource.dict(by_alias=False).copy()
|
|
76
|
+
self.__dict__.update(refreshed_resource.__dict__)
|
|
58
77
|
|
|
59
78
|
def delete(self):
|
|
60
|
-
"""
|
|
79
|
+
"""Deletes this resource from HydroServer."""
|
|
61
80
|
|
|
62
|
-
if
|
|
63
|
-
raise AttributeError("
|
|
81
|
+
if self.uid is None:
|
|
82
|
+
raise AttributeError("Cannot delete data without a valid ID.")
|
|
64
83
|
|
|
65
|
-
self.
|
|
66
|
-
uid=self.
|
|
84
|
+
self.client.workspaces.delete_api_key(
|
|
85
|
+
uid=self.workspace_id, api_key_id=self.uid
|
|
67
86
|
)
|
|
68
|
-
self.
|
|
87
|
+
self.uid = None
|
|
69
88
|
|
|
70
89
|
def regenerate(self):
|
|
71
90
|
"""Regenerates this API key. WARNING: Previous key will be invalidated."""
|
|
72
91
|
|
|
73
|
-
_, key = self.
|
|
74
|
-
uid=self.
|
|
92
|
+
_, key = self.client.workspaces.regenerate_api_key(
|
|
93
|
+
uid=self.workspace_id, api_key_id=self.uid
|
|
75
94
|
)
|
|
76
95
|
|
|
77
96
|
return key
|
|
@@ -1,34 +1,70 @@
|
|
|
1
|
-
from typing import Union, TYPE_CHECKING
|
|
1
|
+
from typing import Union, ClassVar, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import Field, AliasPath
|
|
4
|
+
from hydroserverpy.api.models.iam.role import Role
|
|
5
|
+
from hydroserverpy.api.utils import normalize_uuid
|
|
6
|
+
from ..base import HydroServerBaseModel
|
|
4
7
|
|
|
5
8
|
if TYPE_CHECKING:
|
|
9
|
+
from hydroserverpy import HydroServer
|
|
10
|
+
from hydroserverpy.api.models.iam.workspace import Workspace
|
|
6
11
|
from hydroserverpy.api.models.iam.account import Account
|
|
7
|
-
from hydroserverpy.api.models.iam.role import Role
|
|
8
12
|
|
|
9
13
|
|
|
10
|
-
class
|
|
14
|
+
class Collaborator(HydroServerBaseModel):
|
|
11
15
|
user: "Account"
|
|
12
|
-
|
|
16
|
+
role_id: Union[UUID, str] = Field(..., validation_alias=AliasPath("role", "id"))
|
|
13
17
|
workspace_id: Union[UUID, str]
|
|
14
18
|
|
|
19
|
+
_editable_fields: ClassVar[set[str]] = {"role_id"}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
super().__init__(**data)
|
|
19
|
-
self._connection = _connection
|
|
21
|
+
def __init__(self, client: "HydroServer", **data):
|
|
22
|
+
super().__init__(client=client, service=None, **data)
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
self._workspace = None
|
|
25
|
+
self._role = Role(client=client, **data.get("role"))
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
@property
|
|
28
|
+
def workspace(self) -> "Workspace":
|
|
29
|
+
"""The workspace this collaborator belongs to."""
|
|
30
|
+
|
|
31
|
+
if self._workspace is None:
|
|
32
|
+
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
33
|
+
|
|
34
|
+
return self._workspace
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def role(self) -> "Role":
|
|
38
|
+
"""The role this collaborator is assigned."""
|
|
39
|
+
|
|
40
|
+
if self._role is None:
|
|
41
|
+
self._role = self.client.roles.get(uid=self.role_id)
|
|
42
|
+
|
|
43
|
+
return self._role
|
|
44
|
+
|
|
45
|
+
@role.setter
|
|
46
|
+
def role(self, role: Union["Role", UUID, str] = ...):
|
|
47
|
+
if not role:
|
|
48
|
+
raise ValueError("Role of collaborator cannot be None.")
|
|
49
|
+
if normalize_uuid(role) != str(self.role_id):
|
|
50
|
+
self.role_id = normalize_uuid(role)
|
|
51
|
+
self._role = None
|
|
52
|
+
|
|
53
|
+
def save(self):
|
|
54
|
+
"""Saves changes to this resource to HydroServer."""
|
|
55
|
+
|
|
56
|
+
if self.unsaved_changes:
|
|
57
|
+
self.client.workspaces.edit_collaborator_role(
|
|
58
|
+
uid=str(self.workspace_id), email=self.user.email, role=self.role
|
|
59
|
+
)
|
|
60
|
+
self._role = None
|
|
61
|
+
self._server_data["role_id"] = self.role_id
|
|
62
|
+
self.__dict__.update({"role_id": self.role_id})
|
|
28
63
|
|
|
29
|
-
def
|
|
30
|
-
"""
|
|
64
|
+
def delete(self):
|
|
65
|
+
"""Deletes this resource from HydroServer."""
|
|
31
66
|
|
|
32
|
-
self.
|
|
33
|
-
uid=self.workspace_id, email=self.user.email
|
|
67
|
+
self.client.workspaces.remove_collaborator(
|
|
68
|
+
uid=str(self.workspace_id), email=self.user.email
|
|
34
69
|
)
|
|
70
|
+
self.uid = None
|
|
@@ -1,10 +1,38 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
1
|
+
from typing import Optional, Union, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from ..base import HydroServerBaseModel
|
|
4
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from hydroserverpy import HydroServer
|
|
8
|
+
from hydroserverpy.api.models import Workspace
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
|
|
11
|
+
class Role(HydroServerBaseModel):
|
|
8
12
|
name: str = Field(..., max_length=255)
|
|
9
13
|
description: str
|
|
10
14
|
workspace_id: Optional[Union[UUID, str]] = None
|
|
15
|
+
|
|
16
|
+
def __init__(self, client: "HydroServer", **data):
|
|
17
|
+
super().__init__(client=client, service=client.workspaces, **data)
|
|
18
|
+
|
|
19
|
+
self._workspace = None
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def get_route(cls):
|
|
23
|
+
return "roles"
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def workspace(self) -> "Workspace":
|
|
27
|
+
"""The workspace this role belongs to."""
|
|
28
|
+
|
|
29
|
+
if self._workspace is None and self.workspace_id is not None:
|
|
30
|
+
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
31
|
+
|
|
32
|
+
return self._workspace
|
|
33
|
+
|
|
34
|
+
def save(self):
|
|
35
|
+
raise NotImplementedError("Editing roles not enabled.")
|
|
36
|
+
|
|
37
|
+
def delete(self):
|
|
38
|
+
raise NotImplementedError("Deleting roles not enabled.")
|