1use std::fmt::Display;
2
3use serde::Serialize;
4use tardis::{
5 basic::{dto::TardisContext, error::TardisError, result::TardisResult},
6 web::{
7 poem_openapi::types::{ParseFromJSON, ToJSON},
8 web_client::TardisHttpResponse,
9 web_resp::TardisResp,
10 },
11 TardisFuns, TardisFunsInst,
12};
13
14use crate::invoke_constants::TARDIS_CONTEXT;
15
16#[cfg(feature = "spi_base")]
17pub mod base_spi_client;
18#[cfg(feature = "spi_kv")]
19pub mod spi_kv_client;
20#[cfg(feature = "spi_log")]
21pub mod spi_log_client;
22#[cfg(feature = "spi_search")]
23pub mod spi_search_client;
24#[cfg(feature = "spi_stats")]
25pub mod spi_stats_client;
26
27#[cfg(feature = "iam")]
28pub mod iam_client;
29
30#[cfg(feature = "event")]
31pub mod event_client;
32pub mod flow_client;
33
34#[macro_export]
35macro_rules! impl_tardis_api_client {
76 ($Client:ty: $({$($defs: tt)*})*) => {
77 impl $Client {
78 $(
79 $crate::tardis_api!{$($defs)*}
80 )*
81 }
82 }
83}
84
85#[macro_export]
86macro_rules! tardis_api {
94 ($fn_name:ident, $($tt:tt)*) => {
98 $crate::tardis_api!($fn_name @method $($tt)*);
99 };
100 ($fn_name:ident @method $method:ident $($tt:tt)*) => {
104 $crate::tardis_api!($fn_name @path $method {} {} $($tt)*);
105 };
106 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [] $($tt:tt)*) => {
110 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)*} {$($path,)*} {;} $($tt)*);
111 };
112 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next:ident, $($rest_path:tt)*] $($tt:tt)*) => {
113 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)* $next: &str,} {$($path,)* $next,} [$($rest_path)*] $($tt)*);
114 };
115 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next:ident $(,)?] $($tt:tt)*) => {
116 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)* $next: &str,} {$($path,)* $next,} [] $($tt)*);
117 };
118 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next:literal, $($rest_path:tt)*] $($tt:tt)*) => {
119 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)*} {$($path,)* $next,} [$($rest_path)*] $($tt)*);
120 };
121 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next:literal $(,)?] $($tt:tt)*) => {
122 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)*} {$($path,)* $next,} [] $($tt)*);
123 };
124 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next_ident:ident:$next_type:ty, $($rest_path:tt)*] $($tt:tt)*) => {
125 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)* $next_ident:$next_type,} {$($path,)* $next_ident.to_string().as_str(),} [$($rest_path)*] $($tt)*);
126 };
127 ($fn_name:ident @path $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} [$next_ident:ident:$next_type:ty $(,)?] $($tt:tt)*) => {
128 $crate::tardis_api!($fn_name @path $method {$($args_i:$args_t,)* $next_ident:$next_type,} {$($path,)* $next_ident.to_string().as_str(),} [] $($tt)*);
129 };
130 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {} $($tt:tt)*) => {
135 $crate::tardis_api!($fn_name @build $method {$($args_i:$args_t,)*} {$($path,)*} {$($query,)*;$($optional_query,)*} $($tt)*);
136 };
137 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next_ident:ident:$next_type:ty, $($rest_query:tt)*} $($tt:tt)*) => {
139 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next_ident:$next_type,} {$($path,)*} {$($query,)* (stringify!($next_ident), $next_ident), ; $($optional_query,)*} {$($rest_query)*} $($tt)*);
140 };
141 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next_ident:ident:$next_type:ty $(,)?} $($tt:tt)*) => {
142 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next_ident:$next_type,} {$($path,)*} {$($query,)* (stringify!($next_ident), $next_ident), ; $($optional_query,)*} {} $($tt)*);
143 };
144 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next_ident:ident?:$next_type:ty, $($rest_query:tt)*} $($tt:tt)*) => {
146 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next_ident:Option<$next_type>,} {$($path,)*} {$($query,)* ; $($optional_query,)* (stringify!($next_ident), $next_ident), } {$($rest_query)*} $($tt)*);
147 };
148 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next_ident:ident?:$next_type:ty $(,)?} $($tt:tt)*) => {
149 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next_ident:Option<$next_type>,} {$($path,)*} {$($query,)* ; $($optional_query,)* (stringify!($next_ident), $next_ident), } {} $($tt)*);
150 };
151 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next:ident $(,)?} $($tt:tt)*) => {
153 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next:&str, } {$($path,)*} {$($query,)* (stringify!($next), $next), ; $($optional_query,)*} {} $($tt)*);
154 };
155 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next:ident, $($rest_query:tt)*} $($tt:tt)*) => {
156 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next:&str, } {$($path,)*} {$($query,)* (stringify!($next), $next), ; $($optional_query,)*} {$($rest_query)*} $($tt)*);
157 };
158 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next:ident? $(,)?} $($tt:tt)*) => {
160 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next:Option<&str>, } {$($path,)*} {$($query,)* ; $($optional_query,)* (stringify!($next), $next), } {} $($tt)*);
161 };
162 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} {$next:ident?, $($rest_query:tt)*} $($tt:tt)*) => {
163 $crate::tardis_api!($fn_name @query $method {$($args_i:$args_t,)* $next:Option<&str>, } {$($path,)*} {$($query,)* ; $($optional_query,)* (stringify!($next), $next), } {$($rest_query)*} $($tt)*);
164 };
165 ($fn_name:ident @query $method:ident {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {;} $($tt:tt)*) => {
167 $crate::tardis_api!($fn_name @build $method {$($args_i:$args_t,)*} {$($path,)*} {;} $($tt)*);
168 };
169 ($fn_name:ident @build get {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} $Resp:ty) => {
173 #[allow(unused_mut)]
174 pub async fn $fn_name(&self, $($args_i:$args_t,)*) -> tardis::basic::result::TardisResult<$Resp> {
175 use $crate::clients::SimpleInvokeClient;
176 use tardis::web::web_resp::TardisResp;
177 let mut query = $crate::clients::QueryBuilder::new();
178 $(
179 {
180 let q = $query;
181 query.add(q.0, q.1);
182 }
183 )*
184 $(
185 {
186 let q = $optional_query;
187 query.add_optional(q.0, q.1);
188 }
189 )*
190 let url = self.get_url(&[$($path,)*], query.as_ref());
191 let header = self.get_tardis_context_header()?;
192 let resp = self.get_funs().web_client().get::<TardisResp<$Resp>>(&url, vec![header]).await?;
193 Self::extract_response(resp)
194 }
195 };
196 ($fn_name:ident @build post {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} $Body:ty => $Resp:ty) => {
197 #[allow(unused_mut)]
198 pub async fn $fn_name(&self, $($args_i:$args_t,)* body: &$Body) -> tardis::basic::result::TardisResult<$Resp> {
199 use $crate::clients::SimpleInvokeClient;
200 use tardis::web::web_resp::TardisResp;
201 let mut query = $crate::clients::QueryBuilder::new();
202 $(
203 {
204 let q = $query;
205 query.add(q.0, q.1);
206 }
207 )*
208 $(
209 {
210 let q = $optional_query;
211 query.add_optional(q.0, q.1);
212 }
213 )*
214 let url = self.get_url(&[$($path,)*], query.as_ref());
215 let header = self.get_tardis_context_header()?;
216 let resp = self.get_funs().web_client().post::<$Body, TardisResp<$Resp>>(&url, body, vec![header]).await?;
217 Self::extract_response(resp)
218 }
219 };
220 ($fn_name:ident @build put {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} $Body:ty => $Resp:ty) => {
221 #[allow(unused_mut)]
222 pub async fn $fn_name(&self, $($args_i:$args_t,)* body: &$Body) -> tardis::basic::result::TardisResult<$Resp> {
223 use $crate::clients::SimpleInvokeClient;
224 use tardis::web::web_resp::TardisResp;
225 let mut query = $crate::clients::QueryBuilder::new();
226 $(
227 {
228 let q = $query;
229 query.add(q.0, q.1);
230 }
231 )*
232 $(
233 {
234 let q = $optional_query;
235 query.add_optional(q.0, q.1);
236 }
237 )*
238 let url = self.get_url(&[$($path,)*], query.as_ref());
239 let header = self.get_tardis_context_header()?;
240 let resp = self.get_funs().web_client().put::<$Body, TardisResp<$Resp>>(&url, body, vec![header]).await?;
241 Self::extract_response(resp)
242 }
243 };
244 ($fn_name:ident @build delete {$($args_i:ident:$args_t:ty,)*} {$($path:expr,)*} {$($query:expr,)*;$($optional_query:expr,)*} $Resp:ty) => {
245 #[allow(unused_mut)]
246 pub async fn $fn_name(&self, $($args_i:$args_t),*) -> tardis::basic::result::TardisResult<$Resp> {
247 use $crate::clients::SimpleInvokeClient;
248 use tardis::web::web_resp::TardisResp;
249 let mut query = $crate::clients::QueryBuilder::new();
250 $(
251 {
252 let q = $query;
253 query.add(q.0, q.1);
254 }
255 )*
256 $(
257 {
258 let q = $optional_query;
259 query.add_optional(q.0, q.1);
260 }
261 )*
262 let url = self.get_url(&[$($path,)*], query.as_ref());
263 let header = self.get_tardis_context_header()?;
264 let resp = self.get_funs().web_client().delete::<TardisResp<$Resp>>(&url, vec![header]).await?;
265 Self::extract_response(resp)
266 }
267 };
268}
269
270#[derive(Debug, Default)]
271pub struct QueryBuilder {
272 pub inner: String,
273}
274impl AsRef<str> for QueryBuilder {
275 fn as_ref(&self) -> &str {
276 &self.inner
277 }
278}
279impl QueryBuilder {
280 pub fn new() -> Self {
281 Self::default()
282 }
283 pub fn add<T: Display>(&mut self, key: &'static str, val: T) {
284 if !self.inner.is_empty() {
285 self.inner.push('&')
286 }
287 self.inner.push_str(key);
288 self.inner.push('=');
289 self.inner.push_str(&val.to_string());
290 }
291 pub fn add_optional<T: Display>(&mut self, key: &'static str, val: Option<T>) {
292 if let Some(val) = val {
293 self.add(key, val)
294 }
295 }
296}
297pub trait SimpleInvokeClient {
298 const DOMAIN_CODE: &'static str;
299 fn get_ctx(&self) -> &TardisContext;
300 fn get_funs(&self) -> &TardisFunsInst;
301 fn get_base_url(&self) -> &str;
302
303 fn get_tardis_context_header(&self) -> TardisResult<(String, String)> {
307 let ctx = self.get_ctx();
308 Ok((TARDIS_CONTEXT.to_string(), TardisFuns::crypto.base64.encode(TardisFuns::json.obj_to_string(ctx)?)))
309 }
310 fn get_url(&self, path: &[&str], query: &str) -> String {
311 format!(
312 "{base}/{path}{path_query_spliter}{query}",
313 base = self.get_base_url().trim_end_matches('/'),
315 path = path.join("/").trim_matches('/'),
316 path_query_spliter = if query.is_empty() { "" } else { "?" },
317 query = query
318 )
319 }
320 fn extract_response<T>(resp: TardisHttpResponse<TardisResp<T>>) -> TardisResult<T>
321 where
322 T: ParseFromJSON + ToJSON + Serialize + Send + Sync,
323 {
324 resp.body.map_or_else(
325 || {
326 Err(TardisError::internal_error(
327 &format!("invoke {domain} encounter an error", domain = Self::DOMAIN_CODE),
328 "500-invoke-request-error",
329 ))
330 },
331 |b| {
332 b.data.ok_or_else(|| TardisError {
333 code: b.code,
334 message: format!(
335 "simple invoke client call domain [{domain}] encounter an error: {msg}",
336 domain = Self::DOMAIN_CODE,
337 msg = b.msg
338 ),
339 })
340 },
341 )
342 }
343}