Files
CalendarApi/about/logic.md
Michilis 41f6ae916f first commit
Made-with: Cursor
2026-02-28 02:17:55 +00:00

7.0 KiB

Calendar & Contacts API

logic.md

This document defines the COMPLETE business logic layer of the Calendar & Contacts API.

It explains:

  • Permission enforcement
  • Ownership rules
  • Validation rules
  • Recurrence engine behavior
  • Reminder processing
  • Availability calculation
  • Booking logic
  • Transaction boundaries
  • Audit requirements
  • Edge case handling

This file MUST be treated as the authoritative source for backend logic.


GLOBAL INVARIANTS

  1. All timestamps stored in UTC.
  2. All API timestamps are RFC3339 strings.
  3. Ownership is ALWAYS derived from auth context.
  4. Soft deletes are enforced everywhere.
  5. No endpoint may leak data across users.
  6. All mutations must write audit log entries.

  1. AUTHENTICATION & CONTEXT

Every authenticated request must result in a RequestContext struct:

RequestContext:

  • user_id (uuid)
  • auth_method ("jwt" | "api_key")
  • scopes (if api_key)

JWT auth:

  • Validate signature
  • Validate expiration
  • Extract user_id

API key auth:

  • Hash provided key
  • Lookup in api_keys
  • Ensure revoked_at is NULL
  • Load scopes

If neither provided → AUTH_REQUIRED

If invalid → AUTH_INVALID


  1. PERMISSION MODEL

Calendar roles:

  • owner
  • editor
  • viewer

Permission matrix:

CALENDAR ACTIONS

  • owner: full access
  • editor: read calendar, CRUD events
  • viewer: read-only

EVENT ACTIONS

  • owner/editor: create/update/delete
  • viewer: read

CONTACT ACTIONS

  • Only owner of contacts can CRUD

BOOKING

  • Only owner can create booking links

API KEY SCOPE ENFORCEMENT Each endpoint maps to required scope. Example:

  • GET /calendars → calendars:read
  • POST /events → events:write
  • GET /contacts → contacts:read

If scope missing → FORBIDDEN


  1. USER LOGIC

REGISTER

  • Email lowercase
  • Must be unique
  • Password >=10 chars
  • Hash with bcrypt cost >=12
  • Create default calendar
  • Return tokens

DELETE USER

  • Soft delete user
  • Soft delete calendars
  • Soft delete events
  • Soft delete contacts
  • Revoke API keys

  1. CALENDAR LOGIC

CREATE

  • owner_id = authenticated user
  • name required (1..80)
  • color optional hex validation

LIST Return calendars where:

  • owner_id = user_id OR
  • calendar_members.user_id = user_id

Include role in response.

DELETE

  • Only owner
  • Soft delete calendar
  • Soft delete all related events

SHARING

  • Only owner can share
  • Cannot share with self
  • Upsert membership row

REMOVE MEMBER

  • Only owner
  • Cannot remove owner

  1. EVENT LOGIC

CREATE EVENT Validation:

  • calendar exists
  • user has editor or owner role
  • title 1..140
  • end_time > start_time
  • timezone valid IANA

Time handling:

  • Convert start_time to UTC
  • Convert end_time to UTC
  • Store original timezone string

Overlap rule:

  • Overlap allowed by default
  • Booking system enforces no-overlap

If recurrence_rule provided:

  • Validate via rrule-go
  • Store string

If reminders provided:

  • Insert reminders
  • Schedule jobs

UPDATE EVENT

  • Same permission check
  • Re-validate time constraints
  • If recurrence changed → validate again
  • Reschedule reminders

DELETE EVENT

  • Soft delete

  1. RECURRENCE ENGINE

Recurring events are NOT pre-expanded.

Storage:

  • recurrence_rule string on master event

Expansion occurs ONLY during:

  • GET /events
  • GET /events/{id}/occurrences

Algorithm:

For each event in DB where:

  • event.start_time <= range_end

If no recurrence_rule:

  • Include if intersects range

If recurrence_rule present:

  • Initialize rrule with DTSTART = event.start_time

  • Generate occurrences within [range_start, range_end]

  • For each occurrence:

    • Check against exceptions
    • Create virtual occurrence object

Occurrence response must include:

  • is_occurrence = true
  • occurrence_start_time
  • occurrence_end_time

Exceptions table:

  • event_id
  • exception_date
  • action ("skip")

If occurrence matches exception_date → skip


  1. REMINDER PROCESSING

Reminder scheduling rule:

For each reminder (minutes_before): trigger_time = event.start_time - minutes_before

If trigger_time > now:

  • Enqueue Asynq job

Job payload:

  • event_id
  • reminder_id
  • user_id

Worker logic:

  • Load event
  • If event.deleted_at != NULL → abort
  • Send notification (webhook/email placeholder)
  • Retry on failure with exponential backoff

On event update:

  • Cancel old reminder jobs (if supported)
  • Recompute schedule

  1. CONTACT LOGIC

Contacts are strictly per-user.

CREATE

  • Must have at least one identifying field
  • email validated if present

SEARCH

  • Case-insensitive match on:

    • first_name
    • last_name
    • email
    • company

DELETE

  • Soft delete

  1. AVAILABILITY LOGIC

Endpoint requires:

  • calendar_id
  • start
  • end

Permission:

  • viewer or higher

Busy condition: An event is busy if:

(event.start_time < range_end) AND (event.end_time > range_start)

Recurring events:

  • Expand occurrences
  • Apply same intersection rule

Return busy blocks sorted by start.


  1. BOOKING SYSTEM LOGIC

CREATE BOOKING LINK

  • Only owner
  • Generate secure random token
  • Store configuration

PUBLIC AVAILABILITY

  • No auth
  • Load booking link
  • Validate active
  • Compute working-hour windows
  • Subtract busy blocks
  • Apply buffer_minutes before/after events

RESERVATION

  • Begin DB transaction
  • Re-check slot availability with overlap query
  • If conflict → ROLLBACK + CONFLICT
  • Insert event
  • Commit

Overlap query inside transaction:

SELECT 1 FROM events WHERE calendar_id = ? AND deleted_at IS NULL AND start_time < slot_end AND end_time > slot_start FOR UPDATE

This ensures race safety.


  1. CURSOR PAGINATION

Cursor format: base64url(last_sort_time|last_id)

Decoding:

  • Split by pipe
  • Use as tuple comparison

Events sorting: ORDER BY start_time ASC, id ASC

Contacts sorting: ORDER BY created_at ASC, id ASC

Limit enforcement:

  • Default 50
  • Max 200

  1. AUDIT LOG LOGIC

Every mutation writes:

entity_type entity_id action user_id timestamp

Actions examples:

  • CREATE_EVENT
  • UPDATE_EVENT
  • DELETE_EVENT
  • SHARE_CALENDAR
  • DELETE_CONTACT

Audit writes MUST NOT fail main transaction. If audit insert fails:

  • Log error
  • Continue response

  1. ERROR RULES

AUTH_REQUIRED → no credentials AUTH_INVALID → invalid token FORBIDDEN → permission denied NOT_FOUND → entity not found or not accessible VALIDATION_ERROR → invalid input CONFLICT → overlap or duplicate INTERNAL → unexpected error

Never expose internal DB errors directly.


  1. TRANSACTION RULES

Transactions REQUIRED for:

  • Booking reservation
  • Event creation with reminders
  • Event update affecting reminders
  • Deleting calendar (cascade soft delete)

Service layer must manage transactions. Repository layer must not auto-commit.


  1. PERFORMANCE REQUIREMENTS

All list endpoints:

  • Must use indexed columns
  • Must use pagination
  • Must avoid N+1 queries

Recurring expansion must be bounded by requested range.

Maximum recurrence expansion window allowed per request:

  • 1 year (recommended safeguard)

If range exceeds safeguard → VALIDATION_ERROR


This is the authoritative backend logic specification.