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,63 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Annotated, Generic, TypeVar
|
|
3
|
+
|
|
4
|
+
from pydantic import (
|
|
5
|
+
BaseModel,
|
|
6
|
+
Field,
|
|
7
|
+
JsonValue,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from dataops_sdk.schemas.meta import Meta
|
|
11
|
+
|
|
12
|
+
# 注意:用传统 TypeVar + Generic[T] 写法以兼容 Python 3.11+。
|
|
13
|
+
# PEP 695 ``class Foo[T](Bar)`` 语法需要 Python 3.12+,且其 default 参数需 3.13+,
|
|
14
|
+
# 故此处不使用 PEP 695 语法。
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WorkflowNodeData(BaseModel, Generic[T]):
|
|
19
|
+
"""Workflow node data schema."""
|
|
20
|
+
|
|
21
|
+
label: Annotated[str, Field(description="Node label")]
|
|
22
|
+
config: Annotated[T, Field(description="Node config", exclude=True)]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class WorkflowEdge(BaseModel):
|
|
26
|
+
"""Workflow edge schema."""
|
|
27
|
+
|
|
28
|
+
id: Annotated[str, Field(description="Edge id", exclude=True)]
|
|
29
|
+
source: Annotated[str, Field(description="Edge source")]
|
|
30
|
+
target: Annotated[str, Field(description="Edge target")]
|
|
31
|
+
sourceHandle: Annotated[str | None, Field(description="Edge source handle")] = None
|
|
32
|
+
targetHandle: Annotated[str | None, Field(description="Edge target handle")] = None
|
|
33
|
+
label: Annotated[str, Field(description="Edge label", exclude=True)] = ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class WorkflowNode(BaseModel, Generic[T]):
|
|
37
|
+
"""Workflow node schema."""
|
|
38
|
+
|
|
39
|
+
id: Annotated[str, Field(description="Node id")]
|
|
40
|
+
type: Annotated[str, Field(description="Node type")]
|
|
41
|
+
# name: Annotated[str, Field(description="Node name")]
|
|
42
|
+
initialized: Annotated[bool, Field(description="Node initialized", exclude=True)]
|
|
43
|
+
data: Annotated[WorkflowNodeData[T], Field(description="Node data")]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class WorkflowGraph(BaseModel):
|
|
47
|
+
"""Workflow graph schema."""
|
|
48
|
+
|
|
49
|
+
nodes: Annotated[
|
|
50
|
+
list[WorkflowNode[Mapping[str, JsonValue]]],
|
|
51
|
+
Field(description="Nodes", default_factory=list),
|
|
52
|
+
]
|
|
53
|
+
edges: Annotated[list[WorkflowEdge], Field(description="Edges", default_factory=list)]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DataWorkflow(BaseModel):
|
|
57
|
+
"""Workflow data schema."""
|
|
58
|
+
|
|
59
|
+
graph: Annotated[WorkflowGraph, Field(description="Workflow graph")]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MetaWorkflow(Meta[DataWorkflow]):
|
|
63
|
+
"""Workflow meta schema."""
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from .alarm import (
|
|
2
|
+
service_count_alarm_action,
|
|
3
|
+
service_count_alarm_rule_matches,
|
|
4
|
+
service_count_alarm_rules,
|
|
5
|
+
service_count_alarm_specs,
|
|
6
|
+
service_find_alarm_table_data,
|
|
7
|
+
service_find_meta_by_codes,
|
|
8
|
+
service_get_alarm_rule_enums,
|
|
9
|
+
service_get_alarm_table_columns,
|
|
10
|
+
service_get_rel_table_columns,
|
|
11
|
+
service_list_alarm_rule_matches,
|
|
12
|
+
service_list_alarm_rules,
|
|
13
|
+
service_list_alarm_specs,
|
|
14
|
+
)
|
|
15
|
+
from .api import service_get_api_by_code, service_get_api_by_name
|
|
16
|
+
from .auth import (
|
|
17
|
+
service_get_current_application,
|
|
18
|
+
service_list_applications,
|
|
19
|
+
service_login,
|
|
20
|
+
service_logout,
|
|
21
|
+
service_set_application,
|
|
22
|
+
)
|
|
23
|
+
from .data_table import (
|
|
24
|
+
service_create_data_table,
|
|
25
|
+
service_create_data_table_record,
|
|
26
|
+
service_delete_data_table_record_by_id,
|
|
27
|
+
service_delete_data_table_record_by_id_list,
|
|
28
|
+
service_get_data_table_by_code,
|
|
29
|
+
service_get_data_table_by_name,
|
|
30
|
+
service_get_data_table_data_by_code,
|
|
31
|
+
service_get_data_table_data_by_name,
|
|
32
|
+
service_get_data_table_record_by_id,
|
|
33
|
+
service_get_data_table_reference_list,
|
|
34
|
+
service_get_data_table_total,
|
|
35
|
+
service_update_data_table_record_by_id,
|
|
36
|
+
service_update_data_table_record_by_id_list,
|
|
37
|
+
)
|
|
38
|
+
from .dataset import (
|
|
39
|
+
service_count_dataset_by_code,
|
|
40
|
+
service_get_dataset_data_by_code,
|
|
41
|
+
service_get_dataset_detail_by_code,
|
|
42
|
+
service_list_datasets,
|
|
43
|
+
)
|
|
44
|
+
from .datasource import (
|
|
45
|
+
service_get_datasource_by_name,
|
|
46
|
+
service_get_datasource_config_by_application_id,
|
|
47
|
+
service_get_main_datasource_config_by_application,
|
|
48
|
+
)
|
|
49
|
+
from .dict_ import (
|
|
50
|
+
service_get_dict_detail_by_code,
|
|
51
|
+
service_get_dict_detail_by_name,
|
|
52
|
+
service_list_dicts,
|
|
53
|
+
)
|
|
54
|
+
from .hazard import (
|
|
55
|
+
service_clear_hazard_history_records,
|
|
56
|
+
service_dispatch_hazard_callback,
|
|
57
|
+
service_get_hazard_task_log_records,
|
|
58
|
+
service_get_hazard_task_logs,
|
|
59
|
+
service_list_hazard_history_records,
|
|
60
|
+
service_list_hazard_records,
|
|
61
|
+
service_trigger_hazard_task,
|
|
62
|
+
)
|
|
63
|
+
from .meta import (
|
|
64
|
+
release_list_create,
|
|
65
|
+
release_list_get_by_code,
|
|
66
|
+
release_list_get_by_name,
|
|
67
|
+
release_list_list,
|
|
68
|
+
release_list_page,
|
|
69
|
+
release_list_total,
|
|
70
|
+
release_list_update,
|
|
71
|
+
release_ref_list,
|
|
72
|
+
release_ref_list_by_ref,
|
|
73
|
+
)
|
|
74
|
+
from .metrics import service_get_metrics, service_list_metrics
|
|
75
|
+
from .ref import service_ref_backward, service_ref_forward
|
|
76
|
+
from .report import service_get_report, service_list_reports
|
|
77
|
+
from .skill import (
|
|
78
|
+
service_call_skill_by_code,
|
|
79
|
+
service_delete_skill,
|
|
80
|
+
service_delete_sub_file,
|
|
81
|
+
service_get_skill_by_code,
|
|
82
|
+
service_get_skill_by_name,
|
|
83
|
+
service_import_skill,
|
|
84
|
+
service_list_skill_files,
|
|
85
|
+
service_list_skills,
|
|
86
|
+
service_upload_sub_file,
|
|
87
|
+
)
|
|
88
|
+
from .table import (
|
|
89
|
+
service_count_table_by_code,
|
|
90
|
+
service_count_table_by_name,
|
|
91
|
+
service_exec_sql_by_datasource,
|
|
92
|
+
service_get_table_data_by_code,
|
|
93
|
+
service_get_table_data_by_name,
|
|
94
|
+
service_get_table_detail_by_code,
|
|
95
|
+
service_get_table_detail_by_name,
|
|
96
|
+
service_list_tables,
|
|
97
|
+
)
|
|
98
|
+
from .widget import (
|
|
99
|
+
service_count_widget_by_code,
|
|
100
|
+
service_get_widget_data_by_code,
|
|
101
|
+
service_get_widget_detail_by_code,
|
|
102
|
+
service_list_widgets,
|
|
103
|
+
)
|
|
104
|
+
from .workflow import (
|
|
105
|
+
service_get_workflow_by_code,
|
|
106
|
+
service_get_workflow_by_name,
|
|
107
|
+
service_list_workflows,
|
|
108
|
+
service_start_workflow,
|
|
109
|
+
service_start_workflow_async,
|
|
110
|
+
)
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""告警服务模块.
|
|
2
|
+
|
|
3
|
+
提供告警规则的查询与配置能力,覆盖后端 /alarm/rule/* 接口。
|
|
4
|
+
|
|
5
|
+
- 服务层函数 (service_*):纯 Python 接口,返回原始数据
|
|
6
|
+
- CLI 命令:基于服务层的薄包装,负责格式化输出
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterable, Mapping
|
|
10
|
+
from typing import Any, Literal, overload
|
|
11
|
+
|
|
12
|
+
from pydantic import ConfigDict, JsonValue, TypeAdapter
|
|
13
|
+
from pydantic_core import to_json
|
|
14
|
+
|
|
15
|
+
from dataops_sdk.exceptions import AuthenticationError, DataOpsError
|
|
16
|
+
from dataops_sdk.schemas.alarm import (
|
|
17
|
+
AlarmActionItem,
|
|
18
|
+
AlarmRuleItem,
|
|
19
|
+
AlarmRuleMatchItem,
|
|
20
|
+
AlarmSpecItem,
|
|
21
|
+
)
|
|
22
|
+
from dataops_sdk.schemas.query import PageParam, SqlQueryParam
|
|
23
|
+
from dataops_sdk.session import get_session
|
|
24
|
+
from dataops_sdk.utils import encrypt_body
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"service_list_alarm_rules",
|
|
28
|
+
"service_get_alarm_rule_enums",
|
|
29
|
+
"service_find_meta_by_codes",
|
|
30
|
+
"service_get_alarm_table_columns",
|
|
31
|
+
"service_get_rel_table_columns",
|
|
32
|
+
"service_find_alarm_table_data",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@overload
|
|
37
|
+
def _post_alarm(
|
|
38
|
+
url: str,
|
|
39
|
+
count: Literal[False],
|
|
40
|
+
query_param: SqlQueryParam | None = None,
|
|
41
|
+
page_param: PageParam | None = None,
|
|
42
|
+
) -> list[Mapping[str, JsonValue]]: ...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@overload
|
|
46
|
+
def _post_alarm(
|
|
47
|
+
url: str,
|
|
48
|
+
count: Literal[True],
|
|
49
|
+
query_param: SqlQueryParam | None = None,
|
|
50
|
+
) -> int: ...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _post_alarm(
|
|
54
|
+
url: str,
|
|
55
|
+
count: bool = False,
|
|
56
|
+
query_param: SqlQueryParam | None = None,
|
|
57
|
+
page_param: PageParam | None = None,
|
|
58
|
+
) -> Any:
|
|
59
|
+
"""告警模块 POST 请求,返回 resp_body["data"].
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
DataOpsError: API 返回错误时
|
|
63
|
+
AuthenticationError: 未认证或认证失败时
|
|
64
|
+
"""
|
|
65
|
+
body = dict(pageParam=page_param, queryParam=query_param, count=count)
|
|
66
|
+
try:
|
|
67
|
+
with get_session() as session:
|
|
68
|
+
resp = session.post(url, content=to_json(encrypt_body(body)))
|
|
69
|
+
resp_body = resp.json()
|
|
70
|
+
if resp.status_code == 401:
|
|
71
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
72
|
+
if resp.status_code != 200:
|
|
73
|
+
raise DataOpsError(
|
|
74
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
75
|
+
)
|
|
76
|
+
return resp_body.get("data")
|
|
77
|
+
except SystemExit as e:
|
|
78
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _get_alarm(url: str) -> Any:
|
|
82
|
+
"""告警模块 GET 请求,返回 resp_body["data"].
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
DataOpsError: API 返回错误时
|
|
86
|
+
AuthenticationError: 未认证或认证失败时
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
with get_session() as session:
|
|
90
|
+
resp = session.get(url)
|
|
91
|
+
resp_body = resp.json()
|
|
92
|
+
if resp.status_code == 401:
|
|
93
|
+
raise AuthenticationError("认证失败,请先登录")
|
|
94
|
+
if resp.status_code != 200:
|
|
95
|
+
raise DataOpsError(
|
|
96
|
+
resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
|
|
97
|
+
)
|
|
98
|
+
if resp_body.get("data"):
|
|
99
|
+
return resp_body.get("data")
|
|
100
|
+
except SystemExit as e:
|
|
101
|
+
raise AuthenticationError("认证失败,请先登录") from e
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def service_list_alarm_rules(
|
|
105
|
+
page: int = 0,
|
|
106
|
+
size: int = 50,
|
|
107
|
+
query: str | None = None,
|
|
108
|
+
enabled: bool | None = None,
|
|
109
|
+
):
|
|
110
|
+
url = "/alarm/ruleType/list"
|
|
111
|
+
page_param = PageParam(start=page * max(0, size), limit=size)
|
|
112
|
+
query_param = SqlQueryParam()
|
|
113
|
+
if enabled is not None:
|
|
114
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
115
|
+
if query:
|
|
116
|
+
query_param.add_like("title", query, "mrl")
|
|
117
|
+
|
|
118
|
+
results = _post_alarm(url, False, page_param, query_param)
|
|
119
|
+
return TypeAdapter(list[AlarmRuleItem], config=ConfigDict(str_to_lower=True)).validate_python(
|
|
120
|
+
results
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def service_count_alarm_rules(
|
|
125
|
+
query: str | None = None,
|
|
126
|
+
enabled: bool | None = None,
|
|
127
|
+
):
|
|
128
|
+
url = "/alarm/ruleType/list"
|
|
129
|
+
query_param = SqlQueryParam()
|
|
130
|
+
if enabled is not None:
|
|
131
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
132
|
+
if query:
|
|
133
|
+
query_param.add_like("title", query, "mrl")
|
|
134
|
+
return _post_alarm(url, True, query_param)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def service_list_alarm_specs(
|
|
138
|
+
page: int = 0,
|
|
139
|
+
size: int = 50,
|
|
140
|
+
query: str | None = None,
|
|
141
|
+
enabled: bool | None = None,
|
|
142
|
+
):
|
|
143
|
+
url = "/alarm/specType/list"
|
|
144
|
+
page_param = PageParam(start=page * max(0, size), limit=size)
|
|
145
|
+
query_param = SqlQueryParam()
|
|
146
|
+
if enabled is not None:
|
|
147
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
148
|
+
if query:
|
|
149
|
+
query_param.add_like("title", query, "mrl")
|
|
150
|
+
results = _post_alarm(url, False, page_param, query_param)
|
|
151
|
+
return TypeAdapter(list[AlarmSpecItem], config=ConfigDict(str_to_lower=True)).validate_python(
|
|
152
|
+
results
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def service_count_alarm_specs(
|
|
157
|
+
query: str | None = None,
|
|
158
|
+
enabled: bool | None = None,
|
|
159
|
+
):
|
|
160
|
+
url = "/alarm/specType/list"
|
|
161
|
+
query_param = SqlQueryParam()
|
|
162
|
+
if enabled is not None:
|
|
163
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
164
|
+
if query:
|
|
165
|
+
query_param.add_like("title", query, "mrl")
|
|
166
|
+
return _post_alarm(url, True, query_param)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def service_list_alarm_rule_matches(
|
|
170
|
+
page: int = 0,
|
|
171
|
+
size: int = 50,
|
|
172
|
+
query: str | None = None,
|
|
173
|
+
enabled: bool | None = None,
|
|
174
|
+
specs: Iterable[str] | None = None,
|
|
175
|
+
):
|
|
176
|
+
url = "/alarm/rule/list"
|
|
177
|
+
page_param = PageParam(start=page * max(0, size), limit=size)
|
|
178
|
+
query_param = SqlQueryParam()
|
|
179
|
+
if enabled is not None:
|
|
180
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
181
|
+
if specs:
|
|
182
|
+
query_param.varMap["specType"] = ",".join(specs)
|
|
183
|
+
if query:
|
|
184
|
+
query_param.add_like("title", query, "mrl")
|
|
185
|
+
results = _post_alarm(url, False, page_param, query_param)
|
|
186
|
+
return TypeAdapter(
|
|
187
|
+
list[AlarmRuleMatchItem], config=ConfigDict(str_to_lower=True)
|
|
188
|
+
).validate_python(results)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def service_count_alarm_rule_matches(
|
|
192
|
+
query: str | None = None,
|
|
193
|
+
enabled: bool | None = None,
|
|
194
|
+
specs: Iterable[str] | None = None,
|
|
195
|
+
) -> int:
|
|
196
|
+
url = "/alarm/rule/list"
|
|
197
|
+
query_param = SqlQueryParam()
|
|
198
|
+
if enabled is not None:
|
|
199
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
200
|
+
if specs:
|
|
201
|
+
query_param.varMap["specType"] = ",".join(specs)
|
|
202
|
+
if query:
|
|
203
|
+
query_param.add_like("title", query, "mrl")
|
|
204
|
+
return _post_alarm(url, True, query_param)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def service_list_alarm_action(
|
|
208
|
+
page: int = 0,
|
|
209
|
+
size: int = 50,
|
|
210
|
+
query: str | None = None,
|
|
211
|
+
enabled: bool | None = None,
|
|
212
|
+
rules: Iterable[str] | None = None,
|
|
213
|
+
):
|
|
214
|
+
url = "/alarm/action/list"
|
|
215
|
+
page_param = PageParam(start=page * max(0, size), limit=size)
|
|
216
|
+
query_param = SqlQueryParam()
|
|
217
|
+
if enabled is not None:
|
|
218
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
219
|
+
if rules:
|
|
220
|
+
query_param.varMap["ruleType"] = ",".join(rules)
|
|
221
|
+
if query:
|
|
222
|
+
query_param.add_like("title", query, "mrl")
|
|
223
|
+
results = _post_alarm(url, False, page_param, query_param)
|
|
224
|
+
return TypeAdapter(list[AlarmActionItem], config=ConfigDict(str_to_lower=True)).validate_python(
|
|
225
|
+
results
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def service_count_alarm_action(
|
|
230
|
+
query: str | None = None,
|
|
231
|
+
enabled: bool | None = None,
|
|
232
|
+
rules: Iterable[str] | None = None,
|
|
233
|
+
):
|
|
234
|
+
url = "/alarm/action/list"
|
|
235
|
+
query_param = SqlQueryParam()
|
|
236
|
+
if enabled is not None:
|
|
237
|
+
query_param.varMap["isEnabled"] = "1" if enabled else "0"
|
|
238
|
+
if rules:
|
|
239
|
+
query_param.varMap["ruleType"] = ",".join(rules)
|
|
240
|
+
if query:
|
|
241
|
+
query_param.add_like("title", query, "mrl")
|
|
242
|
+
return _post_alarm(url, True, query_param)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def service_get_alarm_rule_enums() -> dict:
|
|
246
|
+
"""获取告警规则枚举配置.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
枚举项字典
|
|
250
|
+
|
|
251
|
+
Raises:
|
|
252
|
+
DataOpsError: API 请求失败时
|
|
253
|
+
AuthenticationError: 未认证或认证失败时
|
|
254
|
+
"""
|
|
255
|
+
return _get_alarm("/alarm/rule/enums") or {}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def service_find_meta_by_codes(codes: list[str]) -> list[dict]:
|
|
259
|
+
"""根据告警规则编码批量查询关联元数据.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
codes: 告警规则编码列表
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
元数据列表
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
DataOpsError: API 请求失败时
|
|
269
|
+
AuthenticationError: 未认证或认证失败时
|
|
270
|
+
"""
|
|
271
|
+
data = _post_alarm("/alarm/rule/findMetaByCodes", {"codes": codes})
|
|
272
|
+
return data or []
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def service_get_alarm_table_columns(spec_type: str) -> list[dict]:
|
|
276
|
+
"""获取指定 specType 对应的告警表列定义.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
spec_type: 告警规格类型
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
列定义列表
|
|
283
|
+
|
|
284
|
+
Raises:
|
|
285
|
+
DataOpsError: API 请求失败时
|
|
286
|
+
AuthenticationError: 未认证或认证失败时
|
|
287
|
+
"""
|
|
288
|
+
data = _post_alarm(f"/alarm/rule/getAlarmTableColumns/{spec_type}")
|
|
289
|
+
return data or []
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def service_get_rel_table_columns(code: str) -> list[dict]:
|
|
293
|
+
"""获取指定告警规则关联表列.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
code: 告警规则编码
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
列定义列表
|
|
300
|
+
|
|
301
|
+
Raises:
|
|
302
|
+
DataOpsError: API 请求失败时
|
|
303
|
+
AuthenticationError: 未认证或认证失败时
|
|
304
|
+
"""
|
|
305
|
+
data = _post_alarm(f"/alarm/rule/getRelTableColumns/{code}")
|
|
306
|
+
return data or []
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def service_find_alarm_table_data(code: str, query: Mapping[str, JsonValue] | None = None) -> dict:
|
|
310
|
+
"""查询告警表数据.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
code: 告警规则编码
|
|
314
|
+
query: 可选的查询条件
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
查询结果(结构由后端定义)
|
|
318
|
+
|
|
319
|
+
Raises:
|
|
320
|
+
DataOpsError: API 请求失败时
|
|
321
|
+
AuthenticationError: 未认证或认证失败时
|
|
322
|
+
"""
|
|
323
|
+
body = {"code": code, "query": query or {}}
|
|
324
|
+
return _post_alarm("/alarm/rule/findTableData", body) or {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from dataops_sdk.schemas import HttpInterfaceData, MetaType
|
|
2
|
+
from dataops_sdk.services.meta import release_list_get_by_code, release_list_get_by_name
|
|
3
|
+
|
|
4
|
+
__all__ = ["service_get_api_by_code", "service_get_api_by_name"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def service_get_api_by_code(code: str):
|
|
8
|
+
"""根据编码获取 API 接口详情.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
code: API 接口编码
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
ApiMeta 对象,包含 HttpInterfaceData 数据
|
|
15
|
+
"""
|
|
16
|
+
return release_list_get_by_code(MetaType.API, code, _class=HttpInterfaceData)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def service_get_api_by_name(name: str):
|
|
20
|
+
"""根据名称获取 API 接口详情.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
name: API 接口名称
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
ApiMeta 对象,包含 HttpInterfaceData 数据
|
|
27
|
+
"""
|
|
28
|
+
return release_list_get_by_name(MetaType.API, name, _class=HttpInterfaceData)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from pydantic import TypeAdapter
|
|
4
|
+
from pydantic_core import to_json
|
|
5
|
+
|
|
6
|
+
from dataops_sdk.constants import APPLICATION_FILE, DEFAULT_SECRET_KEY, USER_INFO_FILE
|
|
7
|
+
from dataops_sdk.exceptions import AuthenticationError
|
|
8
|
+
from dataops_sdk.schemas.user import Application, UserInfo
|
|
9
|
+
from dataops_sdk.session import (
|
|
10
|
+
get_config,
|
|
11
|
+
get_session,
|
|
12
|
+
)
|
|
13
|
+
from dataops_sdk.utils import (
|
|
14
|
+
encrypt_body,
|
|
15
|
+
encrypt_hex,
|
|
16
|
+
get_work_dir,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def service_login(
|
|
21
|
+
username: str,
|
|
22
|
+
password: str,
|
|
23
|
+
*,
|
|
24
|
+
secret_key: str = DEFAULT_SECRET_KEY,
|
|
25
|
+
) -> UserInfo:
|
|
26
|
+
"""登录 DataOps 并保存 token。
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
username: 登录用户名
|
|
30
|
+
password: 登录密码(将使用 AES 加密后传输)
|
|
31
|
+
secret_key: AES 加密密钥
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
UserInfo 对象(包含 access_token 等)
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
AuthenticationError: 登录失败时
|
|
38
|
+
"""
|
|
39
|
+
config = get_config()
|
|
40
|
+
url = f"{config.base_url}/oauth/token"
|
|
41
|
+
params = {
|
|
42
|
+
"username": username,
|
|
43
|
+
"password": encrypt_hex(password, secret_key),
|
|
44
|
+
"grant_type": "captcha",
|
|
45
|
+
}
|
|
46
|
+
with get_session(False) as session:
|
|
47
|
+
response = session.post(url, params=params, headers={"x-client-id": "admin-web"})
|
|
48
|
+
response_body = response.json()
|
|
49
|
+
if response.status_code == 200 and response_body.get("code") == 200:
|
|
50
|
+
user_info = UserInfo.model_validate(response_body["data"])
|
|
51
|
+
target_path = get_work_dir(USER_INFO_FILE)
|
|
52
|
+
target_path.write_bytes(to_json(user_info))
|
|
53
|
+
return user_info
|
|
54
|
+
else:
|
|
55
|
+
raise AuthenticationError(
|
|
56
|
+
f"Login failed: {response_body.get('msg', f'HTTP {response.status_code}')}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def service_logout() -> bool:
|
|
61
|
+
"""清除本地存储的认证信息。"""
|
|
62
|
+
target_path = get_work_dir(USER_INFO_FILE)
|
|
63
|
+
if target_path.exists() and target_path.is_file():
|
|
64
|
+
target_path.unlink()
|
|
65
|
+
return True
|
|
66
|
+
else:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def service_list_applications(query: str | None = None) -> list[Application]:
|
|
71
|
+
"""列出当前用户可访问的应用。
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
query: 关键字过滤(匹配 LABEL_CN)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Application 对象列表
|
|
78
|
+
"""
|
|
79
|
+
url = "/sys/application/findAccessible"
|
|
80
|
+
with get_session(False) as session:
|
|
81
|
+
response = session.post(url, content=to_json(encrypt_body(b"{}")))
|
|
82
|
+
response_body = response.json()
|
|
83
|
+
applications = TypeAdapter(list[Application]).validate_python(response_body["data"])
|
|
84
|
+
if query:
|
|
85
|
+
pattern = re.compile(query)
|
|
86
|
+
applications = [app for app in applications if pattern.search(app.LABEL_CN)]
|
|
87
|
+
return applications
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def service_set_application(code: str) -> Application:
|
|
91
|
+
"""设置当前应用。
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
code: 应用编码
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
设置的 Application 对象
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: 应用编码不存在时
|
|
101
|
+
"""
|
|
102
|
+
url = "/sys/application/findAccessible"
|
|
103
|
+
with get_session(strict=False) as session:
|
|
104
|
+
response = session.post(url, content=b"{}")
|
|
105
|
+
response_body = response.json()
|
|
106
|
+
applications = TypeAdapter(list[Application]).validate_python(response_body["data"])
|
|
107
|
+
application = next((app for app in applications if code == app.CODE), None)
|
|
108
|
+
if application is None:
|
|
109
|
+
raise ValueError(f"Application with code '{code}' not found")
|
|
110
|
+
target_path = get_work_dir(APPLICATION_FILE)
|
|
111
|
+
target_path.write_bytes(to_json(application, indent=2))
|
|
112
|
+
return application
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def service_get_current_application() -> Application | None:
|
|
116
|
+
"""获取当前已设置的应用信息。"""
|
|
117
|
+
target_path = get_work_dir(APPLICATION_FILE)
|
|
118
|
+
if not target_path.exists():
|
|
119
|
+
return None
|
|
120
|
+
return Application.model_validate_json(target_path.read_bytes())
|