dataops-sdk 0.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 dataops-sdk might be problematic. Click here for more details.
- dataops_sdk/__init__.py +84 -0
- dataops_sdk/constants.py +6 -0
- dataops_sdk/exceptions.py +31 -0
- dataops_sdk/runner/__init__.py +46 -0
- dataops_sdk/runner/datasource_factory.py +83 -0
- dataops_sdk/runner/schemas/nacos/__init__.py +5 -0
- dataops_sdk/runner/schemas/nacos/config.py +40 -0
- dataops_sdk/runner/schemas/nacos/constants.py +235 -0
- dataops_sdk/runner/schemas/nacos/instance.py +103 -0
- dataops_sdk/runner/schemas/nacos/nacos_exception.py +44 -0
- dataops_sdk/runner/schemas/nacos/naming_param.py +62 -0
- dataops_sdk/runner/schemas/nacos/naming_request.py +84 -0
- dataops_sdk/runner/schemas/nacos/naming_response.py +50 -0
- dataops_sdk/runner/schemas/nacos/preserved_metadata_key.py +10 -0
- dataops_sdk/runner/schemas/nacos/service.py +119 -0
- dataops_sdk/runner/utils/__init__.py +42 -0
- dataops_sdk/runner/utils/common.py +209 -0
- dataops_sdk/runner/utils/dbConn_base.py +256 -0
- dataops_sdk/runner/utils/dbConn_sync.py +485 -0
- dataops_sdk/runner/utils/duckdb_manager.py +203 -0
- dataops_sdk/runner/utils/file_transfer_base.py +307 -0
- dataops_sdk/runner/utils/ftp_file_transfer.py +242 -0
- dataops_sdk/runner/utils/log.py +50 -0
- dataops_sdk/runner/utils/nacos.py +96 -0
- dataops_sdk/runner/utils/neo4j_sync.py +236 -0
- dataops_sdk/runner/utils/queue_base.py +224 -0
- dataops_sdk/runner/utils/queue_kafka.py +150 -0
- dataops_sdk/runner/utils/sftp_file_transfer.py +243 -0
- dataops_sdk/schemas/__init__.py +32 -0
- dataops_sdk/schemas/alarm.py +38 -0
- dataops_sdk/schemas/api.py +37 -0
- dataops_sdk/schemas/data_table.py +437 -0
- dataops_sdk/schemas/datasource.py +26 -0
- dataops_sdk/schemas/dict_.py +13 -0
- dataops_sdk/schemas/enums.py +120 -0
- dataops_sdk/schemas/group.py +40 -0
- dataops_sdk/schemas/hazard.py +151 -0
- dataops_sdk/schemas/meta.py +175 -0
- dataops_sdk/schemas/query.py +175 -0
- dataops_sdk/schemas/report.py +22 -0
- dataops_sdk/schemas/skill.py +70 -0
- dataops_sdk/schemas/table.py +65 -0
- dataops_sdk/schemas/user.py +52 -0
- dataops_sdk/schemas/widget.py +651 -0
- dataops_sdk/schemas/workflow.py +63 -0
- dataops_sdk/services/__init__.py +110 -0
- dataops_sdk/services/alarm.py +324 -0
- dataops_sdk/services/api.py +28 -0
- dataops_sdk/services/auth.py +120 -0
- dataops_sdk/services/data_table.py +485 -0
- dataops_sdk/services/dataset.py +127 -0
- dataops_sdk/services/datasource.py +57 -0
- dataops_sdk/services/dict_.py +69 -0
- dataops_sdk/services/hazard.py +260 -0
- dataops_sdk/services/meta.py +523 -0
- dataops_sdk/services/metrics.py +42 -0
- dataops_sdk/services/ref.py +66 -0
- dataops_sdk/services/report.py +36 -0
- dataops_sdk/services/skill.py +276 -0
- dataops_sdk/services/table.py +340 -0
- dataops_sdk/services/widget.py +143 -0
- dataops_sdk/services/workflow.py +102 -0
- dataops_sdk/session.py +292 -0
- dataops_sdk/tiptap/__init__.py +30 -0
- dataops_sdk/tiptap/builder.py +75 -0
- dataops_sdk/tiptap/constants.py +133 -0
- dataops_sdk/tiptap/markdown_parser.py +294 -0
- dataops_sdk/tiptap/models.py +43 -0
- dataops_sdk/tiptap/parse_utils.py +889 -0
- dataops_sdk/tiptap/render_utils.py +846 -0
- dataops_sdk/tiptap/renderers.py +51 -0
- dataops_sdk/tiptap/tool.py +629 -0
- dataops_sdk/utils/__init__.py +15 -0
- dataops_sdk/utils/common.py +211 -0
- dataops_sdk/utils/utils.py +120 -0
- dataops_sdk-0.3.0.dist-info/METADATA +25 -0
- dataops_sdk-0.3.0.dist-info/RECORD +79 -0
- dataops_sdk-0.3.0.dist-info/WHEEL +5 -0
- dataops_sdk-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"""统一的 Meta 操作模块(服务层).
|
|
2
|
+
|
|
3
|
+
提供对 meta release 列表查询和单个获取的通用功能.
|
|
4
|
+
本模块为纯服务层,不依赖 typer/Rich,可供 Python 代码直接调用。
|
|
5
|
+
CLI 命令请使用 meta_cli 模块。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Mapping, Sequence
|
|
9
|
+
|
|
10
|
+
from pydantic import ConfigDict, JsonValue, TypeAdapter
|
|
11
|
+
from pydantic_core import to_json, to_jsonable_python
|
|
12
|
+
|
|
13
|
+
from dataops_sdk.exceptions import (
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
DataOpsError,
|
|
16
|
+
FailedToCreateError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
)
|
|
19
|
+
from dataops_sdk.schemas import (
|
|
20
|
+
DEFAULT_META_DATA_TYPE,
|
|
21
|
+
META_DATA_TYPE,
|
|
22
|
+
InnerMetaType,
|
|
23
|
+
Meta,
|
|
24
|
+
MetaCreate,
|
|
25
|
+
MetaRef,
|
|
26
|
+
MetaRelease,
|
|
27
|
+
MetaType,
|
|
28
|
+
PageParam,
|
|
29
|
+
SqlQueryParam,
|
|
30
|
+
)
|
|
31
|
+
from dataops_sdk.session import get_session
|
|
32
|
+
from dataops_sdk.utils import encrypt_body
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def release_list_list(
|
|
36
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
37
|
+
filters: Sequence[dict] | None = None,
|
|
38
|
+
headers: dict[str, str] | None = None,
|
|
39
|
+
datasource: str | None = None,
|
|
40
|
+
extra_conditions: Sequence[dict] | None = None,
|
|
41
|
+
) -> list[MetaRelease]:
|
|
42
|
+
"""获取 Meta Release 列表.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
meta_type: 元数据类型(MetaType 枚举或字符串).
|
|
46
|
+
filters: SQL 查询过滤条件列表.
|
|
47
|
+
headers: 额外的请求头.
|
|
48
|
+
datasource: 数据源;非空时写入 ``x-sub-system`` 头.
|
|
49
|
+
extra_conditions: 在默认 ``type=meta_type`` 之上追加等值条件列表,每项形如
|
|
50
|
+
``{"name": "parent_id", "value": "xxx", "alias": "mrl"}``. 主要用于按
|
|
51
|
+
parent_id 等辅助字段过滤。
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
MetaRelease 对象列表
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
DataOpsError: API 返回错误时
|
|
58
|
+
AuthenticationError: 未认证或认证失败时
|
|
59
|
+
"""
|
|
60
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
61
|
+
url = "/meta/release/list/list"
|
|
62
|
+
|
|
63
|
+
sql_query_param = SqlQueryParam().add_eq("type", type_str, "mrl")
|
|
64
|
+
for cond in extra_conditions or []:
|
|
65
|
+
sql_query_param.add_eq(cond["name"], cond["value"], cond.get("alias", "mrl"))
|
|
66
|
+
|
|
67
|
+
headers = headers or {}
|
|
68
|
+
if datasource:
|
|
69
|
+
headers["x-sub-system"] = datasource
|
|
70
|
+
|
|
71
|
+
body = {
|
|
72
|
+
"filters": to_jsonable_python(filters or []),
|
|
73
|
+
"pageParam": PageParam(limit=-1),
|
|
74
|
+
"queryParam": sql_query_param,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
with get_session() as session:
|
|
79
|
+
resp = session.post(url, headers=headers, content=to_json(encrypt_body(body)))
|
|
80
|
+
resp_body = resp.json()
|
|
81
|
+
if resp.status_code == 401:
|
|
82
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
83
|
+
if resp.status_code != 200:
|
|
84
|
+
raise DataOpsError(
|
|
85
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
86
|
+
)
|
|
87
|
+
group_nodes = TypeAdapter(
|
|
88
|
+
list[MetaRelease], config=ConfigDict(str_to_lower=True)
|
|
89
|
+
).validate_python(resp_body["data"])
|
|
90
|
+
return group_nodes
|
|
91
|
+
except SystemExit as e:
|
|
92
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def release_list_page(
|
|
96
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
97
|
+
filters: Sequence[dict] | None = None,
|
|
98
|
+
headers: dict[str, str] | None = None,
|
|
99
|
+
datasource: str | None = None,
|
|
100
|
+
page: int = 0,
|
|
101
|
+
size: int = 20,
|
|
102
|
+
):
|
|
103
|
+
"""分页获取 Meta Release 列表.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
type: 类型
|
|
107
|
+
filters: SQL 查询过滤条件列表
|
|
108
|
+
headers: 额外的请求头
|
|
109
|
+
datasource: 数据源
|
|
110
|
+
page: 页码
|
|
111
|
+
size: 每页大小
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
list[MetaRelease] 对象列表
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
DataOpsError: API 返回错误时
|
|
118
|
+
AuthenticationError: 未认证或认证失败时
|
|
119
|
+
"""
|
|
120
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
121
|
+
url = "/meta/release/list/page"
|
|
122
|
+
|
|
123
|
+
sql_query_param = SqlQueryParam().add_eq("type", type_str, "mrl")
|
|
124
|
+
|
|
125
|
+
if page < 0:
|
|
126
|
+
raise ValueError("page must be greater than or equal to 0")
|
|
127
|
+
|
|
128
|
+
headers = headers or {}
|
|
129
|
+
if datasource:
|
|
130
|
+
headers["x-sub-system"] = datasource
|
|
131
|
+
|
|
132
|
+
body = {
|
|
133
|
+
"filters": to_jsonable_python(filters or []),
|
|
134
|
+
"pageParam": PageParam(start=page * size, limit=size),
|
|
135
|
+
"queryParam": sql_query_param,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
with get_session() as session:
|
|
140
|
+
resp = session.post(url, headers=headers, content=to_json(encrypt_body(body)))
|
|
141
|
+
resp_body = resp.json()
|
|
142
|
+
if resp.status_code == 401:
|
|
143
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
144
|
+
if resp.status_code != 200:
|
|
145
|
+
raise DataOpsError(
|
|
146
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
147
|
+
)
|
|
148
|
+
# TODO 修改
|
|
149
|
+
total = resp_body["data"]["total"]
|
|
150
|
+
page_num = resp_body["data"]["pageNum"]
|
|
151
|
+
page_size = resp_body["data"]["pageSize"]
|
|
152
|
+
group_nodes = TypeAdapter(
|
|
153
|
+
list[MetaRelease], config=ConfigDict(str_to_lower=True)
|
|
154
|
+
).validate_python(resp_body["data"]["records"])
|
|
155
|
+
return group_nodes
|
|
156
|
+
except SystemExit as e:
|
|
157
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def release_list_total(
|
|
161
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
162
|
+
filters: Sequence[dict] | None = None,
|
|
163
|
+
headers: dict[str, str] | None = None,
|
|
164
|
+
datasource: str | None = None,
|
|
165
|
+
) -> int:
|
|
166
|
+
"""获取 Meta Release 列表总数.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
meta_type: 元数据类型
|
|
170
|
+
filters: SQL 查询过滤条件列表
|
|
171
|
+
headers: 额外的请求头
|
|
172
|
+
datasource: 数据源
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
int 总数
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
DataOpsError: API 返回错误时
|
|
179
|
+
AuthenticationError: 未认证或认证失败时
|
|
180
|
+
"""
|
|
181
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
182
|
+
url = "/meta/release/list/page"
|
|
183
|
+
|
|
184
|
+
sql_query_param = SqlQueryParam().add_eq("type", type_str, "mrl")
|
|
185
|
+
|
|
186
|
+
headers = headers or {}
|
|
187
|
+
if datasource:
|
|
188
|
+
headers["x-sub-system"] = datasource
|
|
189
|
+
|
|
190
|
+
body = {
|
|
191
|
+
"filters": to_jsonable_python(filters or []),
|
|
192
|
+
"queryParam": sql_query_param,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
with get_session() as session:
|
|
197
|
+
resp = session.post(url, headers=headers, content=to_json(encrypt_body(body)))
|
|
198
|
+
resp_body = resp.json()
|
|
199
|
+
if resp.status_code == 401:
|
|
200
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
201
|
+
if resp.status_code != 200:
|
|
202
|
+
raise DataOpsError(
|
|
203
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
204
|
+
)
|
|
205
|
+
return resp_body["data"]
|
|
206
|
+
except SystemExit as e:
|
|
207
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def release_list_get_by_code(
|
|
211
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
212
|
+
code: str,
|
|
213
|
+
headers: dict[str, str] | None = None,
|
|
214
|
+
*,
|
|
215
|
+
_class: type[META_DATA_TYPE] = DEFAULT_META_DATA_TYPE,
|
|
216
|
+
) -> Meta[META_DATA_TYPE]:
|
|
217
|
+
"""根据编码获取 Meta Release 数据.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
meta_type: 元数据类型字符串
|
|
221
|
+
code: 元数据编码
|
|
222
|
+
headers: 额外的请求头
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
包含元数据的字典
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
NotFoundError: 找不到对应元数据时
|
|
229
|
+
DataOpsError: API 返回错误时
|
|
230
|
+
AuthenticationError: 未认证或认证失败时
|
|
231
|
+
"""
|
|
232
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
233
|
+
url = f"/meta/release/list/getByCode/{type_str}/{code}"
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
with get_session() as session:
|
|
237
|
+
resp = session.get(url, headers=headers)
|
|
238
|
+
if resp.status_code == 401:
|
|
239
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
240
|
+
if resp.status_code != 200:
|
|
241
|
+
msg = resp.json().get("msg", f"HTTP {resp.status_code}")
|
|
242
|
+
raise DataOpsError(
|
|
243
|
+
f"fail to get {type_str} by {code}, cause by {msg}", resp.status_code
|
|
244
|
+
)
|
|
245
|
+
resp_body = resp.json()
|
|
246
|
+
except SystemExit as e:
|
|
247
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
248
|
+
|
|
249
|
+
if resp_body.get("data") is None:
|
|
250
|
+
raise NotFoundError(f"无法找到 {type_str} {code} 的信息")
|
|
251
|
+
|
|
252
|
+
raw_meta = resp_body["data"]
|
|
253
|
+
if not isinstance(raw_meta, dict):
|
|
254
|
+
raise DataOpsError(f"返回的数据格式不正确, 预期为字典, 实际为 {type(raw_meta)}")
|
|
255
|
+
|
|
256
|
+
return Meta[_class].model_validate(raw_meta)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def release_list_get_by_name(
|
|
260
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
261
|
+
name: str,
|
|
262
|
+
headers: dict[str, str] | None = None,
|
|
263
|
+
*,
|
|
264
|
+
_class: type[META_DATA_TYPE] = DEFAULT_META_DATA_TYPE,
|
|
265
|
+
datasource: str | None = None,
|
|
266
|
+
) -> Meta[META_DATA_TYPE]:
|
|
267
|
+
"""根据名称获取 Meta Release 数据.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
meta_type: 元数据类型字符串
|
|
271
|
+
name: 元数据名称
|
|
272
|
+
headers: 额外的请求头
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
包含元数据的字典
|
|
276
|
+
|
|
277
|
+
Raises:
|
|
278
|
+
NotFoundError: 找不到对应元数据时
|
|
279
|
+
DataOpsError: API 返回错误时
|
|
280
|
+
AuthenticationError: 未认证或认证失败时
|
|
281
|
+
"""
|
|
282
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
283
|
+
url = f"/meta/release/list/getByName/{type_str}/{name}"
|
|
284
|
+
|
|
285
|
+
if datasource:
|
|
286
|
+
headers = headers or {}
|
|
287
|
+
headers["x-sub-system"] = datasource
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
with get_session() as session:
|
|
291
|
+
resp = session.get(url, headers=headers)
|
|
292
|
+
if resp.status_code == 401:
|
|
293
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
294
|
+
if resp.status_code != 200:
|
|
295
|
+
msg = resp.json().get("msg", f"HTTP {resp.status_code}")
|
|
296
|
+
raise DataOpsError(
|
|
297
|
+
f"fail to get {type_str} by {name}, cause by {msg}", resp.status_code
|
|
298
|
+
)
|
|
299
|
+
resp_body = resp.json()
|
|
300
|
+
except SystemExit as e:
|
|
301
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
302
|
+
|
|
303
|
+
if resp_body.get("data") is None:
|
|
304
|
+
raise NotFoundError(f"无法找到 {type_str} {name} 的信息")
|
|
305
|
+
|
|
306
|
+
raw_meta = resp_body["data"]
|
|
307
|
+
if not isinstance(raw_meta, dict):
|
|
308
|
+
raise DataOpsError(f"返回的数据格式不正确, 预期为字典, 实际为 {type(raw_meta)}")
|
|
309
|
+
|
|
310
|
+
return Meta[_class].model_validate(raw_meta)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def release_ref_list(
|
|
314
|
+
meta_type: MetaType | InnerMetaType | str,
|
|
315
|
+
code: str,
|
|
316
|
+
headers: dict[str, str] | None = None,
|
|
317
|
+
) -> list[MetaRef]:
|
|
318
|
+
"""获取 Meta Release 的正向引用列表(查询自己引用了谁).
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
meta_type: 元数据类型 (如 "report", "table")
|
|
322
|
+
code: 元数据编码
|
|
323
|
+
headers: 额外的请求头
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
list[MetaRef] 对象列表
|
|
327
|
+
|
|
328
|
+
Raises:
|
|
329
|
+
NotFoundError: 找不到对应元数据时
|
|
330
|
+
DataOpsError: API 返回错误时
|
|
331
|
+
AuthenticationError: 未认证或认证失败时
|
|
332
|
+
"""
|
|
333
|
+
type_str = meta_type.value if isinstance(meta_type, (MetaType, InnerMetaType)) else meta_type
|
|
334
|
+
url = f"/meta/release/ref/list/{type_str}/{code}"
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
with get_session() as session:
|
|
338
|
+
resp = session.get(url, headers=headers)
|
|
339
|
+
if resp.status_code == 401:
|
|
340
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
341
|
+
if resp.status_code != 200:
|
|
342
|
+
raise DataOpsError(f"HTTP {resp.status_code}", resp.status_code)
|
|
343
|
+
resp_body = resp.json()
|
|
344
|
+
except SystemExit as e:
|
|
345
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
346
|
+
|
|
347
|
+
if resp_body.get("data") is None:
|
|
348
|
+
raise NotFoundError(f"无法找到 {meta_type} {code} 的信息")
|
|
349
|
+
results = TypeAdapter(list[MetaRef]).validate_python(resp_body["data"])
|
|
350
|
+
return results
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def release_ref_list_by_ref(
|
|
354
|
+
ref_type: MetaType | InnerMetaType | str,
|
|
355
|
+
ref_code: str,
|
|
356
|
+
headers: dict[str, str] | None = None,
|
|
357
|
+
) -> list[MetaRef]:
|
|
358
|
+
"""获取 Meta Release 的反向引用列表(查询谁引用了自己).
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
ref_type: 引用元数据类型 (如 "report", "table")
|
|
362
|
+
ref_code: 引用元数据编码
|
|
363
|
+
headers: 额外的请求头
|
|
364
|
+
deduplicate: 是否去重
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
list[MetaRef] 对象列表
|
|
368
|
+
|
|
369
|
+
Raises:
|
|
370
|
+
NotFoundError: 找不到对应元数据时
|
|
371
|
+
DataOpsError: API 返回错误时
|
|
372
|
+
AuthenticationError: 未认证或认证失败时
|
|
373
|
+
"""
|
|
374
|
+
type_str = ref_type.value if isinstance(ref_type, (MetaType, InnerMetaType)) else ref_type
|
|
375
|
+
url = f"/meta/release/ref/listByRef/{type_str}/{ref_code}"
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
with get_session() as session:
|
|
379
|
+
resp = session.get(url, headers=headers)
|
|
380
|
+
if resp.status_code == 401:
|
|
381
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
382
|
+
if resp.status_code != 200:
|
|
383
|
+
raise DataOpsError(f"HTTP {resp.status_code}", resp.status_code)
|
|
384
|
+
resp_body = resp.json()
|
|
385
|
+
except SystemExit as e:
|
|
386
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
387
|
+
|
|
388
|
+
if resp_body.get("data") is None:
|
|
389
|
+
raise NotFoundError(f"无法找到 {ref_type} {ref_code} 的引用信息")
|
|
390
|
+
results = TypeAdapter(list[MetaRef]).validate_python(resp_body["data"])
|
|
391
|
+
return results
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def release_list_create(
|
|
395
|
+
meta: MetaCreate[META_DATA_TYPE],
|
|
396
|
+
params: Sequence[Mapping[str, JsonValue]] | None = None,
|
|
397
|
+
*,
|
|
398
|
+
headers: dict[str, str] | None = None,
|
|
399
|
+
_class: type[META_DATA_TYPE] = DEFAULT_META_DATA_TYPE,
|
|
400
|
+
) -> Meta[META_DATA_TYPE]:
|
|
401
|
+
"""创建 Meta 对象.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
meta: 要创建的元数据对象
|
|
405
|
+
params: 可选的查询参数列表,用于模板渲染
|
|
406
|
+
headers: 额外的请求头
|
|
407
|
+
_class: 返回的 Meta 数据类类型,默认为 Meta
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
创建成功的 Meta 对象
|
|
411
|
+
|
|
412
|
+
Raises:
|
|
413
|
+
FailedToCreateError: 创建失败时
|
|
414
|
+
DataOpsError: API 返回错误时
|
|
415
|
+
AuthenticationError: 未认证或认证失败时
|
|
416
|
+
"""
|
|
417
|
+
body = {"meta": meta, "params": params}
|
|
418
|
+
|
|
419
|
+
try:
|
|
420
|
+
with get_session() as session:
|
|
421
|
+
resp = session.post(
|
|
422
|
+
"/meta/release/list/create",
|
|
423
|
+
headers=headers,
|
|
424
|
+
content=to_json(encrypt_body(body)),
|
|
425
|
+
)
|
|
426
|
+
resp_body = resp.json()
|
|
427
|
+
if resp.status_code == 401:
|
|
428
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
429
|
+
if resp.status_code != 200:
|
|
430
|
+
raise DataOpsError(
|
|
431
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
except SystemExit as e:
|
|
435
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
436
|
+
if resp_body.get("data") is None:
|
|
437
|
+
raise FailedToCreateError("创建失败")
|
|
438
|
+
|
|
439
|
+
raw_meta = resp_body["data"]
|
|
440
|
+
if not isinstance(raw_meta, dict):
|
|
441
|
+
raise DataOpsError(f"返回的数据格式不正确, 预期为字典, 实际为 {type(raw_meta)}")
|
|
442
|
+
|
|
443
|
+
return Meta[_class].model_validate(raw_meta)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def release_list_update(
|
|
447
|
+
meta: Meta[META_DATA_TYPE],
|
|
448
|
+
# origin_meta: Meta[META_DATA_TYPE],
|
|
449
|
+
# meta: MetaUpdate[META_DATA_TYPE],
|
|
450
|
+
*,
|
|
451
|
+
headers: dict[str, str] | None = None,
|
|
452
|
+
datasource: str | None = None,
|
|
453
|
+
_class: type[META_DATA_TYPE] = DEFAULT_META_DATA_TYPE,
|
|
454
|
+
):
|
|
455
|
+
"""更新 Meta 对象.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
meta: 元数据对象
|
|
459
|
+
headers: 额外的请求头
|
|
460
|
+
_class: 返回的 Meta 数据类类型,默认为 Meta
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
更新后的 Meta 对象
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
DataOpsError: API 返回错误时
|
|
467
|
+
AuthenticationError: 未认证或认证失败时
|
|
468
|
+
"""
|
|
469
|
+
"""更新 Meta 对象.
|
|
470
|
+
|
|
471
|
+
Args:
|
|
472
|
+
origin_meta: 原始的元数据对象(更新前的完整对象)
|
|
473
|
+
meta: 要更新的元数据字段(支持部分更新,仅传入需要修改的字段)
|
|
474
|
+
headers: 额外的请求头
|
|
475
|
+
_class: 返回的 Meta 数据类类型,默认为 Meta
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
更新后的 Meta 对象
|
|
479
|
+
|
|
480
|
+
Raises:
|
|
481
|
+
DataOpsError: API 返回错误时
|
|
482
|
+
AuthenticationError: 未认证或认证失败时
|
|
483
|
+
"""
|
|
484
|
+
# body = dict(meta=meta)
|
|
485
|
+
body = dict(meta=meta.model_dump(mode="json", exclude_defaults=True))
|
|
486
|
+
# body = {
|
|
487
|
+
# "meta": deep_merge(
|
|
488
|
+
# origin_meta.model_dump(exclude_defaults=True), meta.model_dump(exclude_defaults=True)
|
|
489
|
+
# )
|
|
490
|
+
# }
|
|
491
|
+
# old: 2076921409387933698
|
|
492
|
+
# new: 2076921409387933698
|
|
493
|
+
if datasource:
|
|
494
|
+
headers = headers or {}
|
|
495
|
+
headers["x-sub-system"] = datasource
|
|
496
|
+
# with open('update.json', 'wb') as f:
|
|
497
|
+
# f.write(to_json(body, indent=2))
|
|
498
|
+
|
|
499
|
+
# try:
|
|
500
|
+
# with get_session() as session:
|
|
501
|
+
# resp = session.post(
|
|
502
|
+
# "/meta/release/list/update",
|
|
503
|
+
# headers=headers,
|
|
504
|
+
# content=to_json(encrypt_body(body)),
|
|
505
|
+
# )
|
|
506
|
+
# resp_body = resp.json()
|
|
507
|
+
# if resp.status_code == 401:
|
|
508
|
+
# raise AuthenticationError("认证失败,请先登录")
|
|
509
|
+
# if resp.status_code != 200:
|
|
510
|
+
# raise DataOpsError(
|
|
511
|
+
# resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
512
|
+
# )
|
|
513
|
+
|
|
514
|
+
# except SystemExit as e:
|
|
515
|
+
# raise AuthenticationError("认证失败,请先登录") from e
|
|
516
|
+
# if resp_body.get("data") is None:
|
|
517
|
+
# raise FailedToUpdateError(f"创建失败")
|
|
518
|
+
|
|
519
|
+
# raw_meta = resp_body["data"]
|
|
520
|
+
# if not isinstance(raw_meta, dict):
|
|
521
|
+
# raise DataOpsError(f"返回的数据格式不正确, 预期为字典, 实际为 {type(raw_meta)}")
|
|
522
|
+
|
|
523
|
+
# return Meta[_class].model_validate(raw_meta)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""指标服务."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
|
|
5
|
+
from pydantic import JsonValue
|
|
6
|
+
|
|
7
|
+
from dataops_sdk.schemas import Meta, MetaRelease, MetaType
|
|
8
|
+
from dataops_sdk.services.meta import release_list_get_by_code, release_list_list
|
|
9
|
+
from dataops_sdk.utils import filter_nodes_by_pattern
|
|
10
|
+
|
|
11
|
+
__all__ = ["service_list_metrics", "service_get_metrics"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def service_list_metrics(query: str | None = None) -> list[MetaRelease]:
|
|
15
|
+
"""列出指标(可选正则过滤).
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
query: 正则表达式,匹配 NAME / TITLE / REMARK / DIGEST
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
MetaRelease 对象列表
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
DataOpsError: API 请求失败时
|
|
25
|
+
AuthenticationError: 未认证时
|
|
26
|
+
"""
|
|
27
|
+
nodes = release_list_list(MetaType.METRICS)
|
|
28
|
+
if query:
|
|
29
|
+
nodes = filter_nodes_by_pattern(nodes, query, include_digest=True)
|
|
30
|
+
return nodes
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def service_get_metrics(code: str) -> Meta[Mapping[str, JsonValue]]:
|
|
34
|
+
"""获取指标详情.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
code: 指标编码
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
指标元数据。`meta.data` 结构详见后端指标定义。
|
|
41
|
+
"""
|
|
42
|
+
return release_list_get_by_code(MetaType.METRICS, code)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from dataops_sdk.schemas import MetaRef, MetaType
|
|
4
|
+
from dataops_sdk.services.meta import release_ref_list, release_ref_list_by_ref
|
|
5
|
+
|
|
6
|
+
__all__ = ["service_ref_forward", "service_ref_backward"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def service_ref_forward(
|
|
10
|
+
meta_type: MetaType | str,
|
|
11
|
+
code: str,
|
|
12
|
+
ref_types: Sequence[str] | None = None,
|
|
13
|
+
*,
|
|
14
|
+
deduplicate: bool = False,
|
|
15
|
+
) -> list[MetaRef]:
|
|
16
|
+
"""正向引用查询:查询自己引用了谁。
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
meta_type: 元数据类型 (如 "table")
|
|
20
|
+
code: 元数据编码
|
|
21
|
+
ref_types: 仅返回指定的被引用类型过滤(如 ["table", "dict"])
|
|
22
|
+
deduplicate: 是否去重
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
MetaRef 对象列表
|
|
26
|
+
"""
|
|
27
|
+
results = release_ref_list(meta_type, code)
|
|
28
|
+
if ref_types:
|
|
29
|
+
results = [r for r in results if r.refType in ref_types]
|
|
30
|
+
return deduplicate_refs(results) if deduplicate else results
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def service_ref_backward(
|
|
34
|
+
meta_type: MetaType | str,
|
|
35
|
+
code: str,
|
|
36
|
+
ref_types: Sequence[str] | None = None,
|
|
37
|
+
*,
|
|
38
|
+
deduplicate: bool = False,
|
|
39
|
+
) -> list[MetaRef]:
|
|
40
|
+
"""反向引用查询:查询谁引用了自己。
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
meta_type: 被引用类型 (如 "table")
|
|
44
|
+
code: 被引用编码
|
|
45
|
+
ref_types: 仅返回指定的引用类型过滤(如 ["table", "dict"])
|
|
46
|
+
deduplicate: 是否去重
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
MetaRef 对象列表
|
|
50
|
+
"""
|
|
51
|
+
results = release_ref_list_by_ref(meta_type, code)
|
|
52
|
+
if ref_types:
|
|
53
|
+
results = [r for r in results if r.type in ref_types]
|
|
54
|
+
return deduplicate_refs(results) if deduplicate else results
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def deduplicate_refs(refs: list[MetaRef]) -> list[MetaRef]:
|
|
58
|
+
"""根据 (code, type) 去重。"""
|
|
59
|
+
seen: set[tuple[str, str]] = set()
|
|
60
|
+
deduped: list[MetaRef] = []
|
|
61
|
+
for r in refs:
|
|
62
|
+
key = (r.code, r.type)
|
|
63
|
+
if key not in seen:
|
|
64
|
+
seen.add(key)
|
|
65
|
+
deduped.append(r)
|
|
66
|
+
return deduped
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from dataops_sdk.schemas import MetaRelease, MetaType, ReportMetaData
|
|
2
|
+
from dataops_sdk.services import release_list_get_by_code, release_list_list
|
|
3
|
+
from dataops_sdk.utils import filter_nodes_by_pattern
|
|
4
|
+
|
|
5
|
+
__all__ = ["service_get_report", "service_list_reports"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def service_list_reports(query: str | None = None) -> list[MetaRelease]:
|
|
9
|
+
"""列出文档(可选正则过滤).
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
query: 正则表达式,匹配 NAME / TITLE / REMARK / DIGEST
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
MetaRelease 对象列表
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
DataOpsError: API 请求失败时
|
|
19
|
+
AuthenticationError: 未认证时
|
|
20
|
+
"""
|
|
21
|
+
nodes = release_list_list(MetaType.REPORT)
|
|
22
|
+
if query:
|
|
23
|
+
nodes = filter_nodes_by_pattern(nodes, query, include_digest=True)
|
|
24
|
+
return nodes
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def service_get_report(code: str) -> ReportMetaData:
|
|
28
|
+
"""读取文档内容并渲染为 Markdown.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
code: 文档编码
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
渲染后的 Markdown 字符串
|
|
35
|
+
"""
|
|
36
|
+
return release_list_get_by_code(MetaType.REPORT, code, _class=ReportMetaData)
|