tinet-data-foundry-cli 0.1.6__tar.gz → 0.1.9__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.
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/PKG-INFO +17 -6
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/README.md +16 -5
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/pyproject.toml +1 -1
- tinet_data_foundry_cli-0.1.9/src/tinet_data_foundry_cli/__init__.py +1 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/cli.py +61 -2
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/docs.py +39 -10
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli.egg-info/PKG-INFO +17 -6
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/tests/test_cli.py +6 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/tests/test_docs.py +27 -2
- tinet_data_foundry_cli-0.1.6/src/tinet_data_foundry_cli/__init__.py +0 -1
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/setup.cfg +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/__main__.py +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/auth.py +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/client.py +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli.egg-info/SOURCES.txt +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli.egg-info/dependency_links.txt +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli.egg-info/entry_points.txt +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli.egg-info/top_level.txt +0 -0
- {tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/tests/test_sign.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tinet-data-foundry-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Tinet Data Foundry 取数 CLI(docs / catalog / query / schema)
|
|
5
5
|
Author: Tinet
|
|
6
6
|
License: MIT
|
|
@@ -57,6 +57,15 @@ tdf query \
|
|
|
57
57
|
|
|
58
58
|
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
59
59
|
|
|
60
|
+
ODS 可以查询和过滤 `_source_ds`,值是来源数据源编码。编码清单:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
tdf datasource-codes
|
|
64
|
+
tdf datasource-codes --code nboss
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
只筛分区字段(如 `startTime`)时,服务端按同一范围另补 `_df_pt` 裁分区。其他时间字段不补。`_df_pt` 不能作为查询列。响应 `partitionWindow.prunedFrom` / `prunedTo` 是实际裁剪区间。
|
|
68
|
+
|
|
60
69
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
61
70
|
|
|
62
71
|
## Windows(cmd.exe)
|
|
@@ -87,23 +96,25 @@ tdf catalog --insecure
|
|
|
87
96
|
| `tdf docs rev` | `GET /docs/credentials/{ak}?format=rev` |
|
|
88
97
|
| `tdf docs pull` | 按 `copy.catalog.policy` 只拉取变化的 `part=public` 或 `part=policy` |
|
|
89
98
|
| `tdf catalog` | `GET /data-api/v1/catalog` |
|
|
99
|
+
| `tdf datasource-codes` | `GET /data-api/v1/datasource-codes` |
|
|
90
100
|
| `tdf query` | `POST /data-api/v1/datasets/{id}/query` |
|
|
91
101
|
| `tdf schema` | `GET /data-api/v1/datasets/{id}/schema` |
|
|
92
102
|
|
|
93
103
|
文档站点默认 `https://data-foundry.tinetcloud.com`,可用 `DF_DOCS_BASE` 覆盖。`tdf docs` 只需要 `DF_AK`。缓存位于 `~/.cache/tinet-data-foundry/docs/<AK 哈希>/`,可用 `DF_DOCS_CACHE` 改根目录。stdout 非终端时 `tdf catalog` 改为 JSON。
|
|
94
104
|
|
|
95
|
-
### Skill
|
|
105
|
+
### 业务 Skill 锁定数据集
|
|
96
106
|
|
|
97
|
-
`tdf docs pull`
|
|
107
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。业务 Skill 自行检索和阅读字段,不要把 `public.json` 或 `policy.json` 读进上下文:
|
|
98
108
|
|
|
99
109
|
```bash
|
|
100
|
-
tdf docs
|
|
110
|
+
tdf docs pull
|
|
111
|
+
tdf docs search --query "<检索词>"
|
|
101
112
|
tdf docs describe --dataset "DATASET_ID"
|
|
102
113
|
```
|
|
103
114
|
|
|
104
|
-
`search`
|
|
115
|
+
`search` 默认只返回有表权限的数据集,表名或中文名精确匹配排在前面,最多 20 条。`describe` 只返回该表已授权字段、分区粒度和脱敏规则。需要与网关当前结构核对时再用 `tdf schema --dataset "DATASET_ID"`。不要用不带 `--keyword` 的 `tdf catalog` 拉取整份目录。
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
DataFoundry 查询 Skill 只执行已经写好的查询。查询体用文件传入,避免 shell 转义:
|
|
107
118
|
|
|
108
119
|
```bash
|
|
109
120
|
tdf query --dataset "DATASET_ID" --body-file query.json
|
|
@@ -40,6 +40,15 @@ tdf query \
|
|
|
40
40
|
|
|
41
41
|
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
42
42
|
|
|
43
|
+
ODS 可以查询和过滤 `_source_ds`,值是来源数据源编码。编码清单:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
tdf datasource-codes
|
|
47
|
+
tdf datasource-codes --code nboss
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
只筛分区字段(如 `startTime`)时,服务端按同一范围另补 `_df_pt` 裁分区。其他时间字段不补。`_df_pt` 不能作为查询列。响应 `partitionWindow.prunedFrom` / `prunedTo` 是实际裁剪区间。
|
|
51
|
+
|
|
43
52
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
44
53
|
|
|
45
54
|
## Windows(cmd.exe)
|
|
@@ -70,23 +79,25 @@ tdf catalog --insecure
|
|
|
70
79
|
| `tdf docs rev` | `GET /docs/credentials/{ak}?format=rev` |
|
|
71
80
|
| `tdf docs pull` | 按 `copy.catalog.policy` 只拉取变化的 `part=public` 或 `part=policy` |
|
|
72
81
|
| `tdf catalog` | `GET /data-api/v1/catalog` |
|
|
82
|
+
| `tdf datasource-codes` | `GET /data-api/v1/datasource-codes` |
|
|
73
83
|
| `tdf query` | `POST /data-api/v1/datasets/{id}/query` |
|
|
74
84
|
| `tdf schema` | `GET /data-api/v1/datasets/{id}/schema` |
|
|
75
85
|
|
|
76
86
|
文档站点默认 `https://data-foundry.tinetcloud.com`,可用 `DF_DOCS_BASE` 覆盖。`tdf docs` 只需要 `DF_AK`。缓存位于 `~/.cache/tinet-data-foundry/docs/<AK 哈希>/`,可用 `DF_DOCS_CACHE` 改根目录。stdout 非终端时 `tdf catalog` 改为 JSON。
|
|
77
87
|
|
|
78
|
-
### Skill
|
|
88
|
+
### 业务 Skill 锁定数据集
|
|
79
89
|
|
|
80
|
-
`tdf docs pull`
|
|
90
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。业务 Skill 自行检索和阅读字段,不要把 `public.json` 或 `policy.json` 读进上下文:
|
|
81
91
|
|
|
82
92
|
```bash
|
|
83
|
-
tdf docs
|
|
93
|
+
tdf docs pull
|
|
94
|
+
tdf docs search --query "<检索词>"
|
|
84
95
|
tdf docs describe --dataset "DATASET_ID"
|
|
85
96
|
```
|
|
86
97
|
|
|
87
|
-
`search`
|
|
98
|
+
`search` 默认只返回有表权限的数据集,表名或中文名精确匹配排在前面,最多 20 条。`describe` 只返回该表已授权字段、分区粒度和脱敏规则。需要与网关当前结构核对时再用 `tdf schema --dataset "DATASET_ID"`。不要用不带 `--keyword` 的 `tdf catalog` 拉取整份目录。
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
DataFoundry 查询 Skill 只执行已经写好的查询。查询体用文件传入,避免 shell 转义:
|
|
90
101
|
|
|
91
102
|
```bash
|
|
92
103
|
tdf query --dataset "DATASET_ID" --body-file query.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.9"
|
{tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/cli.py
RENAMED
|
@@ -82,6 +82,56 @@ def _ok_data(parsed: Any) -> Any:
|
|
|
82
82
|
return parsed.get("data") if isinstance(parsed, dict) else parsed
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def _print_datasource_codes(rows: List[Mapping[str, Any]]) -> None:
|
|
86
|
+
if not rows:
|
|
87
|
+
print("当前凭证没有可查询的数据源编码。")
|
|
88
|
+
return
|
|
89
|
+
cols = [
|
|
90
|
+
("code", "编码", 24),
|
|
91
|
+
("name", "名称", 28),
|
|
92
|
+
("id", "id", 36),
|
|
93
|
+
]
|
|
94
|
+
header = " ".join(_cell(title, w) for _, title, w in cols)
|
|
95
|
+
print(header)
|
|
96
|
+
print("-" * len(header))
|
|
97
|
+
for row in rows:
|
|
98
|
+
print(" ".join(_cell(row.get(key), w) for key, _, w in cols))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def cmd_datasource_codes(args: argparse.Namespace) -> int:
|
|
102
|
+
base, ak, sk = credentials(args.endpoint)
|
|
103
|
+
query = {
|
|
104
|
+
"code": args.code or "",
|
|
105
|
+
"keyword": args.keyword or "",
|
|
106
|
+
}
|
|
107
|
+
try:
|
|
108
|
+
_, parsed, _ = request(
|
|
109
|
+
"GET",
|
|
110
|
+
base,
|
|
111
|
+
"/data-api/v1/datasource-codes",
|
|
112
|
+
ak,
|
|
113
|
+
sk,
|
|
114
|
+
query=query,
|
|
115
|
+
timeout=args.timeout,
|
|
116
|
+
insecure=args.insecure,
|
|
117
|
+
dry_run=args.dry_run,
|
|
118
|
+
)
|
|
119
|
+
except ApiError as ex:
|
|
120
|
+
_exit_api(ex)
|
|
121
|
+
except TransportError as ex:
|
|
122
|
+
_exit_transport(ex)
|
|
123
|
+
fmt = args.format
|
|
124
|
+
if fmt == "table" and not sys.stdout.isatty():
|
|
125
|
+
fmt = "json"
|
|
126
|
+
if args.dry_run or fmt == "json":
|
|
127
|
+
_print_json(parsed)
|
|
128
|
+
return 0
|
|
129
|
+
data = _ok_data(parsed) or {}
|
|
130
|
+
rows = data.get("list") if isinstance(data, dict) else []
|
|
131
|
+
_print_datasource_codes(list(rows or []))
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
85
135
|
def cmd_catalog(args: argparse.Namespace) -> int:
|
|
86
136
|
base, ak, sk = credentials(args.endpoint)
|
|
87
137
|
query = {
|
|
@@ -249,6 +299,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
249
299
|
c.add_argument("--format", choices=["table", "json"], default="table")
|
|
250
300
|
c.set_defaults(func=cmd_catalog)
|
|
251
301
|
|
|
302
|
+
codes = sub.add_parser("datasource-codes", help="列出当前凭证可查询数据集关联的数据源编码")
|
|
303
|
+
_add_insecure(codes, overlay=True)
|
|
304
|
+
codes.add_argument("--code", help="按编码精确匹配,忽略大小写")
|
|
305
|
+
codes.add_argument("--keyword", help="按编码或名称包含匹配")
|
|
306
|
+
codes.add_argument("--format", choices=["table", "json"], default="table")
|
|
307
|
+
codes.set_defaults(func=cmd_datasource_codes)
|
|
308
|
+
|
|
252
309
|
q = sub.add_parser("query", help="查询 Dataset")
|
|
253
310
|
_add_insecure(q, overlay=True)
|
|
254
311
|
q.add_argument("--dataset", required=True)
|
|
@@ -260,7 +317,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
260
317
|
help=(
|
|
261
318
|
"filter AST JSON。字符串字段可 starts_with(右模糊)、ends_with(左模糊)、contains(左右模糊)。"
|
|
262
319
|
"仅按日分区必填:省略分区字段时按上海时区补当天,显式 between 不超过 30 天。"
|
|
263
|
-
"
|
|
320
|
+
"按月、按年分区字段不是必填条件。"
|
|
321
|
+
"只筛分区字段时,服务端另补 _df_pt 裁分区;其他时间字段不补。_df_pt 不能作为查询列"
|
|
264
322
|
),
|
|
265
323
|
)
|
|
266
324
|
q.add_argument("--sort", help="sort JSON 数组")
|
|
@@ -280,9 +338,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
280
338
|
d = sub.add_parser("docs", help="核对数据集开发文档修订号,并在本地缓存中检索数据集")
|
|
281
339
|
_add_insecure(d, overlay=True)
|
|
282
340
|
d.add_argument("action", choices=["rev", "pull", "search", "describe"])
|
|
283
|
-
d.add_argument("--query", help="search
|
|
341
|
+
d.add_argument("--query", help="search:按名称、描述、表名或业务域检索本地缓存。表名精确匹配优先")
|
|
284
342
|
d.add_argument("--dataset", help="describe:输出指定数据集的已授权字段")
|
|
285
343
|
d.add_argument("--all", action="store_true", help="search:包含无表权限的数据集")
|
|
344
|
+
d.add_argument("--limit", type=int, default=20, help="search:最多返回条数,默认 20")
|
|
286
345
|
d.add_argument("--docs-base", help="覆盖文档站点。默认 DF_DOCS_BASE 或 https://data-foundry.tinetcloud.com")
|
|
287
346
|
d.set_defaults(func=cmd_docs)
|
|
288
347
|
|
{tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/docs.py
RENAMED
|
@@ -48,7 +48,12 @@ def run_docs(args: Any) -> int:
|
|
|
48
48
|
base = docs_base(getattr(args, "docs_base", None))
|
|
49
49
|
folder = cache_dir(ak)
|
|
50
50
|
if args.action == "search":
|
|
51
|
-
return _search(
|
|
51
|
+
return _search(
|
|
52
|
+
folder,
|
|
53
|
+
str(getattr(args, "query", "") or ""),
|
|
54
|
+
bool(getattr(args, "all", False)),
|
|
55
|
+
int(getattr(args, "limit", None) or 20),
|
|
56
|
+
)
|
|
52
57
|
if args.action == "describe":
|
|
53
58
|
return _describe(folder, str(getattr(args, "dataset", "") or ""))
|
|
54
59
|
url = docs_url(base, ak, "rev")
|
|
@@ -76,25 +81,25 @@ def _cached_docs(folder: Path) -> tuple[Dict[str, Any], Dict[str, Any]]:
|
|
|
76
81
|
return public, policy
|
|
77
82
|
|
|
78
83
|
|
|
79
|
-
def _search(folder: Path, query: str, include_denied: bool) -> int:
|
|
84
|
+
def _search(folder: Path, query: str, include_denied: bool, limit: int = 20) -> int:
|
|
80
85
|
public, policy = _cached_docs(folder)
|
|
81
86
|
needle = query.strip().casefold()
|
|
82
87
|
if not needle:
|
|
83
88
|
raise SystemExit("docs search 需要 --query")
|
|
89
|
+
if limit < 1:
|
|
90
|
+
raise SystemExit("docs search --limit 必须大于 0")
|
|
84
91
|
grants = _index(policy)
|
|
85
|
-
|
|
92
|
+
ranked: List[tuple] = []
|
|
86
93
|
for dataset in public.get("datasets") or []:
|
|
87
94
|
if not isinstance(dataset, dict):
|
|
88
95
|
continue
|
|
89
96
|
grant = grants.get(str(dataset.get("id") or ""), {})
|
|
90
97
|
if not include_denied and not grant.get("tableGranted"):
|
|
91
98
|
continue
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)).casefold()
|
|
95
|
-
if needle not in haystack:
|
|
99
|
+
rank = _match_rank(dataset, needle)
|
|
100
|
+
if rank is None:
|
|
96
101
|
continue
|
|
97
|
-
|
|
102
|
+
ranked.append((rank, str(dataset.get("name") or ""), {
|
|
98
103
|
"id": dataset.get("id"),
|
|
99
104
|
"name": dataset.get("name"),
|
|
100
105
|
"chineseName": dataset.get("chineseName"),
|
|
@@ -103,11 +108,34 @@ def _search(folder: Path, query: str, include_denied: bool) -> int:
|
|
|
103
108
|
"layer": dataset.get("layer"),
|
|
104
109
|
"schemaVersion": dataset.get("schemaVersion"),
|
|
105
110
|
"tableGranted": bool(grant.get("tableGranted")),
|
|
106
|
-
})
|
|
107
|
-
|
|
111
|
+
}))
|
|
112
|
+
ranked.sort(key=lambda row: (row[0], row[1]))
|
|
113
|
+
shown = ranked[:limit]
|
|
114
|
+
_print({
|
|
115
|
+
"query": query,
|
|
116
|
+
"total": len(ranked),
|
|
117
|
+
"truncated": len(ranked) > limit,
|
|
118
|
+
"datasets": [row[2] for row in shown],
|
|
119
|
+
})
|
|
108
120
|
return 0
|
|
109
121
|
|
|
110
122
|
|
|
123
|
+
def _match_rank(dataset: Dict[str, Any], needle: str) -> Optional[int]:
|
|
124
|
+
identity = [str(dataset.get(key) or "").casefold() for key in ("id", "name", "chineseName")]
|
|
125
|
+
if needle in identity:
|
|
126
|
+
return 0
|
|
127
|
+
if any(value.startswith(needle) for value in identity if value):
|
|
128
|
+
return 1
|
|
129
|
+
if any(needle in value for value in identity if value):
|
|
130
|
+
return 2
|
|
131
|
+
extra = " ".join(str(dataset.get(key) or "") for key in (
|
|
132
|
+
"tableComment", "description", "businessDomainName",
|
|
133
|
+
)).casefold()
|
|
134
|
+
if needle in extra:
|
|
135
|
+
return 3
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
111
139
|
def _describe(folder: Path, dataset_id: str) -> int:
|
|
112
140
|
public, policy = _cached_docs(folder)
|
|
113
141
|
if not dataset_id.strip():
|
|
@@ -135,6 +163,7 @@ def _describe(folder: Path, dataset_id: str) -> int:
|
|
|
135
163
|
"partition": column.get("partition"),
|
|
136
164
|
"partitionGrain": column.get("partitionGrain"),
|
|
137
165
|
"partitionRequired": column.get("partitionRequired"),
|
|
166
|
+
"suggestedRange": column.get("suggestedRange"),
|
|
138
167
|
"outputRule": rules[name].get("outputRule"),
|
|
139
168
|
})
|
|
140
169
|
_print({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tinet-data-foundry-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Tinet Data Foundry 取数 CLI(docs / catalog / query / schema)
|
|
5
5
|
Author: Tinet
|
|
6
6
|
License: MIT
|
|
@@ -57,6 +57,15 @@ tdf query \
|
|
|
57
57
|
|
|
58
58
|
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
59
59
|
|
|
60
|
+
ODS 可以查询和过滤 `_source_ds`,值是来源数据源编码。编码清单:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
tdf datasource-codes
|
|
64
|
+
tdf datasource-codes --code nboss
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
只筛分区字段(如 `startTime`)时,服务端按同一范围另补 `_df_pt` 裁分区。其他时间字段不补。`_df_pt` 不能作为查询列。响应 `partitionWindow.prunedFrom` / `prunedTo` 是实际裁剪区间。
|
|
68
|
+
|
|
60
69
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
61
70
|
|
|
62
71
|
## Windows(cmd.exe)
|
|
@@ -87,23 +96,25 @@ tdf catalog --insecure
|
|
|
87
96
|
| `tdf docs rev` | `GET /docs/credentials/{ak}?format=rev` |
|
|
88
97
|
| `tdf docs pull` | 按 `copy.catalog.policy` 只拉取变化的 `part=public` 或 `part=policy` |
|
|
89
98
|
| `tdf catalog` | `GET /data-api/v1/catalog` |
|
|
99
|
+
| `tdf datasource-codes` | `GET /data-api/v1/datasource-codes` |
|
|
90
100
|
| `tdf query` | `POST /data-api/v1/datasets/{id}/query` |
|
|
91
101
|
| `tdf schema` | `GET /data-api/v1/datasets/{id}/schema` |
|
|
92
102
|
|
|
93
103
|
文档站点默认 `https://data-foundry.tinetcloud.com`,可用 `DF_DOCS_BASE` 覆盖。`tdf docs` 只需要 `DF_AK`。缓存位于 `~/.cache/tinet-data-foundry/docs/<AK 哈希>/`,可用 `DF_DOCS_CACHE` 改根目录。stdout 非终端时 `tdf catalog` 改为 JSON。
|
|
94
104
|
|
|
95
|
-
### Skill
|
|
105
|
+
### 业务 Skill 锁定数据集
|
|
96
106
|
|
|
97
|
-
`tdf docs pull`
|
|
107
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。业务 Skill 自行检索和阅读字段,不要把 `public.json` 或 `policy.json` 读进上下文:
|
|
98
108
|
|
|
99
109
|
```bash
|
|
100
|
-
tdf docs
|
|
110
|
+
tdf docs pull
|
|
111
|
+
tdf docs search --query "<检索词>"
|
|
101
112
|
tdf docs describe --dataset "DATASET_ID"
|
|
102
113
|
```
|
|
103
114
|
|
|
104
|
-
`search`
|
|
115
|
+
`search` 默认只返回有表权限的数据集,表名或中文名精确匹配排在前面,最多 20 条。`describe` 只返回该表已授权字段、分区粒度和脱敏规则。需要与网关当前结构核对时再用 `tdf schema --dataset "DATASET_ID"`。不要用不带 `--keyword` 的 `tdf catalog` 拉取整份目录。
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
DataFoundry 查询 Skill 只执行已经写好的查询。查询体用文件传入,避免 shell 转义:
|
|
107
118
|
|
|
108
119
|
```bash
|
|
109
120
|
tdf query --dataset "DATASET_ID" --body-file query.json
|
|
@@ -21,12 +21,18 @@ class CliParserTest(unittest.TestCase):
|
|
|
21
21
|
self.assertIn("30", help_text)
|
|
22
22
|
self.assertIn("当天", help_text)
|
|
23
23
|
self.assertIn("按月、按年分区字段不是必填条件", help_text)
|
|
24
|
+
self.assertIn("_df_pt", help_text)
|
|
25
|
+
self.assertIn("裁分区", help_text)
|
|
26
|
+
self.assertIn("不能作为查询列", help_text)
|
|
24
27
|
self.assertNotIn("按月补当月", help_text)
|
|
25
28
|
|
|
26
29
|
def test_subcommands(self):
|
|
27
30
|
parser = build_parser()
|
|
28
31
|
catalog = parser.parse_args(["catalog", "--format", "json"])
|
|
29
32
|
self.assertEqual(catalog.cmd, "catalog")
|
|
33
|
+
codes = parser.parse_args(["datasource-codes", "--code", "nboss"])
|
|
34
|
+
self.assertEqual(codes.cmd, "datasource-codes")
|
|
35
|
+
self.assertEqual(codes.code, "nboss")
|
|
30
36
|
query = parser.parse_args(["query", "--dataset", "ds_1", "--page-no", "1", "--page-size", "10"])
|
|
31
37
|
self.assertEqual(query.cmd, "query")
|
|
32
38
|
catalog_k = parser.parse_args(["catalog", "--insecure"])
|
|
@@ -17,20 +17,45 @@ class DocsSummaryTest(unittest.TestCase):
|
|
|
17
17
|
"description": "京东云消费", "columns": [
|
|
18
18
|
{"name": "amount", "type": "decimal", "comment": "金额"},
|
|
19
19
|
{"name": "secret", "type": "text", "comment": "无权限"},
|
|
20
|
+
{"name": "_df_pt", "type": "date", "comment": "分区查询时间",
|
|
21
|
+
"suggestedRange": {"from": "2026-10-01", "to": "2026-10-01", "grain": "month"}},
|
|
20
22
|
],
|
|
21
23
|
}]}), encoding="utf-8")
|
|
22
24
|
(folder / "policy.json").write_text(json.dumps({"datasets": [{
|
|
23
25
|
"id": "ds_bill", "tableGranted": True,
|
|
24
|
-
"columns": [
|
|
26
|
+
"columns": [
|
|
27
|
+
{"name": "amount", "granted": True, "outputRule": "PLAIN"},
|
|
28
|
+
{"name": "_df_pt", "granted": True, "outputRule": "PLAIN"},
|
|
29
|
+
],
|
|
25
30
|
}]}), encoding="utf-8")
|
|
26
31
|
self.assertEqual(_search(folder, "京东云", False), 0)
|
|
27
32
|
search = json.loads(out.getvalue())
|
|
28
33
|
self.assertEqual(search["datasets"][0]["id"], "ds_bill")
|
|
34
|
+
self.assertEqual(search["total"], 1)
|
|
35
|
+
self.assertFalse(search["truncated"])
|
|
29
36
|
out.seek(0)
|
|
30
37
|
out.truncate(0)
|
|
31
38
|
self.assertEqual(_describe(folder, "ds_bill"), 0)
|
|
32
39
|
described = json.loads(out.getvalue())
|
|
33
|
-
self.assertEqual([col["name"] for col in described["columns"]], ["amount"])
|
|
40
|
+
self.assertEqual([col["name"] for col in described["columns"]], ["amount", "_df_pt"])
|
|
41
|
+
self.assertEqual(described["columns"][1]["suggestedRange"]["from"], "2026-10-01")
|
|
42
|
+
|
|
43
|
+
def test_search_prefers_exact_name_and_limits_rows(self):
|
|
44
|
+
with tempfile.TemporaryDirectory() as tmp, patch("sys.stdout", new_callable=StringIO) as out:
|
|
45
|
+
folder = Path(tmp)
|
|
46
|
+
datasets = [
|
|
47
|
+
{"id": "ds_desc", "name": "other", "description": "bill detail"},
|
|
48
|
+
{"id": "ds_exact", "name": "bill", "chineseName": "账单"},
|
|
49
|
+
{"id": "ds_more", "name": "bill_extra", "description": "bill"},
|
|
50
|
+
]
|
|
51
|
+
grants = [{"id": row["id"], "tableGranted": True, "columns": []} for row in datasets]
|
|
52
|
+
(folder / "public.json").write_text(json.dumps({"datasets": datasets}), encoding="utf-8")
|
|
53
|
+
(folder / "policy.json").write_text(json.dumps({"datasets": grants}), encoding="utf-8")
|
|
54
|
+
self.assertEqual(_search(folder, "bill", False, 2), 0)
|
|
55
|
+
search = json.loads(out.getvalue())
|
|
56
|
+
self.assertEqual([row["id"] for row in search["datasets"]], ["ds_exact", "ds_more"])
|
|
57
|
+
self.assertEqual(search["total"], 3)
|
|
58
|
+
self.assertTrue(search["truncated"])
|
|
34
59
|
|
|
35
60
|
def test_summarize_catalog_and_policy(self):
|
|
36
61
|
old_public = {"datasets": [{"id": "a", "schemaVersion": "1"}, {"id": "b", "schemaVersion": "1"}]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.6"
|
|
File without changes
|
{tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/__main__.py
RENAMED
|
File without changes
|
{tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/auth.py
RENAMED
|
File without changes
|
{tinet_data_foundry_cli-0.1.6 → tinet_data_foundry_cli-0.1.9}/src/tinet_data_foundry_cli/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|