1use cheetah_string::CheetahString;
16use rocketmq_macros::RequestHeaderCodecV2;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::protocol::header::message_operation_header::TopicRequestHeaderTrait;
21use crate::protocol::header::namesrv::topic_operation_header::TopicRequestHeader;
22
23#[derive(Debug, Clone, Deserialize, Serialize, Default, RequestHeaderCodecV2)]
24#[serde(rename_all = "camelCase")]
25pub struct PullMessageRequestHeader {
26 #[required]
27 pub consumer_group: CheetahString,
28
29 #[required]
30 pub topic: CheetahString,
31
32 pub lite_topic: Option<CheetahString>,
33
34 #[required]
35 pub queue_id: i32,
36
37 #[required]
38 pub queue_offset: i64,
39
40 #[required]
41 pub max_msg_nums: i32,
42
43 #[required]
44 pub sys_flag: i32,
45
46 #[required]
47 pub commit_offset: i64,
48
49 #[required]
50 pub suspend_timeout_millis: u64,
51
52 #[required]
53 pub sub_version: i64,
54
55 pub subscription: Option<CheetahString>,
56 pub expression_type: Option<CheetahString>,
57 pub max_msg_bytes: Option<i32>,
58 pub request_source: Option<i32>,
59 #[serde(rename = "proxyFrowardClientId", alias = "proxyForwardClientId")]
60 pub proxy_forward_client_id: Option<CheetahString>,
61 #[serde(flatten)]
62 pub topic_request: Option<TopicRequestHeader>,
63}
64
65impl TopicRequestHeaderTrait for PullMessageRequestHeader {
66 fn set_lo(&mut self, lo: Option<bool>) {
67 if let Some(header) = self.topic_request.as_mut() {
68 header.lo = lo;
69 }
70 }
71
72 fn lo(&self) -> Option<bool> {
73 self.topic_request.as_ref().and_then(|h| h.lo)
74 }
75
76 fn set_topic(&mut self, topic: CheetahString) {
77 self.topic = topic;
78 }
79
80 fn topic(&self) -> &CheetahString {
81 &self.topic
82 }
83
84 fn broker_name(&self) -> Option<&CheetahString> {
85 self.topic_request
86 .as_ref()
87 .and_then(|h| h.rpc.as_ref())
88 .and_then(|h| h.broker_name.as_ref())
89 }
90
91 fn set_broker_name(&mut self, broker_name: CheetahString) {
92 if let Some(header) = self.topic_request.as_mut() {
93 if let Some(rpc_header) = header.rpc.as_mut() {
94 rpc_header.broker_name = Some(broker_name);
95 }
96 }
97 }
98
99 fn namespace(&self) -> Option<&str> {
100 self.topic_request
101 .as_ref()
102 .and_then(|h| h.rpc.as_ref())
103 .and_then(|r| r.namespace.as_deref())
104 }
105
106 fn set_namespace(&mut self, namespace: CheetahString) {
107 if let Some(header) = self.topic_request.as_mut() {
108 if let Some(rpc_header) = header.rpc.as_mut() {
109 rpc_header.namespace = Some(namespace);
110 }
111 }
112 }
113
114 fn namespaced(&self) -> Option<bool> {
115 self.topic_request
116 .as_ref()
117 .and_then(|h| h.rpc.as_ref())
118 .and_then(|r| r.namespaced)
119 }
120
121 fn set_namespaced(&mut self, namespaced: bool) {
122 if let Some(header) = self.topic_request.as_mut() {
123 if let Some(rpc_header) = header.rpc.as_mut() {
124 rpc_header.namespaced = Some(namespaced);
125 }
126 }
127 }
128
129 fn oneway(&self) -> Option<bool> {
130 self.topic_request
131 .as_ref()
132 .and_then(|h| h.rpc.as_ref())
133 .and_then(|r| r.oneway)
134 }
135
136 fn set_oneway(&mut self, oneway: bool) {
137 if let Some(header) = self.topic_request.as_mut() {
138 if let Some(rpc_header) = header.rpc.as_mut() {
139 rpc_header.oneway = Some(oneway);
140 }
141 }
142 }
143
144 fn queue_id(&self) -> i32 {
145 self.queue_id
146 }
147
148 fn set_queue_id(&mut self, queue_id: i32) {
149 self.queue_id = queue_id;
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use std::collections::HashMap;
156
157 use cheetah_string::CheetahString;
158
159 use super::*;
160 use crate::protocol::command_custom_header::CommandCustomHeader;
161 use crate::protocol::command_custom_header::FromMap;
162
163 #[test]
164 fn pull_message_request_header_serializes_correctly() {
165 let header = PullMessageRequestHeader {
166 consumer_group: CheetahString::from_static_str("test_consumer_group"),
167 topic: CheetahString::from_static_str("test_topic"),
168 lite_topic: Some(CheetahString::from_static_str("test_lite_topic")),
169 queue_id: 1,
170 queue_offset: 100,
171 max_msg_nums: 10,
172 sys_flag: 0,
173 commit_offset: 50,
174 suspend_timeout_millis: 3000,
175 subscription: Some(CheetahString::from_static_str("test_subscription")),
176 sub_version: 1,
177 expression_type: Some(CheetahString::from_static_str("test_expression")),
178 max_msg_bytes: Some(1024),
179 request_source: Some(1),
180 proxy_forward_client_id: Some(CheetahString::from_static_str("test_client_id")),
181 topic_request: None,
182 };
183 let map: HashMap<CheetahString, CheetahString> = header.to_map().unwrap();
184 assert_eq!(
185 map.get(&CheetahString::from_static_str("consumerGroup")).unwrap(),
186 "test_consumer_group"
187 );
188 assert_eq!(map.get(&CheetahString::from_static_str("topic")).unwrap(), "test_topic");
189 assert_eq!(
190 map.get(&CheetahString::from_static_str("liteTopic")).unwrap(),
191 "test_lite_topic"
192 );
193 assert_eq!(map.get(&CheetahString::from_static_str("queueId")).unwrap(), "1");
194 assert_eq!(map.get(&CheetahString::from_static_str("queueOffset")).unwrap(), "100");
195 assert_eq!(map.get(&CheetahString::from_static_str("maxMsgNums")).unwrap(), "10");
196 assert_eq!(map.get(&CheetahString::from_static_str("sysFlag")).unwrap(), "0");
197 assert_eq!(map.get(&CheetahString::from_static_str("commitOffset")).unwrap(), "50");
198 assert_eq!(
199 map.get(&CheetahString::from_static_str("suspendTimeoutMillis"))
200 .unwrap(),
201 "3000"
202 );
203 assert_eq!(
204 map.get(&CheetahString::from_static_str("subscription")).unwrap(),
205 "test_subscription"
206 );
207 assert_eq!(map.get(&CheetahString::from_static_str("subVersion")).unwrap(), "1");
208 assert_eq!(
209 map.get(&CheetahString::from_static_str("expressionType")).unwrap(),
210 "test_expression"
211 );
212 assert_eq!(map.get(&CheetahString::from_static_str("maxMsgBytes")).unwrap(), "1024");
213 assert_eq!(map.get(&CheetahString::from_static_str("requestSource")).unwrap(), "1");
214 assert_eq!(
215 map.get(&CheetahString::from_static_str("proxyFrowardClientId"))
216 .unwrap(),
217 "test_client_id"
218 );
219 }
220
221 #[test]
222 fn pull_message_request_header_deserializes_correctly() {
223 let mut map = HashMap::new();
224 map.insert(
225 CheetahString::from_static_str("consumerGroup"),
226 CheetahString::from_static_str("test_consumer_group"),
227 );
228 map.insert(
229 CheetahString::from_static_str("topic"),
230 CheetahString::from_static_str("test_topic"),
231 );
232 map.insert(
233 CheetahString::from_static_str("liteTopic"),
234 CheetahString::from_static_str("test_lite_topic"),
235 );
236 map.insert(
237 CheetahString::from_static_str("queueId"),
238 CheetahString::from_static_str("1"),
239 );
240 map.insert(
241 CheetahString::from_static_str("queueOffset"),
242 CheetahString::from_static_str("100"),
243 );
244 map.insert(
245 CheetahString::from_static_str("maxMsgNums"),
246 CheetahString::from_static_str("10"),
247 );
248 map.insert(
249 CheetahString::from_static_str("sysFlag"),
250 CheetahString::from_static_str("0"),
251 );
252 map.insert(
253 CheetahString::from_static_str("commitOffset"),
254 CheetahString::from_static_str("50"),
255 );
256 map.insert(
257 CheetahString::from_static_str("suspendTimeoutMillis"),
258 CheetahString::from_static_str("3000"),
259 );
260 map.insert(
261 CheetahString::from_static_str("subscription"),
262 CheetahString::from_static_str("test_subscription"),
263 );
264 map.insert(
265 CheetahString::from_static_str("subVersion"),
266 CheetahString::from_static_str("1"),
267 );
268 map.insert(
269 CheetahString::from_static_str("expressionType"),
270 CheetahString::from_static_str("test_expression"),
271 );
272 map.insert(
273 CheetahString::from_static_str("maxMsgBytes"),
274 CheetahString::from_static_str("1024"),
275 );
276 map.insert(
277 CheetahString::from_static_str("requestSource"),
278 CheetahString::from_static_str("1"),
279 );
280 map.insert(
281 CheetahString::from_static_str("proxyFrowardClientId"),
282 CheetahString::from_static_str("test_client_id"),
283 );
284
285 let header = <PullMessageRequestHeader as FromMap>::from(&map).unwrap();
286 assert_eq!(header.consumer_group, "test_consumer_group");
287 assert_eq!(header.topic, "test_topic");
288 assert_eq!(header.lite_topic.unwrap(), "test_lite_topic");
289 assert_eq!(header.queue_id, 1);
290 assert_eq!(header.queue_offset, 100);
291 assert_eq!(header.max_msg_nums, 10);
292 assert_eq!(header.sys_flag, 0);
293 assert_eq!(header.commit_offset, 50);
294 assert_eq!(header.suspend_timeout_millis, 3000);
295 assert_eq!(header.subscription.unwrap(), "test_subscription");
296 assert_eq!(header.sub_version, 1);
297 assert_eq!(header.expression_type.unwrap(), "test_expression");
298 assert_eq!(header.max_msg_bytes.unwrap(), 1024);
299 assert_eq!(header.request_source.unwrap(), 1);
300 assert_eq!(header.proxy_forward_client_id.unwrap(), "test_client_id");
301 }
302
303 #[test]
304 fn pull_message_request_header_accepts_legacy_corrected_proxy_forward_key() {
305 let mut map = HashMap::new();
306 map.insert(
307 CheetahString::from_static_str("consumerGroup"),
308 CheetahString::from_static_str("test_consumer_group"),
309 );
310 map.insert(
311 CheetahString::from_static_str("topic"),
312 CheetahString::from_static_str("test_topic"),
313 );
314 map.insert(
315 CheetahString::from_static_str("liteTopic"),
316 CheetahString::from_static_str("test_lite_topic"),
317 );
318 map.insert(
319 CheetahString::from_static_str("queueId"),
320 CheetahString::from_static_str("1"),
321 );
322 map.insert(
323 CheetahString::from_static_str("queueOffset"),
324 CheetahString::from_static_str("100"),
325 );
326 map.insert(
327 CheetahString::from_static_str("maxMsgNums"),
328 CheetahString::from_static_str("10"),
329 );
330 map.insert(
331 CheetahString::from_static_str("sysFlag"),
332 CheetahString::from_static_str("0"),
333 );
334 map.insert(
335 CheetahString::from_static_str("commitOffset"),
336 CheetahString::from_static_str("50"),
337 );
338 map.insert(
339 CheetahString::from_static_str("suspendTimeoutMillis"),
340 CheetahString::from_static_str("3000"),
341 );
342 map.insert(
343 CheetahString::from_static_str("subVersion"),
344 CheetahString::from_static_str("1"),
345 );
346 map.insert(
347 CheetahString::from_static_str("proxyForwardClientId"),
348 CheetahString::from_static_str("legacy_client_id"),
349 );
350
351 let header = <PullMessageRequestHeader as FromMap>::from(&map).unwrap();
352
353 assert_eq!(header.proxy_forward_client_id.unwrap(), "legacy_client_id");
354 }
355
356 #[test]
357 fn pull_message_request_header_handles_missing_optional_fields() {
358 let mut map = HashMap::new();
359 map.insert(
360 CheetahString::from_static_str("consumerGroup"),
361 CheetahString::from_static_str("test_consumer_group"),
362 );
363 map.insert(
364 CheetahString::from_static_str("topic"),
365 CheetahString::from_static_str("test_topic"),
366 );
367 map.insert(
368 CheetahString::from_static_str("queueId"),
369 CheetahString::from_static_str("1"),
370 );
371 map.insert(
372 CheetahString::from_static_str("queueOffset"),
373 CheetahString::from_static_str("100"),
374 );
375 map.insert(
376 CheetahString::from_static_str("maxMsgNums"),
377 CheetahString::from_static_str("10"),
378 );
379 map.insert(
380 CheetahString::from_static_str("sysFlag"),
381 CheetahString::from_static_str("0"),
382 );
383 map.insert(
384 CheetahString::from_static_str("commitOffset"),
385 CheetahString::from_static_str("50"),
386 );
387 map.insert(
388 CheetahString::from_static_str("suspendTimeoutMillis"),
389 CheetahString::from_static_str("3000"),
390 );
391 map.insert(
392 CheetahString::from_static_str("subVersion"),
393 CheetahString::from_static_str("1"),
394 );
395
396 let header = <PullMessageRequestHeader as FromMap>::from(&map).unwrap();
397 assert_eq!(header.consumer_group, "test_consumer_group");
398 assert_eq!(header.topic, "test_topic");
399 assert!(header.lite_topic.is_none());
400 assert_eq!(header.queue_id, 1);
401 assert_eq!(header.queue_offset, 100);
402 assert_eq!(header.max_msg_nums, 10);
403 assert_eq!(header.sys_flag, 0);
404 assert_eq!(header.commit_offset, 50);
405 assert_eq!(header.suspend_timeout_millis, 3000);
406 assert_eq!(header.sub_version, 1);
407 assert!(header.subscription.is_none());
408 assert!(header.expression_type.is_none());
409 assert!(header.max_msg_bytes.is_none());
410 assert!(header.request_source.is_none());
411 assert!(header.proxy_forward_client_id.is_none());
412 }
413
414 #[test]
415 fn pull_message_request_header_handles_invalid_data() {
416 let mut map = HashMap::new();
417 map.insert(
418 CheetahString::from_static_str("consumerGroup"),
419 CheetahString::from_static_str("test_consumer_group"),
420 );
421 map.insert(
422 CheetahString::from_static_str("topic"),
423 CheetahString::from_static_str("test_topic"),
424 );
425 map.insert(
426 CheetahString::from_static_str("queueId"),
427 CheetahString::from_static_str("invalid"),
428 );
429 map.insert(
430 CheetahString::from_static_str("queueOffset"),
431 CheetahString::from_static_str("invalid"),
432 );
433 map.insert(
434 CheetahString::from_static_str("maxMsgNums"),
435 CheetahString::from_static_str("invalid"),
436 );
437 map.insert(
438 CheetahString::from_static_str("sysFlag"),
439 CheetahString::from_static_str("invalid"),
440 );
441 map.insert(
442 CheetahString::from_static_str("commitOffset"),
443 CheetahString::from_static_str("invalid"),
444 );
445 map.insert(
446 CheetahString::from_static_str("suspendTimeoutMillis"),
447 CheetahString::from_static_str("invalid"),
448 );
449 map.insert(
450 CheetahString::from_static_str("subVersion"),
451 CheetahString::from_static_str("invalid"),
452 );
453
454 let result = <PullMessageRequestHeader as FromMap>::from(&map);
455 assert!(result.is_err());
456 }
457}