Skip to main content

ossify/ops/bucket/website/
put_bucket_website.rs

1//! PutBucketWebsite.
2//!
3//! Official document: <https://www.alibabacloud.com/help/en/oss/developer-reference/putbucketwebsite>
4
5use std::future::Future;
6
7use http::Method;
8use serde::{Deserialize, Serialize};
9use serde_with::skip_serializing_none;
10
11use crate::body::XMLBody;
12use crate::error::Result;
13use crate::response::EmptyResponseProcessor;
14use crate::ser::OnlyKeyField;
15use crate::{Client, Ops, Prepared, Request};
16
17#[derive(Debug, Clone, Default, Serialize)]
18pub struct PutBucketWebsiteParams {
19    website: OnlyKeyField,
20}
21
22/// `<IndexDocument>` container.
23#[skip_serializing_none]
24#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "PascalCase")]
26pub struct IndexDocument {
27    pub suffix: String,
28    pub support_sub_dir: Option<bool>,
29    /// 0 / 1 / 2 per the PutBucketWebsite reference.
30    #[serde(rename = "Type")]
31    pub type_: Option<u32>,
32}
33
34/// `<ErrorDocument>` container.
35#[skip_serializing_none]
36#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "PascalCase")]
38pub struct ErrorDocument {
39    pub key: String,
40    pub http_status: Option<u32>,
41}
42
43/// `<Condition>` container inside a `<RoutingRule>`.
44#[skip_serializing_none]
45#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "PascalCase")]
47pub struct RoutingRuleCondition {
48    pub key_prefix_equals: Option<String>,
49    pub http_error_code_returned_equals: Option<u32>,
50    pub key_suffix_equals: Option<String>,
51    #[serde(rename = "IncludeHeader", default, skip_serializing_if = "Vec::is_empty")]
52    pub include_headers: Vec<IncludeHeader>,
53}
54
55/// `<IncludeHeader>` entry inside `<Condition>`.
56#[skip_serializing_none]
57#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "PascalCase")]
59pub struct IncludeHeader {
60    pub key: String,
61    pub equals: Option<String>,
62}
63
64/// `<Set>` entry inside `<MirrorHeaders>`.
65#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(rename_all = "PascalCase")]
67pub struct MirrorSetHeader {
68    pub key: String,
69    pub value: String,
70}
71
72/// `<MirrorHeaders>` container.
73#[skip_serializing_none]
74#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "PascalCase")]
76pub struct MirrorHeaders {
77    pub pass_all: Option<bool>,
78    #[serde(rename = "Pass", default, skip_serializing_if = "Vec::is_empty")]
79    pub pass: Vec<String>,
80    #[serde(rename = "Remove", default, skip_serializing_if = "Vec::is_empty")]
81    pub remove: Vec<String>,
82    #[serde(rename = "Set", default, skip_serializing_if = "Vec::is_empty")]
83    pub set: Vec<MirrorSetHeader>,
84}
85
86/// `<Redirect>` container.
87#[skip_serializing_none]
88#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
89#[serde(rename_all = "PascalCase")]
90pub struct RoutingRuleRedirect {
91    pub redirect_type: Option<String>,
92    pub pass_query_string: Option<bool>,
93    #[serde(rename = "MirrorURL")]
94    pub mirror_url: Option<String>,
95    pub mirror_pass_query_string: Option<bool>,
96    pub mirror_follow_redirect: Option<bool>,
97    pub mirror_check_md5: Option<bool>,
98    pub mirror_headers: Option<MirrorHeaders>,
99    pub protocol: Option<String>,
100    pub host_name: Option<String>,
101    pub replace_key_prefix_with: Option<String>,
102    pub enable_replace_prefix: Option<bool>,
103    pub replace_key_with: Option<String>,
104    pub http_redirect_code: Option<u32>,
105}
106
107/// `<RoutingRule>` container.
108#[skip_serializing_none]
109#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
110#[serde(rename_all = "PascalCase")]
111pub struct RoutingRule {
112    pub rule_number: u32,
113    pub condition: RoutingRuleCondition,
114    pub redirect: RoutingRuleRedirect,
115}
116
117/// `<RoutingRules>` container.
118#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
119#[serde(rename_all = "PascalCase")]
120pub struct RoutingRules {
121    #[serde(rename = "RoutingRule", default, skip_serializing_if = "Vec::is_empty")]
122    pub rules: Vec<RoutingRule>,
123}
124
125/// Root `<WebsiteConfiguration>` element.
126#[skip_serializing_none]
127#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
128#[serde(rename = "WebsiteConfiguration", rename_all = "PascalCase")]
129pub struct WebsiteConfiguration {
130    pub index_document: Option<IndexDocument>,
131    pub error_document: Option<ErrorDocument>,
132    pub routing_rules: Option<RoutingRules>,
133}
134
135pub struct PutBucketWebsite {
136    pub config: WebsiteConfiguration,
137}
138
139impl Ops for PutBucketWebsite {
140    type Response = EmptyResponseProcessor;
141    type Body = XMLBody<WebsiteConfiguration>;
142    type Query = PutBucketWebsiteParams;
143
144    fn prepare(self) -> Result<Prepared<PutBucketWebsiteParams, WebsiteConfiguration>> {
145        Ok(Prepared {
146            method: Method::PUT,
147            query: Some(PutBucketWebsiteParams::default()),
148            body: Some(self.config),
149            ..Default::default()
150        })
151    }
152}
153
154pub trait PutBucketWebsiteOps {
155    /// Enable static website hosting on the bucket, optionally with redirect /
156    /// mirroring-based back-to-origin rules.
157    ///
158    /// Official document: <https://www.alibabacloud.com/help/en/oss/developer-reference/putbucketwebsite>
159    fn put_bucket_website(&self, config: WebsiteConfiguration) -> impl Future<Output = Result<()>>;
160}
161
162impl PutBucketWebsiteOps for Client {
163    async fn put_bucket_website(&self, config: WebsiteConfiguration) -> Result<()> {
164        self.request(PutBucketWebsite { config }).await
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn params_serialize() {
174        assert_eq!(crate::ser::to_string(&PutBucketWebsiteParams::default()).unwrap(), "website");
175    }
176
177    #[test]
178    fn parse_minimal_config() {
179        let xml = r#"<WebsiteConfiguration>
180  <IndexDocument>
181    <Suffix>index.html</Suffix>
182  </IndexDocument>
183  <ErrorDocument>
184    <Key>error.html</Key>
185    <HttpStatus>404</HttpStatus>
186  </ErrorDocument>
187</WebsiteConfiguration>"#;
188        let parsed: WebsiteConfiguration = quick_xml::de::from_str(xml).unwrap();
189        assert_eq!(parsed.index_document.as_ref().unwrap().suffix, "index.html");
190        assert_eq!(parsed.error_document.as_ref().unwrap().http_status, Some(404));
191    }
192
193    #[test]
194    fn parse_routing_rules() {
195        let xml = r#"<WebsiteConfiguration>
196  <IndexDocument><Suffix>index.html</Suffix></IndexDocument>
197  <RoutingRules>
198    <RoutingRule>
199      <RuleNumber>1</RuleNumber>
200      <Condition>
201        <KeyPrefixEquals>abc/</KeyPrefixEquals>
202        <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
203      </Condition>
204      <Redirect>
205        <RedirectType>Mirror</RedirectType>
206        <MirrorURL>http://example.com/</MirrorURL>
207        <MirrorHeaders>
208          <PassAll>true</PassAll>
209          <Pass>h1</Pass>
210          <Set><Key>k</Key><Value>v</Value></Set>
211        </MirrorHeaders>
212      </Redirect>
213    </RoutingRule>
214  </RoutingRules>
215</WebsiteConfiguration>"#;
216        let parsed: WebsiteConfiguration = quick_xml::de::from_str(xml).unwrap();
217        let rules = parsed.routing_rules.unwrap().rules;
218        assert_eq!(rules.len(), 1);
219        assert_eq!(rules[0].rule_number, 1);
220        let mh = rules[0].redirect.mirror_headers.as_ref().unwrap();
221        assert_eq!(mh.pass_all, Some(true));
222        assert_eq!(mh.pass, vec!["h1".to_string()]);
223        assert_eq!(mh.set[0].key, "k");
224    }
225}