Skip to main content

openlark_platform/app_engine/apaas/v1/application/function/
invoke.rs

1//! 执行函数
2//!
3//! 文档: <https://open.feishu.cn/document/apaas-v1/application-function/invoke>
4//! docPath: <https://open.feishu.cn/document/apaas-v1/application-function/invoke>
5
6use openlark_core::{
7    SDKResult,
8    api::{ApiRequest, ApiResponseTrait, ResponseFormat},
9    config::Config,
10    http::Transport,
11    req_option::RequestOption,
12};
13use serde::{Deserialize, Serialize};
14
15/// 执行函数 Builder
16#[derive(Debug, Clone)]
17pub struct FunctionInvokeRequestBuilder {
18    config: Config,
19    /// 应用命名空间
20    namespace: String,
21    /// 函数 API 名称
22    function_api_name: String,
23    /// 函数参数
24    params: serde_json::Value,
25}
26
27impl FunctionInvokeRequestBuilder {
28    /// 创建新的 Builder
29    pub fn new(
30        config: Config,
31        namespace: impl Into<String>,
32        function_api_name: impl Into<String>,
33    ) -> Self {
34        Self {
35            config,
36            namespace: namespace.into(),
37            function_api_name: function_api_name.into(),
38            params: serde_json::json!({}),
39        }
40    }
41
42    /// 设置函数参数
43    pub fn params(mut self, params: impl Into<serde_json::Value>) -> Self {
44        self.params = params.into();
45        self
46    }
47
48    /// 执行请求
49    pub async fn execute(self) -> SDKResult<FunctionInvokeResponse> {
50        self.execute_with_options(RequestOption::default()).await
51    }
52
53    /// 使用选项执行请求
54    pub async fn execute_with_options(
55        self,
56        option: RequestOption,
57    ) -> SDKResult<FunctionInvokeResponse> {
58        let url = format!(
59            "/open-apis/apaas/v1/applications/{}/functions/{}/invoke",
60            self.namespace, self.function_api_name
61        );
62
63        let request = FunctionInvokeRequest {
64            params: self.params,
65        };
66
67        let req: ApiRequest<FunctionInvokeResponse> =
68            ApiRequest::post(&url).body(serde_json::to_value(&request)?);
69        let resp = Transport::request(req, &self.config, Some(option)).await?;
70        resp.data
71            .ok_or_else(|| openlark_core::error::validation_error("Operation", "响应数据为空"))
72    }
73}
74
75/// 执行函数请求
76#[derive(Debug, Clone, Deserialize, Serialize)]
77struct FunctionInvokeRequest {
78    /// 函数参数
79    #[serde(rename = "params")]
80    params: serde_json::Value,
81}
82
83/// 执行函数响应
84#[derive(Debug, Clone, Deserialize, Serialize)]
85pub struct FunctionInvokeResponse {
86    /// 执行结果
87    #[serde(rename = "result")]
88    pub result: serde_json::Value,
89    /// 执行状态
90    #[serde(rename = "status")]
91    pub status: String,
92    /// 结果消息
93    #[serde(rename = "message")]
94    pub message: String,
95}
96
97impl ApiResponseTrait for FunctionInvokeResponse {
98    fn data_format() -> ResponseFormat {
99        ResponseFormat::Data
100    }
101}
102
103/// 旧名兼容别名(将在 v1.0 移除)
104#[deprecated(note = "renamed to FunctionInvokeRequestBuilder, will be removed in v1.0 (#271)")]
105pub type FunctionInvokeBuilder = FunctionInvokeRequestBuilder;
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    /// 端到端:POST .../functions/{api_name}/invoke → 强类型 FunctionInvokeResponse。
112    #[tokio::test]
113    async fn test_invoke_function_returns_data_on_success() {
114        use serde_json::json;
115        use wiremock::MockServer;
116        use wiremock::matchers::{method, path};
117        use wiremock::{Mock, ResponseTemplate};
118
119        let server = MockServer::start().await;
120        Mock::given(method("POST"))
121            .and(path(
122                "/open-apis/apaas/v1/applications/ns_test/functions/func_001/invoke",
123            ))
124            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
125                "code": 0,
126                "msg": "success",
127                "data": {
128                    "result": {"output": "ok"},
129                    "status": "SUCCESS",
130                    "message": "执行成功"
131                }
132            })))
133            .mount(&server)
134            .await;
135
136        let config = Config::builder()
137            .app_id("ci_app_id")
138            .app_secret("ci_app_secret")
139            .base_url(server.uri())
140            .enable_token_cache(false)
141            .build();
142
143        let resp = FunctionInvokeRequestBuilder::new(config, "ns_test", "func_001")
144            .params(json!({"arg": 1}))
145            .execute()
146            .await
147            .expect("执行函数应成功");
148        assert_eq!(resp.status, "SUCCESS");
149        assert_eq!(resp.message, "执行成功");
150        assert_eq!(resp.result["output"], "ok");
151
152        let received = server.received_requests().await.unwrap_or_default();
153        assert_eq!(received.len(), 1);
154        assert_eq!(
155            received[0].url.path(),
156            "/open-apis/apaas/v1/applications/ns_test/functions/func_001/invoke"
157        );
158    }
159}