1use super::utils::{de_option_empty_string_as_none, parse_json_response};
2use super::{Client, Error, OPENAPI_STYLE, OPENAPI_VERSION};
3use bon::Builder;
4use serde::{Deserialize, Serialize, Serializer};
5use std::collections::HashMap;
6use time::OffsetDateTime;
7use u_sdk_common::helper::into_header_map;
8use u_sdk_common::open_api_sign::{SignParams, get_openapi_request_header};
9
10#[serde_with::skip_serializing_none]
13#[derive(Builder, Serialize)]
14#[serde(rename_all = "PascalCase")]
15pub struct ListSites<'a> {
16 #[builder(start_fn)]
17 #[serde(skip_serializing)]
18 pub(crate) client: &'a Client,
19
20 #[builder(field)]
22 #[serde(
23 skip_serializing_if = "Vec::is_empty",
24 serialize_with = "serialize_tag_filter_as_json_string"
25 )]
26 tag_filter: Vec<(Option<&'a str>, Option<&'a str>)>,
27
28 site_name: Option<&'a str>,
30
31 site_search_type: Option<SiteSearchType>,
33
34 page_number: Option<i32>,
36
37 page_size: Option<i32>,
39
40 resource_group_id: Option<&'a str>,
42
43 status: Option<&'a str>,
45
46 only_enterprise: Option<bool>,
48
49 plan_subscribe_type: Option<PlanSubscribeType>,
51
52 coverage: Option<Coverage>,
54
55 access_type: Option<AccessType>,
57
58 order_by: Option<OrderBy>,
60}
61
62impl<'a, S: list_sites_builder::State> ListSitesBuilder<'a, S> {
63 pub fn tag_filter(mut self, item: (Option<&'a str>, Option<&'a str>)) -> Self {
65 self.tag_filter.push(item);
66 self
67 }
68
69 pub fn tag_filters(
71 mut self,
72 items: impl IntoIterator<Item = (Option<&'a str>, Option<&'a str>)>,
73 ) -> Self {
74 self.tag_filter.extend(items);
75 self
76 }
77}
78
79#[serde_with::skip_serializing_none]
80#[derive(Serialize)]
81#[serde(rename_all = "PascalCase")]
82struct TagFilterItem<'a> {
83 key: Option<&'a str>,
84 value: Option<&'a str>,
85}
86
87pub fn serialize_tag_filter_as_json_string<'a, S>(
88 tag_filter: &Vec<(Option<&'a str>, Option<&'a str>)>,
89 serializer: S,
90) -> Result<S::Ok, S::Error>
91where
92 S: Serializer,
93{
94 let items: Vec<TagFilterItem<'a>> = tag_filter
96 .iter()
97 .filter(|(k, v)| k.is_some() || v.is_some())
98 .map(|(k, v)| TagFilterItem { key: *k, value: *v })
99 .collect();
100
101 let json = serde_json::to_string(&items).map_err(serde::ser::Error::custom)?;
103
104 serializer.serialize_str(&json)
106}
107
108#[derive(Serialize, Clone)]
110#[serde(rename_all = "lowercase")]
111pub enum SiteSearchType {
112 Prefix,
113 Suffix,
114 Exact,
115 Fuzzy,
116}
117
118#[derive(Serialize, Clone)]
120pub enum PlanSubscribeType {
121 #[serde(rename = "basicplan")]
122 Basic,
123 #[serde(rename = "standardplan")]
124 Standard,
125 #[serde(rename = "advancedplan")]
126 Advanced,
127 #[serde(rename = "enterpriseplan")]
128 Enterprise,
129}
130
131#[derive(Serialize, Clone, Debug, Deserialize)]
133#[serde(rename_all = "lowercase")]
134pub enum Coverage {
135 Domestic,
136 Global,
137 Overseas,
138}
139
140#[derive(Serialize, Clone, Debug, Deserialize)]
142#[serde(rename_all = "UPPERCASE")]
143pub enum AccessType {
144 NS,
145 CName,
146}
147
148#[derive(Serialize, Clone)]
150#[serde(rename_all = "camelCase")]
151pub enum OrderBy {
152 GmtCreate,
153 VisitTime,
154}
155#[derive(Debug, Clone, Deserialize)]
159#[serde(rename_all = "PascalCase")]
160pub struct ListSitesResponse {
161 pub request_id: String,
163
164 pub page_number: i32,
166
167 pub page_size: i32,
169
170 pub total_count: i32,
172
173 pub sites: Vec<SiteInfo>,
175}
176
177#[derive(Debug, Clone, Deserialize)]
178#[serde(rename_all = "PascalCase")]
179pub struct SiteInfo {
180 pub access_type: AccessType,
182
183 pub cname_zone: String,
185
186 pub coverage: Coverage,
188
189 #[serde(with = "time::serde::iso8601")]
191 pub create_time: OffsetDateTime,
192
193 #[serde(with = "time::serde::iso8601")]
195 pub update_time: OffsetDateTime,
196
197 pub instance_id: String,
199
200 pub name_server_list: String,
202
203 pub plan_name: String,
205
206 pub plan_spec_name: Option<String>,
208
209 pub resource_group_id: String,
211
212 pub site_id: i64,
214
215 pub site_name: String,
217
218 pub status: SiteStatus,
220
221 pub tags: Option<HashMap<String, String>>,
223
224 pub verify_code: String,
226
227 #[serde(with = "time::serde::iso8601")]
229 pub visit_time: OffsetDateTime,
230
231 #[serde(deserialize_with = "de_option_empty_string_as_none")]
233 pub offline_reason: Option<OfflineReason>,
234}
235
236#[derive(Debug, Clone, Deserialize)]
237#[serde(rename_all = "lowercase")]
238pub enum SiteStatus {
239 Pending,
240 Active,
241 Offline,
242 Moved,
243}
244
245#[derive(Debug, Clone, Deserialize)]
246#[serde(rename_all = "snake_case")]
247pub enum OfflineReason {
248 ExpirationArrears,
249 InternallyDisabled,
250 MissingIcp,
251 ContentViolation,
252 ProactivelyDisabled,
253}
254
255impl Client {
258 pub fn list_sites(&self) -> ListSitesBuilder<'_> {
259 ListSites::builder(self)
260 }
261}
262
263impl ListSites<'_> {
264 pub async fn send(&self) -> Result<ListSitesResponse, Error> {
265 let client = self.client;
266 let creds = client.credentials_provider.load().await?;
267
268 let sign_params = SignParams {
269 req_method: "GET",
270 host: &client.host,
271 query_map: self,
272 x_acs_action: "ListSites",
273 x_acs_version: OPENAPI_VERSION,
274 x_acs_security_token: creds.sts_security_token.as_deref(),
275 request_body: None,
276 style: &OPENAPI_STYLE,
277 };
278
279 let (common_headers, url_) =
280 get_openapi_request_header(&creds.access_key_secret, &creds.access_key_id, sign_params)
281 .map_err(|e| {
282 Error::Common(format!("failed to get openapi request header: {}", e))
283 })?;
284 let header_map = into_header_map(common_headers);
285
286 let resp = client
287 .http_client
288 .get(url_)
289 .headers(header_map)
290 .send()
291 .await?;
292
293 let data = parse_json_response(resp).await?;
294 Ok(data)
295 }
296}