1use busbar_sf_auth::{Credentials, SalesforceCredentials};
10use busbar_sf_rest::SalesforceRestClient;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Serialize, Deserialize)]
20struct Account {
21 #[serde(rename = "Id", skip_serializing_if = "Option::is_none")]
22 id: Option<String>,
23 #[serde(rename = "Name")]
24 name: String,
25 #[serde(rename = "Industry", skip_serializing_if = "Option::is_none")]
26 industry: Option<String>,
27 #[serde(rename = "Phone", skip_serializing_if = "Option::is_none")]
28 phone: Option<String>,
29 #[serde(rename = "Website", skip_serializing_if = "Option::is_none")]
30 website: Option<String>,
31}
32
33#[tokio::main]
34async fn main() -> Result<(), Box<dyn std::error::Error>> {
35 tracing_subscriber::fmt::init();
37
38 println!("=== Salesforce REST API CRUD Examples ===\n");
39
40 let creds = get_credentials().await?;
42
43 let client = SalesforceRestClient::new(creds.instance_url(), creds.access_token())?;
45
46 println!("--- Type-Safe Struct Pattern ---\n");
48 let account_id = example_create_typed(&client).await?;
49 example_read_typed(&client, &account_id).await?;
50 example_update_typed(&client, &account_id).await?;
51
52 println!("\n--- Dynamic JSON Pattern ---\n");
53 let dynamic_id = example_create_dynamic(&client).await?;
54 example_read_dynamic(&client, &dynamic_id).await?;
55
56 example_delete(&client, &account_id).await?;
58 example_delete(&client, &dynamic_id).await?;
59
60 example_upsert(&client).await?;
62 example_create_multiple(&client).await?;
63
64 println!("\n✓ All CRUD examples completed successfully!");
65
66 Ok(())
67}
68
69async fn example_create_typed(
71 client: &SalesforceRestClient,
72) -> Result<String, Box<dyn std::error::Error>> {
73 println!("Example 1a: Create with Type-Safe Struct");
74 println!("------------------------------------------");
75
76 let account = Account {
77 id: None,
78 name: "Acme Corporation".to_string(),
79 industry: Some("Technology".to_string()),
80 phone: Some("+1-555-0100".to_string()),
81 website: Some("https://acme.example.com".to_string()),
82 };
83
84 let id = client.create("Account", &account).await?;
85 println!("✓ Created account with ID: {}", id);
86 println!(" Benefits: Type safety, IDE support, compile-time checking");
87 println!();
88
89 Ok(id)
90}
91
92async fn example_create_dynamic(
94 client: &SalesforceRestClient,
95) -> Result<String, Box<dyn std::error::Error>> {
96 println!("Example 1b: Create with Dynamic JSON");
97 println!("--------------------------------------");
98
99 let account = serde_json::json!({
100 "Name": "Dynamic Industries",
101 "Industry": "Technology",
102 "Phone": "+1-555-0200"
103 });
104
105 let id = client.create("Account", &account).await?;
106 println!("✓ Created account with ID: {}", id);
107 println!(" Benefits: Flexible, good for exploration/prototyping");
108 println!();
109
110 Ok(id)
111}
112
113async fn example_read_typed(
115 client: &SalesforceRestClient,
116 account_id: &str,
117) -> Result<(), Box<dyn std::error::Error>> {
118 println!("Example 2a: Read with Type-Safe Struct");
119 println!("----------------------------------------");
120
121 let account: Account = client
122 .get(
123 "Account",
124 account_id,
125 Some(&["Id", "Name", "Industry", "Phone", "Website"]),
126 )
127 .await?;
128
129 println!("✓ Retrieved account:");
130 println!(" ID: {:?}", account.id);
131 println!(" Name: {}", account.name);
132 println!(" Industry: {:?}", account.industry);
133 println!(" Phone: {:?}", account.phone);
134 println!(" Website: {:?}", account.website);
135 println!();
136
137 Ok(())
138}
139
140async fn example_read_dynamic(
142 client: &SalesforceRestClient,
143 account_id: &str,
144) -> Result<(), Box<dyn std::error::Error>> {
145 println!("Example 2b: Read with Dynamic JSON");
146 println!("------------------------------------");
147
148 let account: serde_json::Value = client
149 .get(
150 "Account",
151 account_id,
152 Some(&["Id", "Name", "Industry", "Phone"]),
153 )
154 .await?;
155
156 println!("✓ Retrieved account:");
157 println!(" ID: {}", account["Id"]);
158 println!(" Name: {}", account["Name"]);
159 println!(" Industry: {}", account["Industry"]);
160 println!(" Phone: {}", account["Phone"]);
161 println!();
162
163 Ok(())
164}
165
166async fn example_update_typed(
168 client: &SalesforceRestClient,
169 account_id: &str,
170) -> Result<(), Box<dyn std::error::Error>> {
171 println!("Example 3: Update Record");
172 println!("------------------------");
173
174 let updates = serde_json::json!({
176 "Name": "Acme Corporation (Updated)",
177 "Phone": "+1-555-0101"
178 });
179
180 client.update("Account", account_id, &updates).await?;
181 println!("✓ Updated account {}", account_id);
182 println!();
183
184 Ok(())
185}
186
187async fn example_upsert(client: &SalesforceRestClient) -> Result<(), Box<dyn std::error::Error>> {
189 println!("Example 4: Upsert Record");
190 println!("------------------------");
191
192 let account = serde_json::json!({
193 "Name": "Global Industries",
194 "Industry": "Manufacturing"
195 });
196
197 let external_id = "EXT-12345";
200
201 match client
202 .upsert("Account", "AccountNumber", external_id, &account)
203 .await
204 {
205 Ok(result) => {
206 if result.created {
207 println!("✓ Created new account: {}", result.id);
208 } else {
209 println!("✓ Updated existing account: {}", result.id);
210 }
211 }
212 Err(e) => {
213 println!("Note: Upsert requires an external ID field in your org");
214 println!(" Error: {}", e);
215 }
216 }
217 println!();
218
219 Ok(())
220}
221
222async fn example_delete(
224 client: &SalesforceRestClient,
225 account_id: &str,
226) -> Result<(), Box<dyn std::error::Error>> {
227 println!("Example 5: Delete Record");
228 println!("------------------------");
229
230 client.delete("Account", account_id).await?;
231 println!("✓ Deleted account {}", account_id);
232 println!();
233
234 Ok(())
235}
236
237async fn example_create_multiple(
239 client: &SalesforceRestClient,
240) -> Result<(), Box<dyn std::error::Error>> {
241 println!("Example 6: Create Multiple Records");
242 println!("-----------------------------------");
243
244 let accounts = vec![
245 Account {
246 id: None,
247 name: "Tech Startup Inc".to_string(),
248 industry: Some("Technology".to_string()),
249 phone: None,
250 website: Some("https://techstartup.example".to_string()),
251 },
252 Account {
253 id: None,
254 name: "Retail Giant LLC".to_string(),
255 industry: Some("Retail".to_string()),
256 phone: None,
257 website: Some("https://retailgiant.example".to_string()),
258 },
259 Account {
260 id: None,
261 name: "Finance Corp".to_string(),
262 industry: Some("Finance".to_string()),
263 phone: None,
264 website: None,
265 },
266 ];
267
268 let results = client.create_multiple("Account", &accounts, true).await?;
271
272 println!("✓ Created {} accounts", results.len());
273 for (i, result) in results.iter().enumerate() {
274 if result.success {
275 let id = result.id.as_deref().unwrap_or("Unknown");
276 println!(" Account {}: {} - ID: {}", i + 1, accounts[i].name, id);
277 } else {
278 println!(" Account {}: Failed - {:?}", i + 1, result.errors);
279 }
280 }
281
282 let ids: Vec<&str> = results.iter().filter_map(|r| r.id.as_deref()).collect();
284 if !ids.is_empty() {
285 let _ = client.delete_multiple(&ids, false).await;
286 println!("✓ Cleaned up {} test accounts", ids.len());
287 }
288
289 println!();
290
291 Ok(())
292}
293
294async fn get_credentials() -> Result<SalesforceCredentials, Box<dyn std::error::Error>> {
296 if let Ok(creds) = SalesforceCredentials::from_sfdx_alias("default").await {
298 println!("✓ Using credentials from Salesforce CLI\n");
299 return Ok(creds);
300 }
301
302 match SalesforceCredentials::from_env() {
304 Ok(creds) => {
305 println!("✓ Using credentials from environment variables\n");
306 Ok(creds)
307 }
308 Err(e) => {
309 eprintln!("✗ Failed to load credentials: {}", e);
310 eprintln!("\nPlease either:");
311 eprintln!(" 1. Authenticate with Salesforce CLI: sf org login web");
312 eprintln!(" 2. Set environment variables: SF_INSTANCE_URL, SF_ACCESS_TOKEN");
313 Err(e.into())
314 }
315 }
316}