nblm_core/client/api/
sources.rs

1use bytes::Bytes;
2use reqwest::{
3    header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE},
4    Method,
5};
6
7use crate::error::{Error, Result};
8use crate::models::{
9    BatchCreateSourcesRequest, BatchCreateSourcesResponse, BatchDeleteSourcesRequest,
10    BatchDeleteSourcesResponse, UploadSourceFileResponse, UserContent,
11};
12
13use crate::client::NblmClient;
14
15/// Source-related API implementations
16impl NblmClient {
17    pub async fn batch_create_sources(
18        &self,
19        notebook_id: &str,
20        request: BatchCreateSourcesRequest,
21    ) -> Result<BatchCreateSourcesResponse> {
22        let path = format!(
23            "{}/sources:batchCreate",
24            self.url_builder.notebook_path(notebook_id)
25        );
26        let url = self.url_builder.build_url(&path)?;
27        self.http
28            .request_json(Method::POST, url, Some(&request))
29            .await
30    }
31
32    pub async fn add_sources(
33        &self,
34        notebook_id: &str,
35        contents: Vec<UserContent>,
36    ) -> Result<BatchCreateSourcesResponse> {
37        let request = BatchCreateSourcesRequest {
38            user_contents: contents,
39        };
40        self.batch_create_sources(notebook_id, request).await
41    }
42
43    pub async fn batch_delete_sources(
44        &self,
45        notebook_id: &str,
46        request: BatchDeleteSourcesRequest,
47    ) -> Result<BatchDeleteSourcesResponse> {
48        let path = format!(
49            "{}/sources:batchDelete",
50            self.url_builder.notebook_path(notebook_id)
51        );
52        let url = self.url_builder.build_url(&path)?;
53        self.http
54            .request_json(Method::POST, url, Some(&request))
55            .await
56    }
57
58    pub async fn delete_sources(
59        &self,
60        notebook_id: &str,
61        source_names: Vec<String>,
62    ) -> Result<BatchDeleteSourcesResponse> {
63        let request = BatchDeleteSourcesRequest {
64            names: source_names,
65        };
66        self.batch_delete_sources(notebook_id, request).await
67    }
68
69    pub async fn upload_source_file(
70        &self,
71        notebook_id: &str,
72        file_name: &str,
73        content_type: &str,
74        data: Vec<u8>,
75    ) -> Result<UploadSourceFileResponse> {
76        if notebook_id.trim().is_empty() {
77            return Err(Error::validation("notebook_id cannot be empty"));
78        }
79        if file_name.trim().is_empty() {
80            return Err(Error::validation("file name cannot be empty"));
81        }
82        if content_type.trim().is_empty() {
83            return Err(Error::validation("content type cannot be empty"));
84        }
85
86        let path = format!(
87            "{}/sources:uploadFile",
88            self.url_builder.notebook_path(notebook_id)
89        );
90        let mut url = self.url_builder.build_upload_url(&path)?;
91        url.query_pairs_mut().append_pair("uploadType", "media");
92
93        let mut headers = HeaderMap::new();
94        headers.insert(
95            HeaderName::from_static("x-goog-upload-protocol"),
96            HeaderValue::from_static("raw"),
97        );
98        let file_name_header = HeaderValue::from_str(file_name)
99            .map_err(|_| Error::validation("file name contains invalid characters"))?;
100        headers.insert(
101            HeaderName::from_static("x-goog-upload-file-name"),
102            file_name_header,
103        );
104        let content_type_header = HeaderValue::from_str(content_type)
105            .map_err(|_| Error::validation("content type contains invalid characters"))?;
106        headers.insert(CONTENT_TYPE, content_type_header);
107
108        let bytes = Bytes::from(data);
109        self.http
110            .request_binary(Method::POST, url, headers, bytes)
111            .await
112    }
113
114    /// Get a single source by its ID.
115    ///
116    /// # Arguments
117    ///
118    /// * `notebook_id` - The ID of the notebook containing the source
119    /// * `source_id` - The ID of the source to retrieve
120    ///
121    /// # Returns
122    ///
123    /// The requested source information
124    pub async fn get_source(
125        &self,
126        notebook_id: &str,
127        source_id: &str,
128    ) -> Result<crate::models::NotebookSource> {
129        if notebook_id.trim().is_empty() {
130            return Err(Error::validation("notebook_id cannot be empty"));
131        }
132        if source_id.trim().is_empty() {
133            return Err(Error::validation("source_id cannot be empty"));
134        }
135
136        let path = format!(
137            "{}/sources/{}",
138            self.url_builder.notebook_path(notebook_id),
139            source_id
140        );
141        let url = self.url_builder.build_url(&path)?;
142        self.http
143            .request_json::<(), _>(Method::GET, url, None::<&()>)
144            .await
145    }
146}