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>
This commit is contained in:
70
test_export_api.py
Normal file
70
test_export_api.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Test script for Data Export API
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Base URL for API
|
||||
BASE_URL = "http://lvh.me:8000"
|
||||
|
||||
def test_export_endpoints():
|
||||
"""Test all export endpoints"""
|
||||
|
||||
print("Testing Data Export API Endpoints")
|
||||
print("=" * 60)
|
||||
|
||||
# Test endpoints
|
||||
endpoints = [
|
||||
('appointments', 'format=json'),
|
||||
('appointments', 'format=csv'),
|
||||
('appointments', 'format=json&start_date=2024-01-01T00:00:00Z&end_date=2024-12-31T23:59:59Z'),
|
||||
('customers', 'format=json'),
|
||||
('customers', 'format=csv'),
|
||||
('resources', 'format=json'),
|
||||
('resources', 'format=csv'),
|
||||
('services', 'format=json'),
|
||||
('services', 'format=csv'),
|
||||
]
|
||||
|
||||
for endpoint, params in endpoints:
|
||||
url = f"{BASE_URL}/export/{endpoint}/?{params}"
|
||||
print(f"\nTesting: GET {url}")
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
print(f"Status Code: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
# Check Content-Type
|
||||
content_type = response.headers.get('Content-Type', '')
|
||||
print(f"Content-Type: {content_type}")
|
||||
|
||||
# Check Content-Disposition
|
||||
content_disp = response.headers.get('Content-Disposition', '')
|
||||
print(f"Content-Disposition: {content_disp}")
|
||||
|
||||
# Show response preview
|
||||
if 'json' in content_type:
|
||||
try:
|
||||
data = response.json()
|
||||
print(f"Response preview: {json.dumps(data, indent=2)[:200]}...")
|
||||
except:
|
||||
print(f"Response: {response.text[:200]}...")
|
||||
elif 'csv' in content_type:
|
||||
print(f"CSV preview: {response.text[:200]}...")
|
||||
else:
|
||||
print(f"Response: {response.text[:200]}...")
|
||||
elif response.status_code == 403:
|
||||
print(f"Permission denied: {response.text}")
|
||||
else:
|
||||
print(f"Error: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Exception: {e}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Test complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_export_endpoints()
|
||||
Reference in New Issue
Block a user