Skip to main content

casdoor_rust_sdk/
transaction.rs

1// Copyright 2026 The Casdoor Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use serde::{Deserialize, Serialize};
16
17use crate::client::{get_owner, Client, PostBody, Response};
18use crate::error::{CasdoorError, Result};
19
20/// Transaction has the same definition as
21/// <https://github.com/casdoor/casdoor/blob/master/object/transaction.go>.
22#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase", default)]
24pub struct Transaction {
25    pub owner: String,
26    pub name: String,
27    pub created_time: String,
28    pub display_name: String,
29
30    pub application: String,
31    pub domain: String,
32    pub category: String,
33    pub r#type: String,
34    pub subtype: String,
35    pub provider: String,
36    pub user: String,
37    pub tag: String,
38
39    pub amount: f64,
40    pub currency: String,
41
42    pub payment: String,
43
44    pub state: String,
45}
46
47impl Client {
48    /// Get the transactions of the client's organization.
49    pub async fn get_transactions(&self) -> Result<Vec<Transaction>> {
50        self.get_list(
51            "get-transactions",
52            &[("owner", &self.config.organization_name)],
53        )
54        .await
55    }
56
57    /// Get one page of the transactions of the client's organization, together with the
58    /// total number of the transactions.
59    pub async fn get_pagination_transactions(
60        &self,
61        p: i32,
62        page_size: i32,
63        query: &[(&str, &str)],
64    ) -> Result<(Vec<Transaction>, i32)> {
65        self.get_pagination(
66            "get-transactions",
67            &self.config.organization_name,
68            p,
69            page_size,
70            query,
71        )
72        .await
73    }
74
75    /// Get one transaction by name.
76    pub async fn get_transaction(&self, name: &str) -> Result<Option<Transaction>> {
77        self.get_object("get-transaction", &[("id", &self.get_id(name))])
78            .await
79    }
80
81    /// Get the transactions of one user.
82    pub async fn get_user_transactions(&self, user_name: &str) -> Result<Vec<Transaction>> {
83        // Casdoor has no get-user-transactions API, get-transactions filters the
84        // transactions by user.
85        self.get_list(
86            "get-transactions",
87            &[
88                ("owner", &self.config.organization_name),
89                ("field", "user"),
90                ("value", user_name),
91            ],
92        )
93        .await
94    }
95
96    /// Add a transaction, the returned string is the ID of the created transaction.
97    pub async fn add_transaction(&self, transaction: &Transaction) -> Result<(bool, String)> {
98        self.add_transaction_with_dry_run(transaction, false).await
99    }
100
101    /// Add a transaction, or only check whether it would succeed when `dry_run` is true.
102    pub async fn add_transaction_with_dry_run(
103        &self,
104        transaction: &Transaction,
105        dry_run: bool,
106    ) -> Result<(bool, String)> {
107        let (response, affected) = self
108            .modify_transaction_with_dry_run("add-transaction", transaction, &[], dry_run)
109            .await?;
110
111        let transaction_id = response
112            .data
113            .as_str()
114            .ok_or(CasdoorError::Casdoor(
115                "failed to parse transaction id from response".to_string(),
116            ))?
117            .to_string();
118
119        Ok((affected, transaction_id))
120    }
121
122    /// Update a transaction.
123    pub async fn update_transaction(&self, transaction: &Transaction) -> Result<bool> {
124        Ok(self
125            .modify_transaction("update-transaction", transaction, &[])
126            .await?
127            .1)
128    }
129
130    /// Delete a transaction.
131    pub async fn delete_transaction(&self, transaction: &Transaction) -> Result<bool> {
132        Ok(self
133            .modify_transaction("delete-transaction", transaction, &[])
134            .await?
135            .1)
136    }
137
138    async fn modify_transaction(
139        &self,
140        action: &str,
141        transaction: &Transaction,
142        columns: &[&str],
143    ) -> Result<(Response, bool)> {
144        self.modify_transaction_with_dry_run(action, transaction, columns, false)
145            .await
146    }
147
148    /// `dry_run` is only applicable to the `add-transaction` action.
149    async fn modify_transaction_with_dry_run(
150        &self,
151        action: &str,
152        transaction: &Transaction,
153        columns: &[&str],
154        dry_run: bool,
155    ) -> Result<(Response, bool)> {
156        let mut transaction = transaction.clone();
157        transaction.owner = get_owner(&transaction.owner, &self.config.organization_name);
158
159        let id = format!("{}/{}", transaction.owner, transaction.name);
160        let columns = columns.join(",");
161
162        let mut query = vec![("id", id.as_str())];
163        if !columns.is_empty() {
164            query.push(("columns", columns.as_str()));
165        }
166        if dry_run && action == "add-transaction" {
167            query.push(("dryRun", "1"));
168        }
169
170        let post_bytes = serde_json::to_vec(&transaction)?;
171        let response = self
172            .do_post(action, &query, PostBody::Raw(post_bytes))
173            .await?;
174
175        let affected = response.data.as_str() == Some("Affected");
176        Ok((response, affected))
177    }
178}