Skip to main content

context69_sdk/client/library/
files.rs

1use context69_contracts::{
2    ImportLibraryFileFromUrlRequest, LibraryFileDetailResponse, LibraryFileIngestOptions,
3    LibraryFileUploadMetadata, LibraryUploadResponse, LibraryUrlImportJobResponse, MoveFileRequest,
4    PrepareLibraryUploadRequest, PrepareLibraryUploadResponse,
5};
6use reqwest::{Method, multipart::Part};
7use sha2::{Digest, Sha256};
8use uuid::Uuid;
9
10use super::Context69Client;
11use crate::{
12    Error,
13    client::transport::{file_upload_form, file_upload_form_with_options},
14};
15
16pub struct LibraryFilesApi<'a> {
17    client: &'a Context69Client,
18    base_path: String,
19}
20
21impl<'a> LibraryFilesApi<'a> {
22    pub(super) fn new(client: &'a Context69Client, base_path: String) -> Self {
23        Self { client, base_path }
24    }
25
26    pub async fn upload(
27        &self,
28        folder_id: Option<Uuid>,
29        files: Vec<Part>,
30    ) -> Result<LibraryUploadResponse, Error> {
31        let path = format!("{}/files/upload", self.base_path);
32        self.client
33            .execute_json(
34                self.client
35                    .authorized_request(Method::POST, &path)
36                    .await?
37                    .multipart(file_upload_form(folder_id, files)),
38            )
39            .await
40    }
41
42    pub async fn prepare_upload(
43        &self,
44        request: &PrepareLibraryUploadRequest,
45    ) -> Result<PrepareLibraryUploadResponse, Error> {
46        let path = format!("{}/files/prepare-upload", self.base_path);
47        self.client
48            .execute_json(
49                self.client
50                    .authorized_request(Method::POST, &path)
51                    .await?
52                    .json(request),
53            )
54            .await
55    }
56
57    pub async fn import_url(
58        &self,
59        request: &ImportLibraryFileFromUrlRequest,
60    ) -> Result<LibraryUrlImportJobResponse, Error> {
61        let path = format!("{}/files/import-url", self.base_path);
62        self.client
63            .execute_json(
64                self.client
65                    .authorized_request(Method::POST, &path)
66                    .await?
67                    .json(request),
68            )
69            .await
70    }
71
72    pub async fn upload_bytes_deduplicated(
73        &self,
74        folder_id: Option<Uuid>,
75        filename: impl Into<String>,
76        media_type: impl Into<String>,
77        bytes: Vec<u8>,
78    ) -> Result<LibraryUploadResponse, Error> {
79        self.upload_bytes_deduplicated_with_metadata(folder_id, filename, media_type, bytes, None)
80            .await
81    }
82
83    pub async fn upload_bytes_deduplicated_with_metadata(
84        &self,
85        folder_id: Option<Uuid>,
86        filename: impl Into<String>,
87        media_type: impl Into<String>,
88        bytes: Vec<u8>,
89        metadata: Option<LibraryFileUploadMetadata>,
90    ) -> Result<LibraryUploadResponse, Error> {
91        let options = metadata.map(|metadata| LibraryFileIngestOptions {
92            metadata,
93            translation: None,
94        });
95        self.upload_bytes_deduplicated_with_options(folder_id, filename, media_type, bytes, options)
96            .await
97    }
98
99    pub async fn upload_bytes_deduplicated_with_options(
100        &self,
101        folder_id: Option<Uuid>,
102        filename: impl Into<String>,
103        media_type: impl Into<String>,
104        bytes: Vec<u8>,
105        options: Option<LibraryFileIngestOptions>,
106    ) -> Result<LibraryUploadResponse, Error> {
107        let filename = filename.into();
108        let media_type = media_type.into();
109        let sha256: String = Sha256::digest(&bytes)
110            .iter()
111            .map(|byte| format!("{byte:02x}"))
112            .collect();
113        let prepared = self
114            .prepare_upload(&PrepareLibraryUploadRequest {
115                folder_id,
116                filename: filename.clone(),
117                media_type: media_type.clone(),
118                size_bytes: bytes.len() as i64,
119                sha256: sha256.clone(),
120                metadata: options.as_ref().map(|value| value.metadata.clone()),
121                translation: options.as_ref().and_then(|value| value.translation.clone()),
122            })
123            .await?;
124        if !prepared.upload_required {
125            return Ok(LibraryUploadResponse {
126                files: prepared.file.into_iter().collect(),
127                jobs: prepared.job.into_iter().collect(),
128            });
129        }
130
131        let part = Part::bytes(bytes)
132            .file_name(filename)
133            .mime_str(&media_type)?;
134        let path = format!("{}/files/upload", self.base_path);
135        self.client
136            .execute_json(
137                self.client
138                    .authorized_request(Method::POST, &path)
139                    .await?
140                    .multipart(file_upload_form_with_options(
141                        folder_id,
142                        sha256,
143                        options.as_ref(),
144                        part,
145                    )?),
146            )
147            .await
148    }
149}
150
151pub struct LibraryFileApi<'a> {
152    client: &'a Context69Client,
153    base_path: String,
154    file_id: Uuid,
155}
156
157impl<'a> LibraryFileApi<'a> {
158    pub(super) fn new(client: &'a Context69Client, base_path: String, file_id: Uuid) -> Self {
159        Self {
160            client,
161            base_path,
162            file_id,
163        }
164    }
165
166    pub async fn get(&self) -> Result<LibraryFileDetailResponse, Error> {
167        let path = format!("{}/files/{}", self.base_path, self.file_id);
168        self.client
169            .execute_json(self.client.authorized_request(Method::GET, &path).await?)
170            .await
171    }
172
173    pub async fn move_to(
174        &self,
175        request: &MoveFileRequest,
176    ) -> Result<LibraryFileDetailResponse, Error> {
177        let path = format!("{}/files/{}/move", self.base_path, self.file_id);
178        self.client
179            .execute_json(
180                self.client
181                    .authorized_request(Method::POST, &path)
182                    .await?
183                    .json(request),
184            )
185            .await
186    }
187
188    pub async fn delete(&self) -> Result<(), Error> {
189        let path = format!("{}/files/{}", self.base_path, self.file_id);
190        self.client
191            .execute_empty(
192                self.client
193                    .authorized_request(Method::DELETE, &path)
194                    .await?,
195            )
196            .await
197    }
198}