1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ServerStatus {
9 pub server_type: String,
11 pub address: Option<String>,
13 pub running: bool,
15 pub start_time: Option<chrono::DateTime<chrono::Utc>>,
17 pub uptime_seconds: Option<u64>,
19 pub active_connections: u64,
21 pub total_requests: u64,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct RouteInfo {
28 pub method: Option<String>,
30 pub path: String,
32 pub priority: i32,
34 pub has_fixtures: bool,
36 pub latency_ms: Option<u64>,
38 pub request_count: u64,
40 pub last_request: Option<chrono::DateTime<chrono::Utc>>,
42 pub error_count: u64,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct RequestLog {
49 pub id: String,
51 pub timestamp: chrono::DateTime<chrono::Utc>,
53 pub method: String,
55 pub path: String,
57 pub status_code: u16,
59 pub response_time_ms: u64,
61 pub client_ip: Option<String>,
63 pub user_agent: Option<String>,
65 pub headers: HashMap<String, String>,
67 pub response_size_bytes: u64,
69 pub error_message: Option<String>,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SystemInfo {
76 pub version: String,
78 pub uptime_seconds: u64,
80 pub memory_usage_mb: u64,
82 pub cpu_usage_percent: f64,
84 pub active_threads: usize,
86 pub total_routes: usize,
88 pub total_fixtures: usize,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct LatencyProfile {
95 pub name: String,
97 pub base_ms: u64,
99 pub jitter_ms: u64,
101 pub tag_overrides: HashMap<String, u64>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct FaultConfig {
108 pub enabled: bool,
110 pub failure_rate: f64,
112 pub status_codes: Vec<u16>,
114 pub active_failures: u64,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct ProxyConfig {
121 pub enabled: bool,
123 pub upstream_url: Option<String>,
125 pub timeout_seconds: u64,
127 pub requests_proxied: u64,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct BandwidthConfig {
134 pub enabled: bool,
136 pub max_bytes_per_sec: u64,
138 pub burst_capacity_bytes: u64,
140 pub tag_overrides: HashMap<String, u64>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct BurstLossConfig {
147 pub enabled: bool,
149 pub burst_probability: f64,
151 pub burst_duration_ms: u64,
153 pub loss_rate_during_burst: f64,
155 pub recovery_time_ms: u64,
157 pub tag_overrides: HashMap<String, BurstLossOverride>,
159}
160
161#[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#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TrafficShapingConfig {
173 pub enabled: bool,
175 pub bandwidth: BandwidthConfig,
177 pub burst_loss: BurstLossConfig,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct SimpleMetricsData {
184 pub total_requests: u64,
186 pub active_requests: u64,
188 pub average_response_time: f64,
190 pub error_rate: f64,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct DashboardSystemInfo {
197 pub os: String,
199 pub arch: String,
201 pub uptime: u64,
203 pub memory_usage: u64,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct DashboardData {
210 pub server_info: ServerInfo,
212 pub system_info: DashboardSystemInfo,
214 pub metrics: SimpleMetricsData,
216 pub servers: Vec<ServerStatus>,
218 pub recent_logs: Vec<RequestLog>,
220 pub system: SystemInfo,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ApiResponse<T> {
227 pub success: bool,
229 pub data: Option<T>,
231 pub error: Option<String>,
233 pub timestamp: chrono::DateTime<chrono::Utc>,
235}
236
237impl<T> ApiResponse<T> {
238 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ConfigUpdate {
262 pub config_type: String,
264 pub data: serde_json::Value,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct RouteUpdate {
271 pub path: String,
273 pub method: Option<String>,
275 pub operation: String,
277 pub data: Option<serde_json::Value>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct LogFilter {
284 pub method: Option<String>,
286 pub path_pattern: Option<String>,
288 pub status_code: Option<u16>,
290 pub hours_ago: Option<u64>,
292 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#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct MetricsData {
311 pub requests_by_endpoint: HashMap<String, u64>,
313 pub response_time_percentiles: HashMap<String, u64>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub endpoint_percentiles: Option<HashMap<String, HashMap<String, u64>>>,
318 #[serde(skip_serializing_if = "Option::is_none")]
320 pub latency_over_time: Option<Vec<(chrono::DateTime<chrono::Utc>, u64)>>,
321 pub error_rate_by_endpoint: HashMap<String, f64>,
323 pub memory_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, u64)>,
325 pub cpu_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, f64)>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct ValidationSettings {
332 pub mode: String,
334 pub aggregate_errors: bool,
336 pub validate_responses: bool,
338 pub overrides: HashMap<String, String>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct ValidationUpdate {
345 pub mode: String,
347 pub aggregate_errors: bool,
349 pub validate_responses: bool,
351 pub overrides: Option<HashMap<String, String>>,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct LogEntry {
358 pub timestamp: chrono::DateTime<chrono::Utc>,
360 pub status: u16,
362 pub method: String,
364 pub url: String,
366 pub response_time: u64,
368 pub size: u64,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct HealthCheck {
375 pub status: String,
377 pub services: HashMap<String, String>,
379 pub last_check: chrono::DateTime<chrono::Utc>,
381 pub issues: Vec<String>,
383}
384
385impl HealthCheck {
386 pub fn healthy() -> Self {
388 Self {
389 status: "healthy".to_string(),
390 services: HashMap::new(),
391 last_check: chrono::Utc::now(),
392 issues: Vec::new(),
393 }
394 }
395
396 pub fn unhealthy(issues: Vec<String>) -> Self {
398 Self {
399 status: "unhealthy".to_string(),
400 services: HashMap::new(),
401 last_check: chrono::Utc::now(),
402 issues,
403 }
404 }
405
406 pub fn with_service(mut self, name: String, status: String) -> Self {
408 self.services.insert(name, status);
409 self
410 }
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct WorkspaceSummary {
416 pub id: String,
418 pub name: String,
420 pub description: Option<String>,
422 pub active: bool,
424 pub folder_count: usize,
426 pub request_count: usize,
428 pub created_at: chrono::DateTime<chrono::Utc>,
430 pub updated_at: chrono::DateTime<chrono::Utc>,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct FolderSummary {
437 pub id: String,
439 pub name: String,
441 pub description: Option<String>,
443 pub parent_id: Option<String>,
445 pub subfolder_count: usize,
447 pub request_count: usize,
449 pub created_at: chrono::DateTime<chrono::Utc>,
451 pub updated_at: chrono::DateTime<chrono::Utc>,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct RequestSummary {
458 pub id: String,
460 pub name: String,
462 pub description: Option<String>,
464 pub method: String,
466 pub path: String,
468 pub status_code: u16,
470 pub created_at: chrono::DateTime<chrono::Utc>,
472 pub updated_at: chrono::DateTime<chrono::Utc>,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct WorkspaceDetail {
479 pub summary: WorkspaceSummary,
481 pub folders: Vec<FolderSummary>,
483 pub requests: Vec<RequestSummary>,
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct FolderDetail {
490 pub summary: FolderSummary,
492 pub subfolders: Vec<FolderSummary>,
494 pub requests: Vec<RequestSummary>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct CreateWorkspaceRequest {
501 pub name: String,
503 pub description: Option<String>,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CreateFolderRequest {
510 pub name: String,
512 pub description: Option<String>,
514 pub parent_id: Option<String>,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
520pub struct CreateRequestRequest {
521 pub name: String,
523 pub description: Option<String>,
525 pub method: String,
527 pub path: String,
529 pub status_code: Option<u16>,
531 pub response_body: Option<String>,
533 pub folder_id: Option<String>,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct ImportToWorkspaceRequest {
540 pub format: String,
542 pub data: String,
544 pub folder_id: Option<String>,
546 pub create_folders: Option<bool>,
548 pub selected_routes: Option<Vec<usize>>,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct ExportWorkspacesRequest {
555 pub workspace_ids: Vec<String>,
557}
558
559#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct WorkspaceExportData {
562 pub workspaces: Vec<mockforge_core::Workspace>,
564 pub version: String,
566 pub exported_at: chrono::DateTime<chrono::Utc>,
568 pub exporter_version: String,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct EnvironmentColor {
575 pub hex: String,
577 pub name: Option<String>,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct EnvironmentSummary {
584 pub id: String,
586 pub name: String,
588 pub description: Option<String>,
590 pub color: Option<EnvironmentColor>,
592 pub variable_count: usize,
594 pub active: bool,
596 pub is_global: bool,
598 pub created_at: chrono::DateTime<chrono::Utc>,
600 pub updated_at: chrono::DateTime<chrono::Utc>,
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct EnvironmentVariable {
607 pub name: String,
609 pub value: String,
611 pub from_global: bool,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct CreateEnvironmentRequest {
618 pub name: String,
620 pub description: Option<String>,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct UpdateEnvironmentRequest {
627 pub name: Option<String>,
629 pub description: Option<String>,
631 pub color: Option<EnvironmentColor>,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
637pub struct SetVariableRequest {
638 pub name: String,
640 pub value: String,
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize)]
646pub struct SyncConfig {
647 pub enabled: bool,
649 pub target_directory: Option<String>,
651 pub directory_structure: SyncDirectoryStructure,
653 pub sync_direction: SyncDirection,
655 pub include_metadata: bool,
657 pub realtime_monitoring: bool,
659 pub filename_pattern: String,
661 pub exclude_pattern: Option<String>,
663 pub force_overwrite: bool,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize)]
669pub enum SyncDirectoryStructure {
670 Flat,
672 Nested,
674 Grouped,
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize)]
680pub enum SyncDirection {
681 Manual,
683 WorkspaceToDirectory,
685 Bidirectional,
687}
688
689impl From<mockforge_core::workspace::SyncDirection> for SyncDirection {
690 fn from(core: mockforge_core::workspace::SyncDirection) -> Self {
691 match core {
692 mockforge_core::workspace::SyncDirection::Manual => Self::Manual,
693 mockforge_core::workspace::SyncDirection::WorkspaceToDirectory => {
694 Self::WorkspaceToDirectory
695 }
696 mockforge_core::workspace::SyncDirection::Bidirectional => Self::Bidirectional,
697 }
698 }
699}
700
701#[derive(Debug, Clone, Serialize, Deserialize)]
703pub struct SyncStatus {
704 pub workspace_id: String,
706 pub enabled: bool,
708 pub target_directory: Option<String>,
710 pub sync_direction: SyncDirection,
712 pub realtime_monitoring: bool,
714 pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
716 pub status: String,
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct SyncChange {
723 pub change_type: String,
725 pub path: String,
727 pub description: String,
729 pub requires_confirmation: bool,
731}
732
733#[derive(Debug, Clone, Serialize, Deserialize)]
735pub struct ConfigureSyncRequest {
736 pub target_directory: String,
738 pub sync_direction: SyncDirection,
740 pub realtime_monitoring: bool,
742 pub directory_structure: Option<SyncDirectoryStructure>,
744 pub filename_pattern: Option<String>,
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
750pub struct ConfirmSyncChangesRequest {
751 pub workspace_id: String,
753 pub changes: Vec<SyncChange>,
755 pub apply_all: bool,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
761pub struct AutocompleteSuggestion {
762 pub text: String,
764 pub kind: String,
766 pub description: Option<String>,
768}
769
770#[derive(Debug, Clone, Serialize, Deserialize)]
772pub struct AutocompleteRequest {
773 pub input: String,
775 pub cursor_position: usize,
777 pub context: Option<String>,
779}
780
781#[derive(Debug, Clone, Serialize, Deserialize)]
783pub struct AutocompleteResponse {
784 pub suggestions: Vec<AutocompleteSuggestion>,
786 pub start_position: usize,
788 pub end_position: usize,
790}
791
792#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct TimeSeriesPoint {
795 pub timestamp: chrono::DateTime<chrono::Utc>,
797 pub value: f64,
799}
800
801#[derive(Debug, Clone, Serialize, Deserialize)]
803pub struct TimeSeriesData {
804 pub points: Vec<TimeSeriesPoint>,
806 pub metric: String,
808}
809
810#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct RestartStatus {
813 pub restarting: bool,
815 pub progress: f64,
817 pub message: String,
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize)]
823pub struct SmokeTestResult {
824 pub test_name: String,
826 pub passed: bool,
828 pub response_time_ms: Option<u64>,
830 pub error_message: Option<String>,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize)]
836pub struct SmokeTestContext {
837 pub suite_name: String,
839 pub total_tests: usize,
841 pub passed_tests: usize,
843 pub failed_tests: usize,
845 pub start_time: chrono::DateTime<chrono::Utc>,
847 pub end_time: Option<chrono::DateTime<chrono::Utc>>,
849}
850
851#[derive(Debug, Clone, Serialize, Deserialize)]
853pub struct ConfigurationState {
854 pub valid: bool,
856 pub errors: Vec<String>,
858 pub warnings: Vec<String>,
860}
861
862#[derive(Debug, Clone, Serialize, Deserialize)]
864pub struct ImportHistoryEntry {
865 pub id: String,
867 pub format: String,
869 pub timestamp: chrono::DateTime<chrono::Utc>,
871 pub routes_count: usize,
873 pub variables_count: usize,
875 pub warnings_count: usize,
877 pub success: bool,
879 pub filename: Option<String>,
881 pub environment: Option<String>,
883 pub base_url: Option<String>,
885 pub error_message: Option<String>,
887}
888
889#[derive(Debug, Clone, Serialize, Deserialize)]
891pub struct FixtureInfo {
892 pub id: String,
894 pub name: String,
896 pub path: String,
898 pub size_bytes: u64,
900 pub last_modified: chrono::DateTime<chrono::Utc>,
902 pub content_type: Option<String>,
904}
905
906#[derive(Debug, Clone, Serialize, Deserialize)]
908pub struct FixtureDeleteRequest {
909 pub fixture_id: String,
911}
912
913#[derive(Debug, Clone, Serialize, Deserialize)]
915pub struct FixtureBulkDeleteRequest {
916 pub fixture_ids: Vec<String>,
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct FixtureBulkDeleteResult {
923 pub deleted: Vec<String>,
925 pub failed: HashMap<String, String>,
927}
928
929#[derive(Debug, Clone, Serialize, Deserialize)]
931pub struct ImportRoute {
932 pub method: String,
934 pub path: String,
936 pub headers: HashMap<String, String>,
938 pub body: Option<String>,
940 pub response: ImportResponse,
942}
943
944#[derive(Debug, Clone, Serialize, Deserialize)]
946pub struct ImportResponse {
947 pub status: u16,
949 pub headers: HashMap<String, String>,
951 pub body: serde_json::Value,
953}
954
955#[derive(Debug, Clone, Serialize, Deserialize)]
957pub struct ImportResult {
958 pub routes: Vec<ImportRoute>,
960 pub warnings: Vec<String>,
962 pub errors: Vec<String>,
964}
965
966#[derive(Debug, Clone, Serialize, Deserialize)]
968pub struct InsomniaImportResult {
969 pub routes: Vec<ImportRoute>,
971 pub variables: HashMap<String, String>,
973 pub warnings: Vec<String>,
975}
976
977#[derive(Debug, Clone, Serialize, Deserialize)]
979pub struct ServerInfo {
980 pub version: String,
982 pub build_time: String,
984 pub git_sha: String,
986 pub http_server: Option<String>,
988 pub ws_server: Option<String>,
990 pub grpc_server: Option<String>,
992 pub graphql_server: Option<String>,
994 pub api_enabled: bool,
996 pub admin_port: u16,
998}
999
1000#[cfg(test)]
1001mod tests {
1002 use super::*;
1003
1004 #[test]
1005 fn test_api_response_success() {
1006 let data = "test data".to_string();
1007 let response = ApiResponse::success(data.clone());
1008
1009 assert!(response.success);
1010 assert_eq!(response.data, Some(data));
1011 assert!(response.error.is_none());
1012 }
1013
1014 #[test]
1015 fn test_api_response_error() {
1016 let error_msg = "Something went wrong".to_string();
1017 let response: ApiResponse<String> = ApiResponse::error(error_msg.clone());
1018
1019 assert!(!response.success);
1020 assert!(response.data.is_none());
1021 assert_eq!(response.error, Some(error_msg));
1022 }
1023
1024 #[test]
1025 fn test_health_check_healthy() {
1026 let health = HealthCheck::healthy();
1027
1028 assert_eq!(health.status, "healthy");
1029 assert!(health.issues.is_empty());
1030 assert!(health.services.is_empty());
1031 }
1032
1033 #[test]
1034 fn test_health_check_unhealthy() {
1035 let issues = vec!["Database down".to_string(), "Cache error".to_string()];
1036 let health = HealthCheck::unhealthy(issues.clone());
1037
1038 assert_eq!(health.status, "unhealthy");
1039 assert_eq!(health.issues, issues);
1040 }
1041
1042 #[test]
1043 fn test_health_check_with_service() {
1044 let health = HealthCheck::healthy()
1045 .with_service("http".to_string(), "running".to_string())
1046 .with_service("grpc".to_string(), "running".to_string());
1047
1048 assert_eq!(health.services.len(), 2);
1049 assert_eq!(health.services.get("http"), Some(&"running".to_string()));
1050 }
1051
1052 #[test]
1053 fn test_log_filter_default() {
1054 let filter = LogFilter::default();
1055
1056 assert!(filter.method.is_none());
1057 assert!(filter.path_pattern.is_none());
1058 assert_eq!(filter.hours_ago, Some(24));
1059 assert_eq!(filter.limit, Some(100));
1060 }
1061
1062 #[test]
1063 fn test_server_status() {
1064 let status = ServerStatus {
1065 server_type: "HTTP".to_string(),
1066 address: Some("127.0.0.1:3000".to_string()),
1067 running: true,
1068 start_time: Some(chrono::Utc::now()),
1069 uptime_seconds: Some(3600),
1070 active_connections: 10,
1071 total_requests: 1000,
1072 };
1073
1074 assert_eq!(status.server_type, "HTTP");
1075 assert!(status.running);
1076 assert_eq!(status.active_connections, 10);
1077 }
1078
1079 #[test]
1080 fn test_route_info() {
1081 let route = RouteInfo {
1082 method: Some("GET".to_string()),
1083 path: "/api/users".to_string(),
1084 priority: 100,
1085 has_fixtures: true,
1086 latency_ms: Some(50),
1087 request_count: 500,
1088 last_request: None,
1089 error_count: 5,
1090 };
1091
1092 assert_eq!(route.method, Some("GET".to_string()));
1093 assert_eq!(route.path, "/api/users");
1094 assert!(route.has_fixtures);
1095 }
1096
1097 #[test]
1098 fn test_sync_direction_conversion() {
1099 let manual = mockforge_core::workspace::SyncDirection::Manual;
1100 let ui_manual: SyncDirection = manual.into();
1101 assert!(matches!(ui_manual, SyncDirection::Manual));
1102 }
1103
1104 #[test]
1105 fn test_environment_color() {
1106 let color = EnvironmentColor {
1107 hex: "#FF5733".to_string(),
1108 name: Some("Orange".to_string()),
1109 };
1110
1111 assert_eq!(color.hex, "#FF5733");
1112 assert_eq!(color.name, Some("Orange".to_string()));
1113 }
1114}