Skip to main content

casdoor_rust_sdk/
resource.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};
18use crate::error::{CasdoorError, Result};
19
20/// Resource has the same definition as
21/// <https://github.com/casdoor/casdoor/blob/master/object/resource.go>.
22#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase", default)]
24pub struct Resource {
25    pub owner: String,
26    pub name: String,
27    pub created_time: String,
28
29    pub user: String,
30    pub provider: String,
31    pub application: String,
32    pub tag: String,
33    pub parent: String,
34    pub file_name: String,
35    pub file_type: String,
36    pub file_format: String,
37    pub file_size: i32,
38    pub url: String,
39    pub description: String,
40}
41
42impl Client {
43    /// Get one resource by its `owner/name` ID.
44    pub async fn get_resource(&self, id: &str) -> Result<Option<Resource>> {
45        self.get_object(
46            "get-resource",
47            &[("owner", &self.config.organization_name), ("id", id)],
48        )
49        .await
50    }
51
52    /// Get one resource by its owner and name.
53    pub async fn get_resource_ex(&self, owner: &str, name: &str) -> Result<Option<Resource>> {
54        self.get_resource(&format!("{owner}/{name}")).await
55    }
56
57    /// Get the resources that match the given filters.
58    pub async fn get_resources(
59        &self,
60        owner: &str,
61        user: &str,
62        field: &str,
63        value: &str,
64        sort_field: &str,
65        sort_order: &str,
66    ) -> Result<Vec<Resource>> {
67        self.get_list(
68            "get-resources",
69            &[
70                ("owner", owner),
71                ("user", user),
72                ("field", field),
73                ("value", value),
74                ("sortField", sort_field),
75                ("sortOrder", sort_order),
76            ],
77        )
78        .await
79    }
80
81    /// Get one page of the resources that match the given filters.
82    #[allow(clippy::too_many_arguments)]
83    pub async fn get_pagination_resources(
84        &self,
85        owner: &str,
86        user: &str,
87        field: &str,
88        value: &str,
89        page_size: i32,
90        page: i32,
91        sort_field: &str,
92        sort_order: &str,
93    ) -> Result<Vec<Resource>> {
94        self.get_list(
95            "get-resources",
96            &[
97                ("owner", owner),
98                ("user", user),
99                ("field", field),
100                ("value", value),
101                ("p", &page.to_string()),
102                ("pageSize", &page_size.to_string()),
103                ("sortField", sort_field),
104                ("sortOrder", sort_order),
105            ],
106        )
107        .await
108    }
109
110    /// Upload a file to Casdoor, the returned values are the URL and the name of the
111    /// created resource.
112    pub async fn upload_resource(
113        &self,
114        user: &str,
115        tag: &str,
116        parent: &str,
117        full_file_path: &str,
118        file_bytes: &[u8],
119    ) -> Result<(String, String)> {
120        self.do_upload_resource(
121            &[
122                ("owner", &self.config.organization_name),
123                ("user", user),
124                ("application", &self.config.application_name),
125                ("tag", tag),
126                ("parent", parent),
127                ("fullFilePath", full_file_path),
128            ],
129            file_bytes,
130        )
131        .await
132    }
133
134    /// Like [`Client::upload_resource()`], but also sets the created time and the
135    /// description of the resource.
136    #[allow(clippy::too_many_arguments)]
137    pub async fn upload_resource_ex(
138        &self,
139        user: &str,
140        tag: &str,
141        parent: &str,
142        full_file_path: &str,
143        file_bytes: &[u8],
144        created_time: &str,
145        description: &str,
146    ) -> Result<(String, String)> {
147        self.do_upload_resource(
148            &[
149                ("owner", &self.config.organization_name),
150                ("user", user),
151                ("application", &self.config.application_name),
152                ("tag", tag),
153                ("parent", parent),
154                ("fullFilePath", full_file_path),
155                ("createdTime", created_time),
156                ("description", description),
157            ],
158            file_bytes,
159        )
160        .await
161    }
162
163    /// Add a resource.
164    pub async fn add_resource(&self, resource: &Resource) -> Result<bool> {
165        self.modify_resource("add-resource", resource).await
166    }
167
168    /// Update a resource.
169    pub async fn update_resource(&self, resource: &Resource) -> Result<bool> {
170        self.modify_resource("update-resource", resource).await
171    }
172
173    async fn modify_resource(&self, action: &str, resource: &Resource) -> Result<bool> {
174        let mut resource = resource.clone();
175        resource.owner = get_owner(&resource.owner, &self.config.organization_name);
176
177        let id = format!("{}/{}", resource.owner, resource.name);
178        Ok(self.modify_object(action, &id, &resource, &[]).await?.1)
179    }
180
181    /// Delete a resource.
182    pub async fn delete_resource(&self, resource: &Resource) -> Result<bool> {
183        self.delete_resource_with_tag(resource, "").await
184    }
185
186    /// Delete a resource that has the given tag.
187    pub async fn delete_resource_with_tag(&self, resource: &Resource, tag: &str) -> Result<bool> {
188        let mut resource = resource.clone();
189        if resource.owner.is_empty() {
190            resource.owner = self.config.organization_name.clone();
191        }
192
193        let post_bytes = serde_json::to_vec(&resource)?;
194        let response = self
195            .do_post(
196                "delete-resource",
197                &[("tag", tag)],
198                PostBody::Raw(post_bytes),
199            )
200            .await?;
201
202        Ok(response.data.as_str() == Some("Affected"))
203    }
204
205    async fn do_upload_resource(
206        &self,
207        query: &[(&str, &str)],
208        file_bytes: &[u8],
209    ) -> Result<(String, String)> {
210        let response = self
211            .do_post("upload-resource", query, PostBody::File(file_bytes))
212            .await?;
213
214        let file_url = response
215            .data
216            .as_str()
217            .ok_or(CasdoorError::InvalidData)?
218            .to_string();
219        let name = response
220            .data2
221            .as_str()
222            .ok_or(CasdoorError::InvalidData)?
223            .to_string();
224
225        Ok((file_url, name))
226    }
227}