azure_functions/bindings/
blob_trigger.rs1use crate::{
2 bindings::Blob,
3 blob::Properties,
4 rpc::{typed_data::Data, TypedData},
5 util::convert_from,
6};
7use serde_json::from_str;
8use std::collections::HashMap;
9
10const PATH_KEY: &str = "BlobTrigger";
11const URI_KEY: &str = "Uri";
12const PROPERTIES_KEY: &str = "Properties";
13const METADATA_KEY: &str = "Metadata";
14
15#[derive(Debug)]
41pub struct BlobTrigger {
42 pub blob: Blob,
44 pub path: String,
46 pub uri: String,
48 pub properties: Properties,
50 pub metadata: HashMap<String, String>,
52}
53
54impl BlobTrigger {
55 #[doc(hidden)]
56 pub fn new(data: TypedData, mut metadata: HashMap<String, TypedData>) -> Self {
57 BlobTrigger {
58 blob: data.into(),
59 path: metadata
60 .remove(PATH_KEY)
61 .map(|data| match data.data {
62 Some(Data::String(s)) => s,
63 _ => panic!("expected a string for 'path' metadata key"),
64 })
65 .expect("expected a blob path"),
66 uri: metadata.get(URI_KEY).map_or(String::new(), |data| {
67 convert_from(data).unwrap_or_else(|| panic!("failed to convert uri"))
68 }),
69 properties: metadata
70 .remove(PROPERTIES_KEY)
71 .map_or(Properties::default(), |data| match data.data {
72 Some(Data::Json(s)) => {
73 from_str(&s).expect("failed to deserialize blob properties")
74 }
75 _ => panic!("expected a string for properties"),
76 }),
77 metadata: metadata
78 .remove(METADATA_KEY)
79 .map_or(HashMap::new(), |data| match data.data {
80 Some(Data::Json(s)) => {
81 from_str(&s).expect("failed to deserialize blob metadata")
82 }
83 _ => panic!("expected a string for metadata"),
84 }),
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::blob::*;
93 use chrono::Utc;
94 use matches::matches;
95 use serde_json::{json, to_string};
96
97 #[test]
98 fn it_constructs() {
99 const BLOB: &'static str = "blob";
100 const PATH: &'static str = "foo/bar";
101 const URI: &'static str = "https://example.com/blob";
102 const CACHE_CONTROL: &'static str = "cache-control";
103 const CONTENT_DISPOSITION: &'static str = "content-disposition";
104 const CONTENT_ENCODING: &'static str = "content-encoding";
105 const CONTENT_LANGUAGE: &'static str = "content-language";
106 const CONTENT_LENGTH: u32 = 1234;
107 const CONTENT_MD5: &'static str = "abcdef";
108 const CONTENT_TYPE: &'static str = "text/plain";
109 const ETAG: &'static str = "12345";
110 const IS_SERVER_ENCRYPTED: bool = true;
111 const IS_INCREMENTAL_COPY: bool = false;
112 const BLOB_TIER_INFERRED: bool = false;
113 const USER_METADAT_KEY: &'static str = "key";
114 const USER_METADATA_VALUE: &'static str = "value";
115
116 let now = Utc::now();
117
118 let properties = json!({
119 "CacheControl": CACHE_CONTROL,
120 "ContentDisposition": CONTENT_DISPOSITION,
121 "ContentEncoding": CONTENT_ENCODING,
122 "ContentLanguage": CONTENT_LANGUAGE,
123 "Length": CONTENT_LENGTH,
124 "ContentMD5": CONTENT_MD5,
125 "ContentType": CONTENT_TYPE,
126 "ETag": ETAG,
127 "LastModified": now.to_rfc3339(),
128 "BlobType": 2,
129 "LeaseStatus": 2,
130 "LeaseState": 1,
131 "LeaseDuration": 0,
132 "PageBlobSequenceNumber": null,
133 "AppendBlobCommittedBlockCount": null,
134 "IsServerEncrypted": IS_SERVER_ENCRYPTED,
135 "IsIncrementalCopy": IS_INCREMENTAL_COPY,
136 "StandardBlobTier": 0,
137 "RehydrationStatus": null,
138 "PremiumPageBlobTier": null,
139 "BlobTierInferred": BLOB_TIER_INFERRED,
140 "BlobTierLastModifiedTime": null
141 });
142
143 let data = TypedData {
144 data: Some(Data::String(BLOB.to_string())),
145 };
146
147 let mut user_metadata = HashMap::new();
148 user_metadata.insert(
149 USER_METADAT_KEY.to_string(),
150 USER_METADATA_VALUE.to_string(),
151 );
152
153 let mut metadata = HashMap::new();
154 metadata.insert(
155 PATH_KEY.to_string(),
156 TypedData {
157 data: Some(Data::String(PATH.to_string())),
158 },
159 );
160 metadata.insert(
161 URI_KEY.to_string(),
162 TypedData {
163 data: Some(Data::Json("\"".to_string() + URI + "\"")),
164 },
165 );
166 metadata.insert(
167 PROPERTIES_KEY.to_string(),
168 TypedData {
169 data: Some(Data::Json(properties.to_string())),
170 },
171 );
172 metadata.insert(
173 METADATA_KEY.to_string(),
174 TypedData {
175 data: Some(Data::Json(to_string(&user_metadata).unwrap())),
176 },
177 );
178
179 let trigger = BlobTrigger::new(data, metadata);
180 assert_eq!(trigger.path, PATH);
181 assert_eq!(trigger.uri, URI);
182
183 assert!(trigger
184 .properties
185 .append_blob_committed_block_count
186 .is_none());
187 assert_eq!(
188 *trigger.properties.blob_tier_inferred.as_ref().unwrap(),
189 BLOB_TIER_INFERRED
190 );
191 assert!(trigger.properties.blob_tier_last_modified_time.is_none());
192 assert!(matches!(trigger.properties.blob_type, BlobType::BlockBlob));
193 assert_eq!(
194 trigger.properties.cache_control.as_ref().unwrap(),
195 CACHE_CONTROL
196 );
197 assert_eq!(
198 trigger.properties.content_disposition.as_ref().unwrap(),
199 CONTENT_DISPOSITION
200 );
201 assert_eq!(
202 trigger.properties.content_encoding.as_ref().unwrap(),
203 CONTENT_ENCODING
204 );
205 assert_eq!(
206 trigger.properties.content_language.as_ref().unwrap(),
207 CONTENT_LANGUAGE
208 );
209 assert_eq!(
210 trigger.properties.content_md5.as_ref().unwrap(),
211 CONTENT_MD5
212 );
213 assert_eq!(
214 trigger.properties.content_type.as_ref().unwrap(),
215 CONTENT_TYPE
216 );
217 assert!(trigger.properties.created.is_none());
218 assert!(trigger.properties.deleted_time.is_none());
219 assert_eq!(trigger.properties.etag.as_ref().unwrap(), ETAG);
220 assert_eq!(trigger.properties.is_incremental_copy, IS_INCREMENTAL_COPY);
221 assert_eq!(trigger.properties.is_server_encrypted, IS_SERVER_ENCRYPTED);
222 assert_eq!(
223 trigger
224 .properties
225 .last_modified
226 .as_ref()
227 .unwrap()
228 .to_rfc3339(),
229 now.to_rfc3339()
230 );
231 assert!(matches!(
232 trigger.properties.lease_duration,
233 LeaseDuration::Unspecified
234 ));
235 assert!(matches!(
236 trigger.properties.lease_state,
237 LeaseState::Available
238 ));
239 assert!(matches!(
240 trigger.properties.lease_status,
241 LeaseStatus::Unlocked
242 ));
243 assert_eq!(trigger.properties.length, CONTENT_LENGTH as i64);
244 assert!(trigger.properties.page_blob_sequence_number.is_none());
245 assert!(trigger.properties.premium_page_blob_tier.is_none());
246 assert!(trigger.properties.rehydration_status.is_none());
247 assert!(trigger
248 .properties
249 .remaining_days_before_permanent_delete
250 .is_none());
251 assert!(matches!(
252 trigger.properties.standard_blob_tier.as_ref().unwrap(),
253 StandardBlobTier::Unknown
254 ));
255
256 assert_eq!(trigger.metadata.len(), 1);
257 assert_eq!(
258 trigger.metadata.get(USER_METADAT_KEY).unwrap(),
259 USER_METADATA_VALUE
260 );
261 }
262}