Skip to content

Commit 3203baa

Browse files
author
Craig Meyer
committed
Merge remote-tracking branch 'upstream/main'
2 parents e6ac1ef + 0ba4701 commit 3203baa

25 files changed

+1636
-948
lines changed

.gitignore

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ $RECYCLE.BIN/
1414
# Dependency directory for Go examples
1515
go/vendor/
1616

17-
*/target/
17+
*/target/
18+
19+
# Python compiled files
20+
*.pyo
21+
*.pyc

README.md

+13-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
- [About Pub/Sub API](#about-pubsub-api)
44
- [gRPC](#grpc)
55
- [Documentation and Blog Posts](#documentation-and-blog-post)
6-
- [Code Samples](#code-samples)
6+
- [Code Samples](#code-samples-from-salesforce)
77

88
## About Pub/Sub API
99
Welcome to Pub/Sub API! Pub/Sub API provides a single interface for publishing and subscribing to platform events, including real-time event monitoring events, and change data capture events. Based on [gRPC](https://grpc.io/docs/what-is-grpc/introduction/) and HTTP/2, Pub/Sub API enables efficient delivery of binary event messages in the Apache Avro format.
@@ -23,24 +23,32 @@ officially supported languages have well-supported Avro libraries:
2323
|C++|[Apache Avro C++](https://avro.apache.org/docs/current/api/cpp/html/index.html)|
2424
|Dart|[avro-dart](https://github.com/sqs/avro-dart) (last updated 2012)|
2525
|Go|[goavro](https://github.com/linkedin/goavro)|
26-
|Java|[Apache Avro Java](https://avro.apache.org/docs/1.10.2/gettingstartedjava.html)|
26+
|Java|[Apache Avro Java](https://avro.apache.org/docs/current/getting-started-java/)|
2727
|Kotlin|[avro4k](https://github.com/avro-kotlin/avro4k)|
2828
|Node|[avro-js](https://www.npmjs.com/package/avro-js)|
2929
|Objective C|[ObjectiveAvro](https://github.com/jlawton/ObjectiveAvro) (but read [this](https://stackoverflow.com/questions/57216446/data-serialisation-in-objective-c-avro-alternative))|
3030
|PHP|[avro-php](https://github.com/wikimedia/avro-php)|
31-
|Python|[Apache Avro Python](https://avro.apache.org/docs/current/gettingstartedpython.html)|
31+
|Python|[Apache Avro Python](https://avro.apache.org/docs/current/getting-started-python/)|
3232
|Ruby|[AvroTurf](https://github.com/dasch/avro_turf)|
3333

3434
## Documentation, Blog Post and Videos
3535
- [Pub/Sub API Developer Guide](https://developer.salesforce.com/docs/platform/pub-sub-api/overview)
3636
- [Salesforce Architects Blog Post](https://medium.com/salesforce-architects/announcing-pub-sub-api-generally-available-3980c9eaf0b7)
3737
- [Introducing the New gRPC-based Pub Sub API YouTube Developer Quick Takes](https://youtu.be/g9P87_loVVA)
3838

39-
## Code Samples
39+
## Code Samples from Salesforce
40+
Salesforce provides these samples for demonstration purposes. They aren't meant to be used in production code. Before you use these samples in production, make sure you perform thorough functional and performance testing.
41+
- [Java Quick Start in the Developer Guide](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/qs-java-quick-start.html)
4042
- [Python Quick Start in the Developer Guide](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/qs-python-quick-start.html)
4143
- [Python Code Examples](python/)
4244
- [Go Code Examples](go/)
4345
- [Java Code Examples](java/)
46+
47+
## Code Samples from the Developer Community
48+
These examples are developed by the community. They aren't supported by Salesforce. Use at your own discretion.
4449
- [E-Bikes Sample Application](https://github.com/trailheadapps/ebikes-lwc)
4550
- [Pub/Sub API Node Client](https://github.com/pozil/pub-sub-api-node-client)
46-
51+
- [.NET Code Examples](https://github.com/Meyce/pub-sub-api/tree/main/.Net)
52+
- [Ruby Pub/Sub API Example](https://github.com/RenoFi/salesforce-pub-sub-rb)
53+
54+
If you have a code sample for Pub/Sub API that you would like to add a link to in this section, submit a PR with the modified readme page. We don't guarantee that we can link to all samples. Priority will be given to samples implemented in a programming language that is not represented in this repository's samples.

java/README.md

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Pub/Sub API Java Examples
22

33
## Overview
4-
This directory contains some simple Java Examples that can be used with the Pub/Sub API. These examples range from generic Publish and Subscribe, processing CustomEventHeaders in change events and also a specific example of updating the Salesforce Account standard object. It is important to note that these examples are not performance tested nor are they production ready. They are meant to be used as a learning resource or a starting point to understand the flows of each of the Remote Procedure Calls (RPCs) of Pub/Sub API. There are some limitations to these examples as well mentioned below.
4+
This directory contains some Java examples that can be used with the Pub/Sub API. These examples range from generic Publish, Subscribe, ManagedSubscribe (beta), processing CustomEventHeaders in change events, and a specific example of updating the Salesforce Account standard object. It is important to note that these examples are not performance tested nor are they production ready. They are meant to be used as a learning resource or a starting point to understand the flows of each of the Remote Procedure Calls (RPCs) of Pub/Sub API. There are some limitations to these examples as well mentioned below.
55

66
## Project Structure
77
In the `src/main` directory of the project, you will find several sub-directories as follows:
@@ -20,14 +20,14 @@ In the `src/main` directory of the project, you will find several sub-directorie
2020
3. Run `mvn clean install` from the `java` directory to build the project and generate required sources from the proto file.
2121
4. The `arguments.yaml` file in the `src/main/resources` sub-directory contains a list of required and optional configurations needed to run the examples. The file contains detailed comments on how to set the configurations.
2222
5. Get the username, password, and login URL of the Salesforce org you wish to use.
23-
6. For the examples in `genericpubsub` package, a custom **_CarMaintenance_** [platform event](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_define_ui.htm) has to be created in the Salesforce org. Ensure your CarMaintenance platform event matches the following structure:
23+
6. For the examples in `genericpubsub` package, a custom **_Order Event_** [platform event](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_define_ui.htm) has to be created in the Salesforce org. Ensure your `Order Event` platform event matches the following structure:
2424
- Standard Fields
25-
- Label: `CarMaintenance`
26-
- Plural Label: `CarMaintenances`
25+
- Label: `Order Event`
26+
- Plural Label: `Order Events`
2727
- Custom Fields
28-
- `Cost` (Number)
29-
- `Mileage` (Number)
30-
- `WorkDescription` (Text, 200)
28+
- `Order Number` (Text, 18)
29+
- `City` (Text, 50)
30+
- `Amount` (Number, (16,2))
3131
7. For the examples in the `accountupdateapp` package, another custom **_NewAccount_** [platform event](https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_define_ui.htm) has to be created in the Salesforce org. [More info here](src/main/java/accountupdateapp/README.md).
3232

3333
### Execution
@@ -37,14 +37,20 @@ In the `src/main` directory of the project, you will find several sub-directorie
3737
* `PUBSUB_PORT`: Specify the Pub/Sub API port to be used (usually 7443).
3838
* `LOGIN_URL`: Specify the login url of the Salesforce org being used to run the examples.
3939
* `USERNAME` & `PASSWORD`: For authentication via username and password, you will need to specify the username and password of the Salesforce org.
40-
* `ACCESS_TOKEN` & `TENANT_ID`: For authentication via session token an tenant ID, you will need to specify the sessionToken and tenant ID of the Salesforce org.
40+
* `ACCESS_TOKEN` & `TENANT_ID`: For authentication via session token and tenant ID, you will need to specify the sessionToken and tenant ID of the Salesforce org.
41+
* When using managed event subscriptions (beta), one of these configurations is required.
42+
* `MANAGED_SUB_DEVELOPER_NAME`: Specify the developer name of ManagedEventSubscription. This parameter is used in ManagedSubscribe.java.
43+
* `MANAGED_SUB_ID`: Specify the ID of the ManagedEventSubscription Tooling API record. This parameter is used in ManagedSubscribe.java.
44+
4145
2. Optional Parameters:
4246
* `TOPIC`: Specify the topic for which you wish to publish/subscribe.
4347
* `NUMBER_OF_EVENTS_TO_PUBLISH`: Specify the number of events to publish while using the PublishStream RPC.
44-
* `NUMBER_OF_EVENTS_TO_SUBSCRIBE`: Specify the number of events to subscribe while using the Subscribe RPC.
48+
* `SINGLE_PUBLISH_REQUEST`: Specify if you want to publish the events in a single or multiple PublishRequests.
49+
* `NUMBER_OF_EVENTS_IN_FETCHREQUEST`: Specify the number of events that the Subscribe RPC requests from the server in each FetchRequest. The example fetches at most 5 events in each Subscribe request. If you pass in more than 5, it sends multiple Subscribe requests with at most 5 events requested in FetchRequest each. For more information about requesting events, see [Pull Subscription and Flow Control](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/flow-control.html) in the Pub/Sub API documentation.
50+
* `PROCESS_CHANGE_EVENT_HEADER_FIELDS`: Specify whether the Subscribe or ManagedSubscribe client should process the change data capture event bitmap fields in `ChangeEventHeader`. In this sample, only the `changedFields` field is expanded. To expand the `diffFields` and `nulledFields` header fields, modify the sample code. See [Event Deserialization Considerations](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/event-deserialization-considerations.html).
4551
* `REPLAY_PRESET`: Specify the ReplayPreset for subscribe examples.
4652
* If a subscription has to be started using the CUSTOM replay preset, the `REPLAY_ID` parameter is mandatory.
47-
* The `REPLAY_ID` has to be specified in the following format: `[<byte_values_separated_by_commas>]`. Please enter the values as is within the square brackets and without any quotes.
53+
* The `REPLAY_ID` is a byte array and must be specified in this format: `[<byte_values_separated_by_commas>]`. Please enter the values as is within the square brackets and without any quotes.
4854
* Example: `[0, 1, 2, 3, 4, -5, 6, 7, -8]`
4955

5056
2. After setting up the configurations, any example can be executed using the `./run.sh` file available at the parent directory.
@@ -53,10 +59,12 @@ In the `src/main` directory of the project, you will find several sub-directorie
5359

5460
## Implementation
5561
- This repo can be used as a reference point for clients looking to create a Java app to integrate with Pub/Sub API. Note that the project structure and the examples included in this repo are intended for demo purposes only and clients are free to implement their own Java apps in any way they see fit.
56-
- The Subscribe RPC examples demonstrates a basic flow control strategy where a new `fetchRequest` is sent only after the requested number of events in the previous `fetchRequest(s)` are received. Custom flow control strategies can be implemented as needed. More info on flow control available [here](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/flow-control.html).
62+
- The Generic Subscribe and ManagedSubscribe (beta) RPC examples create a long-lived subscription. After all requested events are received, Subscribe sends a new `FetchRequest` and ManagedSubscribe sends a new `ManagedFetchRequest` to keep the subscription alive and the client listening to new events.
63+
- The Generic Subscribe and ManagedSubscribe (beta) RPC examples demonstrate a basic flow control strategy where a new `FetchRequest` or `ManagedFetchRequest` is sent only after the requested number of events in the previous requests are received. The ManagedSubscribe RPC example also shows how to commit a Replay ID by sending commit requests. Custom flow control strategies can be implemented as needed. More info on flow control available [here](https://developer.salesforce.com/docs/platform/pub-sub-api/guide/flow-control.html).
64+
- The Generic Subscribe RPC example demonstrates error handling. After an exception occurs, it attempts to resubscribe after the last received event by implementing Binary Exponential Backoff. The example processes events and sends the retry requests asynchronously. If the error is an invalid replay ID, it tries to resubscribe since the earliest stored event in the event bus. See the `onError()` method in `Subscribe.java`.
5765

5866
# Limitations
5967
1. No guarantees that streams will remain open with `PublishStream` examples - Pub/Sub API has idle timeouts and will close idle streams. If a stream is closed while running these examples, you will most likely need to stop and restart.
6068
2. No support for republishing on error - If an error occurs while publishing the relevant examples will surface the error but will not attempt to republish the event.
6169
3. No security guarantees - Teams using these examples for reference will need to do their own security audits to ensure the dependencies introduced here can be safely used.
62-
4. No performance testing - These examples have not been perf tested.
70+
4. No performance testing - These examples have not been perf tested.

java/pom.xml

+13-10
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
<maven.compiler.source>11</maven.compiler.source>
1414
<maven.compiler.target>11</maven.compiler.target>
1515
<avro.version>1.11.0</avro.version>
16-
<grpc.version>1.35.1</grpc.version>
16+
<grpc.version>1.64.0</grpc.version>
17+
<protoc.version>3.25.3</protoc.version>
18+
<protocJava.version>1.64.0</protocJava.version>
1719
</properties>
1820

1921
<dependencies>
@@ -83,9 +85,9 @@
8385
<artifactId>protobuf-maven-plugin</artifactId>
8486
<version>0.5.0</version>
8587
<configuration>
86-
<protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
88+
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
8789
<pluginId>grpc-java</pluginId>
88-
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact>
90+
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${protocJava.version}:exe:${os.detected.classifier}</pluginArtifact>
8991
</configuration>
9092
<executions>
9193
<execution>
@@ -98,19 +100,20 @@
98100
</plugin>
99101
<plugin>
100102
<groupId>org.apache.maven.plugins</groupId>
101-
<artifactId>maven-assembly-plugin</artifactId>
103+
<artifactId>maven-shade-plugin</artifactId>
102104
<version>3.3.0</version>
103-
<configuration>
104-
<descriptorRefs>
105-
<descriptorRef>jar-with-dependencies</descriptorRef>
106-
</descriptorRefs>
107-
</configuration>
108105
<executions>
109106
<execution>
110107
<phase>package</phase>
111108
<goals>
112-
<goal>single</goal>
109+
<goal>shade</goal>
113110
</goals>
111+
<configuration>
112+
<createDependencyReducedPom>false</createDependencyReducedPom>
113+
<transformers>
114+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
115+
</transformers>
116+
</configuration>
114117
</execution>
115118
</executions>
116119
</plugin>

java/run.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ if [ "x$EXAMPLE" = "x" ]; then
1010
echo "Please specify one of the example class names from the package com.salesforce.eventbusclient.example"
1111
exit -1
1212
fi
13-
java -cp target/pubsub-java-1.0-SNAPSHOT-jar-with-dependencies.jar $EXAMPLE $@
13+
java -cp target/pubsub-java-1.0-SNAPSHOT.jar $EXAMPLE $@

java/src/main/java/accountupdateapp/AccountListener.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,8 @@ public void onNext(FetchResponse fetchResponse) {
7272
logger.info(e.toString());
7373
}
7474
}
75-
if (subscriber.getReceivedEvents().get() < subscriber.getTotalEventsRequested()) {
75+
if (fetchResponse.getPendingNumRequested() == 0) {
7676
subscriber.fetchMore(subscriber.getBatchSize());
77-
} else {
78-
subscriber.receivedAllEvents.set(true);
7977
}
8078
}
8179

@@ -96,7 +94,6 @@ public void onCompleted() {
9694
// Helper function to start the app.
9795
public void startApp() throws InterruptedException {
9896
subscriber.startSubscription();
99-
subscriber.waitForEvents();
10097
}
10198

10299
// Helper function to stop the app.

java/src/main/java/accountupdateapp/AccountUpdateAppUtil.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class AccountUpdateAppUtil {
4646
*/
4747
protected static GenericRecord createNewAccountRecord(Schema schema, String accountRecordId) {
4848
return new GenericRecordBuilder(schema).set("CreatedDate", System.currentTimeMillis() / 1000)
49-
.set("CreatedById", "005xx000001Svwo").set("AccountRecordId__c", accountRecordId).build();
49+
.set("CreatedById", "<User_Id>").set("AccountRecordId__c", accountRecordId).build();
5050
}
5151

5252
/**
@@ -126,6 +126,7 @@ public static void updateAccountRecord(ExampleConfigurations subParams, String a
126126

127127
if (res > 299) {
128128
logger.info("Unable to update Account Record.");
129+
logger.info(response.getContentAsString());
129130
} else {
130131
logger.info("Successfully updated Account Record. Updated AccountNumber: " + accountNumber);
131132
}

java/src/main/java/accountupdateapp/AccountUpdater.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ public void onNext(FetchResponse fetchResponse) {
6565
logger.info(e.toString());
6666
}
6767
}
68-
if (subscriber.getReceivedEvents().get() < subscriber.getTotalEventsRequested()) {
68+
if (fetchResponse.getPendingNumRequested() == 0) {
6969
subscriber.fetchMore(subscriber.getBatchSize());
70-
} else {
71-
subscriber.receivedAllEvents.set(true);
7270
}
7371
}
7472

@@ -89,7 +87,6 @@ public void onCompleted() {
8987
// Helper function to start the app.
9088
public void startApp() throws InterruptedException {
9189
subscriber.startSubscription();
92-
subscriber.waitForEvents();
9390
}
9491

9592
public void stopApp() {

java/src/main/java/accountupdateapp/README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This example subscribes to change events corresponding to the creation of [Accou
99
* Search for the `Account` object and click on the right arrow in the middle of the screen to select the entity.
1010
* Click on the `Save` button to update the changes.
1111
2. The `NewAccount` custom platform needs to be created with the following fields:
12-
- Standard Fields
12+
- Platform Event Name
1313
- Label: `NewAccount`
1414
- Plural Label: `NewAccounts`
1515
- Custom Fields
@@ -35,5 +35,11 @@ This example subscribes to change events corresponding to the creation of [Accou
3535
```
3636

3737
## Notes:
38-
* Subscribers in both the `AccountUpdater` and `AccountListener` subscribe with the ReplayPreset set to LATEST. Therefore, only events generated once the examples have started running will be processed
38+
* Please use the `my domain` URL for your org for running these examples. You can find the my domain URL through Developer Console.
39+
* Open Developer Console
40+
* Click on the Debug menu and select Open Execute Anonymous Window.
41+
* Key in the following in the window: `System.debug(System.url.getOrgDomainUrl());` and execute the same.
42+
* Once done, in the Logs tab below open the logs recently executed code.
43+
* In the logs, get the `my domain` URL from the USER_DEBUG event.
44+
* Subscribers in both the `AccountUpdater` and `AccountListener` subscribe with the ReplayPreset set to LATEST. Therefore, only events generated once the examples have started running will be processed.
3945
* The `AccountUpdater` logs the `AccountNumber` that has been added to the `Account` record which can be used to verify if the update is correct.

0 commit comments

Comments
 (0)