opendal_obs/services/chainsafe/
core.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20
21use bytes::Bytes;
22use http::header;
23use http::Request;
24use http::Response;
25use serde::Deserialize;
26use serde_json::json;
27
28use crate::raw::*;
29use crate::*;
30
31#[derive(Clone)]
33pub struct ChainsafeCore {
34 pub root: String,
36 pub api_key: String,
38 pub bucket_id: String,
40
41 pub client: HttpClient,
42}
43
44impl Debug for ChainsafeCore {
45 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("Backend")
47 .field("root", &self.root)
48 .field("bucket_id", &self.bucket_id)
49 .finish_non_exhaustive()
50 }
51}
52
53impl ChainsafeCore {
54 #[inline]
55 pub async fn send(&self, req: Request<Buffer>) -> Result<Response<Buffer>> {
56 self.client.send(req).await
57 }
58}
59
60impl ChainsafeCore {
61 pub async fn download_object(
62 &self,
63 path: &str,
64 range: BytesRange,
65 ) -> Result<Response<HttpBody>> {
66 let path = build_abs_path(&self.root, path);
67
68 let url = format!(
69 "https://api.chainsafe.io/api/v1/bucket/{}/download",
70 self.bucket_id
71 );
72
73 let req_body = &json!({
74 "path": path,
75 });
76 let body = Buffer::from(Bytes::from(req_body.to_string()));
77
78 let req = Request::post(url)
79 .header(
80 header::AUTHORIZATION,
81 format_authorization_by_bearer(&self.api_key)?,
82 )
83 .header(header::RANGE, range.to_header())
84 .header(header::CONTENT_TYPE, "application/json")
85 .body(body)
86 .map_err(new_request_build_error)?;
87
88 self.client.fetch(req).await
89 }
90
91 pub async fn object_info(&self, path: &str) -> Result<Response<Buffer>> {
92 let path = build_abs_path(&self.root, path);
93
94 let url = format!(
95 "https://api.chainsafe.io/api/v1/bucket/{}/file",
96 self.bucket_id
97 );
98
99 let req_body = &json!({
100 "path": path,
101 });
102
103 let body = Buffer::from(Bytes::from(req_body.to_string()));
104
105 let req = Request::post(url)
106 .header(
107 header::AUTHORIZATION,
108 format_authorization_by_bearer(&self.api_key)?,
109 )
110 .header(header::CONTENT_TYPE, "application/json")
111 .body(body)
112 .map_err(new_request_build_error)?;
113
114 self.send(req).await
115 }
116
117 pub async fn delete_object(&self, path: &str) -> Result<Response<Buffer>> {
118 let path = build_abs_path(&self.root, path);
119
120 let url = format!(
121 "https://api.chainsafe.io/api/v1/bucket/{}/rm",
122 self.bucket_id
123 );
124
125 let req_body = &json!({
126 "paths": vec![path],
127 });
128
129 let body = Buffer::from(Bytes::from(req_body.to_string()));
130
131 let req = Request::post(url)
132 .header(
133 header::AUTHORIZATION,
134 format_authorization_by_bearer(&self.api_key)?,
135 )
136 .header(header::CONTENT_TYPE, "application/json")
137 .body(body)
138 .map_err(new_request_build_error)?;
139
140 self.send(req).await
141 }
142
143 pub async fn upload_object(&self, path: &str, bs: Buffer) -> Result<Response<Buffer>> {
144 let path = build_abs_path(&self.root, path);
145
146 let url = format!(
147 "https://api.chainsafe.io/api/v1/bucket/{}/upload",
148 self.bucket_id
149 );
150
151 let file_part = FormDataPart::new("file").content(bs);
152
153 let multipart = Multipart::new()
154 .part(file_part)
155 .part(FormDataPart::new("path").content(path));
156
157 let req = Request::post(url).header(
158 header::AUTHORIZATION,
159 format_authorization_by_bearer(&self.api_key)?,
160 );
161
162 let req = multipart.apply(req)?;
163
164 self.send(req).await
165 }
166
167 pub async fn list_objects(&self, path: &str) -> Result<Response<Buffer>> {
168 let path = build_abs_path(&self.root, path);
169
170 let url = format!(
171 "https://api.chainsafe.io/api/v1/bucket/{}/ls",
172 self.bucket_id
173 );
174
175 let req_body = &json!({
176 "path": path,
177 });
178
179 let body = Buffer::from(Bytes::from(req_body.to_string()));
180
181 let req = Request::post(url)
182 .header(
183 header::AUTHORIZATION,
184 format_authorization_by_bearer(&self.api_key)?,
185 )
186 .header(header::CONTENT_TYPE, "application/json")
187 .body(body)
188 .map_err(new_request_build_error)?;
189
190 self.send(req).await
191 }
192
193 pub async fn create_dir(&self, path: &str) -> Result<Response<Buffer>> {
194 let path = build_abs_path(&self.root, path);
195
196 let url = format!(
197 "https://api.chainsafe.io/api/v1/bucket/{}/mkdir",
198 self.bucket_id
199 );
200
201 let req_body = &json!({
202 "path": path,
203 });
204
205 let body = Buffer::from(Bytes::from(req_body.to_string()));
206
207 let req = Request::post(url)
208 .header(
209 header::AUTHORIZATION,
210 format_authorization_by_bearer(&self.api_key)?,
211 )
212 .header(header::CONTENT_TYPE, "application/json")
213 .body(body)
214 .map_err(new_request_build_error)?;
215
216 self.send(req).await
217 }
218}
219
220#[derive(Debug, Deserialize)]
221pub struct Info {
222 pub name: String,
223 pub content_type: String,
224 pub size: u64,
225}
226
227#[derive(Deserialize)]
228pub struct ObjectInfoResponse {
229 pub content: Info,
230}
231
232pub(super) fn parse_info(info: Info) -> Metadata {
233 let mode = if info.content_type == "application/chainsafe-files-directory" {
234 EntryMode::DIR
235 } else {
236 EntryMode::FILE
237 };
238
239 let mut md = Metadata::new(mode);
240
241 md.set_content_length(info.size)
242 .set_content_type(&info.content_type);
243
244 md
245}