mockforge_ui/
models.rs

1//! Data models for the admin UI
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Server status information
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ServerStatus {
9    /// Server type (HTTP, WebSocket, gRPC)
10    pub server_type: String,
11    /// Server address
12    pub address: Option<String>,
13    /// Whether server is running
14    pub running: bool,
15    /// Start time
16    pub start_time: Option<chrono::DateTime<chrono::Utc>>,
17    /// Uptime in seconds
18    pub uptime_seconds: Option<u64>,
19    /// Number of active connections
20    pub active_connections: u64,
21    /// Total requests served
22    pub total_requests: u64,
23}
24
25/// Route information
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct RouteInfo {
28    /// HTTP method
29    pub method: Option<String>,
30    /// Route path
31    pub path: String,
32    /// Route priority
33    pub priority: i32,
34    /// Whether route has fixtures
35    pub has_fixtures: bool,
36    /// Latency profile
37    pub latency_ms: Option<u64>,
38    /// Request count
39    pub request_count: u64,
40    /// Last request time
41    pub last_request: Option<chrono::DateTime<chrono::Utc>>,
42    /// Error count
43    pub error_count: u64,
44}
45
46/// Request log entry
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct RequestLog {
49    /// Request ID
50    pub id: String,
51    /// Timestamp
52    pub timestamp: chrono::DateTime<chrono::Utc>,
53    /// HTTP method
54    pub method: String,
55    /// Request path
56    pub path: String,
57    /// Response status code
58    pub status_code: u16,
59    /// Response time in milliseconds
60    pub response_time_ms: u64,
61    /// Client IP address
62    pub client_ip: Option<String>,
63    /// User agent
64    pub user_agent: Option<String>,
65    /// Request headers (filtered)
66    pub headers: HashMap<String, String>,
67    /// Response size in bytes
68    pub response_size_bytes: u64,
69    /// Error message (if any)
70    pub error_message: Option<String>,
71}
72
73/// System information
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SystemInfo {
76    /// MockForge version
77    pub version: String,
78    /// Uptime in seconds
79    pub uptime_seconds: u64,
80    /// Memory usage in MB
81    pub memory_usage_mb: u64,
82    /// CPU usage percentage
83    pub cpu_usage_percent: f64,
84    /// Number of active threads
85    pub active_threads: usize,
86    /// Total routes configured
87    pub total_routes: usize,
88    /// Total fixtures available
89    pub total_fixtures: usize,
90}
91
92/// Latency profile configuration
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct LatencyProfile {
95    /// Profile name
96    pub name: String,
97    /// Base latency in milliseconds
98    pub base_ms: u64,
99    /// Jitter range in milliseconds
100    pub jitter_ms: u64,
101    /// Tag-based overrides
102    pub tag_overrides: HashMap<String, u64>,
103}
104
105/// Fault injection configuration
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct FaultConfig {
108    /// Whether fault injection is enabled
109    pub enabled: bool,
110    /// Failure rate (0.0 to 1.0)
111    pub failure_rate: f64,
112    /// HTTP status codes for failures
113    pub status_codes: Vec<u16>,
114    /// Current active failures
115    pub active_failures: u64,
116}
117
118/// Proxy configuration
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct ProxyConfig {
121    /// Whether proxy is enabled
122    pub enabled: bool,
123    /// Upstream URL
124    pub upstream_url: Option<String>,
125    /// Request timeout seconds
126    pub timeout_seconds: u64,
127    /// Total requests proxied
128    pub requests_proxied: u64,
129}
130
131/// Bandwidth configuration
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct BandwidthConfig {
134    /// Whether bandwidth throttling is enabled
135    pub enabled: bool,
136    /// Maximum bandwidth in bytes per second
137    pub max_bytes_per_sec: u64,
138    /// Burst capacity in bytes
139    pub burst_capacity_bytes: u64,
140    /// Tag-based overrides
141    pub tag_overrides: HashMap<String, u64>,
142}
143
144/// Burst loss configuration
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct BurstLossConfig {
147    /// Whether burst loss is enabled
148    pub enabled: bool,
149    /// Probability of entering burst (0.0 to 1.0)
150    pub burst_probability: f64,
151    /// Duration of burst in milliseconds
152    pub burst_duration_ms: u64,
153    /// Loss rate during burst (0.0 to 1.0)
154    pub loss_rate_during_burst: f64,
155    /// Recovery time between bursts in milliseconds
156    pub recovery_time_ms: u64,
157    /// Tag-based overrides
158    pub tag_overrides: HashMap<String, BurstLossOverride>,
159}
160
161/// Burst loss override for specific tags
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct BurstLossOverride {
164    pub burst_probability: f64,
165    pub burst_duration_ms: u64,
166    pub loss_rate_during_burst: f64,
167    pub recovery_time_ms: u64,
168}
169
170/// Traffic shaping configuration
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TrafficShapingConfig {
173    /// Whether traffic shaping is enabled
174    pub enabled: bool,
175    /// Bandwidth configuration
176    pub bandwidth: BandwidthConfig,
177    /// Burst loss configuration
178    pub burst_loss: BurstLossConfig,
179}
180
181/// Simple metrics data for admin dashboard
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct SimpleMetricsData {
184    /// Total requests served
185    pub total_requests: u64,
186    /// Active requests currently being processed
187    pub active_requests: u64,
188    /// Average response time in milliseconds
189    pub average_response_time: f64,
190    /// Error rate (0.0 to 1.0)
191    pub error_rate: f64,
192}
193
194/// Dashboard system information
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct DashboardSystemInfo {
197    /// Operating system
198    pub os: String,
199    /// Architecture
200    pub arch: String,
201    /// Uptime in seconds
202    pub uptime: u64,
203    /// Memory usage in bytes
204    pub memory_usage: u64,
205}
206
207/// Dashboard data
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct DashboardData {
210    /// Server information
211    pub server_info: ServerInfo,
212    /// System information
213    pub system_info: DashboardSystemInfo,
214    /// Metrics data
215    pub metrics: SimpleMetricsData,
216    /// Server status information
217    pub servers: Vec<ServerStatus>,
218    /// Recent logs
219    pub recent_logs: Vec<RequestLog>,
220    /// System information (for JS compatibility)
221    pub system: SystemInfo,
222}
223
224/// API response wrapper
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ApiResponse<T> {
227    /// Whether request was successful
228    pub success: bool,
229    /// Response data
230    pub data: Option<T>,
231    /// Error message (if any)
232    pub error: Option<String>,
233    /// Response timestamp
234    pub timestamp: chrono::DateTime<chrono::Utc>,
235}
236
237impl<T> ApiResponse<T> {
238    /// Create a successful response
239    pub fn success(data: T) -> Self {
240        Self {
241            success: true,
242            data: Some(data),
243            error: None,
244            timestamp: chrono::Utc::now(),
245        }
246    }
247
248    /// Create an error response
249    pub fn error(message: String) -> Self {
250        Self {
251            success: false,
252            data: None,
253            error: Some(message),
254            timestamp: chrono::Utc::now(),
255        }
256    }
257}
258
259/// Configuration update request
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ConfigUpdate {
262    /// Configuration type
263    pub config_type: String,
264    /// Configuration data
265    pub data: serde_json::Value,
266}
267
268/// Route management request
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct RouteUpdate {
271    /// Route path
272    pub path: String,
273    /// HTTP method (optional)
274    pub method: Option<String>,
275    /// Update operation
276    pub operation: String,
277    /// Update data
278    pub data: Option<serde_json::Value>,
279}
280
281/// Log filter options
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct LogFilter {
284    /// Filter by HTTP method
285    pub method: Option<String>,
286    /// Filter by path pattern
287    pub path_pattern: Option<String>,
288    /// Filter by status code
289    pub status_code: Option<u16>,
290    /// Filter by time range (hours ago)
291    pub hours_ago: Option<u64>,
292    /// Maximum number of results
293    pub limit: Option<usize>,
294}
295
296impl Default for LogFilter {
297    fn default() -> Self {
298        Self {
299            method: None,
300            path_pattern: None,
301            status_code: None,
302            hours_ago: Some(24),
303            limit: Some(100),
304        }
305    }
306}
307
308/// Metrics data
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct MetricsData {
311    /// Request count by endpoint
312    pub requests_by_endpoint: HashMap<String, u64>,
313    /// Response time percentiles
314    pub response_time_percentiles: HashMap<String, u64>,
315    /// Error rate by endpoint
316    pub error_rate_by_endpoint: HashMap<String, f64>,
317    /// Memory usage over time
318    pub memory_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, u64)>,
319    /// CPU usage over time
320    pub cpu_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, f64)>,
321}
322
323/// Validation settings
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct ValidationSettings {
326    /// Validation mode: "enforce", "warn", or "off"
327    pub mode: String,
328    /// Whether to aggregate errors
329    pub aggregate_errors: bool,
330    /// Whether to validate responses
331    pub validate_responses: bool,
332    /// Per-route validation overrides
333    pub overrides: HashMap<String, String>,
334}
335
336/// Validation update request
337#[derive(Debug, Clone, Serialize, Deserialize)]
338pub struct ValidationUpdate {
339    /// Validation mode
340    pub mode: String,
341    /// Whether to aggregate errors
342    pub aggregate_errors: bool,
343    /// Whether to validate responses
344    pub validate_responses: bool,
345    /// Per-route validation overrides
346    pub overrides: Option<HashMap<String, String>>,
347}
348
349/// Log entry for admin UI
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct LogEntry {
352    /// Request timestamp
353    pub timestamp: chrono::DateTime<chrono::Utc>,
354    /// HTTP status code
355    pub status: u16,
356    /// HTTP method
357    pub method: String,
358    /// Request URL/path
359    pub url: String,
360    /// Response time in milliseconds
361    pub response_time: u64,
362    /// Response size in bytes
363    pub size: u64,
364}
365
366/// Health check response
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct HealthCheck {
369    /// Overall health status
370    pub status: String,
371    /// Individual service health
372    pub services: HashMap<String, String>,
373    /// Last health check time
374    pub last_check: chrono::DateTime<chrono::Utc>,
375    /// Any health issues
376    pub issues: Vec<String>,
377}
378
379impl HealthCheck {
380    /// Create a healthy status
381    pub fn healthy() -> Self {
382        Self {
383            status: "healthy".to_string(),
384            services: HashMap::new(),
385            last_check: chrono::Utc::now(),
386            issues: Vec::new(),
387        }
388    }
389
390    /// Create an unhealthy status
391    pub fn unhealthy(issues: Vec<String>) -> Self {
392        Self {
393            status: "unhealthy".to_string(),
394            services: HashMap::new(),
395            last_check: chrono::Utc::now(),
396            issues,
397        }
398    }
399
400    /// Add service status
401    pub fn with_service(mut self, name: String, status: String) -> Self {
402        self.services.insert(name, status);
403        self
404    }
405}
406
407/// Workspace summary information
408#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct WorkspaceSummary {
410    /// Workspace ID
411    pub id: String,
412    /// Workspace name
413    pub name: String,
414    /// Description
415    pub description: Option<String>,
416    /// Whether this is the active workspace
417    pub active: bool,
418    /// Number of folders
419    pub folder_count: usize,
420    /// Number of requests
421    pub request_count: usize,
422    /// Created timestamp
423    pub created_at: chrono::DateTime<chrono::Utc>,
424    /// Updated timestamp
425    pub updated_at: chrono::DateTime<chrono::Utc>,
426}
427
428/// Folder summary information
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct FolderSummary {
431    /// Folder ID
432    pub id: String,
433    /// Folder name
434    pub name: String,
435    /// Description
436    pub description: Option<String>,
437    /// Parent folder ID (None if root)
438    pub parent_id: Option<String>,
439    /// Number of subfolders
440    pub subfolder_count: usize,
441    /// Number of requests
442    pub request_count: usize,
443    /// Created timestamp
444    pub created_at: chrono::DateTime<chrono::Utc>,
445    /// Updated timestamp
446    pub updated_at: chrono::DateTime<chrono::Utc>,
447}
448
449/// Request summary information
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct RequestSummary {
452    /// Request ID
453    pub id: String,
454    /// Request name
455    pub name: String,
456    /// Description
457    pub description: Option<String>,
458    /// HTTP method
459    pub method: String,
460    /// Request path
461    pub path: String,
462    /// Response status code
463    pub status_code: u16,
464    /// Created timestamp
465    pub created_at: chrono::DateTime<chrono::Utc>,
466    /// Updated timestamp
467    pub updated_at: chrono::DateTime<chrono::Utc>,
468}
469
470/// Workspace detailed information
471#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct WorkspaceDetail {
473    /// Workspace summary
474    pub summary: WorkspaceSummary,
475    /// Root folders
476    pub folders: Vec<FolderSummary>,
477    /// Root requests
478    pub requests: Vec<RequestSummary>,
479}
480
481/// Folder detailed information
482#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct FolderDetail {
484    /// Folder summary
485    pub summary: FolderSummary,
486    /// Subfolders
487    pub subfolders: Vec<FolderSummary>,
488    /// Requests in this folder
489    pub requests: Vec<RequestSummary>,
490}
491
492/// Create workspace request
493#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct CreateWorkspaceRequest {
495    /// Workspace name
496    pub name: String,
497    /// Description (optional)
498    pub description: Option<String>,
499}
500
501/// Create folder request
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct CreateFolderRequest {
504    /// Folder name
505    pub name: String,
506    /// Description (optional)
507    pub description: Option<String>,
508    /// Parent folder ID (optional)
509    pub parent_id: Option<String>,
510}
511
512/// Create request request
513#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct CreateRequestRequest {
515    /// Request name
516    pub name: String,
517    /// Description (optional)
518    pub description: Option<String>,
519    /// HTTP method
520    pub method: String,
521    /// Request path
522    pub path: String,
523    /// Response status code
524    pub status_code: Option<u16>,
525    /// Response body
526    pub response_body: Option<String>,
527    /// Folder ID (optional)
528    pub folder_id: Option<String>,
529}
530
531/// Import to workspace request
532#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct ImportToWorkspaceRequest {
534    /// Import format (postman, insomnia, curl)
535    pub format: String,
536    /// Import data (file content or URL)
537    pub data: String,
538    /// Folder ID to import into (optional)
539    pub folder_id: Option<String>,
540    /// Whether to create folders from import structure
541    pub create_folders: Option<bool>,
542    /// Indices of routes to import (for selective import)
543    pub selected_routes: Option<Vec<usize>>,
544}
545
546/// Export workspaces request
547#[derive(Debug, Clone, Serialize, Deserialize)]
548pub struct ExportWorkspacesRequest {
549    /// Workspace IDs to export
550    pub workspace_ids: Vec<String>,
551}
552
553/// Workspace export data
554#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct WorkspaceExportData {
556    /// Exported workspaces
557    pub workspaces: Vec<mockforge_core::Workspace>,
558    /// Export version
559    pub version: String,
560    /// Export timestamp
561    pub exported_at: chrono::DateTime<chrono::Utc>,
562    /// Exporter version
563    pub exporter_version: String,
564}
565
566/// Environment color information
567#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct EnvironmentColor {
569    /// Hex color code (e.g., "#FF5733")
570    pub hex: String,
571    /// Optional color name for accessibility
572    pub name: Option<String>,
573}
574
575/// Environment summary information
576#[derive(Debug, Clone, Serialize, Deserialize)]
577pub struct EnvironmentSummary {
578    /// Environment ID
579    pub id: String,
580    /// Environment name
581    pub name: String,
582    /// Description
583    pub description: Option<String>,
584    /// Color for visual distinction
585    pub color: Option<EnvironmentColor>,
586    /// Number of variables
587    pub variable_count: usize,
588    /// Whether this is the active environment
589    pub active: bool,
590    /// Whether this is the global environment
591    pub is_global: bool,
592    /// Created timestamp
593    pub created_at: chrono::DateTime<chrono::Utc>,
594    /// Updated timestamp
595    pub updated_at: chrono::DateTime<chrono::Utc>,
596}
597
598/// Environment variable information
599#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct EnvironmentVariable {
601    /// Variable name
602    pub name: String,
603    /// Variable value
604    pub value: String,
605    /// Whether this variable is from the global environment
606    pub from_global: bool,
607}
608
609/// Create environment request
610#[derive(Debug, Clone, Serialize, Deserialize)]
611pub struct CreateEnvironmentRequest {
612    /// Environment name
613    pub name: String,
614    /// Description
615    pub description: Option<String>,
616}
617
618/// Update environment request
619#[derive(Debug, Clone, Serialize, Deserialize)]
620pub struct UpdateEnvironmentRequest {
621    /// Environment name
622    pub name: Option<String>,
623    /// Description
624    pub description: Option<String>,
625    /// Color
626    pub color: Option<EnvironmentColor>,
627}
628
629/// Set variable request
630#[derive(Debug, Clone, Serialize, Deserialize)]
631pub struct SetVariableRequest {
632    /// Variable name
633    pub name: String,
634    /// Variable value
635    pub value: String,
636}
637
638/// Directory sync configuration
639#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct SyncConfig {
641    /// Enable directory syncing for this workspace
642    pub enabled: bool,
643    /// Target directory for sync (relative or absolute path)
644    pub target_directory: Option<String>,
645    /// Directory structure to use (flat, nested, grouped)
646    pub directory_structure: SyncDirectoryStructure,
647    /// Auto-sync direction (one-way workspace→directory, bidirectional, or manual)
648    pub sync_direction: SyncDirection,
649    /// Whether to include metadata files
650    pub include_metadata: bool,
651    /// Filesystem monitoring enabled for real-time sync
652    pub realtime_monitoring: bool,
653    /// Custom filename pattern for exported files
654    pub filename_pattern: String,
655    /// Regular expression for excluding workspaces/requests
656    pub exclude_pattern: Option<String>,
657    /// Force overwrite existing files during sync
658    pub force_overwrite: bool,
659}
660
661/// Directory structure options for sync
662#[derive(Debug, Clone, Serialize, Deserialize)]
663pub enum SyncDirectoryStructure {
664    /// All workspaces in flat structure: workspace-name.yaml
665    Flat,
666    /// Nested by workspace: workspaces/{name}/workspace.yaml + requests/
667    Nested,
668    /// Grouped by type: requests/, responses/, metadata/
669    Grouped,
670}
671
672/// Sync direction options
673#[derive(Debug, Clone, Serialize, Deserialize)]
674pub enum SyncDirection {
675    /// Manual sync only (one-off operations)
676    Manual,
677    /// One-way: workspace changes sync silently to directory
678    WorkspaceToDirectory,
679    /// Bidirectional: changes in either direction trigger sync
680    Bidirectional,
681}
682
683impl From<mockforge_core::workspace::SyncDirection> for SyncDirection {
684    fn from(core: mockforge_core::workspace::SyncDirection) -> Self {
685        match core {
686            mockforge_core::workspace::SyncDirection::Manual => Self::Manual,
687            mockforge_core::workspace::SyncDirection::WorkspaceToDirectory => {
688                Self::WorkspaceToDirectory
689            }
690            mockforge_core::workspace::SyncDirection::Bidirectional => Self::Bidirectional,
691        }
692    }
693}
694
695/// Sync status information
696#[derive(Debug, Clone, Serialize, Deserialize)]
697pub struct SyncStatus {
698    /// Workspace ID
699    pub workspace_id: String,
700    /// Whether sync is enabled
701    pub enabled: bool,
702    /// Target directory
703    pub target_directory: Option<String>,
704    /// Current sync direction
705    pub sync_direction: SyncDirection,
706    /// Whether real-time monitoring is active
707    pub realtime_monitoring: bool,
708    /// Last sync timestamp
709    pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
710    /// Sync status message
711    pub status: String,
712}
713
714/// Sync change information
715#[derive(Debug, Clone, Serialize, Deserialize)]
716pub struct SyncChange {
717    /// Change type
718    pub change_type: String,
719    /// File path
720    pub path: String,
721    /// Change description
722    pub description: String,
723    /// Whether this change requires confirmation
724    pub requires_confirmation: bool,
725}
726
727/// Configure sync request
728#[derive(Debug, Clone, Serialize, Deserialize)]
729pub struct ConfigureSyncRequest {
730    /// Target directory
731    pub target_directory: String,
732    /// Sync direction
733    pub sync_direction: SyncDirection,
734    /// Enable real-time monitoring
735    pub realtime_monitoring: bool,
736    /// Directory structure
737    pub directory_structure: Option<SyncDirectoryStructure>,
738    /// Filename pattern
739    pub filename_pattern: Option<String>,
740}
741
742/// Confirm sync changes request
743#[derive(Debug, Clone, Serialize, Deserialize)]
744pub struct ConfirmSyncChangesRequest {
745    /// Workspace ID
746    pub workspace_id: String,
747    /// Changes to confirm
748    pub changes: Vec<SyncChange>,
749    /// Whether to apply all changes
750    pub apply_all: bool,
751}
752
753/// Autocomplete suggestion
754#[derive(Debug, Clone, Serialize, Deserialize)]
755pub struct AutocompleteSuggestion {
756    /// Suggestion text
757    pub text: String,
758    /// Suggestion type (e.g., "variable", "template")
759    pub kind: String,
760    /// Optional description
761    pub description: Option<String>,
762}
763
764/// Autocomplete request
765#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct AutocompleteRequest {
767    /// Current input text
768    pub input: String,
769    /// Cursor position in the text
770    pub cursor_position: usize,
771    /// Context type (e.g., "header", "body", "url")
772    pub context: Option<String>,
773}
774
775/// Autocomplete response
776#[derive(Debug, Clone, Serialize, Deserialize)]
777pub struct AutocompleteResponse {
778    /// List of suggestions
779    pub suggestions: Vec<AutocompleteSuggestion>,
780    /// Start position of the token being completed
781    pub start_position: usize,
782    /// End position of the token being completed
783    pub end_position: usize,
784}
785
786/// Time series data point
787#[derive(Debug, Clone, Serialize, Deserialize)]
788pub struct TimeSeriesPoint {
789    /// Timestamp
790    pub timestamp: chrono::DateTime<chrono::Utc>,
791    /// Value
792    pub value: f64,
793}
794
795/// Time series data
796#[derive(Debug, Clone, Serialize, Deserialize)]
797pub struct TimeSeriesData {
798    /// Data points
799    pub points: Vec<TimeSeriesPoint>,
800    /// Metric name
801    pub metric: String,
802}
803
804/// Restart status
805#[derive(Debug, Clone, Serialize, Deserialize)]
806pub struct RestartStatus {
807    /// Whether restart is in progress
808    pub restarting: bool,
809    /// Restart progress (0.0 to 1.0)
810    pub progress: f64,
811    /// Status message
812    pub message: String,
813}
814
815/// Smoke test result
816#[derive(Debug, Clone, Serialize, Deserialize)]
817pub struct SmokeTestResult {
818    /// Test name
819    pub test_name: String,
820    /// Whether test passed
821    pub passed: bool,
822    /// Response time in milliseconds
823    pub response_time_ms: Option<u64>,
824    /// Error message (if failed)
825    pub error_message: Option<String>,
826}
827
828/// Smoke test context
829#[derive(Debug, Clone, Serialize, Deserialize)]
830pub struct SmokeTestContext {
831    /// Test suite name
832    pub suite_name: String,
833    /// Total tests
834    pub total_tests: usize,
835    /// Passed tests
836    pub passed_tests: usize,
837    /// Failed tests
838    pub failed_tests: usize,
839    /// Start time
840    pub start_time: chrono::DateTime<chrono::Utc>,
841    /// End time
842    pub end_time: Option<chrono::DateTime<chrono::Utc>>,
843}
844
845/// Configuration state
846#[derive(Debug, Clone, Serialize, Deserialize)]
847pub struct ConfigurationState {
848    /// Whether configuration is valid
849    pub valid: bool,
850    /// Configuration errors
851    pub errors: Vec<String>,
852    /// Configuration warnings
853    pub warnings: Vec<String>,
854}
855
856/// Import history entry
857#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct ImportHistoryEntry {
859    /// Entry ID
860    pub id: String,
861    /// Import format
862    pub format: String,
863    /// Import timestamp
864    pub timestamp: chrono::DateTime<chrono::Utc>,
865    /// Number of routes imported
866    pub routes_count: usize,
867    /// Number of variables imported
868    pub variables_count: usize,
869    /// Number of warnings
870    pub warnings_count: usize,
871    /// Whether import was successful
872    pub success: bool,
873    /// Filename (if applicable)
874    pub filename: Option<String>,
875    /// Environment name
876    pub environment: Option<String>,
877    /// Base URL
878    pub base_url: Option<String>,
879    /// Error message (if failed)
880    pub error_message: Option<String>,
881}
882
883/// Fixture information
884#[derive(Debug, Clone, Serialize, Deserialize)]
885pub struct FixtureInfo {
886    /// Fixture ID
887    pub id: String,
888    /// Fixture name
889    pub name: String,
890    /// Fixture path
891    pub path: String,
892    /// File size in bytes
893    pub size_bytes: u64,
894    /// Last modified timestamp
895    pub last_modified: chrono::DateTime<chrono::Utc>,
896    /// Content type
897    pub content_type: Option<String>,
898}
899
900/// Fixture delete request
901#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct FixtureDeleteRequest {
903    /// Fixture ID to delete
904    pub fixture_id: String,
905}
906
907/// Fixture bulk delete request
908#[derive(Debug, Clone, Serialize, Deserialize)]
909pub struct FixtureBulkDeleteRequest {
910    /// Fixture IDs to delete
911    pub fixture_ids: Vec<String>,
912}
913
914/// Fixture bulk delete result
915#[derive(Debug, Clone, Serialize, Deserialize)]
916pub struct FixtureBulkDeleteResult {
917    /// Successfully deleted fixture IDs
918    pub deleted: Vec<String>,
919    /// Failed deletions with error messages
920    pub failed: HashMap<String, String>,
921}
922
923/// Import route
924#[derive(Debug, Clone, Serialize, Deserialize)]
925pub struct ImportRoute {
926    /// HTTP method
927    pub method: String,
928    /// Route path
929    pub path: String,
930    /// Request headers
931    pub headers: HashMap<String, String>,
932    /// Request body
933    pub body: Option<String>,
934    /// Expected response
935    pub response: ImportResponse,
936}
937
938/// Import response
939#[derive(Debug, Clone, Serialize, Deserialize)]
940pub struct ImportResponse {
941    /// HTTP status code
942    pub status: u16,
943    /// Response headers
944    pub headers: HashMap<String, String>,
945    /// Response body
946    pub body: serde_json::Value,
947}
948
949/// Import result
950#[derive(Debug, Clone, Serialize, Deserialize)]
951pub struct ImportResult {
952    /// Imported routes
953    pub routes: Vec<ImportRoute>,
954    /// Import warnings
955    pub warnings: Vec<String>,
956    /// Import errors
957    pub errors: Vec<String>,
958}
959
960/// Insomnia import result
961#[derive(Debug, Clone, Serialize, Deserialize)]
962pub struct InsomniaImportResult {
963    /// Imported routes
964    pub routes: Vec<ImportRoute>,
965    /// Environment variables
966    pub variables: HashMap<String, String>,
967    /// Import warnings
968    pub warnings: Vec<String>,
969}
970
971/// Server information
972#[derive(Debug, Clone, Serialize, Deserialize)]
973pub struct ServerInfo {
974    /// Server version
975    pub version: String,
976    /// Build timestamp
977    pub build_time: String,
978    /// Git SHA
979    pub git_sha: String,
980    /// HTTP server address (optional)
981    pub http_server: Option<String>,
982    /// WebSocket server address (optional)
983    pub ws_server: Option<String>,
984    /// gRPC server address (optional)
985    pub grpc_server: Option<String>,
986    /// GraphQL server address (optional)
987    pub graphql_server: Option<String>,
988    /// Whether API endpoints are enabled
989    pub api_enabled: bool,
990    /// Admin server port
991    pub admin_port: u16,
992}
993
994#[cfg(test)]
995mod tests {
996    use super::*;
997
998    #[test]
999    fn test_api_response_success() {
1000        let data = "test data".to_string();
1001        let response = ApiResponse::success(data.clone());
1002
1003        assert!(response.success);
1004        assert_eq!(response.data, Some(data));
1005        assert!(response.error.is_none());
1006    }
1007
1008    #[test]
1009    fn test_api_response_error() {
1010        let error_msg = "Something went wrong".to_string();
1011        let response: ApiResponse<String> = ApiResponse::error(error_msg.clone());
1012
1013        assert!(!response.success);
1014        assert!(response.data.is_none());
1015        assert_eq!(response.error, Some(error_msg));
1016    }
1017
1018    #[test]
1019    fn test_health_check_healthy() {
1020        let health = HealthCheck::healthy();
1021
1022        assert_eq!(health.status, "healthy");
1023        assert!(health.issues.is_empty());
1024        assert!(health.services.is_empty());
1025    }
1026
1027    #[test]
1028    fn test_health_check_unhealthy() {
1029        let issues = vec!["Database down".to_string(), "Cache error".to_string()];
1030        let health = HealthCheck::unhealthy(issues.clone());
1031
1032        assert_eq!(health.status, "unhealthy");
1033        assert_eq!(health.issues, issues);
1034    }
1035
1036    #[test]
1037    fn test_health_check_with_service() {
1038        let health = HealthCheck::healthy()
1039            .with_service("http".to_string(), "running".to_string())
1040            .with_service("grpc".to_string(), "running".to_string());
1041
1042        assert_eq!(health.services.len(), 2);
1043        assert_eq!(health.services.get("http"), Some(&"running".to_string()));
1044    }
1045
1046    #[test]
1047    fn test_log_filter_default() {
1048        let filter = LogFilter::default();
1049
1050        assert!(filter.method.is_none());
1051        assert!(filter.path_pattern.is_none());
1052        assert_eq!(filter.hours_ago, Some(24));
1053        assert_eq!(filter.limit, Some(100));
1054    }
1055
1056    #[test]
1057    fn test_server_status() {
1058        let status = ServerStatus {
1059            server_type: "HTTP".to_string(),
1060            address: Some("127.0.0.1:3000".to_string()),
1061            running: true,
1062            start_time: Some(chrono::Utc::now()),
1063            uptime_seconds: Some(3600),
1064            active_connections: 10,
1065            total_requests: 1000,
1066        };
1067
1068        assert_eq!(status.server_type, "HTTP");
1069        assert!(status.running);
1070        assert_eq!(status.active_connections, 10);
1071    }
1072
1073    #[test]
1074    fn test_route_info() {
1075        let route = RouteInfo {
1076            method: Some("GET".to_string()),
1077            path: "/api/users".to_string(),
1078            priority: 100,
1079            has_fixtures: true,
1080            latency_ms: Some(50),
1081            request_count: 500,
1082            last_request: None,
1083            error_count: 5,
1084        };
1085
1086        assert_eq!(route.method, Some("GET".to_string()));
1087        assert_eq!(route.path, "/api/users");
1088        assert!(route.has_fixtures);
1089    }
1090
1091    #[test]
1092    fn test_sync_direction_conversion() {
1093        let manual = mockforge_core::workspace::SyncDirection::Manual;
1094        let ui_manual: SyncDirection = manual.into();
1095        assert!(matches!(ui_manual, SyncDirection::Manual));
1096    }
1097
1098    #[test]
1099    fn test_environment_color() {
1100        let color = EnvironmentColor {
1101            hex: "#FF5733".to_string(),
1102            name: Some("Orange".to_string()),
1103        };
1104
1105        assert_eq!(color.hex, "#FF5733");
1106        assert_eq!(color.name, Some("Orange".to_string()));
1107    }
1108}