1use super::{FileType, FromUploadPolicy, StaticUploadTokenProvider, ToStringOptions, UploadTokenProvider};
2use assert_impl::assert_impl;
3use qiniu_credential::{Credential, CredentialProvider};
4use qiniu_utils::{BucketName, ObjectName};
5use serde_json::{
6 json,
7 map::{Keys as JsonMapKeys, Values as JsonMapValues},
8 value::Index as JsonValueIndex,
9 Value as JsonValue,
10};
11use std::{
12 borrow::{Borrow, Cow},
13 fmt,
14 hash::Hash,
15 ops::{Bound, RangeBounds},
16 str::Split,
17 time::{Duration, SystemTime},
18};
19
20const SCOPE_KEY: &str = "scope";
21const IS_PREFIXAL_SCOPE_KEY: &str = "isPrefixalScope";
22const DEADLINE_KEY: &str = "deadline";
23const INSERT_ONLY_KEY: &str = "insertOnly";
24const RETURN_URL_KEY: &str = "returnUrl";
25const RETURN_BODY_KEY: &str = "returnBody";
26const CALLBACK_URL_KEY: &str = "callbackUrl";
27const CALLBACK_HOST_KEY: &str = "callbackHost";
28const CALLBACK_BODY_KEY: &str = "callbackBody";
29const CALLBACK_BODY_TYPE_KEY: &str = "callbackBodyType";
30const SAVE_KEY_KEY: &str = "saveKey";
31const FORCE_SAVE_KEY_KEY: &str = "forceSaveKey";
32const FSIZE_MIN_KEY: &str = "fsizeMin";
33const FSIZE_LIMIT_KEY: &str = "fsizeLimit";
34const DETECT_MIME_KEY: &str = "detectMime";
35const MIME_LIMIT_KEY: &str = "mimeLimit";
36const FILE_TYPE_KEY: &str = "fileType";
37const DELETE_AFTER_DAYS_KEY: &str = "deleteAfterDays";
38
39#[derive(Clone, Eq, PartialEq)]
54pub struct UploadPolicy {
55 inner: JsonValue,
56}
57
58impl UploadPolicy {
59 #[inline]
65 pub fn new_for_bucket(bucket: impl Into<BucketName>, upload_token_lifetime: Duration) -> UploadPolicyBuilder {
66 UploadPolicyBuilder::new_policy_for_bucket(bucket, upload_token_lifetime)
67 }
68
69 #[inline]
76 pub fn new_for_object(
77 bucket: impl Into<BucketName>,
78 object: impl Into<ObjectName>,
79 upload_token_lifetime: Duration,
80 ) -> UploadPolicyBuilder {
81 UploadPolicyBuilder::new_policy_for_object(bucket, object, upload_token_lifetime)
82 }
83
84 #[inline]
91 pub fn new_for_objects_with_prefix(
92 bucket: impl Into<BucketName>,
93 prefix: impl AsRef<str>,
94 upload_token_lifetime: Duration,
95 ) -> UploadPolicyBuilder {
96 UploadPolicyBuilder::new_policy_for_objects_with_prefix(bucket, prefix, upload_token_lifetime)
97 }
98
99 pub fn bucket(&self) -> Option<&str> {
101 self.get(SCOPE_KEY)
102 .as_ref()
103 .and_then(|s| s.as_str())
104 .and_then(|s| s.split(':').next())
105 }
106
107 pub fn key(&self) -> Option<&str> {
109 self.get(SCOPE_KEY)
110 .as_ref()
111 .and_then(|v| v.as_str())
112 .and_then(|s| s.split_once(':').map(|x| x.1))
113 }
114
115 pub fn use_prefixal_object_key(&self) -> bool {
117 self.get(IS_PREFIXAL_SCOPE_KEY).and_then(|v| v.as_u64()).is_some()
118 }
119
120 pub fn is_insert_only(&self) -> bool {
122 self.get(INSERT_ONLY_KEY).and_then(|v| v.as_u64()).unwrap_or_default() > 0
123 }
124
125 pub fn mime_detection_enabled(&self) -> bool {
127 self.get(DETECT_MIME_KEY).and_then(|v| v.as_u64()).unwrap_or_default() > 0
128 }
129
130 pub fn token_deadline(&self) -> Option<SystemTime> {
132 self.get(DEADLINE_KEY)
133 .and_then(|v| v.as_u64())
134 .map(|t| SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(t)).unwrap())
135 }
136
137 pub fn return_url(&self) -> Option<&str> {
139 self.get(RETURN_URL_KEY).and_then(|v| v.as_str())
140 }
141
142 pub fn return_body(&self) -> Option<&str> {
144 self.get(RETURN_BODY_KEY).and_then(|v| v.as_str())
145 }
146
147 pub fn callback_urls(&self) -> Option<Split<char>> {
149 self.get(CALLBACK_URL_KEY)
150 .and_then(|v| v.as_str())
151 .map(|s| s.split(';'))
152 }
153
154 pub fn callback_host(&self) -> Option<&str> {
156 self.get(CALLBACK_HOST_KEY).and_then(|v| v.as_str())
157 }
158
159 pub fn callback_body(&self) -> Option<&str> {
163 self.get(CALLBACK_BODY_KEY).and_then(|v| v.as_str())
164 }
165
166 pub fn callback_body_type(&self) -> Option<&str> {
170 self.get(CALLBACK_BODY_TYPE_KEY).and_then(|v| v.as_str())
171 }
172
173 pub fn save_key(&self) -> Option<&str> {
177 self.get(SAVE_KEY_KEY).and_then(|v| v.as_str())
178 }
179
180 pub fn is_save_key_forced(&self) -> bool {
182 self.get(FORCE_SAVE_KEY_KEY).and_then(|v| v.as_bool()).unwrap_or(false)
183 }
184
185 pub fn file_size_limitation(&self) -> (Option<u64>, Option<u64>) {
189 (
190 self.get(FSIZE_MIN_KEY).and_then(|v| v.as_u64()),
191 self.get(FSIZE_LIMIT_KEY).and_then(|v| v.as_u64()),
192 )
193 }
194
195 pub fn mime_types(&self) -> Option<Split<char>> {
200 self.get(MIME_LIMIT_KEY).and_then(|v| v.as_str()).map(|s| s.split(';'))
201 }
202
203 pub fn file_type(&self) -> Option<FileType> {
205 self.get(FILE_TYPE_KEY).and_then(|v| v.as_u64()).map(FileType::from)
206 }
207
208 pub fn object_lifetime(&self) -> Option<Duration> {
212 self.get(DELETE_AFTER_DAYS_KEY)
213 .and_then(|v| v.as_u64())
214 .map(|d| Duration::from_secs(d * 60 * 60 * 24))
215 }
216
217 pub fn as_json(&self) -> String {
219 serde_json::to_string(&self.inner).unwrap()
220 }
221
222 pub fn from_json(json: impl AsRef<[u8]>) -> serde_json::Result<UploadPolicy> {
224 serde_json::from_slice(json.as_ref()).map(|inner| UploadPolicy { inner })
225 }
226
227 #[inline]
229 pub fn get(&self, key: impl JsonValueIndex) -> Option<&JsonValue> {
230 self.inner.get(key)
231 }
232
233 #[inline]
235 pub fn keys(&self) -> JsonMapKeys {
236 self.inner.as_object().unwrap().keys()
237 }
238
239 #[inline]
241 pub fn values(&self) -> JsonMapValues {
242 self.inner.as_object().unwrap().values()
243 }
244
245 #[inline]
249 pub fn into_dynamic_upload_token_provider<T: CredentialProvider + Clone>(
250 self,
251 credential: T,
252 ) -> FromUploadPolicy<T> {
253 FromUploadPolicy::new(self, credential)
254 }
255
256 pub fn into_static_upload_token_provider(
260 self,
261 credential: Credential,
262 opts: ToStringOptions,
263 ) -> StaticUploadTokenProvider {
264 let provider = self.into_dynamic_upload_token_provider(credential);
265 let token = provider.to_token_string(opts).unwrap();
266 let token: StaticUploadTokenProvider = token.parse().unwrap();
267 let (policy, credential) = provider.split();
268 let (access_key, _) = credential.split();
269 token.set_policy(policy);
270 token.set_access_key(access_key);
271 token
272 }
273
274 #[allow(dead_code)]
275 fn assert() {
276 assert_impl!(Send: Self);
277 assert_impl!(Sync: Self);
278 }
279}
280
281impl fmt::Debug for UploadPolicy {
282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 self.inner.fmt(f)
284 }
285}
286
287#[derive(Clone)]
291pub struct UploadPolicyBuilder {
292 inner: JsonValue,
293}
294
295impl From<UploadPolicy> for UploadPolicyBuilder {
296 fn from(policy: UploadPolicy) -> Self {
297 Self { inner: policy.inner }
298 }
299}
300
301impl UploadPolicyBuilder {
302 pub fn new_policy_for_bucket(bucket: impl Into<BucketName>, upload_token_lifetime: Duration) -> Self {
308 let mut policy = Self {
309 inner: json!({
310 SCOPE_KEY: bucket.into().to_string(),
311 }),
312 };
313 policy.token_lifetime(upload_token_lifetime);
314 policy
315 }
316
317 pub fn new_policy_for_object(
324 bucket: impl Into<BucketName>,
325 object: impl Into<ObjectName>,
326 upload_token_lifetime: Duration,
327 ) -> Self {
328 let mut policy = Self {
329 inner: json!({
330 SCOPE_KEY: bucket.into().to_string() + ":" + object.into().as_str(),
331 }),
332 };
333 policy.token_lifetime(upload_token_lifetime);
334 policy
335 }
336
337 pub fn new_policy_for_objects_with_prefix(
344 bucket: impl Into<BucketName>,
345 prefix: impl AsRef<str>,
346 upload_token_lifetime: Duration,
347 ) -> Self {
348 let mut policy = Self {
349 inner: json!({
350 SCOPE_KEY: bucket.into().to_string() + ":" + prefix.as_ref(),
351 IS_PREFIXAL_SCOPE_KEY: 1,
352 }),
353 };
354 policy.token_lifetime(upload_token_lifetime);
355 policy
356 }
357
358 pub fn token_lifetime(&mut self, lifetime: Duration) -> &mut Self {
360 self.set(
361 DEADLINE_KEY.into(),
362 JsonValue::Number(
363 SystemTime::now()
364 .checked_add(lifetime)
365 .and_then(|t| t.duration_since(SystemTime::UNIX_EPOCH).ok())
366 .map(|t| t.as_secs())
367 .unwrap_or(u64::max_value())
368 .into(),
369 ),
370 )
371 }
372
373 pub fn token_deadline(&mut self, deadline: SystemTime) -> &mut Self {
375 self.set(
376 DEADLINE_KEY.into(),
377 JsonValue::Number(
378 deadline
379 .duration_since(SystemTime::UNIX_EPOCH)
380 .ok()
381 .map(|t| t.as_secs())
382 .unwrap_or(u64::max_value())
383 .into(),
384 ),
385 )
386 }
387
388 pub fn insert_only(&mut self) -> &mut Self {
390 self.set(INSERT_ONLY_KEY.into(), JsonValue::Number(1.into()))
391 }
392
393 pub fn enable_mime_detection(&mut self) -> &mut Self {
395 self.set(DETECT_MIME_KEY.into(), JsonValue::Number(1.into()))
396 }
397
398 pub fn disable_mime_detection(&mut self) -> &mut Self {
400 self.unset(DETECT_MIME_KEY)
401 }
402
403 pub fn file_type(&mut self, file_type: FileType) -> &mut Self {
405 self.set(FILE_TYPE_KEY.into(), JsonValue::Number(u8::from(file_type).into()))
406 }
407
408 pub fn return_url(&mut self, url: impl Into<String>) -> &mut Self {
415 self.set(RETURN_URL_KEY.into(), JsonValue::String(url.into()))
416 }
417
418 pub fn return_body(&mut self, body: impl Into<String>) -> &mut Self {
424 self.set(RETURN_BODY_KEY.into(), JsonValue::String(body.into()))
425 }
426
427 pub fn callback<V: AsRef<[S]>, S: AsRef<str>>(
437 &mut self,
438 urls: V,
439 host: impl Into<String>,
440 body: impl Into<String>,
441 body_type: impl Into<String>,
442 ) -> &mut Self {
443 self.set(CALLBACK_URL_KEY.into(), JsonValue::String(join_str_slice(urls, ";")));
444 {
445 let callback_host = host.into();
446 if callback_host.is_empty() {
447 self.unset(CALLBACK_HOST_KEY);
448 } else {
449 self.set(CALLBACK_HOST_KEY.into(), JsonValue::String(callback_host));
450 }
451 }
452 self.set(CALLBACK_BODY_KEY.into(), JsonValue::String(body.into()));
453 {
454 let callback_body_type = body_type.into();
455 if callback_body_type.is_empty() {
456 self.unset(CALLBACK_BODY_TYPE_KEY);
457 } else {
458 self.set(CALLBACK_BODY_TYPE_KEY.into(), JsonValue::String(callback_body_type));
459 }
460 }
461 self
462 }
463
464 pub fn save_as(&mut self, save_as: impl Into<String>, force: bool) -> &mut Self {
470 self.set(SAVE_KEY_KEY.into(), JsonValue::String(save_as.into()));
471 if force {
472 self.set(FORCE_SAVE_KEY_KEY.into(), JsonValue::Bool(true));
473 } else {
474 self.unset(FORCE_SAVE_KEY_KEY);
475 }
476 self
477 }
478
479 pub fn file_size_limitation(&mut self, size: impl RangeBounds<u64>) -> &mut Self {
483 match size.start_bound() {
484 Bound::Included(&s) => {
485 self.set(FSIZE_MIN_KEY.into(), JsonValue::Number(s.into()));
486 }
487 Bound::Excluded(&s) => {
488 self.set(FSIZE_MIN_KEY.into(), JsonValue::Number((s + 1).into()));
489 }
490 Bound::Unbounded => {
491 self.unset(FSIZE_MIN_KEY);
492 }
493 }
494 match size.end_bound() {
495 Bound::Included(&s) => {
496 self.set(FSIZE_LIMIT_KEY.into(), JsonValue::Number(s.into()));
497 }
498 Bound::Excluded(&s) => {
499 self.set(FSIZE_LIMIT_KEY.into(), JsonValue::Number((s - 1).into()));
500 }
501 Bound::Unbounded => {
502 self.unset(FSIZE_LIMIT_KEY);
503 }
504 }
505 self
506 }
507
508 pub fn mime_types<V: AsRef<[S]>, S: AsRef<str>>(&mut self, content_types: V) -> &mut Self {
513 self.set(
514 MIME_LIMIT_KEY.into(),
515 JsonValue::String(join_str_slice(content_types, ";")),
516 )
517 }
518
519 pub fn object_lifetime(&mut self, lifetime: Duration) -> &mut Self {
523 let lifetime_secs = lifetime.as_secs();
524 let secs_one_day = 60 * 60 * 24;
525
526 self.set(
527 DELETE_AFTER_DAYS_KEY.into(),
528 lifetime_secs
529 .checked_add(secs_one_day)
530 .and_then(|s| s.checked_sub(1))
531 .and_then(|s| s.checked_div(secs_one_day))
532 .map(|s| s.into())
533 .unwrap_or_else(|| JsonValue::Number(u64::max_value().into())),
534 )
535 }
536
537 #[inline]
539 pub fn set(&mut self, k: String, v: JsonValue) -> &mut Self {
540 self.inner.as_object_mut().unwrap().insert(k, v);
541 self
542 }
543
544 #[inline]
546 pub fn unset<Q>(&mut self, k: &Q) -> &mut Self
547 where
548 String: Borrow<Q>,
549 Q: ?Sized + Ord + Eq + Hash,
550 {
551 self.inner.as_object_mut().unwrap().remove(k);
552 self
553 }
554
555 pub fn build(&self) -> UploadPolicy {
557 UploadPolicy {
558 inner: self.inner.clone(),
559 }
560 }
561
562 pub fn build_token(&self, credential: Credential, opts: ToStringOptions) -> StaticUploadTokenProvider {
564 self.build().into_static_upload_token_provider(credential, opts)
565 }
566
567 pub fn reset(&mut self) {
571 let immutable_keys = [SCOPE_KEY, DEADLINE_KEY, IS_PREFIXAL_SCOPE_KEY];
572 self.inner = JsonValue::Object(
573 self.inner
574 .as_object()
575 .unwrap()
576 .iter()
577 .filter_map(|(k, v)| {
578 immutable_keys
579 .iter()
580 .find(|&ik| k == ik)
581 .map(|_| (k.to_owned(), v.to_owned()))
582 })
583 .collect(),
584 );
585 }
586
587 #[allow(dead_code)]
588 fn assert() {
589 assert_impl!(Send: Self);
590 assert_impl!(Sync: Self);
591 }
592}
593
594impl fmt::Debug for UploadPolicyBuilder {
595 #[inline]
596 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597 self.inner.fmt(f)
598 }
599}
600
601impl<'p> From<&'p UploadPolicy> for Cow<'p, UploadPolicy> {
602 #[inline]
603 fn from(policy: &'p UploadPolicy) -> Self {
604 Cow::Borrowed(policy)
605 }
606}
607
608impl From<UploadPolicy> for Cow<'_, UploadPolicy> {
609 #[inline]
610 fn from(policy: UploadPolicy) -> Self {
611 Cow::Owned(policy)
612 }
613}
614
615impl<'p> From<Cow<'p, UploadPolicy>> for UploadPolicy {
616 #[inline]
617 fn from(policy: Cow<'p, UploadPolicy>) -> Self {
618 match policy {
619 Cow::Borrowed(policy) => policy.to_owned(),
620 Cow::Owned(policy) => policy,
621 }
622 }
623}
624
625fn join_str_slice<V: AsRef<[S]>, S: AsRef<str>, Sep: AsRef<str>>(slice: V, sep: Sep) -> String {
626 let mut iter = slice.as_ref().iter().map(|s| s.as_ref());
627 let mut joined = String::new();
628 if let Some(first) = iter.next() {
629 joined.push_str(first);
630 joined = iter.fold(joined, |mut joined, s| {
631 joined.push_str(sep.as_ref());
632 joined.push_str(s);
633 joined
634 })
635 }
636 joined
637}
638
639#[cfg(test)]
640mod tests {
641 use super::*;
642 use anyhow::Result;
643 use mime::APPLICATION_WWW_FORM_URLENCODED;
644 use serde_json::json;
645
646 #[test]
647 fn test_build_upload_policy_for_bucket() -> Result<()> {
648 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600)).build();
649 let now = SystemTime::now();
650 let one_hour_later = now + Duration::from_secs(60 * 60);
651 assert_eq!(policy.bucket(), Some("test_bucket"));
652 assert_eq!(policy.key(), None);
653 assert!(
654 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
655 - policy
656 .token_deadline()
657 .unwrap()
658 .duration_since(SystemTime::UNIX_EPOCH)?
659 < Duration::from_secs(5)
660 );
661
662 assert_eq!(policy.keys().len(), 2);
663 assert_eq!(policy.get("scope"), Some(&json!("test_bucket")));
664 assert!(
665 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
666 - Duration::from_secs(policy.get("deadline").unwrap().as_u64().unwrap())
667 < Duration::from_secs(5)
668 );
669 assert!(policy.get("isPrefixalScope").is_none());
670 Ok(())
671 }
672
673 #[test]
674 fn test_build_upload_policy_for_object() -> Result<()> {
675 let policy =
676 UploadPolicyBuilder::new_policy_for_object("test_bucket", "test:object", Duration::from_secs(3600)).build();
677 let now = SystemTime::now();
678 let one_hour_later = now + Duration::from_secs(60 * 60);
679 assert_eq!(policy.bucket(), Some("test_bucket"));
680 assert_eq!(policy.key(), Some("test:object"));
681 assert!(!policy.use_prefixal_object_key());
682 assert!(
683 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
684 - policy
685 .token_deadline()
686 .unwrap()
687 .duration_since(SystemTime::UNIX_EPOCH)?
688 < Duration::from_secs(5)
689 );
690
691 assert_eq!(policy.keys().len(), 2);
692 assert_eq!(policy.get("scope"), Some(&json!("test_bucket:test:object")));
693 assert!(
694 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
695 - Duration::from_secs(policy.get("deadline").unwrap().as_u64().unwrap())
696 < Duration::from_secs(5)
697 );
698 assert!(policy.get("isPrefixalScope").is_none());
699 Ok(())
700 }
701
702 #[test]
703 fn test_build_upload_policy_for_objects_with_prefix() -> Result<()> {
704 let policy = UploadPolicyBuilder::new_policy_for_objects_with_prefix(
705 "test_bucket",
706 "test:object",
707 Duration::from_secs(3600),
708 )
709 .build();
710 let now = SystemTime::now();
711 let one_hour_later = now + Duration::from_secs(60 * 60);
712 assert_eq!(policy.bucket(), Some("test_bucket"));
713 assert_eq!(policy.key(), Some("test:object"));
714 assert!(policy.use_prefixal_object_key());
715 assert!(
716 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
717 - policy
718 .token_deadline()
719 .unwrap()
720 .duration_since(SystemTime::UNIX_EPOCH)?
721 < Duration::from_secs(5)
722 );
723
724 assert_eq!(policy.keys().len(), 3);
725 assert_eq!(policy.get("scope"), Some(&json!("test_bucket:test:object")));
726 assert!(
727 one_hour_later.duration_since(SystemTime::UNIX_EPOCH)?
728 - Duration::from_secs(policy.get("deadline").unwrap().as_u64().unwrap())
729 < Duration::from_secs(5)
730 );
731 assert_eq!(policy.get("isPrefixalScope"), Some(&json!(1)));
732 Ok(())
733 }
734
735 #[test]
736 fn test_build_upload_policy_with_deadline() -> Result<()> {
737 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
738 .token_deadline(SystemTime::now())
739 .build();
740 assert!(
741 SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?
742 - policy
743 .token_deadline()
744 .unwrap()
745 .duration_since(SystemTime::UNIX_EPOCH)?
746 < Duration::from_secs(5)
747 );
748
749 assert!(
750 SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?
751 - Duration::from_secs(policy.get("deadline").unwrap().as_u64().unwrap())
752 < Duration::from_secs(5)
753 );
754 Ok(())
755 }
756
757 #[test]
758 fn test_build_upload_policy_with_lifetime() -> Result<()> {
759 let one_day = Duration::from_secs(60 * 60 * 24);
760 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
761 .token_lifetime(one_day)
762 .build();
763 let now = SystemTime::now();
764 let tomorrow = now + one_day;
765 assert!(
766 tomorrow.duration_since(SystemTime::UNIX_EPOCH)?
767 - policy
768 .token_deadline()
769 .unwrap()
770 .duration_since(SystemTime::UNIX_EPOCH)?
771 < Duration::from_secs(5)
772 );
773
774 assert!(
775 tomorrow.duration_since(SystemTime::UNIX_EPOCH)?
776 - Duration::from_secs(policy.get("deadline").unwrap().as_u64().unwrap())
777 < Duration::from_secs(5)
778 );
779 Ok(())
780 }
781
782 #[test]
783 fn test_build_upload_policy_with_insert_only() -> Result<()> {
784 {
785 let policy = UploadPolicyBuilder::new_policy_for_object("test_bucket", "test", Duration::from_secs(3600))
786 .insert_only()
787 .build();
788 assert!(policy.is_insert_only());
789 assert_eq!(policy.get("insertOnly"), Some(&json!(1)));
790 }
791
792 {
793 let policy = UploadPolicyBuilder::new_policy_for_object("test_bucket", "test", Duration::from_secs(3600))
794 .set("insertOnly".to_owned(), json!(0))
795 .build();
796 assert!(!policy.is_insert_only());
797 assert_eq!(policy.get("insertOnly"), Some(&json!(0)));
798 }
799 Ok(())
800 }
801
802 #[test]
803 fn test_build_upload_policy_with_mime_detection() -> Result<()> {
804 {
805 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
806 .enable_mime_detection()
807 .build();
808 assert!(policy.mime_detection_enabled());
809 assert_eq!(policy.get("detectMime"), Some(&json!(1)));
810 }
811 {
812 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
813 .set("detectMime".to_owned(), json!(0))
814 .build();
815 assert!(!policy.mime_detection_enabled());
816 assert_eq!(policy.get("detectMime"), Some(&json!(0)));
817 }
818 Ok(())
819 }
820
821 #[test]
822 fn test_build_upload_policy_with_normal_storage() -> Result<()> {
823 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
824 .file_type(FileType::Standard)
825 .build();
826 assert_eq!(policy.file_type(), Some(FileType::Standard));
827 assert_eq!(policy.get("fileType"), Some(&json!(0)));
828 Ok(())
829 }
830
831 #[test]
832 fn test_build_upload_policy_with_infrequent_storage() -> Result<()> {
833 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
834 .file_type(FileType::InfrequentAccess)
835 .build();
836 assert_eq!(policy.file_type(), Some(FileType::InfrequentAccess));
837 assert_eq!(policy.get("fileType"), Some(&json!(1)));
838 Ok(())
839 }
840
841 #[test]
842 fn test_build_upload_policy_with_return_url() -> Result<()> {
843 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
844 .return_url("http://www.qiniu.io/test")
845 .build();
846 assert_eq!(policy.return_url(), Some("http://www.qiniu.io/test"));
847 assert_eq!(policy.get("returnUrl"), Some(&json!("http://www.qiniu.io/test")));
848 Ok(())
849 }
850
851 #[test]
852 fn test_build_upload_policy_with_return_body() -> Result<()> {
853 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
854 .return_body("datadatadata")
855 .build();
856 assert_eq!(policy.return_body(), Some("datadatadata"));
857 assert_eq!(policy.get("returnBody"), Some(&json!("datadatadata")));
858 Ok(())
859 }
860
861 #[test]
862 fn test_build_upload_policy_with_callback() -> Result<()> {
863 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
864 .callback(
865 ["https://1.1.1.1", "https://2.2.2.2", "https://3.3.3.3"],
866 "www.qiniu.com",
867 "a=b&c=d",
868 "",
869 )
870 .build();
871 assert_eq!(
872 policy.callback_urls().map(|urls| urls.collect::<Vec<&str>>()),
873 Some(vec!["https://1.1.1.1", "https://2.2.2.2", "https://3.3.3.3"])
874 );
875 assert_eq!(policy.callback_host(), Some("www.qiniu.com"));
876 assert_eq!(policy.callback_body(), Some("a=b&c=d"));
877 assert_eq!(policy.callback_body_type(), None);
878 assert_eq!(
879 policy.get("callbackUrl"),
880 Some(&json!("https://1.1.1.1;https://2.2.2.2;https://3.3.3.3"))
881 );
882 assert_eq!(policy.get("callbackHost"), Some(&json!("www.qiniu.com")));
883 assert_eq!(policy.get("callbackBody"), Some(&json!("a=b&c=d")));
884 assert!(policy.get("callbackBodyType").is_none());
885 Ok(())
886 }
887
888 #[test]
889 fn test_build_upload_policy_with_callback_body_with_body_type() -> Result<()> {
890 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
891 .callback(
892 ["https://1.1.1.1", "https://2.2.2.2", "https://3.3.3.3"],
893 "www.qiniu.com",
894 "a=b&c=d",
895 APPLICATION_WWW_FORM_URLENCODED.as_ref(),
896 )
897 .build();
898 assert_eq!(
899 policy.callback_urls().map(|urls| urls.collect::<Vec<&str>>()),
900 Some(vec!["https://1.1.1.1", "https://2.2.2.2", "https://3.3.3.3"])
901 );
902 assert_eq!(policy.callback_host(), Some("www.qiniu.com"));
903 assert_eq!(policy.callback_body(), Some("a=b&c=d"));
904 assert_eq!(
905 policy.callback_body_type(),
906 Some(APPLICATION_WWW_FORM_URLENCODED.as_ref())
907 );
908 assert_eq!(
909 policy.get("callbackUrl"),
910 Some(&json!("https://1.1.1.1;https://2.2.2.2;https://3.3.3.3"))
911 );
912 assert_eq!(policy.get("callbackHost"), Some(&json!("www.qiniu.com")));
913 assert_eq!(policy.get("callbackBody"), Some(&json!("a=b&c=d")));
914 assert_eq!(
915 policy.get("callbackBodyType"),
916 Some(&json!(APPLICATION_WWW_FORM_URLENCODED.as_ref()))
917 );
918 Ok(())
919 }
920
921 #[test]
922 fn test_build_upload_policy_with_save_key() -> Result<()> {
923 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
924 .save_as("target_file", false)
925 .build();
926 assert_eq!(policy.save_key(), Some("target_file"));
927 assert!(!policy.is_save_key_forced());
928 assert_eq!(policy.get("saveKey"), Some(&json!("target_file")));
929 assert!(policy.get("forceSaveKey").is_none());
930 Ok(())
931 }
932
933 #[test]
934 fn test_build_upload_policy_with_save_key_by_force() -> Result<()> {
935 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
936 .save_as("target_file", true)
937 .build();
938 assert_eq!(policy.save_key(), Some("target_file"));
939 assert!(policy.is_save_key_forced());
940 assert_eq!(policy.get("saveKey"), Some(&json!("target_file")));
941 assert_eq!(policy.get("forceSaveKey"), Some(&json!(true)));
942 Ok(())
943 }
944
945 #[test]
946 fn test_build_upload_policy_with_file_size_exclusive_limit() -> Result<()> {
947 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
948 .file_size_limitation(15..20)
949 .build();
950 assert_eq!(policy.file_size_limitation(), (Some(15), Some(19)));
951 assert_eq!(policy.get("fsizeMin"), Some(&json!(15)));
952 assert_eq!(policy.get("fsizeLimit"), Some(&json!(19)));
953 Ok(())
954 }
955
956 #[test]
957 fn test_build_upload_policy_with_file_size_inclusive_limit() -> Result<()> {
958 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
959 .file_size_limitation(15..=20)
960 .build();
961 assert_eq!(policy.file_size_limitation(), (Some(15), Some(20)));
962 assert_eq!(policy.get("fsizeMin"), Some(&json!(15)));
963 assert_eq!(policy.get("fsizeLimit"), Some(&json!(20)));
964 Ok(())
965 }
966
967 #[test]
968 fn test_build_upload_policy_with_file_size_max_limit() -> Result<()> {
969 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
970 .file_size_limitation(..20)
971 .build();
972 assert_eq!(policy.file_size_limitation(), (None, Some(19)));
973 assert!(policy.get("fsizeMin").is_none());
974 assert_eq!(policy.get("fsizeLimit"), Some(&json!(19)));
975 Ok(())
976 }
977
978 #[test]
979 fn test_build_upload_policy_with_file_size_min_limit() -> Result<()> {
980 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
981 .file_size_limitation(15..)
982 .build();
983 assert_eq!(policy.file_size_limitation(), (Some(15), None));
984 assert_eq!(policy.get("fsizeMin"), Some(&json!(15)));
985 assert!(policy.get("fsizeLimit").is_none());
986 Ok(())
987 }
988
989 #[test]
990 fn test_build_upload_policy_with_mime() -> Result<()> {
991 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
992 .mime_types(["image/jpeg", "image/png"])
993 .build();
994 assert_eq!(
995 policy.mime_types().map(|ops| ops.collect::<Vec<&str>>()),
996 Some(vec!["image/jpeg", "image/png"])
997 );
998 assert_eq!(policy.get("mimeLimit"), Some(&json!("image/jpeg;image/png")));
999 Ok(())
1000 }
1001
1002 #[test]
1003 fn test_build_upload_policy_with_object_lifetime() -> Result<()> {
1004 let one_hundred_days = Duration::from_secs(100 * 24 * 60 * 60);
1005 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
1006 .object_lifetime(one_hundred_days)
1007 .build();
1008 assert_eq!(policy.object_lifetime(), Some(one_hundred_days));
1009
1010 assert_eq!(policy.get("deleteAfterDays"), Some(&json!(100)));
1011 Ok(())
1012 }
1013
1014 #[test]
1015 fn test_build_upload_policy_with_short_object_lifetime() -> Result<()> {
1016 let one_hundred_secs = Duration::from_secs(100);
1017 let one_day = Duration::from_secs(24 * 60 * 60);
1018 let policy = UploadPolicyBuilder::new_policy_for_bucket("test_bucket", Duration::from_secs(3600))
1019 .object_lifetime(one_hundred_secs)
1020 .build();
1021 assert_eq!(policy.object_lifetime(), Some(one_day));
1022
1023 assert_eq!(policy.get("deleteAfterDays"), Some(&json!(1)));
1024 Ok(())
1025 }
1026}