87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
"""DRF serializers for the ``/mcp_server/api/teams/`` control plane.
|
|
|
|
These endpoints are the Daedalus → Mnemosyne control plane described
|
|
in §7 of ``docs/DAEDALUS_PALLAS_INTEGRATION_v1.md``. They are called
|
|
by Daedalus authenticated as the Mnemosyne user the team belongs to
|
|
(per-user DRF token).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rest_framework import serializers
|
|
|
|
from ..models import Team
|
|
|
|
|
|
class TeamCreateRequestSerializer(serializers.Serializer):
|
|
"""Inbound payload for ``POST /mcp_server/api/teams/``.
|
|
|
|
``id`` is the Daedalus ``PallasInstance.id`` UUID. It is supplied
|
|
by the caller (not generated by Mnemosyne) so the two control
|
|
planes share a stable key.
|
|
"""
|
|
|
|
id = serializers.UUIDField()
|
|
name = serializers.CharField(max_length=200)
|
|
|
|
|
|
class TeamPublicSerializer(serializers.ModelSerializer):
|
|
"""Outbound payload for team reads / updates.
|
|
|
|
Excludes the JWT — it is surfaced exactly once on create / rotate
|
|
and never recoverable after. Include ``workspace_ids`` so Daedalus
|
|
can diff its local state against what Mnemosyne actually holds.
|
|
"""
|
|
|
|
workspace_ids = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Team
|
|
fields = [
|
|
"id",
|
|
"name",
|
|
"active",
|
|
"active_jti",
|
|
"created_at",
|
|
"updated_at",
|
|
"workspace_ids",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
def get_workspace_ids(self, obj: Team) -> list[str]:
|
|
return list(
|
|
obj.workspace_assignments.order_by("workspace_id")
|
|
.values_list("workspace_id", flat=True)
|
|
)
|
|
|
|
|
|
class TeamWorkspacesUpdateSerializer(serializers.Serializer):
|
|
"""Inbound payload for ``PUT /mcp_server/api/teams/{id}/workspaces/``.
|
|
|
|
The request is an idempotent *replace* — the team's assignment set
|
|
after the call equals ``workspace_ids`` exactly. Daedalus is the
|
|
source of truth; Mnemosyne mirrors.
|
|
"""
|
|
|
|
workspace_ids = serializers.ListField(
|
|
child=serializers.CharField(max_length=64, allow_blank=False),
|
|
allow_empty=True,
|
|
)
|
|
|
|
def validate_workspace_ids(self, value: list[str]) -> list[str]:
|
|
# De-duplicate while preserving order; strip whitespace. ``unique_together``
|
|
# would already reject dupes at the DB layer but failing earlier gives
|
|
# the caller a cleaner 400.
|
|
seen = set()
|
|
out = []
|
|
for ws in value:
|
|
ws = ws.strip()
|
|
if not ws:
|
|
raise serializers.ValidationError(
|
|
"workspace_ids entries must be non-empty strings."
|
|
)
|
|
if ws not in seen:
|
|
seen.add(ws)
|
|
out.append(ws)
|
|
return out
|