diff --git a/services/api-server/src/api_server/html5.py b/services/api-server/src/api_server/html5.py
index aedc05a..8a03c4b 100644
--- a/services/api-server/src/api_server/html5.py
+++ b/services/api-server/src/api_server/html5.py
@@ -324,6 +324,7 @@ def render_html5_object_context(
ui: object | None = None,
runtime: Iterable[object] | None = None,
knowledge: Iterable[object] | None = None,
+ privacy: object | None = None,
) -> str:
if schema is None or impact is None:
return f"""
@@ -345,6 +346,7 @@ def render_html5_object_context(
ui_forms = getattr(ui, "forms", []) if ui is not None else []
runtime_items = list(runtime or [])
knowledge_items = list(knowledge or [])
+ privacy_markers = getattr(privacy, "markers", []) if privacy is not None else []
return f"""
Object context
@@ -361,6 +363,7 @@ def render_html5_object_context(
{_metric("Roles", len(grants) or len(roles))}
{_metric("Runtime", len(runtime_items))}
{_metric("Knowledge", len(knowledge_items))}
+ {_metric("Privacy", len(privacy_markers))}
{''.join(_named_node_item("attr", item) for item in attributes[:6]) or '
Реквизиты не найдены
'}
@@ -369,6 +372,7 @@ def render_html5_object_context(
{''.join(_role_access_item(item) for item in grants[:6])}
{''.join(_runtime_summary_item(item) for item in runtime_items[:6])}
{''.join(_knowledge_record_item(item) for item in knowledge_items[:6])}
+ {''.join(_privacy_marker_item(item) for item in privacy_markers[:6])}
{''.join(_named_node_item("routine", item) for item in routines[:6])}
{''.join(_named_node_item("job", item) for item in jobs[:4])}
@@ -1459,6 +1463,18 @@ def _knowledge_record_item(record: object) -> str:
"""
+def _privacy_marker_item(marker: object) -> str:
+ classification = _enum_text(getattr(marker, "classification", ""))
+ reason = str(getattr(marker, "reason", "") or "")
+ target_id = str(getattr(marker, "target_id", "") or "target unavailable")
+ return f"""
+
+ {escape(classification or "privacy")}
+ {escape(reason or target_id)}
+
+ """
+
+
def _authoring_diff_item(line: object) -> str:
kind = str(getattr(line, "kind", ""))
text = str(getattr(line, "text", ""))
diff --git a/services/api-server/src/api_server/main.py b/services/api-server/src/api_server/main.py
index 1f5e9d3..678a324 100644
--- a/services/api-server/src/api_server/main.py
+++ b/services/api-server/src/api_server/main.py
@@ -1747,10 +1747,11 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res
impact = await get_object_impact(project_id, object_name)
access = await get_object_access(project_id, object_name)
ui = await get_object_ui(project_id, object_name)
+ privacy = await object_privacy(project_id, object_name)
runtime = _runtime_for_object_context(project_id, impact)
knowledge = _knowledge_for_object_context(schema, impact, ui)
return Response(
- render_html5_object_context(project_id, schema, impact, access, ui, runtime, knowledge),
+ render_html5_object_context(project_id, schema, impact, access, ui, runtime, knowledge, privacy),
media_type="text/html; charset=utf-8",
)
diff --git a/services/api-server/tests/test_api.py b/services/api-server/tests/test_api.py
index 0531a8d..2b4e156 100644
--- a/services/api-server/tests/test_api.py
+++ b/services/api-server/tests/test_api.py
@@ -256,6 +256,7 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert indexed.status_code == 200
snapshot = client.get(f"/projects/{project_id}/snapshot/export").json()
handler = next(node for node in snapshot["nodes"] if node["name"] == "ПровестиКоманда")
+ attribute = next(node for node in snapshot["nodes"] if node["name"] == "Контрагент")
signal = client.post(
f"/projects/{project_id}/runtime/signals",
json={
@@ -279,6 +280,15 @@ def test_html5_object_context_fragment(tmp_path: Path):
},
)
assert knowledge.status_code == 200
+ marker = client.post(
+ f"/projects/{project_id}/privacy/markers",
+ json={
+ "target_id": attribute["lineage_id"],
+ "classification": "PERSONAL_DATA",
+ "reason": "Контрагент содержит персональные данные",
+ },
+ )
+ assert marker.status_code == 200
editor = client.get(f"/html5/projects/{project_id}/editor")
assert editor.status_code == 200
@@ -301,6 +311,8 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert "125.0 ms" in context.text
assert "Правила проведения HTML5" in context.text
assert "Контекст проведения заказа" in context.text
+ assert "PERSONAL_DATA" in context.text
+ assert "Контрагент содержит персональные данные" in context.text
assert "Роль.Менеджер" in context.text
assert "read, write, post" in context.text or "post, read, write" in context.text
assert "