1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct Transfer {
9 pub id: String,
11
12 pub block_id: String,
14
15 pub block_height: i64,
17
18 pub timestamp: String,
20
21 pub extrinsic_hash: Option<String>,
23
24 pub from_id: String,
26
27 pub to_id: String,
29
30 pub amount: String,
32
33 pub fee: String,
35
36 pub from_hash: String,
38
39 pub to_hash: String,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct TransfersByPrefixResult {
47 pub transfers: Vec<Transfer>,
49
50 pub total_count: i64,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct GraphQLResponse<T> {
57 pub data: Option<T>,
58 pub errors: Option<Vec<GraphQLError>>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct GraphQLError {
64 pub message: String,
65 pub locations: Option<Vec<GraphQLErrorLocation>>,
66 pub path: Option<Vec<serde_json::Value>>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct GraphQLErrorLocation {
72 pub line: i64,
73 pub column: i64,
74}
75
76#[derive(Debug, Clone, Default)]
78pub struct TransferQueryParams {
79 pub after_block: Option<u32>,
81
82 pub before_block: Option<u32>,
84
85 pub min_amount: Option<u128>,
87
88 pub max_amount: Option<u128>,
90
91 pub limit: u32,
93
94 pub offset: u32,
96}
97
98impl TransferQueryParams {
99 pub fn new() -> Self {
100 Self { limit: 100, offset: 0, ..Default::default() }
101 }
102
103 pub fn with_limit(mut self, limit: u32) -> Self {
104 self.limit = limit;
105 self
106 }
107
108 #[allow(dead_code)]
109 pub fn with_offset(mut self, offset: u32) -> Self {
110 self.offset = offset;
111 self
112 }
113
114 pub fn with_after_block(mut self, block: u32) -> Self {
115 self.after_block = Some(block);
116 self
117 }
118
119 pub fn with_before_block(mut self, block: u32) -> Self {
120 self.before_block = Some(block);
121 self
122 }
123
124 pub fn with_min_amount(mut self, amount: u128) -> Self {
125 self.min_amount = Some(amount);
126 self
127 }
128
129 #[allow(dead_code)]
130 pub fn with_max_amount(mut self, amount: u128) -> Self {
131 self.max_amount = Some(amount);
132 self
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_transfer_query_params_default() {
142 let params = TransferQueryParams::default();
143 assert_eq!(params.limit, 0);
144 assert_eq!(params.offset, 0);
145 assert!(params.after_block.is_none());
146 assert!(params.before_block.is_none());
147 assert!(params.min_amount.is_none());
148 assert!(params.max_amount.is_none());
149 }
150
151 #[test]
152 fn test_transfer_query_params_new() {
153 let params = TransferQueryParams::new();
154 assert_eq!(params.limit, 100);
155 assert_eq!(params.offset, 0);
156 }
157
158 #[test]
159 fn test_transfer_query_params_builder() {
160 let params = TransferQueryParams::new()
161 .with_limit(50)
162 .with_offset(10)
163 .with_after_block(1000)
164 .with_before_block(2000)
165 .with_min_amount(1_000_000)
166 .with_max_amount(10_000_000);
167
168 assert_eq!(params.limit, 50);
169 assert_eq!(params.offset, 10);
170 assert_eq!(params.after_block, Some(1000));
171 assert_eq!(params.before_block, Some(2000));
172 assert_eq!(params.min_amount, Some(1_000_000));
173 assert_eq!(params.max_amount, Some(10_000_000));
174 }
175
176 #[test]
177 fn test_transfer_deserialization() {
178 let json = r#"{
179 "id": "transfer-123",
180 "blockId": "block-456",
181 "blockHeight": 12345,
182 "timestamp": "2024-01-15T12:30:00Z",
183 "extrinsicHash": "0xabcd1234",
184 "fromId": "qzAlice123",
185 "toId": "qzBob456",
186 "amount": "1000000000000",
187 "fee": "1000000",
188 "fromHash": "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
189 "toHash": "5678efgh5678efgh5678efgh5678efgh5678efgh5678efgh5678efgh5678efgh"
190 }"#;
191
192 let transfer: Transfer = serde_json::from_str(json).expect("should deserialize");
193
194 assert_eq!(transfer.id, "transfer-123");
195 assert_eq!(transfer.block_id, "block-456");
196 assert_eq!(transfer.block_height, 12345);
197 assert_eq!(transfer.timestamp, "2024-01-15T12:30:00Z");
198 assert_eq!(transfer.extrinsic_hash, Some("0xabcd1234".to_string()));
199 assert_eq!(transfer.from_id, "qzAlice123");
200 assert_eq!(transfer.to_id, "qzBob456");
201 assert_eq!(transfer.amount, "1000000000000");
202 assert_eq!(transfer.fee, "1000000");
203 }
204
205 #[test]
206 fn test_transfer_deserialization_null_extrinsic_hash() {
207 let json = r#"{
208 "id": "transfer-123",
209 "blockId": "block-456",
210 "blockHeight": 12345,
211 "timestamp": "2024-01-15T12:30:00Z",
212 "extrinsicHash": null,
213 "fromId": "qzAlice123",
214 "toId": "qzBob456",
215 "amount": "1000000000000",
216 "fee": "1000000",
217 "fromHash": "abcd1234",
218 "toHash": "5678efgh"
219 }"#;
220
221 let transfer: Transfer = serde_json::from_str(json).expect("should deserialize");
222 assert!(transfer.extrinsic_hash.is_none());
223 }
224
225 #[test]
226 fn test_graphql_response_with_data() {
227 let json = r#"{
228 "data": {
229 "transfers": [],
230 "totalCount": 0
231 }
232 }"#;
233
234 let response: GraphQLResponse<TransfersByPrefixResult> =
235 serde_json::from_str(json).expect("should deserialize");
236
237 assert!(response.data.is_some());
238 assert!(response.errors.is_none());
239 assert_eq!(response.data.unwrap().total_count, 0);
240 }
241
242 #[test]
243 fn test_graphql_response_with_error() {
244 let json = r#"{
245 "data": null,
246 "errors": [
247 {
248 "message": "Query returned too many results",
249 "locations": [{"line": 1, "column": 1}],
250 "path": ["transfersByHashPrefix"]
251 }
252 ]
253 }"#;
254
255 let response: GraphQLResponse<TransfersByPrefixResult> =
256 serde_json::from_str(json).expect("should deserialize");
257
258 assert!(response.data.is_none());
259 assert!(response.errors.is_some());
260
261 let errors = response.errors.unwrap();
262 assert_eq!(errors.len(), 1);
263 assert_eq!(errors[0].message, "Query returned too many results");
264 }
265}