{"repo": "ayyadurai-k/cbaas", "files": {"backend/.postman/generate_postman_collection.py": {"c4b5c25a76fd4a85309d909b68b14c3f855a3bf5": {"t": 1785747196.8501716, "n": 25378, "h": "f3217a1a2df2ff76", "c": "import os\nimport json\nimport re\nfrom datetime import datetime\n\n# Base URL for the API\nBASE_URL = \"http://localhost:8000/api\"\n\n# Postman Collection structure\npostman_collection = {\n \"info\": {\n \"_postman_id\": \"cbaas-api-collection-2025\",\n \"name\": \"CBaaS API Collection\",\n \"description\": \"Complete API collection for CBaaS (Chatbot-as-a-Service) - Multi-tenant RAG SaaS platform\\n\\nGenerated: \" + datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n \"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\",\n \"version\": \"2.0.0\"\n },\n \"item\": [],\n \"variable\": [\n {\n \"key\": \"base_url\",\n \"value\": \"http://localhost:8000/api\",\n \"type\": \"string\"\n },\n {\n \"key\": \"jwt_token\",\n \"value\": \"\",\n \"type\": \"string\"\n },\n {\n \"key\": \"refresh_token\",\n \"value\": \"\",\n \"type\": \"string\"\n }\n ],\n \"event\": [\n {\n \"listen\": \"prerequest\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Auto-set authorization header if jwt_token exists\",\n \"const jwtToken = pm.collectionVariables.get('jwt_token');\",\n \"if (jwtToken && pm.request.headers.has('Authorization')) {\",\n \" pm.request.headers.upsert({key: 'Authorization', value: 'Bearer ' + jwtToken});\",\n \"}\"\n ]\n }\n },\n {\n \"listen\": \"test\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Auto-extract tokens from login response\",\n \"if (pm.response.code === 200 && pm.request.url.path.includes('login')) {\",\n \" const jsonData = pm.response.json();\",\n \" if (jsonData.data && jsonData.data.access) {\",\n \" pm.collectionVariables.set('jwt_token', jsonData.data.access);\",\n \" pm.collectionVariables.set('refresh_token', jsonData.data.refresh);\",\n \" console.log('✓ Tokens saved to collection variables');\",\n \" }\",\n \"}\"\n ]\n }\n }\n ]\n}\n\ndef create_postman_request(name, method, url_path, body=None, headers=None, description=None, is_multipart=False):\n \"\"\"\n Create a Postman request item.\n \n Args:\n name: Display name for the request\n method: HTTP method (GET, POST, PUT, PATCH, DELETE)\n url_path: API endpoint path (will be appended to {{base_url}})\n body: Request body (dict for JSON, or special handling for multipart)\n headers: Dict of headers to include\n description: Detailed description of the endpoint\n is_multipart: If True, use form-data instead of raw JSON\n \"\"\"\n # Ensure url_path starts with /\n if not url_path.startswith('/'):\n url_path = '/' + url_path\n \n # Split path into segments for Postman URL structure\n path_segments = [seg for seg in url_path.split('/') if seg]\n \n request_item = {\n \"name\": name,\n \"request\": {\n \"method\": method,\n \"header\": [],\n \"url\": {\n \"raw\": f\"{{{{base_url}}}}{url_path}\",\n \"host\": [\"{{base_url}}\"],\n \"path\": path_segments,\n },\n \"description\": description or f\"Request for {name}\",\n },\n \"response\": [],\n }\n\n # Add headers\n if headers:\n for key, value in headers.items():\n request_item[\"request\"][\"header\"].append({\n \"key\": key,\n \"value\": value,\n \"type\": \"text\"\n })\n \n # Add default Content-Type for non-multipart requests\n if method in [\"POST\", \"PUT\", \"PATCH\"] and not is_multipart:\n if not any(h.get(\"key\", \"\").lower() == \"content-type\" for h in request_item[\"request\"][\"header\"]):\n request_item[\"request\"][\"header\"].append({\n \"key\": \"Content-Type\",\n \"value\": \"application/json\",\n \"type\": \"text\"\n })\n\n # Add body\n if body:\n if is_multipart:\n # For file uploads\n request_item[\"request\"][\"body\"] = {\n \"mode\": \"formdata\",\n \"formdata\": []\n }\n for key, value in body.items():\n if key == \"file\":\n request_item[\"request\"][\"body\"][\"formdata\"].append({\n \"key\": key,\n \"type\": \"file\",\n \"src\": []\n })\n else:\n request_item[\"request\"][\"body\"][\"formdata\"].append({\n \"key\": key,\n \"value\": value,\n \"type\": \"text\"\n })\n else:\n request_item[\"request\"][\"body\"] = {\n \"mode\": \"raw\",\n \"raw\": json.dumps(body, indent=2),\n \"options\": {\"raw\": {\"language\": \"json\"}},\n }\n \n # Convert Django path parameters to Postman variables\n # e.g., to :pk\n request_item[\"request\"][\"url\"][\"raw\"] = re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", request_item[\"request\"][\"url\"][\"raw\"])\n request_item[\"request\"][\"url\"][\"path\"] = [re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", p) for p in request_item[\"request\"][\"url\"][\"path\"]]\n\n return request_item\n\ndef create_postman_folder(name, items):\n return {\n \"name\": name,\n \"item\": items,\n }\n\n# --- API Endpoints Information ---\n# This dictionary will store the extracted API information\n# Structure: { \"app_name\": { \"folder_name\": [request_items] } }\napi_endpoints = {}\n\n# Helper to add endpoints to the structure\ndef add_endpoint(app_name, folder_name, request_item):\n if app_name not in api_endpoints:\n api_endpoints[app_name] = {}\n if folder_name not in api_endpoints[app_name]:\n api_endpoints[app_name][folder_name] = []\n api_endpoints[app_name][folder_name].append(request_item)\n\n# --- apps/api_keys ---\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"List API Keys\",\n \"GET\",\n \"/keys/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all API keys for the authenticated user's organization.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Create API Key\",\n \"POST\",\n \"/keys/\",\n body={\"name\": \"My New API Key\", \"quota\": 1000, \"scope\": \"FULL\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Create a new API key for programmatic access to the API.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Revoke API Key\",\n \"PATCH\",\n \"/keys//revoke/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Revoke an API key to prevent further use without deleting it.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Delete API Key\",\n \"DELETE\",\n \"/keys//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Permanently delete an API key.\"\n )\n)\n\n# --- apps/auth/signup ---\nadd_endpoint(\n \"auth\",\n \"Auth - Signup\",\n create_postman_request(\n \"Signup User\",\n \"POST\",\n \"/auth/signup/\",\n body={\n \"email\": \"user@example.com\",\n \"password\": \"password123\",\n \"confirm_password\": \"password123\",\n \"name\": \"Test User\",\n \"phone_number\": \"+1234567890\",\n \"organization_name\": \"Test Org\"\n },\n description=\"Create a new user account with organization. Password must be at least 8 characters and match confirm_password.\"\n )\n)\n\n# --- apps/auth/login ---\nadd_endpoint(\n \"auth\",\n \"Auth - Login\",\n create_postman_request(\n \"Login User\",\n \"POST\",\n \"/auth/login/\",\n body={\"email\": \"user@example.com\", \"password\": \"password123\"},\n description=\"Authenticate user and receive JWT access and refresh tokens. Tokens are auto-saved to collection variables.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Login\",\n create_postman_request(\n \"Refresh Token\",\n \"POST\",\n \"/auth/token/refresh/\",\n body={\"refresh\": \"{{refresh_token}}\"},\n description=\"Refresh an expired access token using the refresh token.\"\n )\n)\n\n# --- apps/auth/logout ---\nadd_endpoint(\n \"auth\",\n \"Auth - Logout\",\n create_postman_request(\n \"Logout User\",\n \"POST\",\n \"/auth/logout/\",\n body={\"refresh\": \"{{refresh_token}}\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Logout user and blacklist the refresh token. Requires authentication.\"\n )\n)\n\n# --- apps/auth/reset ---\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Forgot Password\",\n \"POST\",\n \"/auth/forgot-password/\",\n body={\"email\": \"user@example.com\"},\n description=\"Request a password reset token to be sent to the user's email.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Verify Reset Token\",\n \"POST\",\n \"/auth/verify-reset-token/\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\"},\n description=\"Verify that a password reset token is valid before allowing password change.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Reset Password\",\n \"POST\",\n \"/auth/reset-password/\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\", \"new_password\": \"newpassword123\"},\n description=\"Complete password reset with verified token and new password.\"\n )\n)\n\n# --- apps/auth/status ---\nadd_endpoint(\n \"auth\",\n \"Auth - Status\",\n create_postman_request(\n \"Check Auth Status\",\n \"GET\",\n \"/auth/status/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Verify if the current JWT token is valid and get user info.\"\n )\n)\n\n# --- apps/chat ---\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Completions\",\n \"POST\",\n \"/chat/completions\",\n body={\n \"session_id\": None,\n \"messages\": [{\"role\": \"user\", \"content\": \"Hello, how are you?\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6,\n \"filters\": {\"document_ids\": [], \"file_types\": []},\n \"metadata\": {}\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\", \"Idempotency-Key\": \"{{$guid}}\"},\n description=\"Send a chat message and get RAG-enhanced response. Supports document filtering and session management. Max 40 messages, 8000 chars per message, 20000 total chars.\"\n )\n)\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Stream\",\n \"POST\",\n \"/chat/stream\",\n body={\n \"session_id\": None,\n \"messages\": [{\"role\": \"user\", \"content\": \"Tell me a story.\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6,\n \"filters\": {\"document_ids\": []},\n \"metadata\": {}\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Stream chat responses in real-time using Server-Sent Events (SSE).\"\n )\n)\n\n# --- apps/chatbot ---\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Get Chatbot Config\",\n \"GET\",\n \"/chatbot\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the chatbot configuration including LLM provider, model, and system instructions.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Update Chatbot Config\",\n \"PUT\",\n \"/chatbot\",\n body={\n \"name\": \"My Custom Chatbot\",\n \"tone\": \"Friendly\",\n \"system_instructions\": \"Always be helpful.\",\n \"llm_provider\": \"openai\",\n \"llm_model\": \"gpt-3.5-turbo\",\n \"llm_api_key\": \"sk-...\",\n \"llm_system_prompt\": \"You are a helpful assistant.\",\n \"llm_is_active\": True,\n \"documents_connected\": []\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update chatbot configuration. API key is encrypted on save. Use documents_connected to link specific documents.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Test LLM Provider API Key\",\n \"POST\",\n \"/chatbot/test-api-key\",\n body={\n \"api_key\": \"YOUR_PROVIDER_API_KEY\",\n \"provider\": \"openai\",\n \"model_name\": \"gpt-3.5-turbo\"\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Test if an LLM provider API key is valid before saving it to config. Supports: openai, gemini, deepseek.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Send Chatbot Message\",\n \"POST\",\n \"/chatbot/message\",\n body={\n \"message\": \"What services do you offer?\",\n \"session_id\": None\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Send a message directly to the chatbot using the organization's configured LLM and RAG settings.\"\n )\n)\n\n# --- apps/documents ---\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"List Documents\",\n \"GET\",\n \"/documents/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all documents for the authenticated user's organization. Includes metadata and processing status.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Upload Document\",\n \"POST\",\n \"/documents/\",\n body={\"name\": \"My Document\", \"file\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload a document for RAG processing. Supports PDF, DOCX, TXT, etc. File is processed async via Celery (chunking + embeddings).\",\n is_multipart=True\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Get Document Details\",\n \"GET\",\n \"/documents//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get detailed information about a specific document including chunks and processing status.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Update Document\",\n \"PATCH\",\n \"/documents//\",\n body={\"name\": \"Updated Document Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update document metadata (name, etc.). Does not reprocess the file.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Delete Document\",\n \"DELETE\",\n \"/documents//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Permanently delete a document and all associated chunks/embeddings.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Download Document\",\n \"GET\",\n \"/documents//download/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Download the original document file. Returns file from S3 (prod) or local storage (dev).\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Reprocess Document\",\n \"POST\",\n \"/documents//reprocess/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Trigger reprocessing of a document (re-chunk and regenerate embeddings). Useful after embedding model changes.\"\n )\n)\n\n# --- apps/llm_providers ---\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"List LLM Providers\",\n \"GET\",\n \"/providers/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all available LLM providers with their models and capabilities.\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"List Providers (Simple)\",\n \"GET\",\n \"/providers/simple/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get simplified provider list for dropdown/select components (frontend-friendly).\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Provider Models\",\n \"GET\",\n \"/providers//models/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all available models for a specific provider (e.g., 'openai', 'gemini', 'deepseek').\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Provider Config\",\n \"GET\",\n \"/providers/config/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get frontend-compatible provider configuration (matches old hardcoded structure for backwards compatibility).\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Model Details\",\n \"GET\",\n \"/providers//models//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get detailed information about a specific model including context length, capabilities, and pricing.\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Clear Provider Cache\",\n \"POST\",\n \"/providers/cache/clear/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Clear the cached provider data (admin only). Forces refresh from LiteLLM on next request.\"\n )\n)\n\n# --- apps/ops ---\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Health Check\",\n \"GET\",\n \"/healthz\",\n description=\"Basic health check endpoint. Returns 200 if service is running. Used by load balancers.\"\n )\n)\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Readiness Check\",\n \"GET\",\n \"/readyz\",\n description=\"Readiness probe. Checks DB connectivity and critical dependencies. Used by Kubernetes/ECS.\"\n )\n)\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Static Files Debug\",\n \"GET\",\n \"/debug/static\",\n description=\"Debug endpoint to check static files configuration (SERVE_STATIC_FILES, storage backends, etc.).\"\n )\n)\n\n# --- apps/organizations ---\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Get Organization\",\n \"GET\",\n \"/user/organization\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the current user's organization details including name, logo, and settings.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Update Organization\",\n \"PUT\",\n \"/user/organization\",\n body={\"name\": \"My New Org Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update organization information (name, settings). Only organization owners can update.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Update Organization\",\n \"PATCH\",\n \"/user/organization\",\n body={\"name\": \"My New Org Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Partial update of organization information.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Delete Organization\",\n \"DELETE\",\n \"/user/organization\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Delete the organization and all associated data (users, documents, chatbot config). Cannot be undone.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Upload Organization Logo\",\n \"POST\",\n \"/user/organization/logo\",\n body={\"logo\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload or update the organization logo. Stored in S3 (prod) or local media folder (dev).\",\n is_multipart=True\n )\n)\n\n# --- apps/search ---\nadd_endpoint(\n \"search\",\n \"Search\",\n create_postman_request(\n \"Search Documents\",\n \"POST\",\n \"/search\",\n body={\n \"query\": \"What is the capital of France?\",\n \"top_k\": 5,\n \"filters\": {\n \"document_ids\": [],\n \"file_types\": [\"pdf\"]\n }\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Semantic search across document chunks using pgvector. Returns relevant chunks with similarity scores. Max top_k: 50.\"\n )\n)\n\n# --- apps/users ---\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Get User Profile\",\n \"GET\",\n \"/user/profile\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the authenticated user's profile including name, email, phone, role, and organization.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Update User Profile\",\n \"PUT\",\n \"/user/profile\",\n body={\n \"name\": \"Updated Name\",\n \"phone_number\": \"+1234567890\"\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update user profile information. Email cannot be changed.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Update User Profile\",\n \"PATCH\",\n \"/user/profile\",\n body={\"name\": \"Updated Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Partial update of user profile.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Upload Profile Picture\",\n \"POST\",\n \"/user/profile/picture\",\n body={\"picture\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload or update user profile picture. Stored in S3 (prod) or local media folder (dev).\",\n is_multipart=True\n )\n)\n\n# Build the final Postman collection structure\n# Apps with multiple sub-folders get grouped together\ngrouped_apps = [\"auth\"]\n\n# Order for the main collection items (for better UX)\napp_order = [\n \"auth\",\n \"users\", \n \"organizations\",\n \"chatbot\",\n \"llm_providers\",\n \"documents\",\n \"search\",\n \"chat\",\n \"api_keys\",\n \"ops\"\n]\n\n# Build collection in defined order\nfor app_name in app_order:\n if app_name not in api_endpoints:\n continue\n \n folders = api_endpoints[app_name]\n \n # Apps with multiple sub-folders get grouped under a main folder\n if app_name in grouped_apps:\n app_items = []\n for folder_name, requests in folders.items():\n app_items.append(create_postman_folder(folder_name, requests))\n \n # Capitalize app name for display\n display_name = app_name.replace(\"_\", \" \").title()\n postman_collection[\"item\"].append(create_postman_folder(display_name, app_items))\n else:\n # For single-folder apps, add directly to root\n for folder_name, requests in folders.items():\n postman_collection[\"item\"].append(create_postman_folder(folder_name, requests))\n\n# Add any remaining apps not in the predefined order\nfor app_name, folders in api_endpoints.items():\n if app_name not in app_order:\n for folder_name, requests in folders.items():\n postman_collection[\"item\"].append(create_postman_folder(folder_name, requests))\n\n# Save the collection to a JSON file\noutput_file_path = \"postman_collection.json\"\nscript_dir = os.path.dirname(os.path.abspath(__file__))\noutput_path = os.path.join(script_dir, output_file_path)\n\nwith open(output_path, \"w\", encoding='utf-8') as f:\n json.dump(postman_collection, f, indent=2, ensure_ascii=False)\n\nprint(f\"✓ Postman collection generated successfully!\")\nprint(f\" Location: {output_path}\")\nprint(f\" Total endpoints: {sum(len(requests) for folders in api_endpoints.values() for requests in folders.values())}\")\nprint(f\" Apps covered: {len(api_endpoints)}\")\nprint(f\"\\nTo use:\")\nprint(f\" 1. Import '{output_file_path}' into Postman\")\nprint(f\" 2. Set collection variables (base_url, jwt_token, refresh_token)\")\nprint(f\" 3. Run 'Login User' to auto-populate tokens\")\nprint(f\" 4. Other requests will use tokens automatically\")\n"}, "541cf2cb61cc0a237fb1667014a61b61d96c3a4a": {"t": 1785771302.6290789, "n": 25378, "h": "f3217a1a2df2ff76", "c": "import os\nimport json\nimport re\nfrom datetime import datetime\n\n# Base URL for the API\nBASE_URL = \"http://localhost:8000/api\"\n\n# Postman Collection structure\npostman_collection = {\n \"info\": {\n \"_postman_id\": \"cbaas-api-collection-2025\",\n \"name\": \"CBaaS API Collection\",\n \"description\": \"Complete API collection for CBaaS (Chatbot-as-a-Service) - Multi-tenant RAG SaaS platform\\n\\nGenerated: \" + datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n \"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\",\n \"version\": \"2.0.0\"\n },\n \"item\": [],\n \"variable\": [\n {\n \"key\": \"base_url\",\n \"value\": \"http://localhost:8000/api\",\n \"type\": \"string\"\n },\n {\n \"key\": \"jwt_token\",\n \"value\": \"\",\n \"type\": \"string\"\n },\n {\n \"key\": \"refresh_token\",\n \"value\": \"\",\n \"type\": \"string\"\n }\n ],\n \"event\": [\n {\n \"listen\": \"prerequest\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Auto-set authorization header if jwt_token exists\",\n \"const jwtToken = pm.collectionVariables.get('jwt_token');\",\n \"if (jwtToken && pm.request.headers.has('Authorization')) {\",\n \" pm.request.headers.upsert({key: 'Authorization', value: 'Bearer ' + jwtToken});\",\n \"}\"\n ]\n }\n },\n {\n \"listen\": \"test\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Auto-extract tokens from login response\",\n \"if (pm.response.code === 200 && pm.request.url.path.includes('login')) {\",\n \" const jsonData = pm.response.json();\",\n \" if (jsonData.data && jsonData.data.access) {\",\n \" pm.collectionVariables.set('jwt_token', jsonData.data.access);\",\n \" pm.collectionVariables.set('refresh_token', jsonData.data.refresh);\",\n \" console.log('✓ Tokens saved to collection variables');\",\n \" }\",\n \"}\"\n ]\n }\n }\n ]\n}\n\ndef create_postman_request(name, method, url_path, body=None, headers=None, description=None, is_multipart=False):\n \"\"\"\n Create a Postman request item.\n \n Args:\n name: Display name for the request\n method: HTTP method (GET, POST, PUT, PATCH, DELETE)\n url_path: API endpoint path (will be appended to {{base_url}})\n body: Request body (dict for JSON, or special handling for multipart)\n headers: Dict of headers to include\n description: Detailed description of the endpoint\n is_multipart: If True, use form-data instead of raw JSON\n \"\"\"\n # Ensure url_path starts with /\n if not url_path.startswith('/'):\n url_path = '/' + url_path\n \n # Split path into segments for Postman URL structure\n path_segments = [seg for seg in url_path.split('/') if seg]\n \n request_item = {\n \"name\": name,\n \"request\": {\n \"method\": method,\n \"header\": [],\n \"url\": {\n \"raw\": f\"{{{{base_url}}}}{url_path}\",\n \"host\": [\"{{base_url}}\"],\n \"path\": path_segments,\n },\n \"description\": description or f\"Request for {name}\",\n },\n \"response\": [],\n }\n\n # Add headers\n if headers:\n for key, value in headers.items():\n request_item[\"request\"][\"header\"].append({\n \"key\": key,\n \"value\": value,\n \"type\": \"text\"\n })\n \n # Add default Content-Type for non-multipart requests\n if method in [\"POST\", \"PUT\", \"PATCH\"] and not is_multipart:\n if not any(h.get(\"key\", \"\").lower() == \"content-type\" for h in request_item[\"request\"][\"header\"]):\n request_item[\"request\"][\"header\"].append({\n \"key\": \"Content-Type\",\n \"value\": \"application/json\",\n \"type\": \"text\"\n })\n\n # Add body\n if body:\n if is_multipart:\n # For file uploads\n request_item[\"request\"][\"body\"] = {\n \"mode\": \"formdata\",\n \"formdata\": []\n }\n for key, value in body.items():\n if key == \"file\":\n request_item[\"request\"][\"body\"][\"formdata\"].append({\n \"key\": key,\n \"type\": \"file\",\n \"src\": []\n })\n else:\n request_item[\"request\"][\"body\"][\"formdata\"].append({\n \"key\": key,\n \"value\": value,\n \"type\": \"text\"\n })\n else:\n request_item[\"request\"][\"body\"] = {\n \"mode\": \"raw\",\n \"raw\": json.dumps(body, indent=2),\n \"options\": {\"raw\": {\"language\": \"json\"}},\n }\n \n # Convert Django path parameters to Postman variables\n # e.g., to :pk\n request_item[\"request\"][\"url\"][\"raw\"] = re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", request_item[\"request\"][\"url\"][\"raw\"])\n request_item[\"request\"][\"url\"][\"path\"] = [re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", p) for p in request_item[\"request\"][\"url\"][\"path\"]]\n\n return request_item\n\ndef create_postman_folder(name, items):\n return {\n \"name\": name,\n \"item\": items,\n }\n\n# --- API Endpoints Information ---\n# This dictionary will store the extracted API information\n# Structure: { \"app_name\": { \"folder_name\": [request_items] } }\napi_endpoints = {}\n\n# Helper to add endpoints to the structure\ndef add_endpoint(app_name, folder_name, request_item):\n if app_name not in api_endpoints:\n api_endpoints[app_name] = {}\n if folder_name not in api_endpoints[app_name]:\n api_endpoints[app_name][folder_name] = []\n api_endpoints[app_name][folder_name].append(request_item)\n\n# --- apps/api_keys ---\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"List API Keys\",\n \"GET\",\n \"/keys/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all API keys for the authenticated user's organization.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Create API Key\",\n \"POST\",\n \"/keys/\",\n body={\"name\": \"My New API Key\", \"quota\": 1000, \"scope\": \"FULL\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Create a new API key for programmatic access to the API.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Revoke API Key\",\n \"PATCH\",\n \"/keys//revoke/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Revoke an API key to prevent further use without deleting it.\"\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Delete API Key\",\n \"DELETE\",\n \"/keys//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Permanently delete an API key.\"\n )\n)\n\n# --- apps/auth/signup ---\nadd_endpoint(\n \"auth\",\n \"Auth - Signup\",\n create_postman_request(\n \"Signup User\",\n \"POST\",\n \"/auth/signup/\",\n body={\n \"email\": \"user@example.com\",\n \"password\": \"password123\",\n \"confirm_password\": \"password123\",\n \"name\": \"Test User\",\n \"phone_number\": \"+1234567890\",\n \"organization_name\": \"Test Org\"\n },\n description=\"Create a new user account with organization. Password must be at least 8 characters and match confirm_password.\"\n )\n)\n\n# --- apps/auth/login ---\nadd_endpoint(\n \"auth\",\n \"Auth - Login\",\n create_postman_request(\n \"Login User\",\n \"POST\",\n \"/auth/login/\",\n body={\"email\": \"user@example.com\", \"password\": \"password123\"},\n description=\"Authenticate user and receive JWT access and refresh tokens. Tokens are auto-saved to collection variables.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Login\",\n create_postman_request(\n \"Refresh Token\",\n \"POST\",\n \"/auth/token/refresh/\",\n body={\"refresh\": \"{{refresh_token}}\"},\n description=\"Refresh an expired access token using the refresh token.\"\n )\n)\n\n# --- apps/auth/logout ---\nadd_endpoint(\n \"auth\",\n \"Auth - Logout\",\n create_postman_request(\n \"Logout User\",\n \"POST\",\n \"/auth/logout/\",\n body={\"refresh\": \"{{refresh_token}}\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Logout user and blacklist the refresh token. Requires authentication.\"\n )\n)\n\n# --- apps/auth/reset ---\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Forgot Password\",\n \"POST\",\n \"/auth/forgot-password/\",\n body={\"email\": \"user@example.com\"},\n description=\"Request a password reset token to be sent to the user's email.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Verify Reset Token\",\n \"POST\",\n \"/auth/verify-reset-token/\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\"},\n description=\"Verify that a password reset token is valid before allowing password change.\"\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Reset Password\",\n \"POST\",\n \"/auth/reset-password/\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\", \"new_password\": \"newpassword123\"},\n description=\"Complete password reset with verified token and new password.\"\n )\n)\n\n# --- apps/auth/status ---\nadd_endpoint(\n \"auth\",\n \"Auth - Status\",\n create_postman_request(\n \"Check Auth Status\",\n \"GET\",\n \"/auth/status/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Verify if the current JWT token is valid and get user info.\"\n )\n)\n\n# --- apps/chat ---\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Completions\",\n \"POST\",\n \"/chat/completions\",\n body={\n \"session_id\": None,\n \"messages\": [{\"role\": \"user\", \"content\": \"Hello, how are you?\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6,\n \"filters\": {\"document_ids\": [], \"file_types\": []},\n \"metadata\": {}\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\", \"Idempotency-Key\": \"{{$guid}}\"},\n description=\"Send a chat message and get RAG-enhanced response. Supports document filtering and session management. Max 40 messages, 8000 chars per message, 20000 total chars.\"\n )\n)\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Stream\",\n \"POST\",\n \"/chat/stream\",\n body={\n \"session_id\": None,\n \"messages\": [{\"role\": \"user\", \"content\": \"Tell me a story.\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6,\n \"filters\": {\"document_ids\": []},\n \"metadata\": {}\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Stream chat responses in real-time using Server-Sent Events (SSE).\"\n )\n)\n\n# --- apps/chatbot ---\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Get Chatbot Config\",\n \"GET\",\n \"/chatbot\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the chatbot configuration including LLM provider, model, and system instructions.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Update Chatbot Config\",\n \"PUT\",\n \"/chatbot\",\n body={\n \"name\": \"My Custom Chatbot\",\n \"tone\": \"Friendly\",\n \"system_instructions\": \"Always be helpful.\",\n \"llm_provider\": \"openai\",\n \"llm_model\": \"gpt-3.5-turbo\",\n \"llm_api_key\": \"sk-...\",\n \"llm_system_prompt\": \"You are a helpful assistant.\",\n \"llm_is_active\": True,\n \"documents_connected\": []\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update chatbot configuration. API key is encrypted on save. Use documents_connected to link specific documents.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Test LLM Provider API Key\",\n \"POST\",\n \"/chatbot/test-api-key\",\n body={\n \"api_key\": \"YOUR_PROVIDER_API_KEY\",\n \"provider\": \"openai\",\n \"model_name\": \"gpt-3.5-turbo\"\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Test if an LLM provider API key is valid before saving it to config. Supports: openai, gemini, deepseek.\"\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot Configuration\",\n create_postman_request(\n \"Send Chatbot Message\",\n \"POST\",\n \"/chatbot/message\",\n body={\n \"message\": \"What services do you offer?\",\n \"session_id\": None\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Send a message directly to the chatbot using the organization's configured LLM and RAG settings.\"\n )\n)\n\n# --- apps/documents ---\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"List Documents\",\n \"GET\",\n \"/documents/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all documents for the authenticated user's organization. Includes metadata and processing status.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Upload Document\",\n \"POST\",\n \"/documents/\",\n body={\"name\": \"My Document\", \"file\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload a document for RAG processing. Supports PDF, DOCX, TXT, etc. File is processed async via Celery (chunking + embeddings).\",\n is_multipart=True\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Get Document Details\",\n \"GET\",\n \"/documents//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get detailed information about a specific document including chunks and processing status.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Update Document\",\n \"PATCH\",\n \"/documents//\",\n body={\"name\": \"Updated Document Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update document metadata (name, etc.). Does not reprocess the file.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Delete Document\",\n \"DELETE\",\n \"/documents//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Permanently delete a document and all associated chunks/embeddings.\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Download Document\",\n \"GET\",\n \"/documents//download/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Download the original document file. Returns file from S3 (prod) or local storage (dev).\"\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Reprocess Document\",\n \"POST\",\n \"/documents//reprocess/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Trigger reprocessing of a document (re-chunk and regenerate embeddings). Useful after embedding model changes.\"\n )\n)\n\n# --- apps/llm_providers ---\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"List LLM Providers\",\n \"GET\",\n \"/providers/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all available LLM providers with their models and capabilities.\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"List Providers (Simple)\",\n \"GET\",\n \"/providers/simple/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get simplified provider list for dropdown/select components (frontend-friendly).\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Provider Models\",\n \"GET\",\n \"/providers//models/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get all available models for a specific provider (e.g., 'openai', 'gemini', 'deepseek').\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Provider Config\",\n \"GET\",\n \"/providers/config/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get frontend-compatible provider configuration (matches old hardcoded structure for backwards compatibility).\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Get Model Details\",\n \"GET\",\n \"/providers//models//\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get detailed information about a specific model including context length, capabilities, and pricing.\"\n )\n)\nadd_endpoint(\n \"llm_providers\",\n \"LLM Providers\",\n create_postman_request(\n \"Clear Provider Cache\",\n \"POST\",\n \"/providers/cache/clear/\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Clear the cached provider data (admin only). Forces refresh from LiteLLM on next request.\"\n )\n)\n\n# --- apps/ops ---\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Health Check\",\n \"GET\",\n \"/healthz\",\n description=\"Basic health check endpoint. Returns 200 if service is running. Used by load balancers.\"\n )\n)\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Readiness Check\",\n \"GET\",\n \"/readyz\",\n description=\"Readiness probe. Checks DB connectivity and critical dependencies. Used by Kubernetes/ECS.\"\n )\n)\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Static Files Debug\",\n \"GET\",\n \"/debug/static\",\n description=\"Debug endpoint to check static files configuration (SERVE_STATIC_FILES, storage backends, etc.).\"\n )\n)\n\n# --- apps/organizations ---\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Get Organization\",\n \"GET\",\n \"/user/organization\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the current user's organization details including name, logo, and settings.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Update Organization\",\n \"PUT\",\n \"/user/organization\",\n body={\"name\": \"My New Org Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update organization information (name, settings). Only organization owners can update.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Update Organization\",\n \"PATCH\",\n \"/user/organization\",\n body={\"name\": \"My New Org Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Partial update of organization information.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Delete Organization\",\n \"DELETE\",\n \"/user/organization\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Delete the organization and all associated data (users, documents, chatbot config). Cannot be undone.\"\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Upload Organization Logo\",\n \"POST\",\n \"/user/organization/logo\",\n body={\"logo\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload or update the organization logo. Stored in S3 (prod) or local media folder (dev).\",\n is_multipart=True\n )\n)\n\n# --- apps/search ---\nadd_endpoint(\n \"search\",\n \"Search\",\n create_postman_request(\n \"Search Documents\",\n \"POST\",\n \"/search\",\n body={\n \"query\": \"What is the capital of France?\",\n \"top_k\": 5,\n \"filters\": {\n \"document_ids\": [],\n \"file_types\": [\"pdf\"]\n }\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Semantic search across document chunks using pgvector. Returns relevant chunks with similarity scores. Max top_k: 50.\"\n )\n)\n\n# --- apps/users ---\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Get User Profile\",\n \"GET\",\n \"/user/profile\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Get the authenticated user's profile including name, email, phone, role, and organization.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Update User Profile\",\n \"PUT\",\n \"/user/profile\",\n body={\n \"name\": \"Updated Name\",\n \"phone_number\": \"+1234567890\"\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Update user profile information. Email cannot be changed.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Update User Profile\",\n \"PATCH\",\n \"/user/profile\",\n body={\"name\": \"Updated Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Partial update of user profile.\"\n )\n)\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Upload Profile Picture\",\n \"POST\",\n \"/user/profile/picture\",\n body={\"picture\": None},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"},\n description=\"Upload or update user profile picture. Stored in S3 (prod) or local media folder (dev).\",\n is_multipart=True\n )\n)\n\n# Build the final Postman collection structure\n# Apps with multiple sub-folders get grouped together\ngrouped_apps = [\"auth\"]\n\n# Order for the main collection items (for better UX)\napp_order = [\n \"auth\",\n \"users\", \n \"organizations\",\n \"chatbot\",\n \"llm_providers\",\n \"documents\",\n \"search\",\n \"chat\",\n \"api_keys\",\n \"ops\"\n]\n\n# Build collection in defined order\nfor app_name in app_order:\n if app_name not in api_endpoints:\n continue\n \n folders = api_endpoints[app_name]\n \n # Apps with multiple sub-folders get grouped under a main folder\n if app_name in grouped_apps:\n app_items = []\n for folder_name, requests in folders.items():\n app_items.append(create_postman_folder(folder_name, requests))\n \n # Capitalize app name for display\n display_name = app_name.replace(\"_\", \" \").title()\n postman_collection[\"item\"].append(create_postman_folder(display_name, app_items))\n else:\n # For single-folder apps, add directly to root\n for folder_name, requests in folders.items():\n postman_collection[\"item\"].append(create_postman_folder(folder_name, requests))\n\n# Add any remaining apps not in the predefined order\nfor app_name, folders in api_endpoints.items():\n if app_name not in app_order:\n for folder_name, requests in folders.items():\n postman_collection[\"item\"].append(create_postman_folder(folder_name, requests))\n\n# Save the collection to a JSON file\noutput_file_path = \"postman_collection.json\"\nscript_dir = os.path.dirname(os.path.abspath(__file__))\noutput_path = os.path.join(script_dir, output_file_path)\n\nwith open(output_path, \"w\", encoding='utf-8') as f:\n json.dump(postman_collection, f, indent=2, ensure_ascii=False)\n\nprint(f\"✓ Postman collection generated successfully!\")\nprint(f\" Location: {output_path}\")\nprint(f\" Total endpoints: {sum(len(requests) for folders in api_endpoints.values() for requests in folders.values())}\")\nprint(f\" Apps covered: {len(api_endpoints)}\")\nprint(f\"\\nTo use:\")\nprint(f\" 1. Import '{output_file_path}' into Postman\")\nprint(f\" 2. Set collection variables (base_url, jwt_token, refresh_token)\")\nprint(f\" 3. Run 'Login User' to auto-populate tokens\")\nprint(f\" 4. Other requests will use tokens automatically\")\n"}, "fecc86c9f0d3cf37f3af4777ae1ed14f8d9db393": {"t": 1785771303.9361844, "n": 12029, "h": "73d387eccf5eab75", "c": "import os\nimport json\nimport re\n\n# Base URL for the API\nBASE_URL = \"http://localhost:8000/api\"\n\n# Postman Collection structure\npostman_collection = {\n \"info\": {\n \"_postman_id\": \"YOUR_POSTMAN_COLLECTION_ID\",\n \"name\": \"CBaaS API Collection\",\n \"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\",\n },\n \"item\": [],\n \"event\": [\n {\n \"listen\": \"prerequest\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Add your pre-request script here, e.g., for authentication\",\n \"// pm.environment.set(\\\"jwt_token\\\", \\\"YOUR_JWT_TOKEN\\\");\"\n ]\n }\n },\n {\n \"listen\": \"test\",\n \"script\": {\n \"type\": \"text/javascript\",\n \"exec\": [\n \"// Add your test script here\"\n ]\n }\n }\n ]\n}\n\ndef create_postman_request(name, method, url_path, body=None, headers=None):\n request_item = {\n \"name\": name,\n \"request\": {\n \"method\": method,\n \"header\": [],\n \"url\": {\n \"raw\": f\"{{{{base_url}}}}{url_path}\",\n \"host\": [\"{{base_url}}\"],\n \"path\": url_path.split('/'),\n },\n \"description\": f\"Request for {name}\",\n },\n \"response\": [],\n }\n\n if headers:\n for key, value in headers.items():\n request_item[\"request\"][\"header\"].append({\"key\": key, \"value\": value})\n \n # Add default Content-Type for POST/PUT/PATCH if a body is present and not explicitly set\n if method in [\"POST\", \"PUT\", \"PATCH\"] and body and not any(h.get(\"key\", \"\").lower() == \"content-type\" for h in request_item[\"request\"][\"header\"]):\n request_item[\"request\"][\"header\"].append({\"key\": \"Content-Type\", \"value\": \"application/json\"})\n\n if body:\n request_item[\"request\"][\"body\"] = {\n \"mode\": \"raw\",\n \"raw\": json.dumps(body, indent=2),\n \"options\": {\"raw\": {\"language\": \"json\"}},\n }\n \n # Convert Django path parameters to Postman variables\n # e.g., to :pk\n request_item[\"request\"][\"url\"][\"raw\"] = re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", request_item[\"request\"][\"url\"][\"raw\"])\n request_item[\"request\"][\"url\"][\"path\"] = [re.sub(r\"<(\\w+):(\\w+)>\", r\":\\2\", p) for p in request_item[\"request\"][\"url\"][\"path\"]]\n\n return request_item\n\ndef create_postman_folder(name, items):\n return {\n \"name\": name,\n \"item\": items,\n }\n\n# --- API Endpoints Information ---\n# This dictionary will store the extracted API information\n# Structure: { \"app_name\": { \"folder_name\": [request_items] } }\napi_endpoints = {}\n\n# Helper to add endpoints to the structure\ndef add_endpoint(app_name, folder_name, request_item):\n if app_name not in api_endpoints:\n api_endpoints[app_name] = {}\n if folder_name not in api_endpoints[app_name]:\n api_endpoints[app_name][folder_name] = []\n api_endpoints[app_name][folder_name].append(request_item)\n\n# --- apps/api_keys ---\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"List API Keys\",\n \"GET\",\n \"/keys\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Create API Key\",\n \"POST\",\n \"/keys\",\n body={\"name\": \"My New API Key\", \"quota\": 1000, \"scope\": \"FULL\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Revoke API Key\",\n \"PATCH\",\n \"/keys/:pk/revoke\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"api_keys\",\n \"API Keys\",\n create_postman_request(\n \"Delete API Key\",\n \"DELETE\",\n \"/keys/:pk\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/auth/signup ---\nadd_endpoint(\n \"auth\",\n \"Auth - Signup\",\n create_postman_request(\n \"Signup User\",\n \"POST\",\n \"/auth/signup\",\n body={\"email\": \"user@example.com\", \"password\": \"password123\", \"name\": \"Test User\", \"organization_name\": \"Test Org\"},\n headers={\"Content-Type\": \"application/json\"}\n )\n)\n\n# --- apps/auth/login ---\nadd_endpoint(\n \"auth\",\n \"Auth - Login\",\n create_postman_request(\n \"Login User\",\n \"POST\",\n \"/auth/login\",\n body={\"email\": \"user@example.com\", \"password\": \"password123\"},\n headers={\"Content-Type\": \"application/json\"}\n )\n)\n\n# --- apps/auth/logout ---\nadd_endpoint(\n \"auth\",\n \"Auth - Logout\",\n create_postman_request(\n \"Logout User\",\n \"POST\",\n \"/auth/logout\",\n body={\"refresh\": \"{{refresh_token}}\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\", \"Content-Type\": \"application/json\"}\n )\n)\n\n# --- apps/auth/reset ---\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Forgot Password\",\n \"POST\",\n \"/auth/forgot-password\",\n body={\"email\": \"user@example.com\"},\n headers={\"Content-Type\": \"application/json\"}\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Verify Reset Token\",\n \"POST\",\n \"/auth/verify-reset-token\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\"},\n headers={\"Content-Type\": \"application/json\"}\n )\n)\nadd_endpoint(\n \"auth\",\n \"Auth - Reset Password\",\n create_postman_request(\n \"Reset Password\",\n \"POST\",\n \"/auth/reset-password\",\n body={\"email\": \"user@example.com\", \"token\": \"YOUR_RESET_TOKEN\", \"new_password\": \"newpassword123\"},\n headers={\"Content-Type\": \"application/json\"}\n )\n)\n\n# --- apps/chat ---\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Completions\",\n \"POST\",\n \"/chat/completions\",\n body={\n \"messages\": [{\"role\": \"user\", \"content\": \"Hello, how are you?\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\", \"Idempotency-Key\": \"{{$guid}}\"}\n )\n)\nadd_endpoint(\n \"chat\",\n \"Chat\",\n create_postman_request(\n \"Chat Stream\",\n \"POST\",\n \"/chat/stream\",\n body={\n \"messages\": [{\"role\": \"user\", \"content\": \"Tell me a story.\"}],\n \"max_tokens\": 512,\n \"temperature\": 0.2,\n \"top_k\": 6\n },\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/chatbot ---\nadd_endpoint(\n \"chatbot\",\n \"Chatbot\",\n create_postman_request(\n \"Get Chatbot Config\",\n \"GET\",\n \"/chatbot\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"chatbot\",\n \"Chatbot\",\n create_postman_request(\n \"Update Chatbot Config\",\n \"PUT\",\n \"/chatbot\",\n body={\"name\": \"My Custom Chatbot\", \"tone\": \"Friendly\", \"system_instructions\": \"Always be helpful.\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/chatbot_provider ---\nadd_endpoint(\n \"chatbot_provider\",\n \"Chatbot Provider\",\n create_postman_request(\n \"Test Chatbot Provider Key\",\n \"POST\",\n \"/chatbot/test-key\",\n body={\"api_key\": \"YOUR_PROVIDER_API_KEY\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"chatbot_provider\",\n \"Chatbot Provider\",\n create_postman_request(\n \"Upsert Chatbot Provider\",\n \"PUT\",\n \"/chatbot/provider\",\n body={\"provider\": \"openai\", \"model_name\": \"gpt-3.5-turbo\", \"api_key\": \"YOUR_PROVIDER_API_KEY\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/documents ---\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"List Documents\",\n \"GET\",\n \"/documents\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Upload Document\",\n \"POST\",\n \"/documents\",\n body={\"name\": \"My Document\", \"file\": \"SELECT_FILE_FROM_DISK\"}, # Postman will handle file upload via form-data\n headers={\"Authorization\": \"Bearer {{jwt_token}}\", \"Content-Type\": \"multipart/form-data\"} # Note: Postman handles multipart/form-data automatically when 'file' is present\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Get Document Details\",\n \"GET\",\n \"/documents/:pk\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Update Document\",\n \"PATCH\",\n \"/documents/:pk\",\n body={\"name\": \"Updated Document Name\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Delete Document\",\n \"DELETE\",\n \"/documents/:pk\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"documents\",\n \"Documents\",\n create_postman_request(\n \"Reprocess Document\",\n \"POST\",\n \"/documents/:pk/reprocess\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/ops ---\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Health Check\",\n \"GET\",\n \"/healthz\"\n )\n)\nadd_endpoint(\n \"ops\",\n \"Operational Endpoints\",\n create_postman_request(\n \"Readiness Check\",\n \"GET\",\n \"/readyz\"\n )\n)\n\n# --- apps/organizations ---\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Update Organization\",\n \"PUT\",\n \"/user/organization\",\n body={\"name\": \"My New Org Name\", \"logo_url\": \"http://example.com/logo.png\"},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\nadd_endpoint(\n \"organizations\",\n \"Organizations\",\n create_postman_request(\n \"Delete Organization\",\n \"DELETE\",\n \"/user/organization\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/search ---\nadd_endpoint(\n \"search\",\n \"Search\",\n create_postman_request(\n \"Search Documents\",\n \"POST\",\n \"/search\",\n body={\"query\": \"What is the capital of France?\", \"top_k\": 5, \"filters\": {\"document_ids\": [], \"file_types\": [\"pdf\"]}},\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# --- apps/users ---\nadd_endpoint(\n \"users\",\n \"Users\",\n create_postman_request(\n \"Get User Profile\",\n \"GET\",\n \"/user/profile\",\n headers={\"Authorization\": \"Bearer {{jwt_token}}\"}\n )\n)\n\n# Build the final Postman collection structure\nfor app_name, folders in api_endpoints.items():\n app_items = []\n for folder_name, requests in folders.items():\n app_items.append(create_postman_folder(folder_name, requests))\n \n # If an app has sub-folders (like auth), create a top-level folder for it\n if app_name == \"auth\":\n postman_collection[\"item\"].append(create_postman_folder(\"Auth\", app_items))\n else:\n # For other apps, just add their items directly or in a single folder if needed\n # For simplicity, I'm adding them directly to the root if they don't have sub-folders\n # or if they are already grouped by folder_name\n for folder_name, requests in folders.items():\n postman_collection[\"item\"].append(create_postman_folder(folder_name, requests))\n\n\n# Save the collection to a JSON file\noutput_file_path = \"postman_collection.json\"\nwith open(output_file_path, \"w\") as f:\n json.dump(postman_collection, f, indent=2)\n\nprint(f\"Postman collection saved to {output_file_path}\")\n"}}}}