Spaces:
Sleeping
Sleeping
| """ | |
| Sentry API simulator. | |
| Provides realistic mock responses for Sentry API actions. | |
| """ | |
| from typing import Optional | |
| from .base import BaseSimulator | |
| from datetime import datetime | |
| import time | |
| import random | |
| class SentrySimulator(BaseSimulator): | |
| """Simulator for Sentry API.""" | |
| def __init__(self): | |
| super().__init__('sentry') | |
| def load_mock_responses(self): | |
| """Load Sentry mock response templates.""" | |
| self.mock_responses = { | |
| 'get_issues': self._get_issues_template, | |
| 'update_issue': self._update_issue_template, | |
| 'get_events': self._get_events_template, | |
| 'get_issue_details': self._get_issue_details_template, | |
| 'resolve_issue': self._resolve_issue_template, | |
| 'get_projects': self._get_projects_template, | |
| } | |
| def get_required_permissions(self, action: str) -> set[str]: | |
| """Get required Sentry permissions for an action.""" | |
| permissions_map = { | |
| 'get_issues': {'project:read'}, | |
| 'update_issue': {'project:write'}, | |
| 'get_events': {'event:read'}, | |
| 'get_issue_details': {'project:read'}, | |
| 'resolve_issue': {'project:write'}, | |
| 'get_projects': {'project:read'}, | |
| } | |
| return permissions_map.get(action, set()) | |
| def validate_params(self, action: str, params: dict) -> tuple[bool, Optional[str]]: | |
| """Validate parameters for Sentry actions.""" | |
| if action == 'get_issues': | |
| required = {'project_id'} | |
| missing = required - set(params.keys()) | |
| if missing: | |
| return False, f"Missing required parameters: {missing}" | |
| elif action == 'update_issue': | |
| required = {'issue_id'} | |
| missing = required - set(params.keys()) | |
| if missing: | |
| return False, f"Missing required parameters: {missing}" | |
| elif action == 'get_events': | |
| required = {'issue_id'} | |
| missing = required - set(params.keys()) | |
| if missing: | |
| return False, f"Missing required parameters: {missing}" | |
| elif action == 'get_issue_details': | |
| required = {'issue_id'} | |
| missing = required - set(params.keys()) | |
| if missing: | |
| return False, f"Missing required parameters: {missing}" | |
| elif action == 'resolve_issue': | |
| required = {'issue_id'} | |
| missing = required - set(params.keys()) | |
| if missing: | |
| return False, f"Missing required parameters: {missing}" | |
| elif action == 'get_projects': | |
| # No required params | |
| pass | |
| else: | |
| return False, f"Unknown action: {action}" | |
| return True, None | |
| def generate_mock_response(self, action: str, params: dict) -> dict: | |
| """Generate realistic Sentry API response.""" | |
| if action not in self.mock_responses: | |
| raise ValueError(f"Unknown action: {action}") | |
| template_func = self.mock_responses[action] | |
| return template_func(params) | |
| def _get_issues_template(self, params: dict) -> list: | |
| """Mock response for getting project issues.""" | |
| issues = [] | |
| # Generate a few mock issues | |
| error_types = [ | |
| ("TypeError", "Cannot read property 'map' of undefined"), | |
| ("ReferenceError", "userId is not defined"), | |
| ("NetworkError", "Failed to fetch data from API"), | |
| ] | |
| for i, (error_type, message) in enumerate(error_types): | |
| issue_id = f"issue-{1000+i}" | |
| issues.append({ | |
| "id": issue_id, | |
| "shareId": None, | |
| "shortId": f"PROJECT-{i+1}", | |
| "title": f"{error_type}: {message}", | |
| "culprit": f"app/components/UserList.tsx in <UserList>", | |
| "permalink": f"https://sentry.io/organizations/myorg/issues/{issue_id}/", | |
| "logger": None, | |
| "level": "error", | |
| "status": "unresolved" if i < 2 else "resolved", | |
| "statusDetails": {}, | |
| "isPublic": False, | |
| "platform": "javascript", | |
| "project": { | |
| "id": params['project_id'], | |
| "name": "my-app", | |
| "slug": "my-app", | |
| "platform": "javascript" | |
| }, | |
| "type": "error", | |
| "metadata": { | |
| "type": error_type, | |
| "value": message, | |
| "filename": "app/components/UserList.tsx", | |
| "function": "<UserList>" | |
| }, | |
| "numComments": 0, | |
| "assignedTo": None, | |
| "isBookmarked": False, | |
| "isSubscribed": False, | |
| "subscriptionDetails": None, | |
| "hasSeen": True, | |
| "annotations": [], | |
| "isUnhandled": True, | |
| "count": str(random.randint(5, 100)), | |
| "userCount": random.randint(2, 20), | |
| "firstSeen": f"2025-11-{20+i}T10:00:00.000000Z", | |
| "lastSeen": datetime.utcnow().isoformat() + "Z", | |
| "stats": { | |
| "24h": [[int(time.time()) - 86400, random.randint(1, 10)] for _ in range(24)] | |
| } | |
| }) | |
| return issues | |
| def _update_issue_template(self, params: dict) -> dict: | |
| """Mock response for updating an issue.""" | |
| issue_id = params['issue_id'] | |
| return { | |
| "id": issue_id, | |
| "status": params.get('status', 'resolved'), | |
| "statusDetails": {}, | |
| "assignedTo": { | |
| "id": params.get('assignedTo'), | |
| "name": "Team Member", | |
| "email": "member@company.com" | |
| } if params.get('assignedTo') else None, | |
| "hasSeen": True, | |
| "isBookmarked": params.get('isBookmarked', False), | |
| "isSubscribed": params.get('isSubscribed', True) | |
| } | |
| def _get_events_template(self, params: dict) -> list: | |
| """Mock response for getting events for an issue.""" | |
| events = [] | |
| # Generate a few mock events | |
| for i in range(5): | |
| event_id = f"event-{int(time.time())}-{i}" | |
| events.append({ | |
| "id": event_id, | |
| "groupID": params['issue_id'], | |
| "eventID": event_id, | |
| "projectID": "123456", | |
| "size": 12345, | |
| "platform": "javascript", | |
| "message": "TypeError: Cannot read property 'map' of undefined", | |
| "datetime": f"2025-11-30T{10+i}:00:00.000000Z", | |
| "tags": [ | |
| {"key": "browser", "value": "Chrome 119.0.0"}, | |
| {"key": "environment", "value": "production"}, | |
| {"key": "level", "value": "error"}, | |
| {"key": "url", "value": "https://myapp.com/users"} | |
| ], | |
| "context": { | |
| "browser": { | |
| "name": "Chrome", | |
| "version": "119.0.0" | |
| }, | |
| "os": { | |
| "name": "Mac OS X", | |
| "version": "10.15.7" | |
| } | |
| }, | |
| "user": { | |
| "id": f"user-{i}", | |
| "email": f"user{i}@example.com", | |
| "ip_address": f"192.168.1.{i+1}" | |
| }, | |
| "entries": [ | |
| { | |
| "type": "exception", | |
| "data": { | |
| "values": [ | |
| { | |
| "type": "TypeError", | |
| "value": "Cannot read property 'map' of undefined", | |
| "stacktrace": { | |
| "frames": [ | |
| { | |
| "filename": "app/components/UserList.tsx", | |
| "function": "<UserList>", | |
| "lineno": 42, | |
| "colno": 15, | |
| "context_line": " const userNames = users.map(u => u.name);", | |
| "in_app": True | |
| } | |
| ] | |
| } | |
| } | |
| ] | |
| } | |
| } | |
| ] | |
| }) | |
| return events | |
| def _get_issue_details_template(self, params: dict) -> dict: | |
| """Mock response for getting detailed issue information.""" | |
| issue_id = params['issue_id'] | |
| return { | |
| "id": issue_id, | |
| "shareId": None, | |
| "shortId": "PROJECT-1", | |
| "title": "TypeError: Cannot read property 'map' of undefined", | |
| "culprit": "app/components/UserList.tsx in <UserList>", | |
| "permalink": f"https://sentry.io/organizations/myorg/issues/{issue_id}/", | |
| "logger": None, | |
| "level": "error", | |
| "status": "unresolved", | |
| "statusDetails": {}, | |
| "isPublic": False, | |
| "platform": "javascript", | |
| "project": { | |
| "id": "123456", | |
| "name": "my-app", | |
| "slug": "my-app", | |
| "platform": "javascript" | |
| }, | |
| "type": "error", | |
| "metadata": { | |
| "type": "TypeError", | |
| "value": "Cannot read property 'map' of undefined", | |
| "filename": "app/components/UserList.tsx", | |
| "function": "<UserList>" | |
| }, | |
| "numComments": 2, | |
| "assignedTo": { | |
| "id": "user-1", | |
| "name": "Jane Developer", | |
| "email": "jane@company.com" | |
| }, | |
| "isBookmarked": False, | |
| "isSubscribed": True, | |
| "subscriptionDetails": { | |
| "reason": "committed" | |
| }, | |
| "hasSeen": True, | |
| "annotations": [], | |
| "isUnhandled": True, | |
| "count": "47", | |
| "userCount": 12, | |
| "firstSeen": "2025-11-20T10:00:00.000000Z", | |
| "lastSeen": datetime.utcnow().isoformat() + "Z", | |
| "stats": { | |
| "24h": [[int(time.time()) - 86400 + (i * 3600), random.randint(1, 5)] for i in range(24)] | |
| }, | |
| "activity": [ | |
| { | |
| "type": "note", | |
| "user": { | |
| "name": "John Developer", | |
| "email": "john@company.com" | |
| }, | |
| "dateCreated": "2025-11-29T14:00:00.000000Z", | |
| "data": { | |
| "text": "Looking into this issue now. Appears to be related to missing user data." | |
| } | |
| } | |
| ] | |
| } | |
| def _resolve_issue_template(self, params: dict) -> dict: | |
| """Mock response for resolving an issue.""" | |
| issue_id = params['issue_id'] | |
| return { | |
| "id": issue_id, | |
| "status": "resolved", | |
| "statusDetails": { | |
| "inNextRelease": params.get('inNextRelease', False), | |
| "inRelease": params.get('inRelease') | |
| }, | |
| "hasSeen": True | |
| } | |
| def _get_projects_template(self, params: dict) -> list: | |
| """Mock response for listing projects.""" | |
| return [ | |
| { | |
| "id": "123456", | |
| "slug": "my-app", | |
| "name": "My App", | |
| "platform": "javascript", | |
| "dateCreated": "2025-01-15T00:00:00.000000Z", | |
| "isBookmarked": False, | |
| "isMember": True, | |
| "teams": [ | |
| { | |
| "id": "team-1", | |
| "slug": "engineering", | |
| "name": "Engineering" | |
| } | |
| ], | |
| "stats": { | |
| "24h": { | |
| "total": 125 | |
| } | |
| } | |
| }, | |
| { | |
| "id": "123457", | |
| "slug": "backend-api", | |
| "name": "Backend API", | |
| "platform": "python", | |
| "dateCreated": "2025-02-01T00:00:00.000000Z", | |
| "isBookmarked": True, | |
| "isMember": True, | |
| "teams": [ | |
| { | |
| "id": "team-1", | |
| "slug": "engineering", | |
| "name": "Engineering" | |
| } | |
| ], | |
| "stats": { | |
| "24h": { | |
| "total": 43 | |
| } | |
| } | |
| } | |
| ] | |