data-ops-cli 0.2.3__tar.gz → 0.2.4__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.
Files changed (86) hide show
  1. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/PKG-INFO +1 -1
  2. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/PKG-INFO +1 -1
  3. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/SOURCES.txt +4 -0
  4. data_ops_cli-0.2.4/src/dataops_cli/__init__.py +1 -0
  5. data_ops_cli-0.2.4/src/dataops_cli/cli/api.py +227 -0
  6. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/auth.py +8 -5
  7. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/data_table.py +5 -6
  8. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/dataset.py +5 -5
  9. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/datasource.py +5 -6
  10. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/dict_.py +5 -5
  11. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/factory.py +2 -0
  12. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/metrics.py +5 -5
  13. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/report.py +5 -5
  14. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/table.py +103 -31
  15. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/utils.py +12 -0
  16. data_ops_cli-0.2.4/src/dataops_cli/cli/widget.py +464 -0
  17. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/workflow.py +5 -5
  18. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/constants.py +0 -1
  19. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/dbConn_sync.py +16 -7
  20. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/__init__.py +4 -2
  21. data_ops_cli-0.2.4/src/dataops_cli/schems/api.py +37 -0
  22. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/meta.py +6 -1
  23. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/table.py +14 -14
  24. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/widget.py +107 -11
  25. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/__init__.py +7 -0
  26. data_ops_cli-0.2.4/src/dataops_cli/sdk/api.py +28 -0
  27. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/datasource.py +7 -0
  28. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/meta.py +37 -28
  29. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/table.py +128 -0
  30. data_ops_cli-0.2.4/src/dataops_cli/sdk/widget.py +99 -0
  31. data_ops_cli-0.2.3/src/dataops_cli/__init__.py +0 -1
  32. data_ops_cli-0.2.3/src/dataops_cli/cli/widget.py +0 -60
  33. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/README.md +0 -0
  34. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/pyproject.toml +0 -0
  35. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/setup.cfg +0 -0
  36. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/dependency_links.txt +0 -0
  37. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/entry_points.txt +0 -0
  38. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/requires.txt +0 -0
  39. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/data_ops_cli.egg-info/top_level.txt +0 -0
  40. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/__init__.py +0 -0
  41. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/cli/ref.py +0 -0
  42. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/exceptions.py +0 -0
  43. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/__init__.py +0 -0
  44. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/__init__.py +0 -0
  45. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/config.py +0 -0
  46. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/constants.py +0 -0
  47. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/instance.py +0 -0
  48. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/nacos_exception.py +0 -0
  49. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/naming_param.py +0 -0
  50. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/naming_request.py +0 -0
  51. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/naming_response.py +0 -0
  52. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/preserved_metadata_key.py +0 -0
  53. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/schemas/nacos/service.py +0 -0
  54. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/__init__.py +0 -0
  55. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/common.py +0 -0
  56. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/dbConn_base.py +0 -0
  57. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/duckdb_manager.py +0 -0
  58. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/log.py +0 -0
  59. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/nacos.py +0 -0
  60. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/runner/utils/neo4j_sync.py +0 -0
  61. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/data_table.py +0 -0
  62. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/datasource.py +0 -0
  63. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/dict_.py +0 -0
  64. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/enums.py +0 -0
  65. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/group.py +0 -0
  66. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/query.py +0 -0
  67. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/report.py +0 -0
  68. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/user.py +0 -0
  69. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/schems/workflow.py +0 -0
  70. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/auth.py +0 -0
  71. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/data_table.py +0 -0
  72. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/dataset.py +0 -0
  73. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/dict_.py +0 -0
  74. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/ref.py +0 -0
  75. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/report.py +0 -0
  76. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/sdk/workflow.py +0 -0
  77. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/session.py +0 -0
  78. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/__init__.py +0 -0
  79. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/builder.py +0 -0
  80. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/constants.py +0 -0
  81. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/models.py +0 -0
  82. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/parse_utils.py +0 -0
  83. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/render_utils.py +0 -0
  84. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/renderers.py +0 -0
  85. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/tiptap/tool.py +0 -0
  86. {data_ops_cli-0.2.3 → data_ops_cli-0.2.4}/src/dataops_cli/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-ops-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Requires-Python: <3.14,>=3.11
5
5
  Requires-Dist: requests
6
6
  Requires-Dist: pydantic-core
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: data-ops-cli
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Requires-Python: <3.14,>=3.11
5
5
  Requires-Dist: requests
6
6
  Requires-Dist: pydantic-core
@@ -12,6 +12,7 @@ src/dataops_cli/exceptions.py
12
12
  src/dataops_cli/session.py
13
13
  src/dataops_cli/utils.py
14
14
  src/dataops_cli/cli/__init__.py
15
+ src/dataops_cli/cli/api.py
15
16
  src/dataops_cli/cli/auth.py
16
17
  src/dataops_cli/cli/data_table.py
17
18
  src/dataops_cli/cli/dataset.py
@@ -45,6 +46,7 @@ src/dataops_cli/runner/utils/log.py
45
46
  src/dataops_cli/runner/utils/nacos.py
46
47
  src/dataops_cli/runner/utils/neo4j_sync.py
47
48
  src/dataops_cli/schems/__init__.py
49
+ src/dataops_cli/schems/api.py
48
50
  src/dataops_cli/schems/data_table.py
49
51
  src/dataops_cli/schems/datasource.py
50
52
  src/dataops_cli/schems/dict_.py
@@ -58,6 +60,7 @@ src/dataops_cli/schems/user.py
58
60
  src/dataops_cli/schems/widget.py
59
61
  src/dataops_cli/schems/workflow.py
60
62
  src/dataops_cli/sdk/__init__.py
63
+ src/dataops_cli/sdk/api.py
61
64
  src/dataops_cli/sdk/auth.py
62
65
  src/dataops_cli/sdk/data_table.py
63
66
  src/dataops_cli/sdk/dataset.py
@@ -67,6 +70,7 @@ src/dataops_cli/sdk/meta.py
67
70
  src/dataops_cli/sdk/ref.py
68
71
  src/dataops_cli/sdk/report.py
69
72
  src/dataops_cli/sdk/table.py
73
+ src/dataops_cli/sdk/widget.py
70
74
  src/dataops_cli/sdk/workflow.py
71
75
  src/dataops_cli/tiptap/__init__.py
72
76
  src/dataops_cli/tiptap/builder.py
@@ -0,0 +1 @@
1
+ __version__ = "0.2.4"
@@ -0,0 +1,227 @@
1
+ """API 接口管理模块.
2
+
3
+ 提供 API 接口的列表查询和详情功能。
4
+
5
+ - 服务层函数 (service_*):纯 Python 接口,返回原始数据
6
+ - CLI 命令:基于服务层的薄包装,负责格式化输出
7
+ """
8
+
9
+ import shutil
10
+ from typing import Annotated
11
+
12
+ import typer
13
+
14
+ from dataops_cli.exceptions import DataOpsError
15
+ from dataops_cli.schems import MetaType
16
+ from dataops_cli.sdk.api import service_get_api_by_code
17
+ from dataops_cli.sdk.meta import release_list_list
18
+ from dataops_cli.utils import filter_nodes_by_pattern
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # 常量
22
+ # ---------------------------------------------------------------------------
23
+
24
+ _STATUS_MAP: dict[str, str] = {
25
+ "dev": "开发中",
26
+ "test": "测试中",
27
+ "prod": "已发布",
28
+ "deprecated": "将弃用",
29
+ }
30
+
31
+ _SCOPE_MAP: dict[str, str] = {
32
+ "public": "公开接口",
33
+ "internal": "内部接口",
34
+ "external": "外部接口",
35
+ }
36
+
37
+ # ---------------------------------------------------------------------------
38
+ # CLI 命令
39
+ # ---------------------------------------------------------------------------
40
+
41
+ api_typer = typer.Typer(
42
+ name="api",
43
+ help="Manage APIs",
44
+ no_args_is_help=True,
45
+ )
46
+
47
+
48
+ def _first_line(text: str) -> str:
49
+ """取文本的第一行."""
50
+ return text.split("\n")[0]
51
+
52
+
53
+ @api_typer.command("list", rich_help_panel="API 查询")
54
+ def list_apis(
55
+ query: Annotated[
56
+ str | None, typer.Option("-q", "--query", help="按名称/标题/备注正则过滤")
57
+ ] = None,
58
+ ) -> None:
59
+ """列出所有 API 接口.
60
+
61
+ 从元数据服务获取 API 接口列表,支持通过正则表达式按名称、标题或备注进行过滤。
62
+
63
+ 示例:
64
+ 列出所有 API 接口
65
+ >>> data-ops-cli api list
66
+
67
+ 按名称/标题/备注过滤
68
+ >>> data-ops-cli api list --query ".*用户.*"
69
+ >>> data-ops-cli api list -q "用户"
70
+ """
71
+ try:
72
+ group_nodes = release_list_list(MetaType.API)
73
+ except DataOpsError as e:
74
+ typer.echo(f"错误: {e.message}")
75
+ raise typer.Exit(code=1) from None
76
+
77
+ if query:
78
+ group_nodes = filter_nodes_by_pattern(group_nodes, query)
79
+
80
+ if not group_nodes:
81
+ print("(无结果)")
82
+ return
83
+
84
+ print("code title remark")
85
+
86
+ for node in group_nodes:
87
+ code = node.CODE
88
+ title = node.TITLE
89
+ remark_raw = node.REMARK or ""
90
+ remark = _first_line(remark_raw.strip())
91
+ print(code, title, remark)
92
+
93
+
94
+ @api_typer.command("detail", rich_help_panel="API 查询", no_args_is_help=True)
95
+ def get_api_detail(
96
+ code: Annotated[str, typer.Argument(help="API 接口编码")],
97
+ simplify: Annotated[
98
+ bool,
99
+ typer.Option("-s", "--simplify", help="仅显示基本信息,不展示请求/响应参数"),
100
+ ] = False,
101
+ ) -> None:
102
+ """获取 API 接口详情.
103
+
104
+ 根据 API 接口编码获取详细信息,包括请求方法、路径、状态、请求参数和响应参数。
105
+
106
+ 示例:
107
+ 获取 API 接口完整信息
108
+ >>> data-ops-cli api detail user_login
109
+
110
+ 仅显示基本信息
111
+ >>> data-ops-cli api detail user_login --simplify
112
+ >>> data-ops-cli api detail user_login -s
113
+ """
114
+ try:
115
+ meta = service_get_api_by_code(code)
116
+ except DataOpsError as e:
117
+ typer.echo(f"错误: {e.message}")
118
+ raise typer.Exit(code=1) from None
119
+
120
+ d = meta.data
121
+ method = d.method
122
+ status_label = _STATUS_MAP.get(d.status, d.status)
123
+ scope_label = _SCOPE_MAP.get(d.scope, d.scope)
124
+
125
+ # 基本信息
126
+ print(f"方法: {method}")
127
+ print(f"路径: {d.endpoint}")
128
+ print(f"状态: {status_label}")
129
+ print(f"范围: {scope_label}")
130
+ if d.charger:
131
+ print(f"负责人: {d.charger}")
132
+ if d.env_code:
133
+ print(f"API 环境: {d.env_code}")
134
+ if d.description:
135
+ print(f"\n说明:\n{d.description}")
136
+
137
+ if simplify:
138
+ return
139
+
140
+ # 请求参数
141
+ rp = d.requestParam
142
+ print("\n--- 请求参数 ---")
143
+
144
+ # 认证
145
+ auth = rp.get("authParam", {})
146
+ if auth:
147
+ auth_type = auth.get("type", "none")
148
+ print(f" 认证: {auth_type}")
149
+
150
+ # Headers
151
+ headers = rp.get("headers", [])
152
+ if headers:
153
+ print(f" 请求头 ({len(headers)}):")
154
+ for h in headers:
155
+ required = " *" if h.get("required") else ""
156
+ print(f" - {h.get('name', '?')}: {h.get('title', '')}{required}")
157
+
158
+ # Path params
159
+ path_params = rp.get("pathParams", [])
160
+ if path_params:
161
+ print(f" 路径参数 ({len(path_params)}):")
162
+ for p in path_params:
163
+ required = " *" if p.get("required") else ""
164
+ print(
165
+ f" - {p.get('name', '?')}: {p.get('title', '')} ({p.get('type', '?')}){required}"
166
+ )
167
+
168
+ # Query params
169
+ query_params = rp.get("queryParams", [])
170
+ if query_params:
171
+ print(f" Query 参数 ({len(query_params)}):")
172
+ for q in query_params:
173
+ required = " *" if q.get("required") else ""
174
+ print(
175
+ f" - {q.get('name', '?')}: {q.get('title', '')} ({q.get('type', '?')}){required}"
176
+ )
177
+
178
+ # Body
179
+ body = rp.get("bodyParams", {})
180
+ if body:
181
+ mine_type = body.get("mineType", "none")
182
+ if mine_type != "none":
183
+ structure = body.get("structure", [])
184
+ print(f" 请求体: {mine_type}")
185
+ if structure:
186
+ _print_params_tree(structure, indent=4)
187
+
188
+ # Cookies
189
+ cookies = rp.get("cookies", [])
190
+ if cookies:
191
+ print(f" Cookie ({len(cookies)}):")
192
+ for c in cookies:
193
+ print(f" - {c.get('name', '?')}: {c.get('title', '')}")
194
+
195
+ # 响应参数
196
+ response_params = d.responseParams
197
+ if response_params:
198
+ print("\n--- 响应参数 ---")
199
+ for rp_item in response_params:
200
+ status_code = rp_item.get("status_code", "?")
201
+ title = rp_item.get("title", "")
202
+ mine_type = rp_item.get("mineType", "")
203
+ structure = rp_item.get("structure", [])
204
+ print(f" [{status_code}] {title} ({mine_type})")
205
+ if structure:
206
+ _print_params_tree(structure, indent=4)
207
+
208
+
209
+ def _print_params_tree(params: list[dict], indent: int = 0, prefix: str = "") -> None:
210
+ """递归打印参数树结构."""
211
+ for p in params:
212
+ ptype = p.get("type", "?")
213
+ name = p.get("name", "?")
214
+ title = p.get("title", "")
215
+ required = " *" if p.get("required") else ""
216
+ desc = p.get("description", "")
217
+ line = f"{' ' * indent}- {prefix}{name}: {title} ({ptype}){required}"
218
+ if desc:
219
+ line += f" # {desc}"
220
+ print(line)
221
+ if ptype in ("object", "array"):
222
+ children = p.get("children", [])
223
+ if children:
224
+ if ptype == "array":
225
+ _print_params_tree(children, indent + 2)
226
+ else:
227
+ _print_params_tree(children, indent + 2)
@@ -12,6 +12,7 @@ import typer
12
12
  from rich.console import Console
13
13
  from rich.table import Table
14
14
 
15
+ from dataops_cli.cli.utils import print_list
15
16
  from dataops_cli.constants import USER_INFO_FILE
16
17
  from dataops_cli.exceptions import AuthenticationError
17
18
  from dataops_cli.schems import UserInfo
@@ -135,11 +136,13 @@ def application_list(query: Annotated[str | None, typer.Argument(help="关键字
135
136
  typer.echo(f"错误: {e}")
136
137
  raise typer.Exit(code=1) from None
137
138
 
138
- table = Table("code", "title", "remark", title="应用列表")
139
- for application in applications:
140
- table.add_row(application.CODE, application.LABEL_CN, application.REMARK or "")
141
- console = Console(no_color=True)
142
- console.print(table)
139
+ print_list(
140
+ ["code", "title", "remark"],
141
+ [
142
+ [application.CODE, application.LABEL_CN, application.REMARK]
143
+ for application in applications
144
+ ],
145
+ )
143
146
 
144
147
 
145
148
  @application_typer.command("set")
@@ -11,7 +11,7 @@ import typer
11
11
  from rich.console import Console
12
12
  from rich.table import Table
13
13
 
14
- from dataops_cli.cli.utils import render_table
14
+ from dataops_cli.cli.utils import print_list, render_table
15
15
  from dataops_cli.exceptions import DataOpsError
16
16
  from dataops_cli.schems import MetaType, PageParam, SqlQueryParam
17
17
  from dataops_cli.sdk import (
@@ -63,11 +63,10 @@ def list_datatable(
63
63
  if query:
64
64
  group_nodes = filter_nodes_by_pattern(group_nodes, query, include_digest=True)
65
65
 
66
- table = Table("code", "title", "remark")
67
- console = Console(no_color=True)
68
- for column in group_nodes:
69
- table.add_row(column.CODE, column.TITLE, column.REMARK or "")
70
- console.print(table)
66
+ print_list(
67
+ ["code", "title", "remark"],
68
+ [[column.CODE, column.TITLE, column.REMARK] for column in group_nodes],
69
+ )
71
70
 
72
71
 
73
72
  @data_table_typer.command("detail", no_args_is_help=True)
@@ -4,6 +4,7 @@ import typer
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
6
 
7
+ from dataops_cli.cli.utils import print_list
7
8
  from dataops_cli.exceptions import DataOpsError
8
9
  from dataops_cli.sdk import service_get_dataset_detail_by_code, service_list_datasets
9
10
  from dataops_cli.utils import filter_nodes_by_pattern
@@ -35,11 +36,10 @@ def list_dataset(
35
36
  if query:
36
37
  group_nodes = filter_nodes_by_pattern(group_nodes, query, include_digest=True)
37
38
 
38
- table = Table("code", "name", "alias", "remark", title="dataset list")
39
- console = Console(no_color=True)
40
- for column in group_nodes:
41
- table.add_row(column.CODE, column.NAME, column.TITLE, column.REMARK or "")
42
- console.print(table)
39
+ print_list(
40
+ ["code", "name", "alias", "remark"],
41
+ [[column.CODE, column.NAME, column.TITLE, column.REMARK] for column in group_nodes],
42
+ )
43
43
 
44
44
 
45
45
  @dataset_typer.command("detail", no_args_is_help=True)
@@ -19,19 +19,18 @@ from dataops_cli.schems import (
19
19
  DataDataSource,
20
20
  InnerMetaType,
21
21
  MetaCreate,
22
- MetaUpdate,
23
22
  )
24
23
  from dataops_cli.schems.enums import DataSourceType
25
- from dataops_cli.sdk.datasource import get_application_datasource_config, get_datasource
24
+ from dataops_cli.sdk.datasource import (
25
+ get_application_datasource_config,
26
+ service_get_datasource_by_name,
27
+ )
26
28
  from dataops_cli.sdk.meta import (
27
29
  release_list_create,
28
30
  release_list_get_by_name,
29
31
  release_list_list,
30
32
  release_list_update,
31
33
  )
32
- from dataops_cli.sdk.table import (
33
- service_get_table_data_by_name,
34
- )
35
34
  from dataops_cli.session import get_application
36
35
  from dataops_cli.utils import filter_nodes_by_pattern
37
36
 
@@ -113,7 +112,7 @@ def get_datasource_detail(
113
112
  Args:
114
113
  name: 数据源名称
115
114
  """
116
- meta = release_list_get_by_name(InnerMetaType.DATASOURCE.value, name, _class=DataDataSource)
115
+ meta = service_get_datasource_by_name(name)
117
116
  table = Table("field", "value", show_header=False)
118
117
  table.add_row("name", meta.name)
119
118
  table.add_row("title", meta.title)
@@ -4,6 +4,7 @@ import typer
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
6
 
7
+ from dataops_cli.cli.utils import print_list
7
8
  from dataops_cli.exceptions import DataOpsError
8
9
  from dataops_cli.sdk.dict_ import (
9
10
  service_get_dict_detail_by_code,
@@ -47,11 +48,10 @@ def list_dict(
47
48
  if query:
48
49
  group_nodes = filter_nodes_by_pattern(group_nodes, query)
49
50
 
50
- table = Table("code", "name", "alias", "remark", title="dict list")
51
- console = Console(no_color=True)
52
- for column in group_nodes:
53
- table.add_row(column.CODE, column.NAME, column.TITLE)
54
- console.print(table)
51
+ print_list(
52
+ ["code", "name", "alias"],
53
+ [[column.CODE, column.NAME, column.TITLE] for column in group_nodes],
54
+ )
55
55
 
56
56
 
57
57
  @dict_typer.command("detail", no_args_is_help=True)
@@ -25,6 +25,7 @@ def main(
25
25
 
26
26
 
27
27
  def create_app():
28
+ from dataops_cli.cli.api import api_typer
28
29
  from dataops_cli.cli.auth import auth_typer
29
30
  from dataops_cli.cli.data_table import data_table_typer
30
31
  from dataops_cli.cli.dataset import dataset_typer
@@ -44,6 +45,7 @@ def create_app():
44
45
  short_help=True,
45
46
  )
46
47
  app.add_typer(auth_typer)
48
+ app.add_typer(api_typer)
47
49
  app.add_typer(datasource_typer)
48
50
  app.add_typer(dataset_typer)
49
51
  app.add_typer(table_typer)
@@ -12,6 +12,7 @@ import typer
12
12
  from rich.console import Console
13
13
  from rich.table import Table
14
14
 
15
+ from dataops_cli.cli.utils import print_list
15
16
  from dataops_cli.schems.enums import MetaType
16
17
  from dataops_cli.schems.query import SqlQueryParam
17
18
  from dataops_cli.sdk.meta import (
@@ -56,11 +57,10 @@ def list_metrics(
56
57
 
57
58
  if query:
58
59
  group_nodes = filter_nodes_by_pattern(group_nodes, query)
59
- table = Table("code", "title", "subType", title="metrics")
60
- console = Console(no_color=True)
61
- for column in group_nodes:
62
- table.add_row(column.CODE, column.TITLE, column.SUB_TYPE or "atomic")
63
- console.print(table)
60
+ print_list(
61
+ ["code", "title", "subType"],
62
+ [[column.CODE, column.TITLE, column.SUB_TYPE or "atomic"] for column in group_nodes],
63
+ )
64
64
 
65
65
 
66
66
  @metrics_typer.command("detail", no_args_is_help=True)
@@ -12,6 +12,7 @@ import typer
12
12
  from rich.console import Console
13
13
  from rich.table import Table
14
14
 
15
+ from dataops_cli.cli.utils import print_list
15
16
  from dataops_cli.exceptions import DataOpsError
16
17
  from dataops_cli.schems import MetaType
17
18
  from dataops_cli.sdk.meta import (
@@ -51,11 +52,10 @@ def list_reports(
51
52
  if query:
52
53
  group_nodes = filter_nodes_by_pattern(group_nodes, query)
53
54
 
54
- table = Table("code", "title", "subType", title="report")
55
- console = Console(no_color=True)
56
- for column in group_nodes:
57
- table.add_row(column.CODE, column.TITLE, column.SUB_TYPE or "doc")
58
- console.print(table)
55
+ print_list(
56
+ ["code", "title", "subType"],
57
+ [[column.CODE, column.TITLE, column.SUB_TYPE or "doc"] for column in group_nodes],
58
+ )
59
59
 
60
60
 
61
61
  @report_typer.command("detail", rich_help_panel="报告查询")
@@ -1,18 +1,19 @@
1
1
  from typing import Annotated, Optional
2
2
 
3
3
  import typer
4
- from rich.console import Console
5
- from rich.table import Table
4
+ from pydantic import BaseModel
6
5
 
7
- from dataops_cli.cli.utils import render_table
6
+ from dataops_cli.cli.utils import print_list, render_table
8
7
  from dataops_cli.exceptions import DataOpsError
9
8
  from dataops_cli.schems import (
9
+ DataBaseColumnDictExternal,
10
10
  DataBaseColumnReference,
11
11
  DataBaseColumnReferenceValue,
12
- DataBaseColumnTypeExternal,
12
+ DataBaseColumnTableExternal,
13
13
  PageParam,
14
14
  SqlQueryParam,
15
15
  )
16
+ from dataops_cli.schems.table import ColumnItem
16
17
  from dataops_cli.sdk import (
17
18
  release_list_update,
18
19
  service_count_table_by_code,
@@ -24,6 +25,7 @@ from dataops_cli.sdk import (
24
25
  service_get_table_detail_by_name,
25
26
  service_list_tables,
26
27
  )
28
+ from dataops_cli.sdk.table import service_exec_sql_by_datasource
27
29
  from dataops_cli.utils import filter_nodes_by_pattern
28
30
 
29
31
  table_typer = typer.Typer(name="table", help="Manage table.", no_args_is_help=True)
@@ -64,19 +66,17 @@ def list_table(
64
66
 
65
67
  if query:
66
68
  group_nodes = filter_nodes_by_pattern(group_nodes, query)
67
-
68
- table = Table("code", "name", "alias", "remark", title="table list")
69
- console = Console(no_color=True)
70
- for column in group_nodes:
71
- table.add_row(column.CODE, column.NAME, column.TITLE, column.REMARK or "")
72
- console.print(table)
69
+ print_list(
70
+ ["code", "name", "alias", "remark"],
71
+ [[column.CODE, column.NAME, column.TITLE, column.REMARK] for column in group_nodes],
72
+ )
73
73
 
74
74
 
75
75
  @table_typer.command("detail", no_args_is_help=True)
76
76
  def get_table_detail(
77
77
  arg1: Annotated[str, typer.Argument(help="编码或数据源")],
78
78
  name: Annotated[Optional[str], typer.Argument(help="表名")] = None,
79
- full: Annotated[bool, typer.Option(help="是否显示完整信息")] = False,
79
+ full: Annotated[bool, typer.Option("--full", help="显示完整DDL信息")] = False,
80
80
  ) -> None:
81
81
  """获取表详情(DDL)信息.
82
82
 
@@ -109,26 +109,58 @@ def get_table_detail(
109
109
 
110
110
  datasource = meta.subSystem
111
111
 
112
- console = Console(no_color=True)
113
- if full:
114
- table = Table("column", "alias", "dbType", "relation", title=f"{datasource} {name} DDL")
115
- for column in meta.data.columns:
116
- relation = None
117
- if column.typeExt is not None:
118
- relation = column.typeExt.ref__dict or column.typeExt.ref__valueField
119
- if relation is not None:
120
- relation = relation.model_dump_json()
121
- table.add_row(column.name, column.title, column.dbType, relation)
122
- console.print(table)
123
- for column in filter(lambda c: c.typeExt is not None, meta.data.columns):
124
- relation = column.typeExt.ref__dict or column.typeExt.ref__valueField
125
- if relation is not None:
126
- console.print(f"{column.name} -> {relation.model_dump_json()}")
112
+ print(datasource, meta.name)
113
+
114
+ def show_column(column: ColumnItem):
115
+ if column.pk and column.labelKey:
116
+ return f"{column.name}(√,√)"
117
+ elif column.pk:
118
+ return f"{column.name}(√,x)"
119
+ elif column.labelKey:
120
+ return f"{column.name}(x,√)"
121
+ else:
122
+ return column.name
123
+
124
+ def show_extend(column: ColumnItem):
125
+ output = []
126
+ if column.decimalPlace:
127
+ output.append(f"{column.decimalPlace}dp")
128
+ if column.nullable:
129
+ output.append("nullable")
130
+ else:
131
+ output.append("not-null")
132
+ return ",".join(output)
133
+
134
+ if not full:
135
+ print("column(pk,labelKey)", "alias", "dbType(length)")
136
+ print(
137
+ "\n".join(
138
+ (
139
+ f"{show_column(column)} {column.title} {column.dbType}({column.length})"
140
+ if column.pk
141
+ else ""
142
+ for column in meta.data.columns
143
+ )
144
+ )
145
+ )
127
146
  else:
128
- table = Table("column", "alias", "dbType", title=f"{datasource} {name} DDL")
147
+ print("columns info\ncolumn(pk,labelKey)", "alias", "dbType(length)", "extend", "relation")
129
148
  for column in meta.data.columns:
130
- table.add_row(column.name, column.title, column.dbType)
131
- console.print(table)
149
+ relation = None
150
+ if isinstance(column.typeExt, BaseModel):
151
+ relation = column.typeExt.model_dump_json()
152
+ print(
153
+ show_column(column),
154
+ column.title,
155
+ f"{column.dbType}({column.length})",
156
+ show_extend(column),
157
+ relation,
158
+ )
159
+ if meta.data.indexes:
160
+ print("\nindexes info\nname columns")
161
+ print("\n".join((f"{index.name} {index.columns}" for index in meta.data.indexes)))
162
+ else:
163
+ print("\nindexes info\n无索引信息")
132
164
 
133
165
 
134
166
  @table_typer.command("count", no_args_is_help=True)
@@ -222,6 +254,46 @@ def get_table_data(
222
254
  print(render_table(data))
223
255
 
224
256
 
257
+ @table_typer.command("exec", no_args_is_help=True)
258
+ def exec_sql(
259
+ datasource: Annotated[str, typer.Argument(help="数据源")],
260
+ sql: Annotated[str, typer.Argument(help="SQL 查询语句")],
261
+ page_num: Annotated[int, typer.Option("-n", "--page-num", help="页码")] = 0,
262
+ page_size: Annotated[int, typer.Option("-s", "--page-size", help="每页大小")] = 20,
263
+ ) -> None:
264
+ """执行任意 SQL 查询语句.
265
+
266
+ 支持两种查询方式:
267
+ 1. 通过表编码(CODE)直接查询
268
+ 2. 通过数据源名称和表名组合查询
269
+
270
+ 示例:
271
+ # 通过数据源 SQL
272
+ >>> data-ops-cli table exec-sql mydatasource "SELECT * FROM table LIMIT 10"
273
+
274
+ # 分页查询
275
+ >>> data-ops-cli table exec-sql mydatasource "SELECT * FROM table" --page-num 1 --page-size 50
276
+
277
+ # 以字典格式输出
278
+ >>> data-ops-cli table exec-sql mydatasource "SELECT * FROM table" --map
279
+ """
280
+ if not sql:
281
+ typer.echo("请输入 SQL 查询语句")
282
+ raise typer.Exit(code=1)
283
+ try:
284
+ datas = service_exec_sql_by_datasource(datasource, sql, page_num, page_size)
285
+ except DataOpsError as e:
286
+ typer.echo(f"错误: {e.message}")
287
+ raise typer.Exit(code=1) from None
288
+ if not datas:
289
+ print("未查询到数据")
290
+ return
291
+ columns_name = datas[0].keys()
292
+ print(*columns_name)
293
+ for data in datas:
294
+ print(*data.values())
295
+
296
+
225
297
  @column_typer.command("base", no_args_is_help=True)
226
298
  def set_column_config(
227
299
  datasource: Annotated[str, typer.Argument(help="编数据源")],
@@ -262,7 +334,7 @@ def set_column_dict_config(
262
334
  column_item = next(filter(lambda c: c.name == column, table_meta.data.columns))
263
335
  dict_meta = service_get_dict_detail_by_name(datasource, dict_name)
264
336
  column_item.type = "dict"
265
- column_item.typeExt = DataBaseColumnTypeExternal(
337
+ column_item.typeExt = DataBaseColumnDictExternal(
266
338
  ref__dict=DataBaseColumnReference(type="dict", code=dict_meta.code)
267
339
  )
268
340
  result = release_list_update(table_meta)
@@ -283,7 +355,7 @@ def set_column_dict_config(
283
355
  dst_table_meta = service_get_table_detail_by_name(datasource, r_name)
284
356
  dst_column_item = next(filter(lambda c: c.name == r_col_name, dst_table_meta.data.columns))
285
357
  column_item.type = "relation"
286
- column_item.typeExt = DataBaseColumnTypeExternal(
358
+ column_item.typeExt = DataBaseColumnTableExternal(
287
359
  ref__table=DataBaseColumnReference(type="table", code=dst_table_meta.code),
288
360
  ref__valueField=DataBaseColumnReferenceValue(
289
361
  type="table", code=dst_table_meta.code, subtype="column", subcode=dst_column_item.id