1use std::collections::HashMap;
4use std::sync::Arc;
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use super::{WechatApi, WechatContext};
10use crate::error::WechatError;
11
12#[non_exhaustive]
13#[derive(Debug, Clone, Serialize)]
14pub struct InvokeCloudFunctionRequest {
15 #[serde(flatten)]
16 pub payload: HashMap<String, Value>,
17}
18
19#[non_exhaustive]
20#[derive(Debug, Clone, Serialize)]
21pub struct DelayedFunctionTaskRequest {
22 #[serde(flatten)]
23 pub payload: HashMap<String, Value>,
24}
25
26#[non_exhaustive]
27#[derive(Debug, Clone, Serialize)]
28pub struct CloudDatabaseRequest {
29 pub query: String,
30}
31
32#[non_exhaustive]
33#[derive(Debug, Clone, Serialize)]
34pub struct UploadFileLinkRequest {
35 pub env: String,
36 pub path: String,
37}
38
39#[non_exhaustive]
40#[derive(Debug, Clone, Serialize)]
41pub struct DownloadFileLinkRequest {
42 pub env: String,
43 pub file_list: Vec<String>,
44}
45
46#[non_exhaustive]
47#[derive(Debug, Clone, Serialize)]
48pub struct DeleteCloudFileRequest {
49 pub env: String,
50 pub fileid_list: Vec<String>,
51}
52
53#[non_exhaustive]
54#[derive(Debug, Clone, Serialize)]
55pub struct SendCloudBaseSmsRequest {
56 #[serde(flatten)]
57 pub payload: HashMap<String, Value>,
58}
59
60#[non_exhaustive]
61#[derive(Debug, Clone, Deserialize, Serialize)]
62pub struct CloudResponse {
63 #[serde(default)]
64 pub(crate) errcode: i32,
65 #[serde(default)]
66 pub(crate) errmsg: String,
67 #[serde(flatten)]
68 pub extra: HashMap<String, Value>,
69}
70
71pub struct CloudApi {
72 context: Arc<WechatContext>,
73}
74
75impl CloudApi {
76 pub fn new(context: Arc<WechatContext>) -> Self {
77 Self { context }
78 }
79
80 pub async fn invoke_cloud_function(
81 &self,
82 request: &InvokeCloudFunctionRequest,
83 ) -> Result<CloudResponse, WechatError> {
84 self.post_json("/tcb/invokecloudfunction", request).await
85 }
86
87 pub async fn add_delayed_function_task(
88 &self,
89 request: &DelayedFunctionTaskRequest,
90 ) -> Result<CloudResponse, WechatError> {
91 self.post_json("/tcb/adddelayedfunctiontask", request).await
92 }
93
94 pub async fn database_add(
95 &self,
96 request: &CloudDatabaseRequest,
97 ) -> Result<CloudResponse, WechatError> {
98 self.post_json("/tcb/databaseadd", request).await
99 }
100
101 pub async fn database_delete(
102 &self,
103 request: &CloudDatabaseRequest,
104 ) -> Result<CloudResponse, WechatError> {
105 self.post_json("/tcb/databasedelete", request).await
106 }
107
108 pub async fn database_update(
109 &self,
110 request: &CloudDatabaseRequest,
111 ) -> Result<CloudResponse, WechatError> {
112 self.post_json("/tcb/databaseupdate", request).await
113 }
114
115 pub async fn database_query(
116 &self,
117 request: &CloudDatabaseRequest,
118 ) -> Result<CloudResponse, WechatError> {
119 self.post_json("/tcb/databasequery", request).await
120 }
121
122 pub async fn get_upload_file_link(
123 &self,
124 request: &UploadFileLinkRequest,
125 ) -> Result<CloudResponse, WechatError> {
126 self.post_json("/tcb/uploadfile", request).await
127 }
128
129 pub async fn get_download_file_link(
130 &self,
131 request: &DownloadFileLinkRequest,
132 ) -> Result<CloudResponse, WechatError> {
133 self.post_json("/tcb/batchdownloadfile", request).await
134 }
135
136 pub async fn delete_cloud_file(
137 &self,
138 request: &DeleteCloudFileRequest,
139 ) -> Result<CloudResponse, WechatError> {
140 self.post_json("/tcb/batchdeletefile", request).await
141 }
142
143 pub async fn new_send_cloud_base_sms(
144 &self,
145 request: &SendCloudBaseSmsRequest,
146 ) -> Result<CloudResponse, WechatError> {
147 self.post_json("/tcb/sendsms_v2", request).await
148 }
149
150 async fn post_json<B: Serialize>(
151 &self,
152 endpoint: &str,
153 body: &B,
154 ) -> Result<CloudResponse, WechatError> {
155 let response: CloudResponse = self.context.authed_post(endpoint, body).await?;
156 WechatError::check_api(response.errcode, &response.errmsg)?;
157 Ok(response)
158 }
159}
160
161impl WechatApi for CloudApi {
162 fn context(&self) -> &WechatContext {
163 &self.context
164 }
165
166 fn api_name(&self) -> &'static str {
167 "cloud"
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn cloud_response_deserializes() {
177 let json = r#"{"errcode":0,"errmsg":"ok","request_id":"x"}"#;
178 let response: CloudResponse = serde_json::from_str(json).unwrap();
179 assert_eq!(response.errcode, 0);
180 assert!(response.extra.contains_key("request_id"));
181 }
182}