openzeppelin_relayer/models/network/
request.rs1use crate::models::{deserialize_rpc_urls, ApiError, RpcConfig};
12use serde::{Deserialize, Serialize};
13use utoipa::ToSchema;
14
15#[derive(Serialize, ToSchema)]
22#[serde(untagged)]
23#[schema(as = RpcUrlEntry)]
24#[allow(dead_code)] pub enum RpcUrlEntry {
26 String(String),
29 Config(RpcConfig),
31}
32
33#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, ToSchema)]
36#[serde(deny_unknown_fields)]
37pub struct UpdateNetworkRequest {
38 #[serde(
45 default,
46 skip_serializing_if = "Option::is_none",
47 deserialize_with = "deserialize_rpc_urls"
48 )]
49 #[schema(
50 nullable = false,
51 example = json!([{"url": "https://rpc.example.com", "weight": 100}]),
52 value_type = Vec<RpcUrlEntry>
53 )]
54 pub rpc_urls: Option<Vec<RpcConfig>>,
55}
56
57impl UpdateNetworkRequest {
58 pub fn validate(&self) -> Result<(), ApiError> {
64 if let Some(ref rpc_urls) = self.rpc_urls {
66 if rpc_urls.is_empty() {
68 return Err(ApiError::BadRequest(
69 "rpc_urls must contain at least one RPC endpoint".to_string(),
70 ));
71 }
72
73 RpcConfig::validate_list(rpc_urls)
75 .map_err(|e| ApiError::BadRequest(format!("Invalid RPC URL configuration: {e}")))?;
76 }
77
78 Ok(())
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_update_network_request_validation_empty_rpc_urls() {
88 let request = UpdateNetworkRequest {
89 rpc_urls: Some(vec![]),
90 };
91 assert!(request.validate().is_err());
92 }
93
94 #[test]
95 fn test_update_network_request_validation_valid() {
96 let request = UpdateNetworkRequest {
97 rpc_urls: Some(vec![RpcConfig::new("https://rpc.example.com".to_string())]),
98 };
99 assert!(request.validate().is_ok());
100 }
101
102 #[test]
103 fn test_update_network_request_validation_invalid_url() {
104 let request = UpdateNetworkRequest {
105 rpc_urls: Some(vec![RpcConfig::new("ftp://invalid.com".to_string())]),
106 };
107 assert!(request.validate().is_err());
108 }
109
110 #[test]
111 fn test_update_network_request_validation_none_rpc_urls() {
112 let request = UpdateNetworkRequest { rpc_urls: None };
113 assert!(request.validate().is_ok());
114 }
115
116 #[test]
117 fn test_deserialize_rpc_urls_simple_format() {
118 let json = r#"{"rpc_urls": ["https://rpc1.com", "https://rpc2.com"]}"#;
119 let request: UpdateNetworkRequest = serde_json::from_str(json).unwrap();
120 assert_eq!(request.rpc_urls.as_ref().unwrap().len(), 2);
121 assert_eq!(
122 request.rpc_urls.as_ref().unwrap()[0].url,
123 "https://rpc1.com"
124 );
125 assert_eq!(request.rpc_urls.as_ref().unwrap()[0].weight, 100u8); assert_eq!(
127 request.rpc_urls.as_ref().unwrap()[1].url,
128 "https://rpc2.com"
129 );
130 assert_eq!(request.rpc_urls.as_ref().unwrap()[1].weight, 100u8); }
132
133 #[test]
134 fn test_deserialize_rpc_urls_extended_format() {
135 let json = r#"{"rpc_urls": [{"url": "https://rpc1.com", "weight": 50}, {"url": "https://rpc2.com", "weight": 75}]}"#;
136 let request: UpdateNetworkRequest = serde_json::from_str(json).unwrap();
137 assert_eq!(request.rpc_urls.as_ref().unwrap().len(), 2);
138 assert_eq!(
139 request.rpc_urls.as_ref().unwrap()[0].url,
140 "https://rpc1.com"
141 );
142 assert_eq!(request.rpc_urls.as_ref().unwrap()[0].weight, 50u8);
143 assert_eq!(
144 request.rpc_urls.as_ref().unwrap()[1].url,
145 "https://rpc2.com"
146 );
147 assert_eq!(request.rpc_urls.as_ref().unwrap()[1].weight, 75u8);
148 }
149
150 #[test]
151 fn test_deserialize_rpc_urls_mixed_format() {
152 let json =
153 r#"{"rpc_urls": ["https://rpc1.com", {"url": "https://rpc2.com", "weight": 50}]}"#;
154 let request: UpdateNetworkRequest = serde_json::from_str(json).unwrap();
155 assert_eq!(request.rpc_urls.as_ref().unwrap().len(), 2);
156 assert_eq!(
157 request.rpc_urls.as_ref().unwrap()[0].url,
158 "https://rpc1.com"
159 );
160 assert_eq!(request.rpc_urls.as_ref().unwrap()[0].weight, 100u8); assert_eq!(
162 request.rpc_urls.as_ref().unwrap()[1].url,
163 "https://rpc2.com"
164 );
165 assert_eq!(request.rpc_urls.as_ref().unwrap()[1].weight, 50u8); }
167
168 #[test]
169 fn test_deserialize_rpc_urls_none() {
170 let json = r#"{}"#;
171 let request: UpdateNetworkRequest = serde_json::from_str(json).unwrap();
172 assert!(request.rpc_urls.is_none());
173 }
174
175 #[test]
176 fn test_deserialize_rpc_urls_invalid_format() {
177 let json = r#"{"rpc_urls": [123, 456]}"#;
178 let result: Result<UpdateNetworkRequest, _> = serde_json::from_str(json);
179 assert!(result.is_err());
180 }
181}