api-mocker 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,386 @@
1
+ """
2
+ Web dashboard for api-mocker analytics and monitoring.
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import time
8
+ from datetime import datetime, timedelta
9
+ from typing import Dict, List, Any, Optional
10
+ from pathlib import Path
11
+ import aiofiles
12
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
13
+ from fastapi.responses import HTMLResponse
14
+ from fastapi.staticfiles import StaticFiles
15
+ import uvicorn
16
+ from .analytics import AnalyticsManager
17
+
18
+ class DashboardManager:
19
+ """Manages the web dashboard for analytics visualization."""
20
+
21
+ def __init__(self, analytics_manager: AnalyticsManager, port: int = 8080):
22
+ self.analytics = analytics_manager
23
+ self.port = port
24
+ self.app = FastAPI(title="API-Mocker Dashboard", version="1.0.0")
25
+ self.active_connections: List[WebSocket] = []
26
+ self._setup_routes()
27
+
28
+ def _setup_routes(self):
29
+ """Setup dashboard routes."""
30
+
31
+ @self.app.get("/", response_class=HTMLResponse)
32
+ async def dashboard_home():
33
+ """Main dashboard page."""
34
+ return self._get_dashboard_html()
35
+
36
+ @self.app.get("/api/metrics")
37
+ async def get_metrics():
38
+ """Get current server metrics."""
39
+ return self.analytics.get_server_metrics()
40
+
41
+ @self.app.get("/api/analytics")
42
+ async def get_analytics(hours: int = 24):
43
+ """Get analytics summary."""
44
+ return self.analytics.get_analytics_summary(hours)
45
+
46
+ @self.app.get("/api/endpoints")
47
+ async def get_endpoints():
48
+ """Get endpoint statistics."""
49
+ summary = self.analytics.get_analytics_summary(24)
50
+ return {
51
+ "popular_endpoints": summary["popular_endpoints"],
52
+ "slowest_endpoints": summary["slowest_endpoints"]
53
+ }
54
+
55
+ @self.app.get("/api/requests")
56
+ async def get_recent_requests(limit: int = 50):
57
+ """Get recent requests."""
58
+ # TODO: Implement recent requests endpoint
59
+ return {"requests": []}
60
+
61
+ @self.app.websocket("/ws")
62
+ async def websocket_endpoint(websocket: WebSocket):
63
+ """WebSocket endpoint for real-time updates."""
64
+ await websocket.accept()
65
+ self.active_connections.append(websocket)
66
+ try:
67
+ while True:
68
+ # Send real-time metrics every 5 seconds
69
+ metrics = self.analytics.get_server_metrics()
70
+ await websocket.send_text(json.dumps({
71
+ "type": "metrics",
72
+ "data": {
73
+ "uptime": metrics.uptime_seconds,
74
+ "total_requests": metrics.total_requests,
75
+ "requests_per_minute": metrics.requests_per_minute,
76
+ "average_response_time": metrics.average_response_time_ms,
77
+ "error_rate": metrics.error_rate,
78
+ "memory_usage": metrics.memory_usage_mb,
79
+ "cpu_usage": metrics.cpu_usage_percent
80
+ }
81
+ }))
82
+ await asyncio.sleep(5)
83
+ except WebSocketDisconnect:
84
+ self.active_connections.remove(websocket)
85
+
86
+ def _get_dashboard_html(self) -> str:
87
+ """Generate the dashboard HTML."""
88
+ return """
89
+ <!DOCTYPE html>
90
+ <html lang="en">
91
+ <head>
92
+ <meta charset="UTF-8">
93
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
94
+ <title>API-Mocker Dashboard</title>
95
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
96
+ <style>
97
+ body {
98
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
99
+ margin: 0;
100
+ padding: 20px;
101
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
102
+ min-height: 100vh;
103
+ }
104
+ .container {
105
+ max-width: 1200px;
106
+ margin: 0 auto;
107
+ background: white;
108
+ border-radius: 10px;
109
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
110
+ overflow: hidden;
111
+ }
112
+ .header {
113
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
114
+ color: white;
115
+ padding: 20px;
116
+ text-align: center;
117
+ }
118
+ .header h1 {
119
+ margin: 0;
120
+ font-size: 2.5em;
121
+ }
122
+ .metrics-grid {
123
+ display: grid;
124
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
125
+ gap: 20px;
126
+ padding: 20px;
127
+ }
128
+ .metric-card {
129
+ background: #f8f9fa;
130
+ border-radius: 8px;
131
+ padding: 20px;
132
+ text-align: center;
133
+ border-left: 4px solid #667eea;
134
+ }
135
+ .metric-value {
136
+ font-size: 2em;
137
+ font-weight: bold;
138
+ color: #667eea;
139
+ }
140
+ .metric-label {
141
+ color: #6c757d;
142
+ margin-top: 5px;
143
+ }
144
+ .charts-section {
145
+ padding: 20px;
146
+ }
147
+ .chart-container {
148
+ background: white;
149
+ border-radius: 8px;
150
+ padding: 20px;
151
+ margin-bottom: 20px;
152
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
153
+ }
154
+ .chart-title {
155
+ font-size: 1.5em;
156
+ margin-bottom: 15px;
157
+ color: #333;
158
+ }
159
+ .endpoints-list {
160
+ background: #f8f9fa;
161
+ border-radius: 8px;
162
+ padding: 15px;
163
+ margin-top: 10px;
164
+ }
165
+ .endpoint-item {
166
+ display: flex;
167
+ justify-content: space-between;
168
+ padding: 8px 0;
169
+ border-bottom: 1px solid #dee2e6;
170
+ }
171
+ .endpoint-item:last-child {
172
+ border-bottom: none;
173
+ }
174
+ .status-indicator {
175
+ display: inline-block;
176
+ width: 10px;
177
+ height: 10px;
178
+ border-radius: 50%;
179
+ margin-right: 10px;
180
+ }
181
+ .status-healthy { background: #28a745; }
182
+ .status-warning { background: #ffc107; }
183
+ .status-error { background: #dc3545; }
184
+ </style>
185
+ </head>
186
+ <body>
187
+ <div class="container">
188
+ <div class="header">
189
+ <h1>🚀 API-Mocker Dashboard</h1>
190
+ <p>Real-time analytics and monitoring</p>
191
+ </div>
192
+
193
+ <div class="metrics-grid" id="metrics-grid">
194
+ <div class="metric-card">
195
+ <div class="metric-value" id="uptime">--</div>
196
+ <div class="metric-label">Uptime</div>
197
+ </div>
198
+ <div class="metric-card">
199
+ <div class="metric-value" id="total-requests">--</div>
200
+ <div class="metric-label">Total Requests</div>
201
+ </div>
202
+ <div class="metric-card">
203
+ <div class="metric-value" id="requests-per-minute">--</div>
204
+ <div class="metric-label">Requests/Min</div>
205
+ </div>
206
+ <div class="metric-card">
207
+ <div class="metric-value" id="avg-response-time">--</div>
208
+ <div class="metric-label">Avg Response Time (ms)</div>
209
+ </div>
210
+ <div class="metric-card">
211
+ <div class="metric-value" id="error-rate">--</div>
212
+ <div class="metric-label">Error Rate (%)</div>
213
+ </div>
214
+ <div class="metric-card">
215
+ <div class="metric-value" id="memory-usage">--</div>
216
+ <div class="metric-label">Memory Usage (%)</div>
217
+ </div>
218
+ </div>
219
+
220
+ <div class="charts-section">
221
+ <div class="chart-container">
222
+ <div class="chart-title">Request Methods Distribution</div>
223
+ <canvas id="methods-chart" width="400" height="200"></canvas>
224
+ </div>
225
+
226
+ <div class="chart-container">
227
+ <div class="chart-title">Status Codes Distribution</div>
228
+ <canvas id="status-chart" width="400" height="200"></canvas>
229
+ </div>
230
+
231
+ <div class="chart-container">
232
+ <div class="chart-title">Most Popular Endpoints</div>
233
+ <div id="popular-endpoints" class="endpoints-list"></div>
234
+ </div>
235
+
236
+ <div class="chart-container">
237
+ <div class="chart-title">Slowest Endpoints</div>
238
+ <div id="slowest-endpoints" class="endpoints-list"></div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ <script>
244
+ // Initialize charts
245
+ const methodsChart = new Chart(document.getElementById('methods-chart'), {
246
+ type: 'doughnut',
247
+ data: {
248
+ labels: [],
249
+ datasets: [{
250
+ data: [],
251
+ backgroundColor: [
252
+ '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF'
253
+ ]
254
+ }]
255
+ },
256
+ options: {
257
+ responsive: true,
258
+ plugins: {
259
+ legend: {
260
+ position: 'bottom'
261
+ }
262
+ }
263
+ }
264
+ });
265
+
266
+ const statusChart = new Chart(document.getElementById('status-chart'), {
267
+ type: 'bar',
268
+ data: {
269
+ labels: [],
270
+ datasets: [{
271
+ label: 'Requests',
272
+ data: [],
273
+ backgroundColor: '#36A2EB'
274
+ }]
275
+ },
276
+ options: {
277
+ responsive: true,
278
+ scales: {
279
+ y: {
280
+ beginAtZero: true
281
+ }
282
+ }
283
+ }
284
+ });
285
+
286
+ // WebSocket connection for real-time updates
287
+ const ws = new WebSocket(`ws://${window.location.host}/ws`);
288
+
289
+ ws.onmessage = function(event) {
290
+ const data = JSON.parse(event.data);
291
+ if (data.type === 'metrics') {
292
+ updateMetrics(data.data);
293
+ }
294
+ };
295
+
296
+ function updateMetrics(metrics) {
297
+ document.getElementById('uptime').textContent = formatUptime(metrics.uptime);
298
+ document.getElementById('total-requests').textContent = metrics.total_requests;
299
+ document.getElementById('requests-per-minute').textContent = metrics.requests_per_minute.toFixed(1);
300
+ document.getElementById('avg-response-time').textContent = metrics.average_response_time.toFixed(1);
301
+ document.getElementById('error-rate').textContent = metrics.error_rate.toFixed(1);
302
+ document.getElementById('memory-usage').textContent = metrics.memory_usage.toFixed(1);
303
+ }
304
+
305
+ function formatUptime(seconds) {
306
+ const hours = Math.floor(seconds / 3600);
307
+ const minutes = Math.floor((seconds % 3600) / 60);
308
+ return `${hours}h ${minutes}m`;
309
+ }
310
+
311
+ // Load initial data
312
+ async function loadInitialData() {
313
+ try {
314
+ const [analytics, endpoints] = await Promise.all([
315
+ fetch('/api/analytics').then(r => r.json()),
316
+ fetch('/api/endpoints').then(r => r.json())
317
+ ]);
318
+
319
+ // Update methods chart
320
+ methodsChart.data.labels = Object.keys(analytics.methods);
321
+ methodsChart.data.datasets[0].data = Object.values(analytics.methods);
322
+ methodsChart.update();
323
+
324
+ // Update status chart
325
+ statusChart.data.labels = Object.keys(analytics.status_codes);
326
+ statusChart.data.datasets[0].data = Object.values(analytics.status_codes);
327
+ statusChart.update();
328
+
329
+ // Update endpoints lists
330
+ updateEndpointsList('popular-endpoints', analytics.popular_endpoints);
331
+ updateEndpointsList('slowest-endpoints', analytics.slowest_endpoints);
332
+ } catch (error) {
333
+ console.error('Error loading data:', error);
334
+ }
335
+ }
336
+
337
+ function updateEndpointsList(elementId, endpoints) {
338
+ const container = document.getElementById(elementId);
339
+ container.innerHTML = '';
340
+
341
+ Object.entries(endpoints).forEach(([endpoint, value]) => {
342
+ const item = document.createElement('div');
343
+ item.className = 'endpoint-item';
344
+ item.innerHTML = `
345
+ <span>${endpoint}</span>
346
+ <span>${typeof value === 'number' ? value.toFixed(2) : value}</span>
347
+ `;
348
+ container.appendChild(item);
349
+ });
350
+ }
351
+
352
+ // Load data on page load
353
+ loadInitialData();
354
+
355
+ // Refresh data every 30 seconds
356
+ setInterval(loadInitialData, 30000);
357
+ </script>
358
+ </body>
359
+ </html>
360
+ """
361
+
362
+ def start(self, host: str = "127.0.0.1"):
363
+ """Start the dashboard server."""
364
+ uvicorn.run(self.app, host=host, port=self.port)
365
+
366
+ async def broadcast_metrics(self):
367
+ """Broadcast metrics to all connected WebSocket clients."""
368
+ metrics = self.analytics.get_server_metrics()
369
+ message = {
370
+ "type": "metrics",
371
+ "data": {
372
+ "uptime": metrics.uptime_seconds,
373
+ "total_requests": metrics.total_requests,
374
+ "requests_per_minute": metrics.requests_per_minute,
375
+ "average_response_time": metrics.average_response_time_ms,
376
+ "error_rate": metrics.error_rate,
377
+ "memory_usage": metrics.memory_usage_mb,
378
+ "cpu_usage": metrics.cpu_usage_percent
379
+ }
380
+ }
381
+
382
+ for connection in self.active_connections:
383
+ try:
384
+ await connection.send_text(json.dumps(message))
385
+ except:
386
+ self.active_connections.remove(connection)