feat: add configurable view-aware dashboards

This commit is contained in:
2026-07-29 14:32:54 +02:00
parent b28229c003
commit f0732bead2
24 changed files with 3169 additions and 146 deletions
+92
View File
@@ -0,0 +1,92 @@
from __future__ import annotations
import math
from datetime import datetime
from typing import Annotated, Literal
from pydantic import BaseModel, Field, field_validator, model_validator
WidgetId = Annotated[str, Field(min_length=1, max_length=160)]
ConfigurationValue = str | int | float | bool | None
class DashboardWidgetPlacementPayload(BaseModel):
instance_id: str = Field(min_length=1, max_length=120)
widget_id: WidgetId
size: Literal["small", "medium", "wide", "full"] = "medium"
configuration: dict[str, ConfigurationValue] = Field(
default_factory=dict,
max_length=40,
)
@field_validator("configuration")
@classmethod
def validate_configuration(
cls,
value: dict[str, ConfigurationValue],
) -> dict[str, ConfigurationValue]:
approximate_size = 0
for key, item in value.items():
if not key or len(key) > 120:
raise ValueError(
"Widget configuration keys must contain 1 to 120 characters."
)
approximate_size += len(key)
if isinstance(item, str):
if len(item) > 4_000:
raise ValueError(
"Widget configuration text values may contain at most "
"4,000 characters."
)
approximate_size += len(item)
else:
approximate_size += len(str(item))
if isinstance(item, float) and not math.isfinite(item):
raise ValueError(
"Widget configuration numbers must be finite."
)
if approximate_size > 32_000:
raise ValueError(
"A widget configuration may contain at most 32,000 characters."
)
return value
class DashboardLayoutUpdateRequest(BaseModel):
expected_revision: int | None = Field(default=None, ge=0)
layout_version: Literal[1] = 1
placements: list[DashboardWidgetPlacementPayload] = Field(
default_factory=list,
max_length=100,
)
known_widget_ids: list[WidgetId] = Field(
default_factory=list,
max_length=500,
)
@model_validator(mode="after")
def validate_unique_ids(self) -> "DashboardLayoutUpdateRequest":
instance_ids = [item.instance_id for item in self.placements]
if len(instance_ids) != len(set(instance_ids)):
raise ValueError("Dashboard widget instance ids must be unique.")
return self
class DashboardLayoutResponse(BaseModel):
exists: bool
view_id: str | None = None
layout_version: int = 1
revision: int = 0
placements: list[DashboardWidgetPlacementPayload] = Field(
default_factory=list
)
known_widget_ids: list[str] = Field(default_factory=list)
updated_at: datetime | None = None
__all__ = [
"DashboardLayoutResponse",
"DashboardLayoutUpdateRequest",
"DashboardWidgetPlacementPayload",
]