Add rename and remove for a workspace's placeholder partner
Settings is gaining a partner display, which hides the add-form once a
partner exists. Without these routes a typo'd partner name would be
permanently unfixable, since no endpoint could change or drop one.
PATCH /workspace/{id}/parents/{uuid} renames; DELETE soft-deletes.
Soft rather than hard delete because PersonalData holds PII and the
repository is a SoftDeleteRepository specifically so erasure stays
auditable.
Removal refuses (422) when the member still has leave wishes, planned
leave, parental-leave rows or vacation days, naming the blockers, so a
removal can never orphan plan data. It also refuses for the caller's own
profile, and for a member who has their own login -- the invite/redeem
flow re-points a real user's personal_data into the inviter's workspace,
so soft-deleting that row would silently break their login.
Errors carry message_key + data rather than prose, per the no-Swedish-on
-the-wire rule; NotFoundError/ValidationError gained the same optional
shape DubbeldagError already had, defaulting empty so existing call
sites are unaffected.
Also fixes seed_demo.py constructing WorkspaceService with a stale
3-arg signature, which would have crashed at runtime -- invisible to
pytest since it is a standalone script.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
This commit is contained in:
@@ -10,11 +10,41 @@ class DomainError(Exception):
|
||||
|
||||
|
||||
class NotFoundError(DomainError):
|
||||
"""Raised when an entity cannot be found by its identifier."""
|
||||
"""Raised when an entity cannot be found by its identifier.
|
||||
|
||||
May optionally carry a `message_key` + `data` pair (§3.5) so a router can
|
||||
surface a structured, i18n-ready refusal instead of a hardcoded string —
|
||||
see `DubbeldagError` for the established shape. Existing call sites that
|
||||
only pass `message` are unaffected: `message_key` defaults to `""`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
message_key: str = "",
|
||||
data: dict[str, object] | None = None,
|
||||
) -> None:
|
||||
super().__init__(message)
|
||||
self.message_key = message_key
|
||||
self.data: dict[str, object] = data if data is not None else {}
|
||||
|
||||
|
||||
class ValidationError(DomainError):
|
||||
"""Raised when input fails a business rule (not just schema validation)."""
|
||||
"""Raised when input fails a business rule (not just schema validation).
|
||||
|
||||
May optionally carry a `message_key` + `data` pair (§3.5); see
|
||||
`NotFoundError` above for the same shape and rationale.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
message_key: str = "",
|
||||
data: dict[str, object] | None = None,
|
||||
) -> None:
|
||||
super().__init__(message)
|
||||
self.message_key = message_key
|
||||
self.data: dict[str, object] = data if data is not None else {}
|
||||
|
||||
|
||||
class InsufficientDaysError(DomainError):
|
||||
|
||||
Reference in New Issue
Block a user