Files
smoothschedule/QUICK_REFERENCE_CALENDAR_SYNC.md
poduck e4ad7fca87 feat: Plan-based feature permissions and quota enforcement
Backend:
- Add HasQuota() permission factory for quota limits (resources, users, services, appointments, email templates, automated tasks)
- Add HasFeaturePermission() factory for feature-based permissions (SMS, masked calling, custom domains, white label, plugins, webhooks, calendar sync, analytics)
- Add has_feature() method to Tenant model for flexible permission checking
- Add new tenant permission fields: can_create_plugins, can_use_webhooks, can_use_calendar_sync, can_export_data
- Create Data Export API with CSV/JSON support for appointments, customers, resources, services
- Create Analytics API with dashboard, appointments, revenue endpoints
- Add calendar sync views and URL configuration

Frontend:
- Add usePlanFeatures hook for checking feature availability
- Add UpgradePrompt components (inline, banner, overlay variants)
- Add LockedSection wrapper and LockedButton for feature gating
- Update settings pages with permission checks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 11:21:11 -05:00

5.5 KiB

Calendar Sync Permission - Quick Reference

What Was Added

A permission gating system for calendar sync features in the Django backend.

Key Components

1. Database Field

# core/models.py - Added to Tenant model
can_use_calendar_sync = models.BooleanField(default=False)

2. Permission Check Factory

# core/permissions.py - Added to FEATURE_NAMES
'can_use_calendar_sync': 'Calendar Sync',

3. OAuth Integration

# core/oauth_views.py - Check when purpose is 'calendar'
if purpose == 'calendar':
    calendar_permission = HasFeaturePermission('can_use_calendar_sync')
    if not calendar_permission().has_permission(request, self):
        return Response({'error': 'Feature not available'}, status=403)

4. Calendar Sync Views

# schedule/calendar_sync_views.py
CalendarListView           # GET /api/calendar/list/
CalendarSyncView           # POST /api/calendar/sync/
CalendarDeleteView         # DELETE /api/calendar/disconnect/
CalendarStatusView         # GET /api/calendar/status/

How to Use

Enable for a Tenant

# Via Django shell
from core.models import Tenant
tenant = Tenant.objects.get(schema_name='demo')
tenant.can_use_calendar_sync = True
tenant.save()

Use in ViewSet

from rest_framework import viewsets
from core.permissions import HasFeaturePermission

class MyViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated, HasFeaturePermission('can_use_calendar_sync')]

Use in APIView

from rest_framework.views import APIView

class MyView(APIView):
    permission_classes = [CalendarSyncPermission]
    # CalendarSyncPermission = IsAuthenticated + has_feature check

API Endpoints

Method Endpoint Description Permission
GET /api/calendar/status/ Check if calendar sync is available Auth only
GET /api/calendar/list/ List connected calendars Calendar sync
POST /api/calendar/sync/ Start calendar sync Calendar sync
DELETE /api/calendar/disconnect/ Disconnect a calendar Calendar sync
POST /api/oauth/google/initiate/ Start Google OAuth for calendar Calendar sync (if purpose=calendar)
POST /api/oauth/microsoft/initiate/ Start MS OAuth for calendar Calendar sync (if purpose=calendar)

Testing

Run tests

cd /home/poduck/Desktop/smoothschedule2/smoothschedule
docker compose -f docker-compose.local.yml exec django pytest schedule/tests/test_calendar_sync_permissions.py -v

Test endpoints manually

# Check status (always works)
curl http://lvh.me:8000/api/calendar/status/ -H "Authorization: Bearer <token>"

# List calendars (requires permission)
curl http://lvh.me:8000/api/calendar/list/ -H "Authorization: Bearer <token>"
# Returns 403 if permission not granted

Files Modified

File Changes
core/models.py Added can_use_calendar_sync field
core/permissions.py Added to FEATURE_NAMES
core/oauth_views.py Added permission check for calendar

Files Created

File Purpose
core/migrations/0016_tenant_can_use_calendar_sync.py Database migration
schedule/calendar_sync_views.py Calendar sync API views
schedule/calendar_sync_urls.py URL routing
schedule/tests/test_calendar_sync_permissions.py Test suite
CALENDAR_SYNC_INTEGRATION.md Developer guide

Permission Check Pattern

Request to calendar endpoint
  ↓
Check: Is user authenticated?
  ├─ NO → 401 Unauthorized
  └─ YES ↓
Check: Does tenant have can_use_calendar_sync=True?
  ├─ NO → 403 Forbidden (upgrade message)
  └─ YES ↓
Process request
  ├─ Success → 200 OK
  └─ Error → 500 Server Error

Example: Full Permission Setup

# 1. Enable feature for tenant
from core.models import Tenant
tenant = Tenant.objects.get(schema_name='demo')
tenant.can_use_calendar_sync = True
tenant.save()

# 2. User tries to access calendar endpoint
# GET /api/calendar/list/
# → Check: tenant.has_feature('can_use_calendar_sync')
# → True! → 200 OK with calendar list

# 3. Without permission
tenant.can_use_calendar_sync = False
tenant.save()
# GET /api/calendar/list/
# → Check: tenant.has_feature('can_use_calendar_sync')
# → False! → 403 Forbidden with upgrade message
  • Full Guide: CALENDAR_SYNC_INTEGRATION.md in smoothschedule/ folder
  • Implementation Details: CALENDAR_SYNC_PERMISSION_IMPLEMENTATION.md in project root
  • Code: schedule/calendar_sync_views.py (well-commented)
  • Tests: schedule/tests/test_calendar_sync_permissions.py

Common Tasks

Check if feature is enabled

tenant.has_feature('can_use_calendar_sync')  # Returns bool

Get list of connected calendars

from core.models import OAuthCredential

credentials = OAuthCredential.objects.filter(
    tenant=tenant,
    purpose='calendar',
    is_valid=True
)

Handle permission denied

from core.permissions import HasFeaturePermission

permission = HasFeaturePermission('can_use_calendar_sync')
if not permission().has_permission(request, view):
    # User doesn't have permission
    # Show upgrade prompt

Notes

  • Feature defaults to False for all tenants (opt-in)
  • Works alongside existing subscription plan system
  • Follows same pattern as SMS reminders, webhooks, etc.
  • Multi-tenant isolation built-in
  • OAuth tokens are encrypted at rest
  • All operations logged for audit trail