tinet-data-foundry-cli 0.1.5__tar.gz → 0.1.6__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.5 → tinet_data_foundry_cli-0.1.6}/PKG-INFO +13 -2
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/README.md +12 -1
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/pyproject.toml +1 -1
- tinet_data_foundry_cli-0.1.6/src/tinet_data_foundry_cli/__init__.py +1 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/cli.py +7 -4
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/docs.py +88 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli.egg-info/PKG-INFO +13 -2
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/tests/test_cli.py +3 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/tests/test_docs.py +25 -1
- tinet_data_foundry_cli-0.1.5/src/tinet_data_foundry_cli/__init__.py +0 -1
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/setup.cfg +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/__main__.py +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/auth.py +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/client.py +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli.egg-info/SOURCES.txt +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli.egg-info/dependency_links.txt +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli.egg-info/entry_points.txt +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli.egg-info/top_level.txt +0 -0
- {tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/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.6
|
|
4
4
|
Summary: Tinet Data Foundry 取数 CLI(docs / catalog / query / schema)
|
|
5
5
|
Author: Tinet
|
|
6
6
|
License: MIT
|
|
@@ -55,7 +55,7 @@ tdf query \
|
|
|
55
55
|
| `ends_with` | 左模糊,匹配后缀 | `{"op":"ends_with","field":"name","value":"公司"}` |
|
|
56
56
|
| `contains` | 左右模糊 | `{"op":"contains","field":"name","value":"科技"}` |
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
59
59
|
|
|
60
60
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
61
61
|
|
|
@@ -92,6 +92,17 @@ tdf catalog --insecure
|
|
|
92
92
|
|
|
93
93
|
文档站点默认 `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
94
|
|
|
95
|
+
### Skill 查询大目录
|
|
96
|
+
|
|
97
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。Skill / Agent 不应直接把大 `public.json` 读进上下文,而是通过以下命令只取需要的精简结果:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
tdf docs search --query "京东云消费"
|
|
101
|
+
tdf docs describe --dataset "DATASET_ID"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`search` 默认只返回有表权限的数据集;`describe` 只返回该表已授权字段及其脱敏规则。首次使用先执行 `tdf docs pull`。
|
|
105
|
+
|
|
95
106
|
Agent / Skill 传查询体用文件,避免 shell 转义:
|
|
96
107
|
|
|
97
108
|
```bash
|
|
@@ -38,7 +38,7 @@ tdf query \
|
|
|
38
38
|
| `ends_with` | 左模糊,匹配后缀 | `{"op":"ends_with","field":"name","value":"公司"}` |
|
|
39
39
|
| `contains` | 左右模糊 | `{"op":"contains","field":"name","value":"科技"}` |
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
42
42
|
|
|
43
43
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
44
44
|
|
|
@@ -75,6 +75,17 @@ tdf catalog --insecure
|
|
|
75
75
|
|
|
76
76
|
文档站点默认 `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
77
|
|
|
78
|
+
### Skill 查询大目录
|
|
79
|
+
|
|
80
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。Skill / Agent 不应直接把大 `public.json` 读进上下文,而是通过以下命令只取需要的精简结果:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
tdf docs search --query "京东云消费"
|
|
84
|
+
tdf docs describe --dataset "DATASET_ID"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`search` 默认只返回有表权限的数据集;`describe` 只返回该表已授权字段及其脱敏规则。首次使用先执行 `tdf docs pull`。
|
|
88
|
+
|
|
78
89
|
Agent / Skill 传查询体用文件,避免 shell 转义:
|
|
79
90
|
|
|
80
91
|
```bash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.6"
|
{tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/cli.py
RENAMED
|
@@ -259,8 +259,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
259
259
|
"--filter",
|
|
260
260
|
help=(
|
|
261
261
|
"filter AST JSON。字符串字段可 starts_with(右模糊)、ends_with(左模糊)、contains(左右模糊)。"
|
|
262
|
-
"
|
|
263
|
-
"
|
|
262
|
+
"仅按日分区必填:省略分区字段时按上海时区补当天,显式 between 不超过 30 天。"
|
|
263
|
+
"按月、按年分区字段不是必填条件"
|
|
264
264
|
),
|
|
265
265
|
)
|
|
266
266
|
q.add_argument("--sort", help="sort JSON 数组")
|
|
@@ -277,9 +277,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
277
277
|
)
|
|
278
278
|
q.set_defaults(func=cmd_query)
|
|
279
279
|
|
|
280
|
-
d = sub.add_parser("docs", help="
|
|
280
|
+
d = sub.add_parser("docs", help="核对数据集开发文档修订号,并在本地缓存中检索数据集")
|
|
281
281
|
_add_insecure(d, overlay=True)
|
|
282
|
-
d.add_argument("action", choices=["rev", "pull"])
|
|
282
|
+
d.add_argument("action", choices=["rev", "pull", "search", "describe"])
|
|
283
|
+
d.add_argument("--query", help="search:按名称、描述、表名或业务域检索本地缓存")
|
|
284
|
+
d.add_argument("--dataset", help="describe:输出指定数据集的已授权字段")
|
|
285
|
+
d.add_argument("--all", action="store_true", help="search:包含无表权限的数据集")
|
|
283
286
|
d.add_argument("--docs-base", help="覆盖文档站点。默认 DF_DOCS_BASE 或 https://data-foundry.tinetcloud.com")
|
|
284
287
|
d.set_defaults(func=cmd_docs)
|
|
285
288
|
|
{tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/docs.py
RENAMED
|
@@ -47,6 +47,10 @@ def run_docs(args: Any) -> int:
|
|
|
47
47
|
ak = access_key()
|
|
48
48
|
base = docs_base(getattr(args, "docs_base", None))
|
|
49
49
|
folder = cache_dir(ak)
|
|
50
|
+
if args.action == "search":
|
|
51
|
+
return _search(folder, str(getattr(args, "query", "") or ""), bool(getattr(args, "all", False)))
|
|
52
|
+
if args.action == "describe":
|
|
53
|
+
return _describe(folder, str(getattr(args, "dataset", "") or ""))
|
|
50
54
|
url = docs_url(base, ak, "rev")
|
|
51
55
|
if args.dry_run:
|
|
52
56
|
_print({
|
|
@@ -64,6 +68,90 @@ def run_docs(args: Any) -> int:
|
|
|
64
68
|
return _pull(folder, base, ak, args.timeout, args.insecure)
|
|
65
69
|
|
|
66
70
|
|
|
71
|
+
def _cached_docs(folder: Path) -> tuple[Dict[str, Any], Dict[str, Any]]:
|
|
72
|
+
public = _read_json(folder / "public.json")
|
|
73
|
+
policy = _read_json(folder / "policy.json")
|
|
74
|
+
if not public or not policy:
|
|
75
|
+
raise SystemExit("本地文档缓存不存在,请先运行 tdf docs pull")
|
|
76
|
+
return public, policy
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _search(folder: Path, query: str, include_denied: bool) -> int:
|
|
80
|
+
public, policy = _cached_docs(folder)
|
|
81
|
+
needle = query.strip().casefold()
|
|
82
|
+
if not needle:
|
|
83
|
+
raise SystemExit("docs search 需要 --query")
|
|
84
|
+
grants = _index(policy)
|
|
85
|
+
matches: List[Dict[str, Any]] = []
|
|
86
|
+
for dataset in public.get("datasets") or []:
|
|
87
|
+
if not isinstance(dataset, dict):
|
|
88
|
+
continue
|
|
89
|
+
grant = grants.get(str(dataset.get("id") or ""), {})
|
|
90
|
+
if not include_denied and not grant.get("tableGranted"):
|
|
91
|
+
continue
|
|
92
|
+
haystack = " ".join(str(dataset.get(key) or "") for key in (
|
|
93
|
+
"id", "name", "chineseName", "tableComment", "description", "businessDomainName",
|
|
94
|
+
)).casefold()
|
|
95
|
+
if needle not in haystack:
|
|
96
|
+
continue
|
|
97
|
+
matches.append({
|
|
98
|
+
"id": dataset.get("id"),
|
|
99
|
+
"name": dataset.get("name"),
|
|
100
|
+
"chineseName": dataset.get("chineseName"),
|
|
101
|
+
"description": dataset.get("description"),
|
|
102
|
+
"businessDomainName": dataset.get("businessDomainName"),
|
|
103
|
+
"layer": dataset.get("layer"),
|
|
104
|
+
"schemaVersion": dataset.get("schemaVersion"),
|
|
105
|
+
"tableGranted": bool(grant.get("tableGranted")),
|
|
106
|
+
})
|
|
107
|
+
_print({"query": query, "datasets": matches})
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _describe(folder: Path, dataset_id: str) -> int:
|
|
112
|
+
public, policy = _cached_docs(folder)
|
|
113
|
+
if not dataset_id.strip():
|
|
114
|
+
raise SystemExit("docs describe 需要 --dataset")
|
|
115
|
+
datasets = _index(public)
|
|
116
|
+
dataset = datasets.get(dataset_id)
|
|
117
|
+
if not dataset:
|
|
118
|
+
raise SystemExit(f"缓存中没有数据集 {dataset_id};先执行 tdf docs search --query <关键词>")
|
|
119
|
+
grant = _index(policy).get(dataset_id, {})
|
|
120
|
+
rules = {
|
|
121
|
+
str(col.get("name")): col
|
|
122
|
+
for col in grant.get("columns") or []
|
|
123
|
+
if isinstance(col, dict) and col.get("granted") and col.get("name")
|
|
124
|
+
}
|
|
125
|
+
columns = []
|
|
126
|
+
for column in dataset.get("columns") or []:
|
|
127
|
+
if not isinstance(column, dict) or str(column.get("name")) not in rules:
|
|
128
|
+
continue
|
|
129
|
+
name = str(column["name"])
|
|
130
|
+
columns.append({
|
|
131
|
+
"name": name,
|
|
132
|
+
"type": column.get("type"),
|
|
133
|
+
"comment": column.get("comment"),
|
|
134
|
+
"pk": column.get("pk"),
|
|
135
|
+
"partition": column.get("partition"),
|
|
136
|
+
"partitionGrain": column.get("partitionGrain"),
|
|
137
|
+
"partitionRequired": column.get("partitionRequired"),
|
|
138
|
+
"outputRule": rules[name].get("outputRule"),
|
|
139
|
+
})
|
|
140
|
+
_print({
|
|
141
|
+
"id": dataset.get("id"),
|
|
142
|
+
"name": dataset.get("name"),
|
|
143
|
+
"chineseName": dataset.get("chineseName"),
|
|
144
|
+
"description": dataset.get("description"),
|
|
145
|
+
"businessDomainName": dataset.get("businessDomainName"),
|
|
146
|
+
"schemaVersion": dataset.get("schemaVersion"),
|
|
147
|
+
"primaryKey": dataset.get("primaryKey"),
|
|
148
|
+
"partitionFields": dataset.get("partitionFields"),
|
|
149
|
+
"tableGranted": bool(grant.get("tableGranted")),
|
|
150
|
+
"columns": columns,
|
|
151
|
+
})
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
|
|
67
155
|
def _pull(folder: Path, base: str, ak: str, timeout: float, insecure: bool) -> int:
|
|
68
156
|
local = _read_json(folder / "rev.json")
|
|
69
157
|
local_etag = str(local.get("etag") or "")
|
|
@@ -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.6
|
|
4
4
|
Summary: Tinet Data Foundry 取数 CLI(docs / catalog / query / schema)
|
|
5
5
|
Author: Tinet
|
|
6
6
|
License: MIT
|
|
@@ -55,7 +55,7 @@ tdf query \
|
|
|
55
55
|
| `ends_with` | 左模糊,匹配后缀 | `{"op":"ends_with","field":"name","value":"公司"}` |
|
|
56
56
|
| `contains` | 左右模糊 | `{"op":"contains","field":"name","value":"科技"}` |
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
仅按日分区表必须带分区条件。未传分区字段时,服务端按上海时区补当天闭区间,并在响应 `data.partitionWindow` 里返回实际 `field/from/to`。显式 `between` 不超过 30 天,否则 422 `PARTITION_RANGE_INVALID`。按月、按年分区字段不是必填条件。
|
|
59
59
|
|
|
60
60
|
若 pip 提示 scripts 目录不在 PATH,把该目录加入 PATH,或用 `python3 -m tinet_data_foundry_cli`。
|
|
61
61
|
|
|
@@ -92,6 +92,17 @@ tdf catalog --insecure
|
|
|
92
92
|
|
|
93
93
|
文档站点默认 `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
94
|
|
|
95
|
+
### Skill 查询大目录
|
|
96
|
+
|
|
97
|
+
`tdf docs pull` 把公共目录与授权策略缓存到本机。Skill / Agent 不应直接把大 `public.json` 读进上下文,而是通过以下命令只取需要的精简结果:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
tdf docs search --query "京东云消费"
|
|
101
|
+
tdf docs describe --dataset "DATASET_ID"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`search` 默认只返回有表权限的数据集;`describe` 只返回该表已授权字段及其脱敏规则。首次使用先执行 `tdf docs pull`。
|
|
105
|
+
|
|
95
106
|
Agent / Skill 传查询体用文件,避免 shell 转义:
|
|
96
107
|
|
|
97
108
|
```bash
|
|
@@ -17,8 +17,11 @@ class CliParserTest(unittest.TestCase):
|
|
|
17
17
|
self.assertIn("starts_with", help_text)
|
|
18
18
|
self.assertIn("ends_with", help_text)
|
|
19
19
|
self.assertIn("contains", help_text)
|
|
20
|
+
self.assertIn("仅按日分区必填", help_text)
|
|
20
21
|
self.assertIn("30", help_text)
|
|
21
22
|
self.assertIn("当天", help_text)
|
|
23
|
+
self.assertIn("按月、按年分区字段不是必填条件", help_text)
|
|
24
|
+
self.assertNotIn("按月补当月", help_text)
|
|
22
25
|
|
|
23
26
|
def test_subcommands(self):
|
|
24
27
|
parser = build_parser()
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import tempfile
|
|
3
3
|
import unittest
|
|
4
|
+
from io import StringIO
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from unittest.mock import patch
|
|
6
7
|
|
|
7
|
-
from tinet_data_foundry_cli.docs import summarize
|
|
8
|
+
from tinet_data_foundry_cli.docs import _describe, _search, summarize
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class DocsSummaryTest(unittest.TestCase):
|
|
12
|
+
def test_search_and_describe_only_print_authorized_dataset_content(self):
|
|
13
|
+
with tempfile.TemporaryDirectory() as tmp, patch("sys.stdout", new_callable=StringIO) as out:
|
|
14
|
+
folder = Path(tmp)
|
|
15
|
+
(folder / "public.json").write_text(json.dumps({"datasets": [{
|
|
16
|
+
"id": "ds_bill", "name": "monthly_bill", "chineseName": "月账单",
|
|
17
|
+
"description": "京东云消费", "columns": [
|
|
18
|
+
{"name": "amount", "type": "decimal", "comment": "金额"},
|
|
19
|
+
{"name": "secret", "type": "text", "comment": "无权限"},
|
|
20
|
+
],
|
|
21
|
+
}]}), encoding="utf-8")
|
|
22
|
+
(folder / "policy.json").write_text(json.dumps({"datasets": [{
|
|
23
|
+
"id": "ds_bill", "tableGranted": True,
|
|
24
|
+
"columns": [{"name": "amount", "granted": True, "outputRule": "PLAIN"}],
|
|
25
|
+
}]}), encoding="utf-8")
|
|
26
|
+
self.assertEqual(_search(folder, "京东云", False), 0)
|
|
27
|
+
search = json.loads(out.getvalue())
|
|
28
|
+
self.assertEqual(search["datasets"][0]["id"], "ds_bill")
|
|
29
|
+
out.seek(0)
|
|
30
|
+
out.truncate(0)
|
|
31
|
+
self.assertEqual(_describe(folder, "ds_bill"), 0)
|
|
32
|
+
described = json.loads(out.getvalue())
|
|
33
|
+
self.assertEqual([col["name"] for col in described["columns"]], ["amount"])
|
|
34
|
+
|
|
11
35
|
def test_summarize_catalog_and_policy(self):
|
|
12
36
|
old_public = {"datasets": [{"id": "a", "schemaVersion": "1"}, {"id": "b", "schemaVersion": "1"}]}
|
|
13
37
|
new_public = {"datasets": [{"id": "b", "schemaVersion": "2"}, {"id": "c", "schemaVersion": "1"}]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.5"
|
|
File without changes
|
{tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/__main__.py
RENAMED
|
File without changes
|
{tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/src/tinet_data_foundry_cli/auth.py
RENAMED
|
File without changes
|
{tinet_data_foundry_cli-0.1.5 → tinet_data_foundry_cli-0.1.6}/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
|