feat: 完成 issue #30 ② PostgreSQL 关系表 schema(模板/模型/用户/权限)

This commit is contained in:
2026-08-05 00:54:58 +08:00
parent 793dd0a3b8
commit 87eff86090
3 changed files with 99 additions and 2 deletions
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# 模板「PostgreSQL 关系库」配置资产示例:ti-cl4(氯化车间/海绵钛,Template-Ti 一期)。
#
# 说明(issue #30 / PRD 5.2「② 数据总线 + 时序库」):
# - 隔离 schema:`tpl_{template}`,内含基础关系表(模板/模型/用户/权限);
# - schema_tables:可选子集/扩展(缺省使用内核默认四表,见 postgres_schema.py);
# - roles:关系角色声明(生成 {role}_ro / {role}_rw 授权语句);
# - 换行业只改本文件(template / roles / schema_tables),内核零改动。
template: ti-cl4
version: 1.0.0
postgres:
# 关系角色:每个角色生成 `{role}_ro`(只读)与 `{role}_rw`(读写)
roles:
- databus
- modeler
# 基础关系表(缺省 = templates / models / users / permissions;
# 此处仅声明,列定义用内核默认,避免与实现漂移)
schema_tables:
- templates
- models
- users
- permissions
+38
View File
@@ -114,3 +114,41 @@ def generate_grant_ddl(
f"GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA {schema} TO {role_name};"
)
return grants
# ---------------------------------------------------------------------------
# 模板配置资产加载(issue #30:换行业只改 config/postgres.template.yaml)
# ---------------------------------------------------------------------------
def load_postgres_config(path: str) -> dict:
"""加载模板配置资产(config/postgres.template.yaml)。"""
import yaml
with open(path, "r", encoding="utf-8") as fh:
return yaml.safe_load(fh) or {}
def generate_from_config(
path: str,
naming: Optional[TemplateNaming] = None,
) -> tuple:
"""按模板配置生成 PostgreSQL schema DDL 与授权语句。
Args:
path: 模板配置资产路径(config/postgres.template.yaml)。
naming: 模板命名器(缺省按资产 template 字段构造)。
Returns:
(schema_ddl, grant_statements):DDL 文本与 GRANT 语句列表。
"""
raw = load_postgres_config(path)
pg = raw.get("postgres", {}) or {}
naming = naming or TemplateNaming(template=str(raw.get("template", "default")))
ddl = generate_schema_ddl(
naming,
tables=pg.get("schema_tables") or None,
table_columns=None, # 列定义用内核默认,避免与实现漂移
)
grants = generate_grant_ddl(naming, roles=pg.get("roles") or None)
return ddl, grants
+38 -2
View File
@@ -7,11 +7,22 @@ import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import _bootstrap # noqa: F401
from data_bus.postgres_schema import DEFAULT_TABLES, generate_grant_ddl, generate_schema_ddl
from data_bus.templating import TemplateNaming
from data_bus.postgres_schema import ( # noqa: E402
DEFAULT_TABLES,
generate_from_config,
generate_grant_ddl,
generate_schema_ddl,
load_postgres_config,
)
from data_bus.templating import TemplateNaming # noqa: E402
NAMING = TemplateNaming(template="ti-cl4")
CONFIG = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"config", "postgres.template.yaml",
)
class SchemaDDLTest(unittest.TestCase):
def test_schema_and_tables(self):
@@ -35,5 +46,30 @@ class GrantDDLTest(unittest.TestCase):
self.assertIn("GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA tpl_ti_cl4 TO databus_rw;", text)
class ConfigDrivenTest(unittest.TestCase):
"""模板配置资产(issue #30):换行业只改配置生成 schema/授权。"""
def test_config_parses(self):
raw = load_postgres_config(CONFIG)
self.assertEqual(raw["template"], "ti-cl4")
self.assertIn("databus", raw["postgres"]["roles"])
self.assertEqual(raw["postgres"]["schema_tables"], DEFAULT_TABLES)
def test_generate_from_config(self):
ddl, grants = generate_from_config(CONFIG)
# schema 按资产 template 推导
self.assertIn("CREATE SCHEMA IF NOT EXISTS tpl_ti_cl4;", ddl)
for table in DEFAULT_TABLES:
self.assertIn(f"CREATE TABLE IF NOT EXISTS tpl_ti_cl4.{table} (", ddl)
# 角色来自资产声明(databus / modeler → 4 个角色)
text = "\n".join(grants)
for role in ("databus_ro", "databus_rw", "modeler_ro", "modeler_rw"):
self.assertIn(f"TO {role};", text)
def test_generate_from_config_with_naming_override(self):
ddl, grants = generate_from_config(CONFIG, naming=TemplateNaming("resin"))
self.assertIn("CREATE SCHEMA IF NOT EXISTS tpl_resin;", ddl)
if __name__ == "__main__":
unittest.main()