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.

Files changed (79) hide show
  1. dataops_sdk/__init__.py +84 -0
  2. dataops_sdk/constants.py +6 -0
  3. dataops_sdk/exceptions.py +31 -0
  4. dataops_sdk/runner/__init__.py +46 -0
  5. dataops_sdk/runner/datasource_factory.py +83 -0
  6. dataops_sdk/runner/schemas/nacos/__init__.py +5 -0
  7. dataops_sdk/runner/schemas/nacos/config.py +40 -0
  8. dataops_sdk/runner/schemas/nacos/constants.py +235 -0
  9. dataops_sdk/runner/schemas/nacos/instance.py +103 -0
  10. dataops_sdk/runner/schemas/nacos/nacos_exception.py +44 -0
  11. dataops_sdk/runner/schemas/nacos/naming_param.py +62 -0
  12. dataops_sdk/runner/schemas/nacos/naming_request.py +84 -0
  13. dataops_sdk/runner/schemas/nacos/naming_response.py +50 -0
  14. dataops_sdk/runner/schemas/nacos/preserved_metadata_key.py +10 -0
  15. dataops_sdk/runner/schemas/nacos/service.py +119 -0
  16. dataops_sdk/runner/utils/__init__.py +42 -0
  17. dataops_sdk/runner/utils/common.py +209 -0
  18. dataops_sdk/runner/utils/dbConn_base.py +256 -0
  19. dataops_sdk/runner/utils/dbConn_sync.py +485 -0
  20. dataops_sdk/runner/utils/duckdb_manager.py +203 -0
  21. dataops_sdk/runner/utils/file_transfer_base.py +307 -0
  22. dataops_sdk/runner/utils/ftp_file_transfer.py +242 -0
  23. dataops_sdk/runner/utils/log.py +50 -0
  24. dataops_sdk/runner/utils/nacos.py +96 -0
  25. dataops_sdk/runner/utils/neo4j_sync.py +236 -0
  26. dataops_sdk/runner/utils/queue_base.py +224 -0
  27. dataops_sdk/runner/utils/queue_kafka.py +150 -0
  28. dataops_sdk/runner/utils/sftp_file_transfer.py +243 -0
  29. dataops_sdk/schemas/__init__.py +32 -0
  30. dataops_sdk/schemas/alarm.py +38 -0
  31. dataops_sdk/schemas/api.py +37 -0
  32. dataops_sdk/schemas/data_table.py +437 -0
  33. dataops_sdk/schemas/datasource.py +26 -0
  34. dataops_sdk/schemas/dict_.py +13 -0
  35. dataops_sdk/schemas/enums.py +120 -0
  36. dataops_sdk/schemas/group.py +40 -0
  37. dataops_sdk/schemas/hazard.py +151 -0
  38. dataops_sdk/schemas/meta.py +175 -0
  39. dataops_sdk/schemas/query.py +175 -0
  40. dataops_sdk/schemas/report.py +22 -0
  41. dataops_sdk/schemas/skill.py +70 -0
  42. dataops_sdk/schemas/table.py +65 -0
  43. dataops_sdk/schemas/user.py +52 -0
  44. dataops_sdk/schemas/widget.py +651 -0
  45. dataops_sdk/schemas/workflow.py +63 -0
  46. dataops_sdk/services/__init__.py +110 -0
  47. dataops_sdk/services/alarm.py +324 -0
  48. dataops_sdk/services/api.py +28 -0
  49. dataops_sdk/services/auth.py +120 -0
  50. dataops_sdk/services/data_table.py +485 -0
  51. dataops_sdk/services/dataset.py +127 -0
  52. dataops_sdk/services/datasource.py +57 -0
  53. dataops_sdk/services/dict_.py +69 -0
  54. dataops_sdk/services/hazard.py +260 -0
  55. dataops_sdk/services/meta.py +523 -0
  56. dataops_sdk/services/metrics.py +42 -0
  57. dataops_sdk/services/ref.py +66 -0
  58. dataops_sdk/services/report.py +36 -0
  59. dataops_sdk/services/skill.py +276 -0
  60. dataops_sdk/services/table.py +340 -0
  61. dataops_sdk/services/widget.py +143 -0
  62. dataops_sdk/services/workflow.py +102 -0
  63. dataops_sdk/session.py +292 -0
  64. dataops_sdk/tiptap/__init__.py +30 -0
  65. dataops_sdk/tiptap/builder.py +75 -0
  66. dataops_sdk/tiptap/constants.py +133 -0
  67. dataops_sdk/tiptap/markdown_parser.py +294 -0
  68. dataops_sdk/tiptap/models.py +43 -0
  69. dataops_sdk/tiptap/parse_utils.py +889 -0
  70. dataops_sdk/tiptap/render_utils.py +846 -0
  71. dataops_sdk/tiptap/renderers.py +51 -0
  72. dataops_sdk/tiptap/tool.py +629 -0
  73. dataops_sdk/utils/__init__.py +15 -0
  74. dataops_sdk/utils/common.py +211 -0
  75. dataops_sdk/utils/utils.py +120 -0
  76. dataops_sdk-0.3.0.dist-info/METADATA +25 -0
  77. dataops_sdk-0.3.0.dist-info/RECORD +79 -0
  78. dataops_sdk-0.3.0.dist-info/WHEEL +5 -0
  79. dataops_sdk-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,143 @@
1
+ """卡片 SDK 模块.
2
+
3
+ 封装卡片 (widget) 相关的 API 调用。
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from pydantic_core import to_json
9
+
10
+ from dataops_sdk.exceptions import DataOpsError
11
+ from dataops_sdk.schemas import MetaRelease, MetaType, PageParam, SqlQueryParam
12
+ from dataops_sdk.services.meta import release_list_get_by_code, release_list_list
13
+ from dataops_sdk.session import get_session
14
+ from dataops_sdk.utils import encrypt_body
15
+
16
+
17
+ def service_list_widgets() -> list[MetaRelease]:
18
+ """搜索卡片列表.
19
+
20
+ Returns:
21
+ MetaRelease 对象列表
22
+
23
+ Raises:
24
+ DataOpsError: API 请求失败时
25
+ AuthenticationError: 未认证时
26
+ """
27
+ return release_list_list(MetaType.WIDGET)
28
+
29
+
30
+ def service_get_widget_detail_by_code(code: str):
31
+ """指定卡片详情.
32
+
33
+ Args:
34
+ code: 卡片编码
35
+
36
+ Returns:
37
+ 卡片元数据模型。meta.data 为 dict,结构为:
38
+ subType: chart|metric|detail|topology|table|gis|statistic
39
+
40
+
41
+ Raises:
42
+ ValueError: 无效类型
43
+ NotFoundError: 卡片不存在
44
+ DataOpsError: API 请求失败
45
+ AuthenticationError: 未认证
46
+ """
47
+ # 不传 _class,让 meta.data 保持为 dict,避免 schema 与后端实际数据不一致
48
+ return release_list_get_by_code(MetaType.WIDGET, code)
49
+
50
+
51
+ def service_get_widget_data_by_code(
52
+ code: str,
53
+ page_param: PageParam | None = None,
54
+ query_param: SqlQueryParam | None = None,
55
+ ) -> list[dict[str, Any]]:
56
+ """获取卡片实际数据(通过后端 /data/widget/findData 接口).
57
+
58
+ Args:
59
+ code: 卡片编码
60
+ page_param: 分页参数,默认为第一页,每页 20 条
61
+ query_param: 查询过滤条件
62
+
63
+ Returns:
64
+ 数据行列表(每行为字典)
65
+
66
+ Raises:
67
+ NotFoundError: 卡片不存在
68
+ DataOpsError: API 请求失败
69
+ AuthenticationError: 未认证
70
+ """
71
+ # 先获取卡片元数据,以提取 tenantId / subSystem / prodCode
72
+ meta = service_get_widget_detail_by_code(code)
73
+ sub_system = getattr(meta, "subSystem", None) or "default"
74
+
75
+ if page_param is None:
76
+ page_param = PageParam(limit=20)
77
+ if query_param is None:
78
+ query_param = SqlQueryParam() # pyright: ignore[reportCallIssue]
79
+
80
+ body = {
81
+ "subSystem": sub_system,
82
+ "type": "widget",
83
+ "code": code,
84
+ "pageParam": page_param,
85
+ "queryParam": query_param,
86
+ }
87
+
88
+ with get_session(timeout=30) as session:
89
+ resp = session.post("/data/widget/findData", content=to_json(encrypt_body(body)))
90
+ resp_body = resp.json()
91
+
92
+ if not (200 <= resp.status_code < 300):
93
+ raise DataOpsError(f"请求失败({resp.status_code}): {resp_body}")
94
+
95
+ data = resp_body.get("data")
96
+ if data is None:
97
+ raise DataOpsError("查询异常")
98
+ return data
99
+
100
+
101
+ def service_count_widget_by_code(
102
+ code: str,
103
+ query_param: SqlQueryParam | None = None,
104
+ ) -> int:
105
+ """获取卡片数据数量.
106
+
107
+ 通过 `/data/widget/findData` 接口(请求体 `count=true`)。
108
+
109
+ Args:
110
+ code: 卡片编码
111
+ query_param: 查询条件
112
+
113
+ Returns:
114
+ 卡片数据数量
115
+
116
+ Raises:
117
+ NotFoundError: 卡片不存在时
118
+ DataOpsError: API 请求失败时
119
+ AuthenticationError: 未认证时
120
+ """
121
+ meta = service_get_widget_detail_by_code(code)
122
+ sub_system = getattr(meta, "subSystem", None) or "default"
123
+
124
+ if query_param is None:
125
+ query_param = SqlQueryParam() # pyright: ignore[reportCallIssue]
126
+
127
+ body = {
128
+ "subSystem": sub_system,
129
+ "type": MetaType.WIDGET,
130
+ "code": code,
131
+ "queryParam": query_param,
132
+ "count": True,
133
+ }
134
+
135
+ with get_session() as session:
136
+ resp = session.post("/data/widget/findData", content=to_json(encrypt_body(body)))
137
+ resp_body = resp.json()
138
+ if resp.status_code != 200:
139
+ raise DataOpsError(f"查询失败,{resp_body.get('msg')}", resp.status_code)
140
+ data = resp_body.get("data")
141
+ if data is None:
142
+ return 0
143
+ return int(data)
@@ -0,0 +1,102 @@
1
+ from pydantic_core import to_json
2
+
3
+ from dataops_sdk.exceptions import AuthenticationError, DataOpsError
4
+ from dataops_sdk.schemas import DataWorkflow, MetaRelease, MetaType
5
+ from dataops_sdk.services.meta import (
6
+ release_list_get_by_code,
7
+ release_list_get_by_name,
8
+ release_list_list,
9
+ )
10
+ from dataops_sdk.session import get_session
11
+ from dataops_sdk.utils import encrypt_body, filter_nodes_by_pattern
12
+
13
+ __all__ = [
14
+ "service_get_workflow_by_name",
15
+ "service_get_workflow_by_code",
16
+ "service_list_workflows",
17
+ "service_start_workflow",
18
+ "service_start_workflow_async",
19
+ ]
20
+
21
+
22
+ def service_list_workflows(query: str | None = None) -> list[MetaRelease]:
23
+ """列出工作流(可选正则过滤).
24
+
25
+ Args:
26
+ query: 正则表达式,匹配 NAME / TITLE / REMARK / DIGEST
27
+
28
+ Returns:
29
+ MetaRelease 对象列表
30
+
31
+ Raises:
32
+ DataOpsError: API 请求失败时
33
+ AuthenticationError: 未认证时
34
+ """
35
+ nodes = release_list_list(MetaType.WORKFLOW)
36
+ if query:
37
+ nodes = filter_nodes_by_pattern(nodes, query, include_digest=True)
38
+ return nodes
39
+
40
+
41
+ def service_get_workflow_by_name(name: str):
42
+ return release_list_get_by_name(MetaType.WORKFLOW, name, _class=DataWorkflow)
43
+
44
+
45
+ def service_get_workflow_by_code(code: str):
46
+ return release_list_get_by_code(MetaType.WORKFLOW, code, _class=DataWorkflow)
47
+
48
+
49
+ def _post_workflow(url: str, body: dict) -> dict:
50
+ """通用工作流 POST 请求,返回 resp_body["data"].
51
+
52
+ Raises:
53
+ DataOpsError: API 返回错误时
54
+ AuthenticationError: 未认证或认证失败时
55
+ """
56
+ try:
57
+ with get_session() as session:
58
+ resp = session.post(url, content=to_json(encrypt_body(body)))
59
+ resp_body = resp.json()
60
+ if resp.status_code == 401:
61
+ raise AuthenticationError("认证失败,请先登录")
62
+ if resp.status_code != 200:
63
+ raise DataOpsError(
64
+ resp_body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
65
+ )
66
+ return resp_body.get("data") or {}
67
+ except SystemExit as e:
68
+ raise AuthenticationError("认证失败,请先登录") from e
69
+
70
+
71
+ def service_start_workflow(code: str, params: dict | None = None) -> dict:
72
+ """同步启动工作流实例.
73
+
74
+ Args:
75
+ code: 工作流编码
76
+ params: 启动参数(透传给流程节点的入参)
77
+
78
+ Returns:
79
+ 后端返回的启动结果(包含 flowInstCuid 等字段)
80
+
81
+ Raises:
82
+ DataOpsError: API 请求失败时
83
+ AuthenticationError: 未认证或认证失败时
84
+ """
85
+ return _post_workflow("/workflow/start", {"code": code, "params": params or {}})
86
+
87
+
88
+ def service_start_workflow_async(code: str, params: dict | None = None) -> dict:
89
+ """异步启动工作流实例.
90
+
91
+ Args:
92
+ code: 工作流编码
93
+ params: 启动参数(透传给流程节点的入参)
94
+
95
+ Returns:
96
+ 后端返回的启动结果(通常包含 taskId 等字段)
97
+
98
+ Raises:
99
+ DataOpsError: API 请求失败时
100
+ AuthenticationError: 未认证或认证失败时
101
+ """
102
+ return _post_workflow("/workflow/startAsync", {"code": code, "params": params or {}})
dataops_sdk/session.py ADDED
@@ -0,0 +1,292 @@
1
+ import os
2
+ from collections.abc import Mapping
3
+ from contextlib import contextmanager
4
+ from os import getenv
5
+ from typing import IO, BinaryIO
6
+
7
+ import httpx2
8
+ from dataops_sdk.constants import APPLICATION_FILE, USER_INFO_FILE
9
+ from dataops_sdk.exceptions import AuthenticationError, DataOpsError
10
+ from dataops_sdk.schemas.user import Application, UserInfo
11
+ from dataops_sdk.utils import get_work_dir
12
+ from pydantic import BaseModel
13
+ from pydantic_core import from_json, to_json
14
+
15
+
16
+ class DataOpsConfig(BaseModel):
17
+ """可编程配置的 DataOps 会话参数.
18
+
19
+ 优先级: 显式传入的参数 > 环境变量 > 默认值
20
+ """
21
+
22
+ base_url: str = getenv("DATA_OPS_BASE_URL", "http://172.168.63.85/bgw/data-ops-service")
23
+ username: str | None = None
24
+ password: str | None = None
25
+ token: str | None = None
26
+ token_type: str = "Bearer"
27
+ app_code: str | None = None
28
+ app_release: str = "1.0.0"
29
+
30
+
31
+ # 模块级配置,可通过 dataops_sdk.configure() 修改
32
+ _config: DataOpsConfig = DataOpsConfig()
33
+
34
+
35
+ def configure(
36
+ *,
37
+ base_url: str | None = None,
38
+ username: str | None = None,
39
+ password: str | None = None,
40
+ token: str | None = None,
41
+ token_type: str | None = None,
42
+ app_code: str | None = None,
43
+ app_release: str | None = None,
44
+ ) -> DataOpsConfig:
45
+ """配置 data-ops-cli 会话参数.
46
+
47
+ 可用于替代登录或补充环境变量配置,使包可在无 CLI 登录的情况下使用。
48
+
49
+ Args:
50
+ base_url: API 基础 URL,覆盖环境变量 DATA_OPS_BASE_URL
51
+ username: DataOps 用户名
52
+ password: DataOps 密码(仅在使用内置 login 时需要)
53
+ token: 直接传入访问令牌
54
+ token_type: 令牌类型,默认 Bearer
55
+ app_code: 应用编码
56
+ app_release: 应用版本号
57
+
58
+ Returns:
59
+ 更新后的配置对象
60
+ """
61
+ global _config
62
+ if base_url is not None:
63
+ _config.base_url = base_url
64
+ if username is not None:
65
+ _config.username = username
66
+ if password is not None:
67
+ _config.password = password
68
+ if token is not None:
69
+ _config.token = token
70
+ if token_type is not None:
71
+ _config.token_type = token_type
72
+ if app_code is not None:
73
+ _config.app_code = app_code
74
+ if app_release is not None:
75
+ _config.app_release = app_release
76
+ return _config
77
+
78
+
79
+ def get_config() -> DataOpsConfig:
80
+ """返回当前配置。"""
81
+ return _config
82
+
83
+
84
+ def get_user_info_file_path() -> str:
85
+ """返回用户信息文件路径。"""
86
+ return get_work_dir(USER_INFO_FILE)
87
+
88
+
89
+ def get_application_file_path() -> str:
90
+ """返回应用信息文件路径。"""
91
+ return get_work_dir(APPLICATION_FILE)
92
+
93
+
94
+ def _load_user_info_from_file() -> UserInfo | None:
95
+ """从 ~/.dataops_cli/user_info.json 加载用户信息。"""
96
+ target_path = get_work_dir(USER_INFO_FILE)
97
+ if not target_path.exists():
98
+ return None
99
+ return UserInfo.model_validate_json(target_path.read_bytes())
100
+
101
+
102
+ def _load_application_from_file() -> Application | None:
103
+ """从 ~/.dataops_cli/application.json 加载应用信息。"""
104
+ target_path = get_work_dir(APPLICATION_FILE)
105
+ if not target_path.exists():
106
+ return None
107
+ return Application.model_validate_json(target_path.read_bytes())
108
+
109
+
110
+ def get_user_info(strict: bool = True) -> UserInfo | None:
111
+ """获取本地存储的用户信息。
112
+
113
+ 优先级: 显式配置的 token > user_info.json 文件
114
+ """
115
+ if _config.token:
116
+ return UserInfo(
117
+ token_type=_config.token_type,
118
+ access_token=_config.token,
119
+ refresh_token="",
120
+ expires_in=0,
121
+ )
122
+ user_info = _load_user_info_from_file()
123
+ if user_info is None and strict:
124
+ print("using `data-ops-cli auth` to login first")
125
+ raise SystemExit(401)
126
+ return user_info
127
+
128
+
129
+ def get_headers(strict: bool = True) -> Mapping[str, str]:
130
+ # 优先从环境变量获取
131
+ fmind_auth_headers = os.getenv("FMIND_AUTH_HEADERS", None)
132
+ if fmind_auth_headers is not None:
133
+ headers = from_json(fmind_auth_headers)
134
+ assert isinstance(headers, dict), "FMIND_AUTH_HEADERS must be a JSON object string"
135
+ return headers
136
+ # 其次从配置获取
137
+ user_info = get_user_info(strict=strict)
138
+ application = get_application(strict=strict)
139
+ headers = {}
140
+ if application is not None:
141
+ headers["X-App-Code"] = application.CODE
142
+ headers["X-App-Release"] = "1.0.0"
143
+ elif _config.app_code:
144
+ headers["X-App-Code"] = _config.app_code
145
+ headers["X-App-Release"] = _config.app_release
146
+ if not strict and user_info is None:
147
+ return headers
148
+ headers["Fmind-Auth"] = f"{user_info.token_type} {user_info.access_token}"
149
+ return headers
150
+
151
+
152
+ def get_application(strict: bool = True) -> Application | None:
153
+ """获取当前应用信息。
154
+
155
+ 优先级: 显式配置的 app_code > application.json 文件
156
+ """
157
+ if _config.app_code:
158
+ app_file = _load_application_from_file()
159
+ if app_file is not None:
160
+ return app_file
161
+ if strict:
162
+ print("using `data-ops-cli auth application` to set application first")
163
+ raise SystemExit(401)
164
+ return None
165
+ app = _load_application_from_file()
166
+ if app is None and strict:
167
+ print("using `data-ops-cli auth application` to set application first")
168
+ raise SystemExit(401)
169
+ return app
170
+
171
+
172
+ def refresh_token(response: httpx2.Response):
173
+ if response.url.path.endswith("/oauth/token"):
174
+ return
175
+ if response.status_code == 200:
176
+ result: dict = response.json()
177
+ else:
178
+ result = dict()
179
+ need_auth = response.status_code == 401 and result.get("code", 400) == 401
180
+ if not need_auth:
181
+ return
182
+ url = "/oauth/token"
183
+ user_info = get_user_info(strict=True)
184
+ params = {
185
+ "refresh_token": user_info.refresh_token,
186
+ "grant_type": "refresh_token",
187
+ "scope": "all",
188
+ }
189
+ with get_session(False) as session:
190
+ resp = session.post(url, params=params)
191
+ response_body = resp.json()
192
+
193
+ if response.status_code == 200 and response_body.get("code") == 200:
194
+ user_info = UserInfo.model_validate(response_body["data"])
195
+ target_path = get_work_dir(USER_INFO_FILE)
196
+ target_path.write_bytes(to_json(user_info))
197
+ print("refresh token successfully")
198
+ return user_info
199
+ else:
200
+ raise AuthenticationError(
201
+ f"Login failed: {response_body.get('msg', f'HTTP {response.status_code}')}"
202
+ )
203
+
204
+
205
+ @contextmanager
206
+ def get_session(strict: bool = True, timeout: int = 10):
207
+ """获取请求 Session。
208
+
209
+ 使用 httpx2 管理 HTTP 连接,base_url 指向 DataOps 服务。
210
+ """
211
+ headers = get_headers(strict)
212
+ with httpx2.Client(
213
+ headers=headers,
214
+ base_url=_config.base_url,
215
+ verify=False,
216
+ timeout=timeout,
217
+ # event_hooks={"response": [refresh_token]},
218
+ ) as session:
219
+ try:
220
+ yield session
221
+ finally:
222
+ session.close()
223
+
224
+
225
+ @contextmanager
226
+ def post_multipart(
227
+ url: str,
228
+ *,
229
+ files: Mapping[str, tuple[str, IO[bytes] | BinaryIO] | IO[bytes] | BinaryIO],
230
+ data: Mapping[str, str] | None = None,
231
+ headers: Mapping[str, str] | None = None,
232
+ timeout: int = 30,
233
+ ):
234
+ """multipart/form-data POST 请求上下文管理器.
235
+
236
+ 自动注入 base headers(含 ``Fmind-Auth``/``X-App-Code``),并做统一的异常映射:
237
+ - 401 → :class:`AuthenticationError`
238
+ - 非 200 → :class:`DataOpsError`
239
+ - 未登录抛出 ``SystemExit`` → :class:`AuthenticationError`
240
+
241
+ Args:
242
+ url: 相对 ``base_url`` 的路径,如 ``"/data/skill/import"``.
243
+ files: 表单文件字段,格式为 ``{"字段名": (filename, file_obj)}`` 或 ``{"字段名": file_obj}``.
244
+ data: 文本表单字段,如 ``{"subType": "llm"}``.
245
+ headers: 额外请求头,会与 base headers 合并.
246
+ timeout: 请求超时秒数,默认 30s(ZIP 等大文件可能需要更长时间).
247
+
248
+ Yields:
249
+ httpx2.Response: 已通过状态码校验的响应对象.
250
+
251
+ Raises:
252
+ AuthenticationError: 未登录或认证失效.
253
+ DataOpsError: API 返回非 200.
254
+
255
+ Examples:
256
+ >>> from pathlib import Path
257
+ >>> with open("skill.zip", "rb") as f:
258
+ ... with post_multipart(
259
+ ... "/data/skill/import",
260
+ ... files={"file": ("skill.zip", f)},
261
+ ... data={"subType": "llm"},
262
+ ... ) as resp:
263
+ ... payload = resp.json()
264
+ """
265
+ extra_headers: dict[str, str] = dict(get_headers()) # type: ignore[arg-type]
266
+ if headers:
267
+ extra_headers.update(dict(headers))
268
+ try:
269
+ with httpx2.Client(
270
+ headers=extra_headers,
271
+ base_url=_config.base_url,
272
+ verify=False,
273
+ timeout=timeout,
274
+ ) as client:
275
+ try:
276
+ resp = client.post(url, files=files, data=data)
277
+ if resp.status_code == 401:
278
+ raise AuthenticationError("认证失败,请先登录")
279
+ if resp.status_code != 200:
280
+ body: dict = {}
281
+ try:
282
+ body = resp.json()
283
+ except Exception:
284
+ pass
285
+ raise DataOpsError(
286
+ body.get("msg", f"HTTP {resp.status_code}"), resp.status_code
287
+ )
288
+ yield resp
289
+ finally:
290
+ client.close()
291
+ except SystemExit as e:
292
+ raise AuthenticationError("认证失败,请先登录") from e
@@ -0,0 +1,30 @@
1
+ """TipTap module for converting between TipTap JSON and Markdown."""
2
+
3
+ from dataops_sdk.tiptap.builder import TipTapBuilder
4
+ from dataops_sdk.tiptap.constants import (
5
+ AttrTypes,
6
+ IndentStyle,
7
+ Markdown,
8
+ MarkTypes,
9
+ NodeTypes,
10
+ Style,
11
+ TableStyle,
12
+ )
13
+ from dataops_sdk.tiptap.models import DocNode, Mark, Node, TextNode
14
+ from dataops_sdk.tiptap.tool import TipTapTool
15
+
16
+ __all__ = [
17
+ "TipTapTool",
18
+ "TipTapBuilder",
19
+ "AttrTypes",
20
+ "IndentStyle",
21
+ "Markdown",
22
+ "MarkTypes",
23
+ "NodeTypes",
24
+ "Style",
25
+ "TableStyle",
26
+ "DocNode",
27
+ "Mark",
28
+ "Node",
29
+ "TextNode",
30
+ ]
@@ -0,0 +1,75 @@
1
+ """TipTapBuilder factory for creating TipTapTool instances."""
2
+
3
+ from dataops_sdk.tiptap import constants
4
+ from dataops_sdk.tiptap.tool import TipTapTool
5
+
6
+
7
+ class TipTapBuilder:
8
+ """Builder for TipTapTool instances.
9
+
10
+ Supports creating TipTapTool with default or custom configurations.
11
+ """
12
+
13
+ @staticmethod
14
+ def build(
15
+ indent_style: str = constants.Style.INDENT_STYLE_SPACE,
16
+ indent_size: int = constants.Style.DEFAULT_INDENT_SIZE,
17
+ table_style: str = constants.Style.DEFAULT_TABLE_STYLE,
18
+ ) -> TipTapTool:
19
+ """Build with configuration.
20
+
21
+ Args:
22
+ indent_style: Indent style (space or tab)
23
+ indent_size: Number of indent characters
24
+ table_style: Table rendering style (markdown or html)
25
+
26
+ Returns:
27
+ TipTapTool instance with settings
28
+ """
29
+ return TipTapTool(
30
+ indent_style=indent_style,
31
+ indent_size=indent_size,
32
+ table_style=table_style,
33
+ )
34
+
35
+ @staticmethod
36
+ def create_default() -> TipTapTool:
37
+ """Create with default configuration.
38
+
39
+ Returns:
40
+ TipTapTool instance with default settings
41
+ """
42
+ return TipTapTool()
43
+
44
+ @staticmethod
45
+ def create_with_table_style(table_style: str) -> TipTapTool:
46
+ """Create with custom table style.
47
+
48
+ Args:
49
+ table_style: Table rendering style (markdown or html)
50
+
51
+ Returns:
52
+ TipTapTool instance with custom table style
53
+ """
54
+ return TipTapTool(
55
+ indent_style=constants.Style.INDENT_STYLE_SPACE,
56
+ indent_size=constants.Style.DEFAULT_INDENT_SIZE,
57
+ table_style=table_style,
58
+ )
59
+
60
+ @staticmethod
61
+ def create_with_indent_config(indent_style: str, indent_size: int) -> TipTapTool:
62
+ """Create with custom indent configuration.
63
+
64
+ Args:
65
+ indent_style: Indent style (space or tab)
66
+ indent_size: Number of indent characters
67
+
68
+ Returns:
69
+ TipTapTool instance with custom indent settings
70
+ """
71
+ return TipTapTool(
72
+ indent_style=indent_style,
73
+ indent_size=indent_size,
74
+ table_style=constants.Style.DEFAULT_TABLE_STYLE,
75
+ )