busbar_sf_rest/client/
collections.rs1use serde::{de::DeserializeOwned, Serialize};
2use tracing::instrument;
3
4use busbar_sf_client::security::{soql, url as url_security};
5
6use crate::collections::{CollectionRequest, CollectionResult};
7use crate::error::{Error, ErrorKind, Result};
8
9impl super::SalesforceRestClient {
10 #[instrument(skip(self, records))]
12 pub async fn create_multiple<T: Serialize>(
13 &self,
14 sobject: &str,
15 records: &[T],
16 all_or_none: bool,
17 ) -> Result<Vec<CollectionResult>> {
18 if !soql::is_safe_sobject_name(sobject) {
19 return Err(Error::new(ErrorKind::Salesforce {
20 error_code: "INVALID_SOBJECT".to_string(),
21 message: "Invalid SObject name".to_string(),
22 }));
23 }
24 let request = CollectionRequest {
25 all_or_none,
26 records: records
27 .iter()
28 .map(|r| {
29 let mut value = serde_json::to_value(r).unwrap_or(serde_json::Value::Null);
30 if let serde_json::Value::Object(ref mut map) = value {
31 map.insert(
32 "attributes".to_string(),
33 serde_json::json!({"type": sobject}),
34 );
35 }
36 value
37 })
38 .collect(),
39 };
40 self.client
41 .rest_post("composite/sobjects", &request)
42 .await
43 .map_err(Into::into)
44 }
45
46 #[instrument(skip(self, records))]
48 pub async fn update_multiple<T: Serialize>(
49 &self,
50 sobject: &str,
51 records: &[(String, T)], all_or_none: bool,
53 ) -> Result<Vec<CollectionResult>> {
54 if !soql::is_safe_sobject_name(sobject) {
55 return Err(Error::new(ErrorKind::Salesforce {
56 error_code: "INVALID_SOBJECT".to_string(),
57 message: "Invalid SObject name".to_string(),
58 }));
59 }
60 for (id, _) in records {
62 if !url_security::is_valid_salesforce_id(id) {
63 return Err(Error::new(ErrorKind::Salesforce {
64 error_code: "INVALID_ID".to_string(),
65 message: "Invalid Salesforce ID format".to_string(),
66 }));
67 }
68 }
69 let request = CollectionRequest {
70 all_or_none,
71 records: records
72 .iter()
73 .map(|(id, r)| {
74 let mut value = serde_json::to_value(r).unwrap_or(serde_json::Value::Null);
75 if let serde_json::Value::Object(ref mut map) = value {
76 map.insert(
77 "attributes".to_string(),
78 serde_json::json!({"type": sobject}),
79 );
80 map.insert("Id".to_string(), serde_json::json!(id));
81 }
82 value
83 })
84 .collect(),
85 };
86
87 let url = self.client.rest_url("composite/sobjects");
88 let request_builder = self.client.patch(&url).json(&request)?;
89 let response = self.client.execute(request_builder).await?;
90 response.json().await.map_err(Into::into)
91 }
92
93 #[instrument(skip(self))]
95 pub async fn delete_multiple(
96 &self,
97 ids: &[&str],
98 all_or_none: bool,
99 ) -> Result<Vec<CollectionResult>> {
100 for id in ids {
102 if !url_security::is_valid_salesforce_id(id) {
103 return Err(Error::new(ErrorKind::Salesforce {
104 error_code: "INVALID_ID".to_string(),
105 message: "Invalid Salesforce ID format".to_string(),
106 }));
107 }
108 }
109 let ids_param = ids.join(",");
110 let url = format!(
111 "{}/services/data/v{}/composite/sobjects?ids={}&allOrNone={}",
112 self.client.instance_url(),
113 self.client.api_version(),
114 ids_param,
115 all_or_none
116 );
117 let request = self.client.delete(&url);
118 let response = self.client.execute(request).await?;
119 response.json().await.map_err(Into::into)
120 }
121
122 #[instrument(skip(self))]
124 pub async fn get_multiple<T: DeserializeOwned>(
125 &self,
126 sobject: &str,
127 ids: &[&str],
128 fields: &[&str],
129 ) -> Result<Vec<T>> {
130 if !soql::is_safe_sobject_name(sobject) {
131 return Err(Error::new(ErrorKind::Salesforce {
132 error_code: "INVALID_SOBJECT".to_string(),
133 message: "Invalid SObject name".to_string(),
134 }));
135 }
136 for id in ids {
138 if !url_security::is_valid_salesforce_id(id) {
139 return Err(Error::new(ErrorKind::Salesforce {
140 error_code: "INVALID_ID".to_string(),
141 message: "Invalid Salesforce ID format".to_string(),
142 }));
143 }
144 }
145 let safe_fields: Vec<&str> = soql::filter_safe_fields(fields.iter().copied()).collect();
147 if safe_fields.is_empty() {
148 return Err(Error::new(ErrorKind::Salesforce {
149 error_code: "INVALID_FIELDS".to_string(),
150 message: "No valid field names provided".to_string(),
151 }));
152 }
153 let ids_param = ids.join(",");
154 let fields_param = safe_fields.join(",");
155 let url = format!(
156 "{}/services/data/v{}/composite/sobjects/{}?ids={}&fields={}",
157 self.client.instance_url(),
158 self.client.api_version(),
159 sobject,
160 ids_param,
161 fields_param
162 );
163 let results: Vec<Option<T>> = self.client.get_json(&url).await.map_err(Error::from)?;
167 Ok(results.into_iter().flatten().collect())
168 }
169}