This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git
The following commit(s) were added to refs/heads/main by this push:
new 53eba73 CAMEL-21910: Add Salesforce example (#155)
53eba73 is described below
commit 53eba73afcfbeb813a306dcdc205b3b294bad2c7
Author: Stanislav Deviatov <[email protected]>
AuthorDate: Fri Mar 28 20:15:20 2025 +0100
CAMEL-21910: Add Salesforce example (#155)
* Initial version
* Add Salesforce example with REST API and CDC monitoring
* CAMEL-21910: Add route IDs for Salesforce contact queries and updates
---
README.adoc | 13 ++-
pom.xml | 1 +
salesforce/.gitignore | 1 +
salesforce/README.adoc | 101 ++++++++++++++++++
salesforce/pom.xml | 115 ++++++++++++++++++++
.../camel/example/salesforce/SalesforceApp.java | 29 +++++
.../camel/example/salesforce/SalesforceRouter.java | 117 +++++++++++++++++++++
.../main/resources/application.properties.example | 8 ++
8 files changed, 380 insertions(+), 5 deletions(-)
diff --git a/README.adoc b/README.adoc
index d42734a..8c7faa9 100644
--- a/README.adoc
+++ b/README.adoc
@@ -27,7 +27,7 @@ readme's instructions.
=== Examples
// examples: START
-Number of Examples: 61 (0 deprecated)
+Number of Examples: 62 (0 deprecated)
[width="100%",cols="4,2,4",options="header"]
|===
@@ -98,7 +98,7 @@ Number of Examples: 61 (0 deprecated)
| link:fhir/readme.adoc[Fhir] (fhir) | Health Care | An example showing how to
work with Camel, FHIR and Spring Boot
| link:fhir-auth-tx/readme.adoc[Fhir Auth Tx] (fhir-auth-tx) | Health Care |
An example showing how to work with Camel, FHIR Authorization, FHIR Transaction
and Spring Boot
-
+
| link:validator/readme.adoc[Validator Spring Boot] (validator) | Input/Output
Type Contract | An example showing how to work with declarative validation and
Spring Boot
@@ -114,7 +114,7 @@ Number of Examples: 61 (0 deprecated)
| link:metrics/README.adoc[Metrics] (metrics) | Management and Monitoring | An
example showing how to work with Camel and Spring Boot and report metrics to
Graphite
| link:observation/README.adoc[Micrometer Observation] (observation) |
Management and Monitoring | An example showing how to trace incoming and
outgoing messages from Camel with Micrometer Observation
-
+
| link:opentelemetry/README.adoc[OpenTelemetry] (opentelemetry) | Management
and Monitoring | An example showing how to use Camel with OpenTelemetry
@@ -128,7 +128,7 @@ Number of Examples: 61 (0 deprecated)
| link:kafka-avro/README.adoc[Kafka Avro] (kafka-avro) | Messaging | An
example for Kafka avro
-| link:kafka-oauth/README.adoc[Kafka OAuth] (kafka-oauth) | Messaging | An
example for Kafka authentication using OAuth
+| link:kafka-oauth/README.adoc[Kafka Oauth] (kafka-oauth) | Messaging | An
example of Kafka authentication using OAuth.
| link:kafka-offsetrepository/README.adoc[Kafka Offsetrepository]
(kafka-offsetrepository) | Messaging | An example for Kafka offsetrepository
@@ -141,7 +141,7 @@ Number of Examples: 61 (0 deprecated)
| link:widget-gadget/README.adoc[Widget Gadget] (widget-gadget) | Messaging |
The widget and gadget example from EIP book, running on Spring Boot
| link:reactive-streams/readme.adoc[Reactive Streams] (reactive-streams) |
Reactive | An example that shows how Camel can exchange data using reactive
streams with Spring Boot reactor
-
+
| link:http-ssl/README.adoc[Http Ssl] (http-ssl) | Rest | An example showing
the Camel HTTP component with Spring Boot and SSL
@@ -160,6 +160,9 @@ Number of Examples: 61 (0 deprecated)
| link:jira/README.adoc[Jira] (jira) | SaaS | An example that uses Jira Camel
API
| link:twitter-salesforce/README.adoc[Twitter Salesforce] (twitter-salesforce)
| SaaS | Twitter mentions is created as contacts in Salesforce
+
+| link:salesforce/README.adoc[Salesforce] (salesforce) | SaaS | How to work
with Salesforce contacts using REST endpoints and Streaming API
+
|===
// examples: END
diff --git a/pom.xml b/pom.xml
index 530dd40..cacb117 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,7 @@
<module>routetemplate-xml</module>
<module>route-reload</module>
<module>routes-configuration</module>
+ <module>salesforce</module>
<module>saga</module>
<module>soap-cxf</module>
<module>supervising-route-controller</module>
diff --git a/salesforce/.gitignore b/salesforce/.gitignore
new file mode 100644
index 0000000..e75f8ad
--- /dev/null
+++ b/salesforce/.gitignore
@@ -0,0 +1 @@
+src/main/resources/application.properties
\ No newline at end of file
diff --git a/salesforce/README.adoc b/salesforce/README.adoc
new file mode 100644
index 0000000..1181cd9
--- /dev/null
+++ b/salesforce/README.adoc
@@ -0,0 +1,101 @@
+= Camel Salesforce Example
+
+The example provides REST API endpoints for managing Salesforce contacts (list
all, get by ID, update) and implements real-time monitoring of Contact changes
through Change Data Capture (CDC) events.
+
+== Features
+
+* REST API endpoints to fetch all Salesforce contacts, get contact by ID and
update a contact by ID
+* Listens continuously for Change Data Capture events (CDC)
+* Salesforce authentication using client credentials flow
+
+== Prerequisites
+
+* Java 17 or higher
+* Maven 3.6+
+* Salesforce developer account
+* Salesforce Connected App credentials
+
+== Configuration
+
+1. Create a Connected App in your Salesforce org:
+ * Go to Setup > Apps > App Manager > New Connected App
+ * Enable OAuth Settings
+ * Set Callback URL (can be http://localhost:8080)
+ * Add 'Perform requests at any time' to Selected OAuth Scopes
+ * Save and wait for activation
+
+2. Enable CDC events for Contact object:
+ * Go to Setup > Integrations > Change Data Capture
+ * Add `Contact (Contact)` to Selected Entities
+ * Save
+
+3. Copy `src/main/resources/application.properties.example` to
`src/main/resources/application.properties`
+
+4. Update the properties with your Connected App credentials:
+[source,properties]
+----
+camel.component.salesforce.client-id=<YOUR_CLIENT_ID> # Consumer Key
from Connected App
+camel.component.salesforce.client-secret=<YOUR_CLIENT_SECRET> # Consumer
Secret from Connected App
+camel.component.salesforce.instance-url=<YOUR_DOMAIN> # e.g.
https://your-org.my.salesforce.com
+camel.component.salesforce.login-url=<YOUR_DOMAIN> # Same as
instance-url
+----
+
+== Building
+
+[source,bash]
+----
+mvn clean install
+----
+
+== Running
+
+[source,bash]
+----
+mvn spring-boot:run
+----
+
+The application will start on port 8080.
+
+== Testing
+
+=== REST Endpoints
+
+1. Fetch all contacts:
+[source,bash]
+----
+curl -X GET http://localhost:8080/camel/contacts | jq
+----
+
+2. Fetch a specific contact:
+[source,bash]
+----
+curl -X GET http://localhost:8080/camel/contacts/003XXXXXXXXXXXXXXX | jq
+----
+Replace `003XXXXXXXXXXXXXXX` with an actual Salesforce Contact ID.
+
+3. Update a specific contact:
+[source,bash]
+----
+curl --location --request PUT
'http://localhost:8080/camel/contacts/003XXXXXXXXXXXXXXX' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+ "LastName": "Smith",
+ "FirstName": "John",
+ "Salutation": "Mr.",
+ "Email": "[email protected]",
+ "Description": "Test description"
+}'
+----
+Replace `003XXXXXXXXXXXXXXX` with an actual Salesforce Contact ID.
+
+== Monitor CDC events
+Listens continuously for Contact Change Events (CDC):
+
+ * Make changes to contacts in Salesforce or update a specific contact
+ * Watch the application logs for real-time change events
+
+== Project Structure
+
+* `SalesforceRouter.java`: Contains Camel route definitions
+* `SalesforceApp.java`: Spring Boot application entry point
+* `application.properties`: Configuration properties
diff --git a/salesforce/pom.xml b/salesforce/pom.xml
new file mode 100644
index 0000000..e3f1d35
--- /dev/null
+++ b/salesforce/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel.springboot.example</groupId>
+ <artifactId>examples</artifactId>
+ <version>4.11.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-example-spring-boot-salesforce</artifactId>
+ <packaging>jar</packaging>
+ <name>Camel SB Examples :: Salesforce</name>
+ <description>How to work with Salesforce contacts using REST endpoints and
Streaming API</description>
+
+ <properties>
+ <category>SaaS</category>
+
+ <camelSalesforce.clientId></camelSalesforce.clientId>
+ <camelSalesforce.clientSecret></camelSalesforce.clientSecret>
+
<camelSalesforce.sslContextParameters.secureSocketProtocol>TLSv1.3</camelSalesforce.sslContextParameters.secureSocketProtocol>
+
<camelSalesforce.loginUrl>https://login.salesforce.com</camelSalesforce.loginUrl>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-bom</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${spring-boot-version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+
+ <!-- Camel -->
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-salesforce-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-rest-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-servlet-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-jackson-starter</artifactId>
+ </dependency>
+
+ <!-- Spring Boot -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+
<artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+
+</project>
diff --git
a/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceApp.java
b/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceApp.java
new file mode 100644
index 0000000..3fd0342
--- /dev/null
+++
b/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceApp.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.salesforce;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SalesforceApp {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SalesforceApp.class, args);
+ }
+
+}
diff --git
a/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceRouter.java
b/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceRouter.java
new file mode 100644
index 0000000..02e96ee
--- /dev/null
+++
b/salesforce/src/main/java/org/apache/camel/example/salesforce/SalesforceRouter.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.example.salesforce;
+
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.dataformat.JsonLibrary;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.springframework.stereotype.Component;
+
+/**
+ * A Camel router class that integrates with Salesforce to manage contact
information.
+ * This router implements five main routes:
+ * 1. REST GET endpoint to fetch all contacts
+ * 2. REST GET endpoint to retrieve a specific contact by ID
+ * 3. REST PUT endpoint to update a specific contact by ID
+ * 4. Salesforce CDC event listener for Contact changes
+ *
+ * Key features:
+ * - REST API: Servlet-based REST endpoints with JSON binding
+ * - CRUD Operations: Support for reading and updating Salesforce contacts
+ * - Real-time Updates: CDC (Change Data Capture) event monitoring
+ *
+ * Endpoints:
+ * - GET /contacts: Retrieves all contacts
+ * - GET /contacts/{id}: Retrieves a specific contact
+ * - PUT /contacts/{id}: Updates a specific contact
+ *
+ * Technical details:
+ * - Uses Spring @Component for dependency injection
+ * - Implements RestBindingMode.json for automatic JSON serialization
+ * - Leverages direct endpoints for synchronous route execution
+ * - Integrates with Salesforce using SOQL queries and CDC events
+ * - Employs Jackson for JSON data transformation
+ *
+ * @see org.apache.camel.builder.RouteBuilder
+ * @see org.springframework.stereotype.Component
+ */
+@Component
+public class SalesforceRouter extends RouteBuilder {
+
+ @Override
+ public void configure() throws Exception {
+ // Configure REST endpoint using servlet component and JSON binding
+ restConfiguration()
+ .component("servlet") // Use servlet as the HTTP server
+ .bindingMode(RestBindingMode.json); // Enable automatic JSON
data binding
+
+ // Define REST endpoint that responds to GET requests
+ rest("/contacts")
+ .get()
+ .id("Rest-based route: all contacts") // Create GET
endpoint at /contacts path
+ .to("direct:getContacts?synchronous=true") // Route requests
to direct:getContacts endpoint
+ .get("/{id}")
+ .id("Rest-based route: contact by id") // Create GET
endpoint with path parameter
+ .to("direct:getContactById?synchronous=true") // Route
requests to direct:getContactById endpoint
+ .put("/{id}")
+ .id("Rest-based route: update contact by id") // Create PUT
endpoint with path parameter
+ .to("direct:updateContactById?synchronous=true"); // Route
requests to direct:updateContactById endpoint
+
+ // Define route that queries Salesforce contacts
+ from("direct:getContacts")
+ .id("getContacts")
+ // Execute SOQL query to get Contact objects from Salesforce
+ .to("salesforce:queryAll?sObjectQuery=SELECT Id, Name, Email FROM
Contact")
+ // Uncommented debug logging line
+ // .to("log:debug?showAll=true&multiline=true")
+ // Convert Salesforce response to JSON using Jackson library
+ .unmarshal().json(JsonLibrary.Jackson);
+
+ // Define route that queries Salesforce contacts
+ from("direct:getContactById")
+ .id("getContactById")
+ // Execute SOQL query to get Contact objects from Salesforce
+
.toD("salesforce:getSObject?sObjectName=Contact&sObjectId=${header.id}")
+ // Uncommented debug logging line
+ // .to("log:debug?showAll=true&multiline=true");
+ // Convert Salesforce response to JSON using Jackson library
+ .unmarshal().json(JsonLibrary.Jackson);
+
+ // Define route that updates a Salesforce contact by ID
+ from("direct:updateContactById")
+ .id("updateContactById")
+ // Convert the input body to JSON format using Jackson library
+ .marshal().json(JsonLibrary.Jackson)
+ // Convert the JSON to String format for Salesforce update
+ .convertBodyTo(String.class)
+ // Uncommented debug logging line for troubleshooting
+ // .to("log:debug?showAll=true&multiline=true")
+ // Update the Contact object in Salesforce using the ID from the
header
+
.toD("salesforce:updateSObject?sObjectName=Contact&sObjectId=${header.id}");
+
+ // Define route that listens for Salesforce CDC events for Contact
objects
+ from("salesforce:subscribe:data/ContactChangeEvent")
+ .id("Listener Salesforce CDC events") // Set route ID for
monitoring
+ // Uncommented debug logging line
+ // .to("log:debug?showAll=true&multiline=true");
+ // Convert Salesforce response to JSON using Jackson library
+ .unmarshal().json(JsonLibrary.Jackson)
+ // Log the CDC event at INFO level
+ .log(LoggingLevel.INFO, "A new event: ${body}");
+ }
+}
diff --git a/salesforce/src/main/resources/application.properties.example
b/salesforce/src/main/resources/application.properties.example
new file mode 100644
index 0000000..9c4492d
--- /dev/null
+++ b/salesforce/src/main/resources/application.properties.example
@@ -0,0 +1,8 @@
+spring.application.name=Salesforce Example
+camel.component.salesforce.config.raw-payload=true
+camel.component.salesforce.authentication-type=CLIENT_CREDENTIALS
+camel.component.salesforce.client-id=<YOUR_CLIENT_ID>
+camel.component.salesforce.client-secret=<YOUR_CLIENT_SECRET>
+camel.component.salesforce.instance-url=<YOUR_DOMAIN>
+camel.component.salesforce.login-url=<YOUR_DOMAIN>
+