Skip to main content

Subscribe to Opportunity Change Events

Learning Objectives

After completing this unit, you’ll be able to:

  • Configure a managed subscription in Tooling API.
  • Use the ManagedSubscribe RPC to subscribe to change events by running the Java sample client.
  • Resume the subscription from where it left off after disconnecting and restarting the client.

Robert Bullard is a software developer at Get Cloudy Consulting, a high-tech consulting firm specializing in CRM implementations. Robert is developing an event-driven app for managing orders for a client that sells solar panels. When an opportunity is closed for the sale of solar panels, the order app sends a request to an external order fulfillment system for solar panels. When the order has been fulfilled, the external system sends an Order Fulfillment platform event back to Salesforce with a shipping tracking code.

To start off, Robert decides to use Pub/Sub API to subscribe to the opportunity change events and publish the Order Fulfillment event back to Salesforce. Robert also decides that using a managed subscription is a good fit because it enables him to easily track the events last received and processed and be able to resume the subscription if the client disconnects unexpectedly. A managed subscription ensures a more robust client.

To learn how to write a Pub/Sub API client, Robert checks out the quick start example client. He takes a look at the steps outlined in the Java Quick Start for Managed Event Subscriptions (Beta) in the Pub/Sub API Guide. He forks the Java client and modifies it to customize it for his own needs, and also tests the client. We replicate the steps in this Trailhead module.

Configure the Managed Subscription with Tooling API

Want to Get Hands-On with Managed Subscriptions (Beta)?

In this module, we show you the steps to create a managed subscription configuration for the OpportunityChangeEvent. You can use either Tooling API or Metadata API. In this unit, you use Tooling API.

We don’t have any hands-on challenges in this module, but if you want to follow along and try out the steps, here’s how to launch your Trailhead Playground. First, make sure you’re logged in to Trailhead. Then click your user avatar in the upper-right corner of this page and select Hands-on Orgs from the dropdown. We recommend you use a brand-new playground for these steps–to do that, click Create Playground.

Connect Your Trailhead Playground Org with Postman

  1. Make sure you are logged in to your Trailhead Playground org.
  2. After you set up Postman by completing the Quick Start: Connect Postman to Salesforce project, open the Postman app, select a Workspace, and navigate to your fork of the Salesforce API collection.
  3. On the Authorization tab, scroll to the bottom and click Get New Access Token.
  4. Click Allow.
  5. In the Manage Access Tokens dialog, copy the instance URL to your clipboard.
  6. Click Use Token.
  7. On the Variables tab, in the _endpoint row, in the CURRENT VALUE column, paste the instance URL that you just copied, then click Save. You may need to close the documentation pane to see the Save button.
  8. Test that your connection is working.
  9. In Collections, select your fork of the Salesforce Platform APIs collection.
  10. Select REST to expand the REST APIs.
  11. Select GET Limits, then click Send.
  12. In the response window, the Status field should show as Status: 200 OK. If it does not, repeat the steps to get a new token.

Make a Tooling API Call to Create ManagedEventSubscription

  1. Send a POST request to this REST endpoint: /tooling/sobjects/ManagedEventSubscription. In Postman, in the Salesforce Platform APIs collection, expand Event Platform and Managed Event Subscriptions, and then click Create managed event subscription.
Note

If you don't find the Managed Event Subscriptions folder, refresh the collection. Click View more actions (...) next to the collection name, click Pull changes, and in the new tab, click Pull Changes.

  1. Use this request body.
{
  "FullName": "Managed_Sub_OpportunityChangeEvent",
  "Metadata":
  {
      "label" : "Managed Sub OpportunityChangeEvent",
      "topicName" : "/data/OpportunityChangeEvent",
      "defaultReplay": "LATEST",
      "state" : "RUN",
      "errorRecoveryReplay" : "LATEST"
  }
}
  1. Click Send. After you create the ManagedEventSubscription in Tooling API, the response returned contains the ID of the created ManagedEventSubscription record, similar to this response.
{
  "id": "18xak000000XZyHAAW",
  "success": true,
  "errors": [],
  "warnings": [],
  "infos": []
}

Create Opportunity Records and Enable Change Events

Create Opportunity Records

Create a couple of opportunities that you update later and receive change events for. (To ensure the sample account is intact, we recommend you use a brand-new Trailhead Playground, as mentioned earlier.)

  1. Locate the account for which you add opportunities. In your Trailhead Playground, click the App Launcher, enter Accounts, and then click Accounts.
  2. In the list view dropdown, click All Accounts.
  3. Click Grand Hotels & Resorts Ltd.
  4. On the account page, in the Opportunities related list, click New.
  5. Fill the following fields from the values in the 'First Opportunity Record Field Values' column in the table provided after these steps. Save your work.
  6. Repeat the steps to create another opportunity for the same account. Use the field values from the Second Opportunity Record Field Values column in the table. Save your work.

Field Name

First Opportunity Record Field Values

Second Opportunity Record Field Values

Opportunity Name

Grand Hotels - 100 solar panels

Grand Hotels - solar panel installation

Amount

30,000

20,000

Close Date

Choose a date in the future.

Choose a date in the future.

Stage

Value Proposition

Value Proposition

Enable Change Events for Opportunity Records

To receive change events for changes in opportunity records, select the Opportunity object in the Change Data Capture page in Setup.

  1. From Setup, enter Change Data Capture in the Quick Find box, and then click Change Data Capture.
  2. In Available Entities, select Opportunity (Opportunity) and click the > arrow.
  3. Click Save.

Start the Managed Subscription

Start a managed subscription by using a Java sample client, which calls the ManagedSubscribe RPC method (beta).

Prerequisites

Note

The Java sample in https://github.com/forcedotcom/pub-sub-api provides enough instructions and code snippets to build your own client. The examples are for learning purposes only. They aren’t intended for production use and haven’t gone through functional and performance testing. You can use them as a starting point.

  1. Fork the pub-sub-api GitHub repository: https://github.com/forcedotcom/pub-sub-api. Then clone your forked repository to your local system. For more information, see Fork a repository in GitHub Docs.
  2. In a terminal window, go to the java folder in your cloned pub-sub-api folder.
  3. Build the Java client package and generate required sources from the proto file. From the java folder, enter: mvn clean install
  4. Then, configure client parameters and supply the configuration parameters in arguments.yaml.
    • Keep the default values for PUBSUB_HOST and PUBSUB_PORT.
    • For LOGIN_URL, enter the URL that you use to log in to Salesforce. This can be https://login.salesforce.comor your My Domain login URL, such as https://mycompany.my.salesforce.com.
    • Get the username and password for your Trailhead Playground org in the Playground Starter app. For more information, see Get Your Trailhead Playground Username and Password unit in the Trailhead Playground Management module.
    • For USERNAME, enter your Trailhead Playground username.
    • For PASSWORD, provide the Trailhead Playground password and append the security token to your password. To get a security token, see Reset Your Security Token in Salesforce Help.
    • Specify the managed subscription developer name or ID. In this case, you use the developer name: MANAGED_SUB_DEVELOPER_NAME: Managed_Sub_OpportunityChangeEvent
    • Enable the expansion of some ChangeEventHeader fields, such as changedFields, by setting this argument to true: PROCESS_CHANGE_EVENT_HEADER_FIELDS: true
  1. Save your changes.
  2. In a Terminal window, navigate to the top-level java folder.
  3. To subscribe to the opportunity change events using the ManagedSubscribe RPC, enter:./run.sh genericpubsub.ManagedSubscribe
Note

When you create a ManagedEventSuscription, there can be a delay before the new configuration takes effect in Pub/Sub API. If the subscription returns an error saying that the managed subscription configuration isn't found, wait a little while and subscribe again.

Because no events are published yet, the client doesn’t receive any events. In the next step, you publish some events.

Close an Opportunity and Receive the Change Event

To receive a change event, let's create and then close an opportunity in Salesforce.

  1. In your Trailhead Playground, click the App Launcher, enter Opportunities, and then click Opportunities.
  2. In the list view dropdown, click All Opportunities.
  3. Click Grand Hotels - 100 solar panels.
  4. Click Edit.
  5. For The Description, enter Closing Grand Hotels - 100 solar panels opportunity.
  6. Change the State to Closed Won.
  7. Save your work. The opportunity record change causes a change event to be generated, which our subscriber client receives.
Received event:
{
  "ChangeEventHeader": {
    "entityName": "Opportunity",
    "recordIds": [
      "006QP000000mzlzYAA"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/61.0;client=SfdcInternalAPI/",
    "transactionKey": "0000efbd-97da-dceb-e528-30f8f8e69fb4",
    "sequenceNumber": 1,
    "commitTimestamp": 1722619862000,
    "commitNumber": 1722619862406914000,
    "commitUser": "005QP0000004skzYAA",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
      "0x0503C0B0"
    ]
  },
  "AccountId": null,
  "IsPrivate": null,
  "Name": null,
  "Description": "Closing Grand Hotels - 100 solar panels opportunity.",
  "StageName": "Closed Won",
  "Amount": null,
  "Probability": 100,
  "ExpectedRevenue": null,
  "TotalOpportunityQuantity": null,
  "CloseDate": null,
  "Type": null,
  "NextStep": null,
  "LeadSource": null,
  "IsClosed": true,
  "IsWon": true,
  "ForecastCategory": "Closed",
  "ForecastCategoryName": "Closed",
  "CampaignId": null,
  "HasOpportunityLineItem": null,
  "Pricebook2Id": null,
  "OwnerId": null,
  "CreatedDate": null,
  "CreatedById": null,
  "LastModifiedDate": 1722619862000,
  "LastModifiedById": null,
  "LastStageChangeDate": 1722619862000,
  "ContactId": null,
  "ContractId": null,
  "LastAmountChangedHistoryId": null,
  "LastCloseDateChangedHistoryId": null
}
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - ============================
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class -        ChangedFields
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - ============================
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - Description
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - StageName
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - Probability
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - IsClosed
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - IsWon
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - ForecastCategory
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - ForecastCategoryName
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - LastModifiedDate
2024-08-02 10:31:02,645 [grpc-default-executor-2] java.lang.Class - LastStageChangeDate
2024-08-02 10:31:02,646 [grpc-default-executor-2] java.lang.Class - ============================

Resume the Managed Subscription from the Last Committed Replay ID

Let's verify that the managed subscription tracks processed events and resumes from where it left off. The Java client committed the last processed event on your behalf. We'll dive into how the Java code implements the subscription and replay ID commits later. For now, let's try out this functionality. You'll make a couple more changes to the same opportunity after disconnecting the client. Then you reconnect the client and verify that it picks up the last two missed change events.

  1. Disconnect the pub-sub-api client by pressing Ctrl+C in the Terminal window.
  2. In your Trailhead Playground, in App Launcher, enter Opportunities, and then click Opportunities.
  3. In the Opportunities list view, click All Opportunities.
  4. Open another opportunity record. Click Grand Hotels - solar panel installation.
  5. Click Edit.
  6. For the Description, enter Closing Grand Hotels - solar panel installation opportunity.
  7. Change the State to Closed Won.
  8. Save your work.
  9. Restart the pub-sub-api client by entering this command in the same terminal window: ./run.sh genericpubsub.ManagedSubscribe

The subscription returns the change event that was missed during client shutdown.

Received event:
{
  "ChangeEventHeader": {
    "entityName": "Opportunity",
    "recordIds": [
      "006QP000000n06yYAA"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/61.0;client=SfdcInternalAPI/",
    "transactionKey": "00002b1b-51e6-f277-5c44-4057b18c3105",
    "sequenceNumber": 1,
    "commitTimestamp": 1722619905000,
    "commitNumber": 1722619905480130600,
    "commitUser": "005QP0000004skzYAA",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
      "0x0503C0B0"
    ]
  },
  "AccountId": null,
  "IsPrivate": null,
  "Name": null,
  "Description": "Closing Grand Hotels - solar panel installation opportunity.",
  "StageName": "Closed Won",
  "Amount": null,
  "Probability": 100,
  "ExpectedRevenue": null,
  "TotalOpportunityQuantity": null,
  "CloseDate": null,
  "Type": null,
  "NextStep": null,
  "LeadSource": null,
  "IsClosed": true,
  "IsWon": true,
  "ForecastCategory": "Closed",
  "ForecastCategoryName": "Closed",
  "CampaignId": null,
  "HasOpportunityLineItem": null,
  "Pricebook2Id": null,
  "OwnerId": null,
  "CreatedDate": null,
  "CreatedById": null,
  "LastModifiedDate": 1722619905000,
  "LastModifiedById": null,
  "LastStageChangeDate": 1722619905000,
  "ContactId": null,
  "ContractId": null,
  "LastAmountChangedHistoryId": null,
  "LastCloseDateChangedHistoryId": null
}
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - ============================
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class -        ChangedFields
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - ============================
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - Description
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - StageName
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - Probability
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - IsClosed
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - IsWon
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - ForecastCategory
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - ForecastCategoryName
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - LastModifiedDate
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - LastStageChangeDate
2024-08-02 10:31:52,868 [grpc-default-executor-2] java.lang.Class - ============================

Let’s Inspect the Sample Code

In the sample Java client, the ManagedSubscribe RPC method is called in ManagedSubscribe.java.

  • The managed subscription is started in the startManagedSubscription() method. The method builds a ManagedFetchRequest that contains either the Tooling API record ID or developer name of the ManagedEventSubscription metadata type. It then sends the ManagedFetchRequest to the server on a stream with this statement: serverStream.onNext(). The serverStream variable is of type StreamObserver<ManagedFetchRequest>.
  • The Replay ID of the last processed event is committed in the doCommitReplay() method in the sample client. The method builds a CommitReplayRequest that contains the Replay ID, and a commit request ID, which is a random UUID. It adds the CommitReplayRequest to a ManagedFetchRequest and sends ManagedFetchRequest on the same stream.

To learn about the fields in ManagedFetchRequest, CommitReplayRequest, and other messages, see the Pub/Sub API proto file in GitHub.

In this unit, you used a sample Java client to subscribe to change events using managed subscriptions. Also, you saw how the managed subscription can resume from where it left off after disconnecting the client and restarting the subscription. In the next unit, you learn how to publish platform events and receive them in a managed subscription.

Resources

在 Salesforce 帮助中分享 Trailhead 反馈

我们很想听听您使用 Trailhead 的经验——您现在可以随时从 Salesforce 帮助网站访问新的反馈表单。

了解更多 继续分享反馈