Phase 0: Add booking permissions and constants

- Add AppointmentStatus enum (booked, cancelled_by_user, cancelled_by_admin)
- Add booking permissions for regular users (book_appointment, view_own_appointments, cancel_own_appointment)
- Add availability/appointments permissions for admin (manage_availability, view_all_appointments, cancel_any_appointment)
- Add booking constants to shared/constants.json (slotDurationMinutes, maxAdvanceDays, minAdvanceDays, noteMaxLength)
- Update validate_constants.py to validate new sections
This commit is contained in:
counterweight 2025-12-20 23:30:08 +01:00
parent c9b5cab0d6
commit 6c1a05d93d
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
3 changed files with 55 additions and 6 deletions

View file

@ -27,6 +27,16 @@ class Permission(str, PyEnum):
# Invite permissions
MANAGE_INVITES = "manage_invites"
VIEW_OWN_INVITES = "view_own_invites"
# Booking permissions (regular users)
BOOK_APPOINTMENT = "book_appointment"
VIEW_OWN_APPOINTMENTS = "view_own_appointments"
CANCEL_OWN_APPOINTMENT = "cancel_own_appointment"
# Availability/Appointments permissions (admin)
MANAGE_AVAILABILITY = "manage_availability"
VIEW_ALL_APPOINTMENTS = "view_all_appointments"
CANCEL_ANY_APPOINTMENT = "cancel_any_appointment"
class InviteStatus(str, PyEnum):
@ -36,6 +46,13 @@ class InviteStatus(str, PyEnum):
REVOKED = "revoked"
class AppointmentStatus(str, PyEnum):
"""Status of an appointment."""
BOOKED = "booked"
CANCELLED_BY_USER = "cancelled_by_user"
CANCELLED_BY_ADMIN = "cancelled_by_admin"
# Role name constants
ROLE_ADMIN = "admin"
ROLE_REGULAR = "regular"
@ -43,19 +60,25 @@ ROLE_REGULAR = "regular"
# Role definitions with their permissions
ROLE_DEFINITIONS: dict[str, RoleConfig] = {
ROLE_ADMIN: {
"description": "Administrator with audit and invite management access",
"description": "Administrator with audit, invite, and appointment management access",
"permissions": [
Permission.VIEW_AUDIT,
Permission.MANAGE_INVITES,
Permission.MANAGE_AVAILABILITY,
Permission.VIEW_ALL_APPOINTMENTS,
Permission.CANCEL_ANY_APPOINTMENT,
],
},
ROLE_REGULAR: {
"description": "Regular user with counter, sum, and invite access",
"description": "Regular user with counter, sum, invite, and booking access",
"permissions": [
Permission.VIEW_COUNTER,
Permission.INCREMENT_COUNTER,
Permission.USE_SUM,
Permission.VIEW_OWN_INVITES,
Permission.BOOK_APPOINTMENT,
Permission.VIEW_OWN_APPOINTMENTS,
Permission.CANCEL_OWN_APPOINTMENT,
],
},
}

View file

@ -2,7 +2,7 @@
import json
from pathlib import Path
from models import ROLE_ADMIN, ROLE_REGULAR, InviteStatus
from models import ROLE_ADMIN, ROLE_REGULAR, InviteStatus, AppointmentStatus
def validate_shared_constants() -> None:
@ -27,13 +27,28 @@ def validate_shared_constants() -> None:
)
# Validate invite statuses
expected_statuses = {s.name: s.value for s in InviteStatus}
if constants.get("inviteStatuses") != expected_statuses:
expected_invite_statuses = {s.name: s.value for s in InviteStatus}
if constants.get("inviteStatuses") != expected_invite_statuses:
raise ValueError(
f"Invite status mismatch in shared/constants.json. "
f"Expected: {expected_statuses}, Got: {constants.get('inviteStatuses')}"
f"Expected: {expected_invite_statuses}, Got: {constants.get('inviteStatuses')}"
)
# Validate appointment statuses
expected_appointment_statuses = {s.name: s.value for s in AppointmentStatus}
if constants.get("appointmentStatuses") != expected_appointment_statuses:
raise ValueError(
f"Appointment status mismatch in shared/constants.json. "
f"Expected: {expected_appointment_statuses}, Got: {constants.get('appointmentStatuses')}"
)
# Validate booking constants exist with required fields
booking = constants.get("booking", {})
required_booking_fields = ["slotDurationMinutes", "maxAdvanceDays", "minAdvanceDays", "noteMaxLength"]
for field in required_booking_fields:
if field not in booking:
raise ValueError(f"Missing booking constant '{field}' in shared/constants.json")
# Validate validation rules exist (structure check only)
validation = constants.get("validation", {})
required_fields = ["telegram", "signal", "nostrNpub"]

View file

@ -8,6 +8,17 @@
"SPENT": "spent",
"REVOKED": "revoked"
},
"appointmentStatuses": {
"BOOKED": "booked",
"CANCELLED_BY_USER": "cancelled_by_user",
"CANCELLED_BY_ADMIN": "cancelled_by_admin"
},
"booking": {
"slotDurationMinutes": 15,
"maxAdvanceDays": 30,
"minAdvanceDays": 1,
"noteMaxLength": 144
},
"validation": {
"telegram": {
"maxLengthAfterAt": 32,