fix(profile-filter): support owner object payloads and normalize owners response
This commit is contained in:
@@ -274,6 +274,72 @@ def _normalize_actor_alias_token(value: Any) -> Optional[str]:
|
||||
# [/DEF:_normalize_actor_alias_token:Function]
|
||||
|
||||
|
||||
# [DEF:_normalize_owner_display_token:Function]
|
||||
# @PURPOSE: Project owner payload value into stable display string for API response contracts.
|
||||
# @PRE: owner can be scalar, dict or None.
|
||||
# @POST: Returns trimmed non-empty owner display token or None.
|
||||
def _normalize_owner_display_token(owner: Any) -> Optional[str]:
|
||||
if owner is None:
|
||||
return None
|
||||
|
||||
if isinstance(owner, dict):
|
||||
username = str(owner.get("username") or owner.get("user_name") or owner.get("name") or "").strip()
|
||||
full_name = str(owner.get("full_name") or "").strip()
|
||||
first_name = str(owner.get("first_name") or "").strip()
|
||||
last_name = str(owner.get("last_name") or "").strip()
|
||||
combined = " ".join(part for part in [first_name, last_name] if part).strip()
|
||||
email = str(owner.get("email") or "").strip()
|
||||
|
||||
for candidate in [username, full_name, combined, email]:
|
||||
if candidate:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
normalized = str(owner).strip()
|
||||
return normalized or None
|
||||
# [/DEF:_normalize_owner_display_token:Function]
|
||||
|
||||
|
||||
# [DEF:_normalize_dashboard_owner_values:Function]
|
||||
# @PURPOSE: Normalize dashboard owners payload to optional list of display strings.
|
||||
# @PRE: owners payload can be None, scalar, or list with mixed values.
|
||||
# @POST: Returns deduplicated owner labels preserving order, or None when absent.
|
||||
def _normalize_dashboard_owner_values(owners: Any) -> Optional[List[str]]:
|
||||
if owners is None:
|
||||
return None
|
||||
|
||||
raw_items: List[Any]
|
||||
if isinstance(owners, list):
|
||||
raw_items = owners
|
||||
else:
|
||||
raw_items = [owners]
|
||||
|
||||
normalized: List[str] = []
|
||||
for owner in raw_items:
|
||||
token = _normalize_owner_display_token(owner)
|
||||
if token and token not in normalized:
|
||||
normalized.append(token)
|
||||
|
||||
return normalized
|
||||
# [/DEF:_normalize_dashboard_owner_values:Function]
|
||||
|
||||
|
||||
# [DEF:_project_dashboard_response_items:Function]
|
||||
# @PURPOSE: Project dashboard payloads to response-contract-safe shape.
|
||||
# @PRE: dashboards is a list of dict-like dashboard payloads.
|
||||
# @POST: Returned items satisfy DashboardItem owners=list[str]|None contract.
|
||||
def _project_dashboard_response_items(dashboards: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
projected: List[Dict[str, Any]] = []
|
||||
for dashboard in dashboards:
|
||||
projected_dashboard = dict(dashboard)
|
||||
projected_dashboard["owners"] = _normalize_dashboard_owner_values(
|
||||
projected_dashboard.get("owners")
|
||||
)
|
||||
projected.append(projected_dashboard)
|
||||
return projected
|
||||
# [/DEF:_project_dashboard_response_items:Function]
|
||||
|
||||
|
||||
# [DEF:_resolve_profile_actor_aliases:Function]
|
||||
# @PURPOSE: Resolve stable actor aliases for profile filtering without per-dashboard detail fan-out.
|
||||
# @PRE: bound username is available and env is valid.
|
||||
@@ -620,8 +686,10 @@ async def get_dashboards(
|
||||
f"(page {page}/{total_pages}, total: {total}, profile_filter_applied={effective_profile_filter.applied})"
|
||||
)
|
||||
|
||||
response_dashboards = _project_dashboard_response_items(paginated_dashboards)
|
||||
|
||||
return DashboardsResponse(
|
||||
dashboards=paginated_dashboards,
|
||||
dashboards=response_dashboards,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
|
||||
Reference in New Issue
Block a user