hydroserverpy 2.0.0b2__tar.gz → 2.0.0b4__tar.gz
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.
- {hydroserverpy-2.0.0b2/src/hydroserverpy.egg-info → hydroserverpy-2.0.0b4}/PKG-INFO +1 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/setup.cfg +1 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/client.py +27 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/__init__.py +2 -2
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/base.py +49 -58
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/etl/data_connection.py +1 -1
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/models/etl/mapping.py +26 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/etl/task.py +21 -14
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/collaborator.py +22 -9
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/role.py +2 -2
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/models/iam/apikey.py → hydroserverpy-2.0.0b4/src/hydroserverpy/api/models/iam/service_account.py +18 -33
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/workspace.py +38 -40
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/models/monitoring/rule.py +35 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/monitoring/task.py +8 -8
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/products/rating_curve.py +1 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/products/task.py +27 -10
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/products/transformation.py +28 -3
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/datastream.py +13 -23
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/method.py +5 -5
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/monitoring_site.py +1 -1
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/models/sta/observation.py +144 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/observed_property.py +4 -4
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/processing_level.py +3 -3
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/result_qualifier.py +4 -4
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/unit.py +2 -2
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/__init__.py +1 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/base.py +15 -18
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/etl/data_connection.py +6 -6
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/services/etl/mapping.py +70 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/etl/task.py +16 -28
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/iam/role.py +6 -10
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/iam/workspace.py +78 -68
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/services/monitoring/rule.py +84 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/monitoring/task.py +16 -16
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/products/rating_curve.py +6 -6
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/products/task.py +16 -16
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/products/transformation.py +112 -93
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/datastream.py +127 -62
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/method.py +6 -6
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/monitoring_site.py +70 -7
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/observed_property.py +8 -8
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/processing_level.py +10 -9
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/result_qualifier.py +12 -12
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/unit.py +6 -6
- hydroserverpy-2.0.0b4/src/hydroserverpy/api/utils.py +51 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/loaders/hydroserver.py +3 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4/src/hydroserverpy.egg-info}/PKG-INFO +1 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy.egg-info/SOURCES.txt +2 -1
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/tests/test_api_client.py +60 -1
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/models/etl/mapping.py +0 -10
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/models/monitoring/rule.py +0 -50
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/models/sta/observation.py +0 -134
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/services/monitoring/rule.py +0 -142
- hydroserverpy-2.0.0b2/src/hydroserverpy/api/utils.py +0 -22
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/LICENSE +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/README.md +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/pyproject.toml +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/etl/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/etl/run.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/account.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/monitoring/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/orchestration/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/orchestration/run.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/products/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/sta/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/etl/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/iam/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/monitoring/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/products/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/services/sta/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/core/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/core/duration.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/core/timeseries.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/exceptions.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/extractors/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/extractors/base.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/extractors/ftp.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/extractors/http.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/extractors/local.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/hydroserver.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/loaders/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/loaders/base.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/models/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/models/base.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/models/pipeline.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/models/timestamp.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/operations/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/operations/arithmetic_expression.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/operations/base.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/operations/rating_curve.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/operations/temporal_aggregation.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/transformers/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/transformers/base.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/transformers/csv.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/transformers/json.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/user_facing_errors.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/etl/utils.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/products/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/products/aggregation.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/products/derivation.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/products/rating_curve.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/quality/__init__.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/quality/check.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/quality/correction.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/quality/service.py +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy.egg-info/dependency_links.txt +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy.egg-info/requires.txt +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy.egg-info/top_level.txt +0 -0
- {hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy.egg-info/zip-safe +0 -0
|
@@ -13,6 +13,7 @@ from hydroserverpy.api.services import (
|
|
|
13
13
|
DatastreamService,
|
|
14
14
|
DataConnectionService,
|
|
15
15
|
TaskService,
|
|
16
|
+
EtlMappingService,
|
|
16
17
|
MonitoringTaskService,
|
|
17
18
|
MonitoringRuleService,
|
|
18
19
|
RatingCurveService,
|
|
@@ -25,7 +26,7 @@ class HydroServer:
|
|
|
25
26
|
def __init__(
|
|
26
27
|
self,
|
|
27
28
|
host: str,
|
|
28
|
-
base_route: str = "/api/
|
|
29
|
+
base_route: str = "/api/ogc",
|
|
29
30
|
email: Optional[str] = None,
|
|
30
31
|
password: Optional[str] = None,
|
|
31
32
|
apikey: Optional[str] = None,
|
|
@@ -45,9 +46,28 @@ class HydroServer:
|
|
|
45
46
|
|
|
46
47
|
self._init_session()
|
|
47
48
|
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _encode_query_value(value):
|
|
51
|
+
"""
|
|
52
|
+
Encodes booleans as the lowercase 'true' and 'false' the API requires; requests would
|
|
53
|
+
otherwise send str(value), i.e. 'True' and 'False'.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
if isinstance(value, bool):
|
|
57
|
+
return "true" if value else "false"
|
|
58
|
+
if isinstance(value, (list, tuple)):
|
|
59
|
+
return [HydroServer._encode_query_value(item) for item in value]
|
|
60
|
+
|
|
61
|
+
return value
|
|
62
|
+
|
|
48
63
|
def request(self, method, path, *args, **kwargs) -> requests.Response:
|
|
49
64
|
"""Sends a request to HydroServer's API."""
|
|
50
65
|
|
|
66
|
+
if isinstance(kwargs.get("params"), dict):
|
|
67
|
+
kwargs["params"] = {
|
|
68
|
+
key: self._encode_query_value(value) for key, value in kwargs["params"].items()
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
for attempt in range(2):
|
|
52
72
|
try:
|
|
53
73
|
response = getattr(self._session, method)(
|
|
@@ -172,6 +192,12 @@ class HydroServer:
|
|
|
172
192
|
|
|
173
193
|
return TaskService(self)
|
|
174
194
|
|
|
195
|
+
@property
|
|
196
|
+
def etlmappings(self):
|
|
197
|
+
"""Utilities for managing HydroServer ETL mappings."""
|
|
198
|
+
|
|
199
|
+
return EtlMappingService(self)
|
|
200
|
+
|
|
175
201
|
@property
|
|
176
202
|
def monitoringtasks(self):
|
|
177
203
|
"""Utilities for managing HydroServer monitoring tasks."""
|
|
@@ -2,7 +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.
|
|
5
|
+
from .iam.service_account import ServiceAccount
|
|
6
6
|
from .iam.account import Account
|
|
7
7
|
from .sta.datastream import Datastream
|
|
8
8
|
from .sta.observation import ObservationCollection
|
|
@@ -30,4 +30,4 @@ from .products.transformation import (
|
|
|
30
30
|
Workspace.model_rebuild()
|
|
31
31
|
Role.model_rebuild()
|
|
32
32
|
Collaborator.model_rebuild()
|
|
33
|
-
|
|
33
|
+
ServiceAccount.model_rebuild()
|
|
@@ -97,10 +97,9 @@ class HydroServerBaseModel(BaseModel):
|
|
|
97
97
|
class HydroServerCollection:
|
|
98
98
|
items: List["HydroServerBaseModel"]
|
|
99
99
|
filters: Optional[dict[str, Any]] = None
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
total_pages: Optional[int] = None
|
|
100
|
+
sortby: Optional[List[str]] = None
|
|
101
|
+
offset: Optional[int] = None
|
|
102
|
+
limit: Optional[int] = None
|
|
104
103
|
total_count: Optional[int] = None
|
|
105
104
|
|
|
106
105
|
_service: Optional[Any] = field(init=False, repr=False)
|
|
@@ -116,40 +115,36 @@ class HydroServerCollection:
|
|
|
116
115
|
self._service = service
|
|
117
116
|
|
|
118
117
|
self.filters = data.get("filters")
|
|
119
|
-
self.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
self.
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
self.total_count = self._resolve_int_metadata(
|
|
128
|
-
"total_count", "X-Total-Count", response, data
|
|
129
|
-
)
|
|
118
|
+
self.sortby = data.get("sortby")
|
|
119
|
+
|
|
120
|
+
payload = response.json() if response is not None else {}
|
|
121
|
+
meta = payload.get("meta", {}) if isinstance(payload, dict) else {}
|
|
122
|
+
|
|
123
|
+
self.offset = self._resolve_int_metadata("offset", meta, data)
|
|
124
|
+
self.limit = self._resolve_int_metadata("limit", meta, data)
|
|
125
|
+
self.total_count = self._resolve_int_metadata("total_count", meta, data, meta_key="numberMatched")
|
|
130
126
|
|
|
131
127
|
if "items" in data:
|
|
132
128
|
self.items = data["items"]
|
|
133
129
|
elif response is not None:
|
|
134
|
-
self.items = [model(client=client, **entity) for entity in
|
|
130
|
+
self.items = [model(client=client, **entity) for entity in payload.get("data", [])]
|
|
135
131
|
else:
|
|
136
132
|
self.items = []
|
|
137
133
|
|
|
138
134
|
@staticmethod
|
|
139
135
|
def _resolve_int_metadata(
|
|
140
136
|
field_name: str,
|
|
141
|
-
|
|
142
|
-
response: Optional[Response],
|
|
137
|
+
meta: dict,
|
|
143
138
|
data: dict,
|
|
139
|
+
meta_key: Optional[str] = None,
|
|
144
140
|
) -> Optional[int]:
|
|
145
141
|
field_value = data.get(field_name)
|
|
146
142
|
if field_value is not None:
|
|
147
143
|
return int(field_value)
|
|
148
144
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return int(header_value)
|
|
145
|
+
meta_value = meta.get(meta_key or field_name)
|
|
146
|
+
if meta_value is not None:
|
|
147
|
+
return int(meta_value)
|
|
153
148
|
|
|
154
149
|
return None
|
|
155
150
|
|
|
@@ -163,11 +158,13 @@ class HydroServerCollection:
|
|
|
163
158
|
if not self._service:
|
|
164
159
|
raise NotImplementedError("Pagination not enabled for this collection.")
|
|
165
160
|
|
|
161
|
+
limit = self.limit or 100
|
|
162
|
+
|
|
166
163
|
return self._service.list(
|
|
167
164
|
**(self.filters or {}),
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
offset=(self.offset or 0) + limit,
|
|
166
|
+
limit=limit,
|
|
167
|
+
sortby=self.sortby or ...
|
|
171
168
|
)
|
|
172
169
|
|
|
173
170
|
def previous_page(self):
|
|
@@ -176,14 +173,16 @@ class HydroServerCollection:
|
|
|
176
173
|
if not self._service:
|
|
177
174
|
raise NotImplementedError("Pagination not enabled for this collection.")
|
|
178
175
|
|
|
179
|
-
if not self.
|
|
176
|
+
if not self.offset:
|
|
180
177
|
return None
|
|
181
178
|
|
|
179
|
+
limit = self.limit or 100
|
|
180
|
+
|
|
182
181
|
return self._service.list(
|
|
183
182
|
**(self.filters or {}),
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
offset=max(0, self.offset - limit),
|
|
184
|
+
limit=limit,
|
|
185
|
+
sortby=self.sortby or ...
|
|
187
186
|
)
|
|
188
187
|
|
|
189
188
|
def fetch_all(self) -> "HydroServerCollection":
|
|
@@ -192,30 +191,23 @@ class HydroServerCollection:
|
|
|
192
191
|
if not self._service:
|
|
193
192
|
raise NotImplementedError("Pagination not enabled for this collection.")
|
|
194
193
|
|
|
195
|
-
all_items =
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
break
|
|
213
|
-
all_items.extend(page.items)
|
|
214
|
-
|
|
215
|
-
if page.total_pages is not None:
|
|
216
|
-
total_pages = page.total_pages
|
|
217
|
-
|
|
218
|
-
page_num += 1
|
|
194
|
+
all_items = list(self.items)
|
|
195
|
+
limit = self.limit or 100
|
|
196
|
+
next_offset = (self.offset or 0) + len(self.items)
|
|
197
|
+
last_page_size = len(self.items)
|
|
198
|
+
|
|
199
|
+
while last_page_size == limit:
|
|
200
|
+
page = self._service.list(
|
|
201
|
+
**(self.filters or {}),
|
|
202
|
+
offset=next_offset,
|
|
203
|
+
limit=limit,
|
|
204
|
+
sortby=self.sortby or ...
|
|
205
|
+
)
|
|
206
|
+
if not page.items:
|
|
207
|
+
break
|
|
208
|
+
all_items.extend(page.items)
|
|
209
|
+
last_page_size = len(page.items)
|
|
210
|
+
next_offset += last_page_size
|
|
219
211
|
|
|
220
212
|
return self.__class__(
|
|
221
213
|
model=type(self.items[0]) if self.items else None,
|
|
@@ -223,9 +215,8 @@ class HydroServerCollection:
|
|
|
223
215
|
service=self._service,
|
|
224
216
|
items=all_items,
|
|
225
217
|
filters=self.filters,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
total_count=len(all_items)
|
|
218
|
+
sortby=self.sortby,
|
|
219
|
+
offset=0,
|
|
220
|
+
limit=len(all_items),
|
|
221
|
+
total_count=len(all_items),
|
|
231
222
|
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import ClassVar, TYPE_CHECKING
|
|
3
|
+
from pydantic import AliasChoices, AliasPath, Field
|
|
4
|
+
from ..base import HydroServerBaseModel
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from hydroserverpy import HydroServer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EtlMapping(HydroServerBaseModel):
|
|
11
|
+
task_id: uuid.UUID = Field(validation_alias="etlTaskId")
|
|
12
|
+
source_identifier: str
|
|
13
|
+
target_datastream_id: uuid.UUID = Field(
|
|
14
|
+
validation_alias=AliasChoices(
|
|
15
|
+
"targetDatastreamId", AliasPath("targetDatastream", "id")
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
_editable_fields: ClassVar[set[str]] = {"source_identifier", "target_datastream_id"}
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: "HydroServer", **data):
|
|
22
|
+
super().__init__(client=client, service=client.etlmappings, **data)
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def get_route(cls):
|
|
26
|
+
return "collections/etl-mappings/items"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from datetime import datetime
|
|
3
|
+
from functools import cached_property
|
|
3
4
|
from typing import Any, ClassVar, Dict, List, Literal, Optional, Union, TYPE_CHECKING
|
|
4
|
-
from pydantic import Field, AliasPath
|
|
5
|
+
from pydantic import AliasChoices, Field, AliasPath
|
|
5
6
|
from ..base import HydroServerBaseModel
|
|
6
7
|
from ..orchestration.run import TaskRun
|
|
7
8
|
from .mapping import EtlMapping
|
|
@@ -15,7 +16,11 @@ class EtlTask(HydroServerBaseModel):
|
|
|
15
16
|
name: str
|
|
16
17
|
description: Optional[str] = None
|
|
17
18
|
task_variables: Dict[str, Any] = Field(default_factory=dict)
|
|
18
|
-
data_connection_id: uuid.UUID
|
|
19
|
+
data_connection_id: uuid.UUID = Field(
|
|
20
|
+
validation_alias=AliasChoices(
|
|
21
|
+
"dataConnectionId", AliasPath("dataConnection", "id")
|
|
22
|
+
)
|
|
23
|
+
)
|
|
19
24
|
enabled: Optional[bool] = Field(None, validation_alias=AliasPath("schedule", "enabled"))
|
|
20
25
|
start_time: Optional[datetime] = Field(None, validation_alias=AliasPath("schedule", "startTime"))
|
|
21
26
|
crontab: Optional[str] = Field(None, validation_alias=AliasPath("schedule", "crontab"))
|
|
@@ -25,7 +30,7 @@ class EtlTask(HydroServerBaseModel):
|
|
|
25
30
|
)
|
|
26
31
|
next_run_at: Optional[datetime] = Field(None, validation_alias=AliasPath("schedule", "nextRunAt"))
|
|
27
32
|
latest_run: Optional[TaskRun] = None
|
|
28
|
-
|
|
33
|
+
mapping_count: int = 0
|
|
29
34
|
|
|
30
35
|
_editable_fields: ClassVar[set[str]] = set()
|
|
31
36
|
|
|
@@ -46,10 +51,6 @@ class EtlTask(HydroServerBaseModel):
|
|
|
46
51
|
name=self.name,
|
|
47
52
|
description=self.description,
|
|
48
53
|
task_variables=self.task_variables,
|
|
49
|
-
mappings=[
|
|
50
|
-
{"source_identifier": m.source_identifier, "target_datastream_id": str(m.target_datastream_id)}
|
|
51
|
-
for m in self.mappings
|
|
52
|
-
],
|
|
53
54
|
crontab=self.crontab,
|
|
54
55
|
interval=self.interval,
|
|
55
56
|
interval_period=self.interval_period,
|
|
@@ -61,7 +62,13 @@ class EtlTask(HydroServerBaseModel):
|
|
|
61
62
|
|
|
62
63
|
@classmethod
|
|
63
64
|
def get_route(cls):
|
|
64
|
-
return "etl/
|
|
65
|
+
return "collections/etl-tasks/items"
|
|
66
|
+
|
|
67
|
+
@cached_property
|
|
68
|
+
def mappings(self) -> List[EtlMapping]:
|
|
69
|
+
"""All source-to-datastream mappings for this ETL task."""
|
|
70
|
+
|
|
71
|
+
return self.client.etlmappings.list(etl_task=self.uid, fetch_all=True).items
|
|
65
72
|
|
|
66
73
|
@property
|
|
67
74
|
def data_connection(self) -> "DataConnection":
|
|
@@ -76,9 +83,9 @@ class EtlTask(HydroServerBaseModel):
|
|
|
76
83
|
|
|
77
84
|
def list_runs(
|
|
78
85
|
self,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
offset: int = ...,
|
|
87
|
+
limit: int = ...,
|
|
88
|
+
sortby: List[str] = ...,
|
|
82
89
|
status: str = ...,
|
|
83
90
|
started_at_min: datetime = ...,
|
|
84
91
|
started_at_max: datetime = ...,
|
|
@@ -89,9 +96,9 @@ class EtlTask(HydroServerBaseModel):
|
|
|
89
96
|
|
|
90
97
|
return self.client.etltasks.list_runs(
|
|
91
98
|
uid=self.uid,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
offset=offset,
|
|
100
|
+
limit=limit,
|
|
101
|
+
sortby=sortby,
|
|
95
102
|
status=status,
|
|
96
103
|
started_at_min=started_at_min,
|
|
97
104
|
started_at_max=started_at_max,
|
{hydroserverpy-2.0.0b2 → hydroserverpy-2.0.0b4}/src/hydroserverpy/api/models/iam/collaborator.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from typing import Union, ClassVar, TYPE_CHECKING
|
|
1
|
+
from typing import Optional, Union, ClassVar, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
|
-
from pydantic import Field, AliasPath
|
|
4
3
|
from hydroserverpy.api.models.iam.role import Role
|
|
5
4
|
from hydroserverpy.api.utils import normalize_uuid
|
|
6
5
|
from ..base import HydroServerBaseModel
|
|
@@ -8,13 +7,13 @@ from ..base import HydroServerBaseModel
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
9
8
|
from hydroserverpy import HydroServer
|
|
10
9
|
from hydroserverpy.api.models.iam.workspace import Workspace
|
|
11
|
-
from hydroserverpy.api.models.iam.account import Account
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class Collaborator(HydroServerBaseModel):
|
|
15
|
-
|
|
16
|
-
role_id: Union[UUID, str] = Field(..., validation_alias=AliasPath("role", "id"))
|
|
13
|
+
role_id: Union[UUID, str]
|
|
17
14
|
workspace_id: Union[UUID, str]
|
|
15
|
+
user_email: Optional[str] = None
|
|
16
|
+
service_account_email: Optional[str] = None
|
|
18
17
|
|
|
19
18
|
_editable_fields: ClassVar[set[str]] = {"role_id"}
|
|
20
19
|
|
|
@@ -22,7 +21,19 @@ class Collaborator(HydroServerBaseModel):
|
|
|
22
21
|
super().__init__(client=client, service=None, **data)
|
|
23
22
|
|
|
24
23
|
self._workspace = None
|
|
25
|
-
self._role =
|
|
24
|
+
self._role = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def email(self) -> str:
|
|
28
|
+
"""The email identifying whichever principal (user or service account) this collaborator is."""
|
|
29
|
+
|
|
30
|
+
return self.user_email or self.service_account_email
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def is_service_account(self) -> bool:
|
|
34
|
+
"""Whether this collaborator is a service account rather than a user."""
|
|
35
|
+
|
|
36
|
+
return self.service_account_email is not None
|
|
26
37
|
|
|
27
38
|
@property
|
|
28
39
|
def workspace(self) -> "Workspace":
|
|
@@ -54,17 +65,19 @@ class Collaborator(HydroServerBaseModel):
|
|
|
54
65
|
"""Saves changes to this resource to HydroServer."""
|
|
55
66
|
|
|
56
67
|
if self.unsaved_changes:
|
|
57
|
-
self.client.workspaces.edit_collaborator_role(
|
|
58
|
-
uid=str(self.workspace_id), email=self.
|
|
68
|
+
saved = self.client.workspaces.edit_collaborator_role(
|
|
69
|
+
uid=str(self.workspace_id), email=self.email, role=self.role_id
|
|
59
70
|
)
|
|
60
71
|
self._role = None
|
|
61
72
|
self._server_data["role_id"] = self.role_id
|
|
62
73
|
self.__dict__.update({"role_id": self.role_id})
|
|
74
|
+
if saved is not None:
|
|
75
|
+
self.__dict__.update(saved.__dict__)
|
|
63
76
|
|
|
64
77
|
def delete(self):
|
|
65
78
|
"""Deletes this resource from HydroServer."""
|
|
66
79
|
|
|
67
80
|
self.client.workspaces.remove_collaborator(
|
|
68
|
-
uid=str(self.workspace_id), email=self.
|
|
81
|
+
uid=str(self.workspace_id), email=self.email
|
|
69
82
|
)
|
|
70
83
|
self.uid = None
|
|
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
|
|
|
10
10
|
|
|
11
11
|
class Role(HydroServerBaseModel):
|
|
12
12
|
name: str = Field(..., max_length=255)
|
|
13
|
-
description: str
|
|
13
|
+
description: Optional[str] = None
|
|
14
14
|
workspace_id: Optional[Union[UUID, str]] = None
|
|
15
15
|
|
|
16
16
|
def __init__(self, client: "HydroServer", **data):
|
|
@@ -20,7 +20,7 @@ class Role(HydroServerBaseModel):
|
|
|
20
20
|
|
|
21
21
|
@classmethod
|
|
22
22
|
def get_route(cls):
|
|
23
|
-
return "roles"
|
|
23
|
+
return "collections/roles/items"
|
|
24
24
|
|
|
25
25
|
@property
|
|
26
26
|
def workspace(self) -> "Workspace":
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import Optional, Union, ClassVar, TYPE_CHECKING
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from hydroserverpy.api.models.iam.role import Role
|
|
5
4
|
from hydroserverpy.api.utils import normalize_uuid
|
|
6
5
|
from ..base import HydroServerBaseModel
|
|
7
6
|
|
|
@@ -10,48 +9,32 @@ if TYPE_CHECKING:
|
|
|
10
9
|
from hydroserverpy.api.models import Workspace
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class
|
|
12
|
+
class ServiceAccount(HydroServerBaseModel):
|
|
14
13
|
name: str
|
|
15
|
-
|
|
14
|
+
email: str
|
|
16
15
|
workspace_id: Union[UUID, str]
|
|
17
16
|
description: Optional[str] = None
|
|
18
17
|
is_active: bool
|
|
19
|
-
|
|
18
|
+
key_expires_at: Optional[datetime] = None
|
|
19
|
+
created_at: datetime
|
|
20
|
+
last_used_at: Optional[datetime] = None
|
|
20
21
|
|
|
21
|
-
_editable_fields: ClassVar[set[str]] = {"name", "description", "
|
|
22
|
+
_editable_fields: ClassVar[set[str]] = {"name", "description", "is_active", "key_expires_at"}
|
|
22
23
|
|
|
23
24
|
def __init__(self, client: "HydroServer", **data):
|
|
24
25
|
super().__init__(client=client, service=None, **data)
|
|
25
26
|
|
|
26
27
|
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 service account belongs to."""
|
|
32
32
|
|
|
33
33
|
if self._workspace is None:
|
|
34
34
|
self._workspace = self.client.workspaces.get(uid=self.workspace_id)
|
|
35
35
|
|
|
36
36
|
return self._workspace
|
|
37
37
|
|
|
38
|
-
@property
|
|
39
|
-
def role(self) -> "Role":
|
|
40
|
-
"""The role this API key is assigned."""
|
|
41
|
-
|
|
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
|
|
54
|
-
|
|
55
38
|
def save(self):
|
|
56
39
|
"""Saves changes to this resource to HydroServer."""
|
|
57
40
|
|
|
@@ -59,8 +42,8 @@ class APIKey(HydroServerBaseModel):
|
|
|
59
42
|
raise AttributeError("Data cannot be saved: UID is not set.")
|
|
60
43
|
|
|
61
44
|
if self.unsaved_changes:
|
|
62
|
-
saved_resource = self.client.workspaces.
|
|
63
|
-
uid=self.workspace_id,
|
|
45
|
+
saved_resource = self.client.workspaces.update_service_account(
|
|
46
|
+
uid=self.workspace_id, service_account_id=self.uid, **self.unsaved_changes
|
|
64
47
|
)
|
|
65
48
|
self._server_data = saved_resource.dict(by_alias=False).copy()
|
|
66
49
|
self.__dict__.update(saved_resource.__dict__)
|
|
@@ -71,7 +54,9 @@ class APIKey(HydroServerBaseModel):
|
|
|
71
54
|
if self.uid is None:
|
|
72
55
|
raise ValueError("Cannot refresh data without a valid ID.")
|
|
73
56
|
|
|
74
|
-
refreshed_resource = self.client.workspaces.
|
|
57
|
+
refreshed_resource = self.client.workspaces.get_service_account(
|
|
58
|
+
uid=self.workspace_id, service_account_id=self.uid
|
|
59
|
+
)
|
|
75
60
|
self._server_data = refreshed_resource.dict(by_alias=False).copy()
|
|
76
61
|
self.__dict__.update(refreshed_resource.__dict__)
|
|
77
62
|
|
|
@@ -81,16 +66,16 @@ class APIKey(HydroServerBaseModel):
|
|
|
81
66
|
if self.uid is None:
|
|
82
67
|
raise AttributeError("Cannot delete data without a valid ID.")
|
|
83
68
|
|
|
84
|
-
self.client.workspaces.
|
|
85
|
-
uid=self.workspace_id,
|
|
69
|
+
self.client.workspaces.delete_service_account(
|
|
70
|
+
uid=self.workspace_id, service_account_id=self.uid
|
|
86
71
|
)
|
|
87
72
|
self.uid = None
|
|
88
73
|
|
|
89
|
-
def regenerate(self):
|
|
90
|
-
"""Regenerates this
|
|
74
|
+
def regenerate(self) -> str:
|
|
75
|
+
"""Regenerates this service account's key. WARNING: the previous key will be invalidated."""
|
|
91
76
|
|
|
92
|
-
_, key = self.client.workspaces.
|
|
93
|
-
uid=self.workspace_id,
|
|
77
|
+
_, key = self.client.workspaces.regenerate_service_account_key(
|
|
78
|
+
uid=self.workspace_id, service_account_id=self.uid
|
|
94
79
|
)
|
|
95
80
|
|
|
96
81
|
return key
|