Save 1C access profile drafts
This commit is contained in:
@@ -194,6 +194,8 @@ from operations_core import (
|
|||||||
build_project_report,
|
build_project_report,
|
||||||
)
|
)
|
||||||
from one_c_normalizer import (
|
from one_c_normalizer import (
|
||||||
|
AccessProfile,
|
||||||
|
AccessRoleAssignment,
|
||||||
COMMON_BRANCH_CHILDREN,
|
COMMON_BRANCH_CHILDREN,
|
||||||
METADATA_CHILD_OBJECT_SPECS,
|
METADATA_CHILD_OBJECT_SPECS,
|
||||||
METADATA_TYPE_CONTEXT_ACTIONS,
|
METADATA_TYPE_CONTEXT_ACTIONS,
|
||||||
@@ -853,6 +855,19 @@ class AccessProfileDraftResponse(BaseModel):
|
|||||||
proposed_profile: dict = Field(default_factory=dict)
|
proposed_profile: dict = Field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class AccessProfileApplyRequest(AccessProfileDraftRequest):
|
||||||
|
allow_incomplete: bool = False
|
||||||
|
replace_existing: bool = False
|
||||||
|
author: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class AccessProfileApplyResponse(BaseModel):
|
||||||
|
applied: bool
|
||||||
|
profile: dict
|
||||||
|
draft: AccessProfileDraftResponse
|
||||||
|
message: str
|
||||||
|
|
||||||
|
|
||||||
class ProjectSetupResponse(BaseModel):
|
class ProjectSetupResponse(BaseModel):
|
||||||
project_id: str
|
project_id: str
|
||||||
status: ProjectSetupStatus
|
status: ProjectSetupStatus
|
||||||
@@ -2948,6 +2963,39 @@ async def preview_project_access_profile(project_id: str, request: AccessProfile
|
|||||||
return _build_access_profile_draft(normalized, request)
|
return _build_access_profile_draft(normalized, request)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/projects/{project_id}/access/profiles", response_model=AccessProfileApplyResponse)
|
||||||
|
async def apply_project_access_profile(project_id: str, request: AccessProfileApplyRequest) -> AccessProfileApplyResponse:
|
||||||
|
normalized = _load_normalized_project(project_id)
|
||||||
|
if normalized is None:
|
||||||
|
raise HTTPException(status_code=404, detail="NormalizedProject not found")
|
||||||
|
draft = _build_access_profile_draft(normalized, request)
|
||||||
|
if draft.missing_objects and not request.allow_incomplete:
|
||||||
|
raise HTTPException(status_code=409, detail="Access profile draft has missing objects")
|
||||||
|
profile = _access_profile_from_draft(draft, request)
|
||||||
|
existing_index = next(
|
||||||
|
(
|
||||||
|
index
|
||||||
|
for index, item in enumerate(normalized.access.profiles)
|
||||||
|
if item.name.casefold() == profile.name.casefold()
|
||||||
|
or str(item.qualified_name or "").casefold() == str(profile.qualified_name or "").casefold()
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if existing_index is not None and not request.replace_existing:
|
||||||
|
raise HTTPException(status_code=409, detail="Access profile already exists")
|
||||||
|
if existing_index is None:
|
||||||
|
normalized.access.profiles.append(profile)
|
||||||
|
else:
|
||||||
|
normalized.access.profiles[existing_index] = profile
|
||||||
|
_save_normalized_project(project_id, normalized)
|
||||||
|
return AccessProfileApplyResponse(
|
||||||
|
applied=True,
|
||||||
|
profile=profile.model_dump(mode="json"),
|
||||||
|
draft=draft,
|
||||||
|
message="Access profile saved in SFERA workspace model",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/projects/{project_id}/imports/quality", response_model=ImportQualityResponse)
|
@app.get("/projects/{project_id}/imports/quality", response_model=ImportQualityResponse)
|
||||||
async def get_import_quality(project_id: str) -> ImportQualityResponse:
|
async def get_import_quality(project_id: str) -> ImportQualityResponse:
|
||||||
return _import_quality_response(project_id)
|
return _import_quality_response(project_id)
|
||||||
@@ -7374,6 +7422,38 @@ def _build_access_profile_draft(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _access_profile_from_draft(
|
||||||
|
draft: AccessProfileDraftResponse,
|
||||||
|
request: AccessProfileApplyRequest,
|
||||||
|
) -> AccessProfile:
|
||||||
|
now = _current_timestamp()
|
||||||
|
attributes = {
|
||||||
|
"status": "workspace_draft",
|
||||||
|
"created_by": request.author or "",
|
||||||
|
"created_at": now,
|
||||||
|
"target_objects": list(draft.proposed_profile.get("target_objects", [])),
|
||||||
|
"permissions": list(draft.proposed_profile.get("permissions", [])),
|
||||||
|
"missing_objects": list(draft.missing_objects),
|
||||||
|
}
|
||||||
|
return AccessProfile(
|
||||||
|
name=draft.name,
|
||||||
|
qualified_name=str(draft.proposed_profile.get("qualified_name") or f"ПрофильГруппыДоступа.{draft.name}"),
|
||||||
|
attributes=attributes,
|
||||||
|
roles=[
|
||||||
|
AccessRoleAssignment(
|
||||||
|
role=role.role,
|
||||||
|
role_qualified_name=role.role_qualified_name,
|
||||||
|
source=role.source,
|
||||||
|
attributes={
|
||||||
|
"matched_objects": role.matched_objects,
|
||||||
|
"permissions": role.permissions,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for role in draft.roles
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _access_roles_for_targets(
|
def _access_roles_for_targets(
|
||||||
normalized: NormalizedProject,
|
normalized: NormalizedProject,
|
||||||
target_objects: list[str],
|
target_objects: list[str],
|
||||||
|
|||||||
@@ -1558,6 +1558,22 @@ def test_import_supports_structure_only_indexing(tmp_path: Path):
|
|||||||
assert "Роль.Менеджер" in preview_payload["proposed_profile"]["roles"]
|
assert "Роль.Менеджер" in preview_payload["proposed_profile"]["roles"]
|
||||||
assert preview_payload["missing_objects"] == []
|
assert preview_payload["missing_objects"] == []
|
||||||
|
|
||||||
|
apply_profile = client.post(
|
||||||
|
f"/projects/{project_id}/access/profiles",
|
||||||
|
json={
|
||||||
|
"name": "НовыйПрофильHTTP",
|
||||||
|
"target_objects": ["HTTPСервис.ПубличныйAPI"],
|
||||||
|
"permissions": ["read"],
|
||||||
|
"author": "dev.ivan",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert apply_profile.status_code == 200
|
||||||
|
assert apply_profile.json()["profile"]["attributes"]["status"] == "workspace_draft"
|
||||||
|
|
||||||
|
updated_access = client.get(f"/projects/{project_id}/access")
|
||||||
|
assert updated_access.status_code == 200
|
||||||
|
assert any(item["name"] == "НовыйПрофильHTTP" for item in updated_access.json()["profiles"])
|
||||||
|
|
||||||
tree = client.get(f"/projects/{project_id}/metadata/tree")
|
tree = client.get(f"/projects/{project_id}/metadata/tree")
|
||||||
assert tree.status_code == 200
|
assert tree.status_code == 200
|
||||||
root = tree.json()["root"]
|
root = tree.json()["root"]
|
||||||
|
|||||||
Reference in New Issue
Block a user