azure_functions/bindings/
queue_trigger.rs

1use crate::{
2    bindings::QueueMessage,
3    rpc::{typed_data::Data, TypedData},
4    util::convert_from,
5};
6use chrono::{DateTime, Utc};
7use std::collections::HashMap;
8
9const ID_KEY: &str = "Id";
10const DEQUEUE_COUNT_KEY: &str = "DequeueCount";
11const EXPIRATION_TIME_KEY: &str = "ExpirationTime";
12const INSERTION_TIME_KEY: &str = "InsertionTime";
13const NEXT_VISIBLE_TIME_KEY: &str = "NextVisibleTime";
14const POP_RECEIPT_KEY: &str = "PopReceipt";
15
16/// Represents a queue trigger binding.
17///
18/// The following binding attributes are supported:
19///
20/// | Name         | Description                                                                                                                                  |
21/// |--------------|----------------------------------------------------------------------------------------------------------------------------------------------|
22/// | `name`       | The name of the parameter being bound.                                                                                                       |
23/// | `queue_name` | The name of the queue to poll.                                                                                                               |
24/// | `connection` | The name of an app setting that contains the Azure Storage connection string to use for this binding. Defaults to the `AzureWebJobsStorage`. |
25///
26/// # Examples
27///
28/// A function that runs when a message is posted to a queue called `example`:
29///
30/// ```rust
31/// use azure_functions::bindings::QueueTrigger;
32/// use azure_functions::func;
33/// use log::info;
34///
35/// #[func]
36/// #[binding(name = "trigger", queue_name = "example")]
37/// pub fn run_on_message(trigger: QueueTrigger) {
38///     info!("Rust function ran due to queue message: {}", trigger.message);
39/// }
40/// ```
41#[derive(Debug)]
42pub struct QueueTrigger {
43    /// The queue message that triggered the function.
44    pub message: QueueMessage,
45    /// The queue message identifier.
46    pub id: String,
47    /// The number of times this message has been dequeued.
48    pub dequeue_count: u32,
49    /// The time that the message expires.
50    pub expiration_time: DateTime<Utc>,
51    /// The time that the message was added to the queue.
52    pub insertion_time: DateTime<Utc>,
53    /// The time that the message will next be visible.
54    pub next_visible_time: DateTime<Utc>,
55    /// The message's pop receipt.
56    pub pop_receipt: String,
57}
58
59impl QueueTrigger {
60    #[doc(hidden)]
61    pub fn new(data: TypedData, mut metadata: HashMap<String, TypedData>) -> Self {
62        QueueTrigger {
63            message: data.into(),
64            id: metadata
65                .remove(ID_KEY)
66                .map(|data| match data.data {
67                    Some(Data::String(s)) => s,
68                    _ => panic!("expected a string for message id"),
69                })
70                .expect("expected a message id"),
71            dequeue_count: convert_from(
72                metadata
73                    .get(DEQUEUE_COUNT_KEY)
74                    .expect("expected a dequeue count"),
75            )
76            .expect("failed to convert dequeue count"),
77            expiration_time: convert_from(
78                metadata
79                    .get(EXPIRATION_TIME_KEY)
80                    .expect("expected an expiration time"),
81            )
82            .expect("failed to convert expiration time"),
83            insertion_time: convert_from(
84                metadata
85                    .get(INSERTION_TIME_KEY)
86                    .expect("expected an insertion time"),
87            )
88            .expect("failed to convert insertion time"),
89            next_visible_time: convert_from(
90                metadata
91                    .get(NEXT_VISIBLE_TIME_KEY)
92                    .expect("expected a next visible time"),
93            )
94            .expect("failed to convert next visible time"),
95            pop_receipt: metadata
96                .remove(POP_RECEIPT_KEY)
97                .map(|data| match data.data {
98                    Some(Data::String(s)) => s,
99                    _ => panic!("expected a string for pop receipt"),
100                })
101                .expect("expected a pop receipt"),
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn it_constructs() {
112        const ID: &'static str = "12345";
113        const DEQUEUE_COUNT: u32 = 101;
114        const POP_RECEIPT: &'static str = "pop!";
115        const MESSAGE: &'static str = "\"hello world\"";
116        let now = Utc::now();
117
118        let data = TypedData {
119            data: Some(Data::Json(MESSAGE.to_string())),
120        };
121
122        let mut metadata = HashMap::new();
123
124        metadata.insert(
125            ID_KEY.to_string(),
126            TypedData {
127                data: Some(Data::String(ID.to_string())),
128            },
129        );
130
131        metadata.insert(
132            DEQUEUE_COUNT_KEY.to_string(),
133            TypedData {
134                data: Some(Data::Json(DEQUEUE_COUNT.to_string())),
135            },
136        );
137
138        metadata.insert(
139            EXPIRATION_TIME_KEY.to_string(),
140            TypedData {
141                data: Some(Data::String(now.to_rfc3339())),
142            },
143        );
144
145        metadata.insert(
146            INSERTION_TIME_KEY.to_string(),
147            TypedData {
148                data: Some(Data::String(now.to_rfc3339())),
149            },
150        );
151
152        metadata.insert(
153            NEXT_VISIBLE_TIME_KEY.to_string(),
154            TypedData {
155                data: Some(Data::Json("\"".to_string() + &now.to_rfc3339() + "\"")),
156            },
157        );
158
159        metadata.insert(
160            POP_RECEIPT_KEY.to_string(),
161            TypedData {
162                data: Some(Data::String(POP_RECEIPT.to_string())),
163            },
164        );
165
166        let trigger = QueueTrigger::new(data, metadata);
167        assert_eq!(trigger.id, ID);
168        assert_eq!(trigger.dequeue_count, DEQUEUE_COUNT);
169        assert_eq!(trigger.expiration_time.to_rfc3339(), now.to_rfc3339());
170        assert_eq!(trigger.insertion_time.to_rfc3339(), now.to_rfc3339());
171        assert_eq!(trigger.next_visible_time.to_rfc3339(), now.to_rfc3339());
172        assert_eq!(trigger.pop_receipt, POP_RECEIPT);
173        assert_eq!(trigger.message.as_str().unwrap(), MESSAGE);
174    }
175}