A Salesforce library for communicating with the IBM Watson REST APIs
Clone or download

README.md

IBM Watson Salesforce SDK

Build Status Slack semantic-release

The IBM Watson Salesforce SDK uses the Watson API services to help you solve complex problems using Apex in your Salesforce environment. If you'd like, you can follow along with our video playlist here to start using the SDK. Otherwise, continue reading to learn how to get started.

Note about Assistant v2

Assistant v2 is currently in private beta and requires access to use. To learn more and find out how to get access, please read here.

Before you begin

Getting credentials

To find out which authentication to use, view the service credentials. You find the service credentials for authentication the same way for all Watson services:

  1. Go to the IBM Cloud Dashboard page.
  2. Either click an existing Watson service instance or click Create resource > AI and create a service instance.
  3. Copy the credentials you need for authentication. Click Show if the credentials are masked.

You'll also need a Salesforce account to run your Apex code. To get one, you can visit this link.

Installation

There are three supported installation methods: automatically using Salesforce DX, manually using Salesforce DX, and manually using the Ant build tool. By default, all classes in the SDK will be deployed to your Salesforce org, but using Ant allows you to specify which services you want to deploy to save space. Note though that this is only supported with the Ant build tool method.

Salesforce DX automatic deployment

You can automatically deploy the SDK to a new scratch environment using the Deploy to SFDX button.

Note: To use the automatic deployment button, you must log in using a Dev Hub org. If you do not have one, there should be a link to get one after you click the button.

Deploy

Salesforce DX manual deployment

You can also manually deploy the SDK code using the Salesforce DX CLI, which you can install here.

The first step to manual deployment is cloning the repository from GitHub using the following command:

git clone https://github.com/watson-developer-cloud/salesforce-sdk

Be sure to navigate to the cloned directory before continuing.

To deploy to a scratch org, do the following:

  1. Create a new scratch environment (optional if you don't want to re-use an existing one):

    sfdx force:org:create -a watson-sdk -s -f config/project-scratch-def.json
  2. Push the source to the scratch environment:

    sfdx force:source:push

If you want to use the Watson SDK within a non-scratch environment you can follow these steps:

  1. Authenticate the Salesforce DX CLI to the target environment:

    sfdx force:auth:web:login --setdefaultusername

    In the browser window that opens, sign in to your org with your credentials. More information here

  2. Convert the source code:

    sfdx force:source:convert -d mdapioutput/
  3. Deploy the source code:

    sfdx force:mdapi:deploy -d mdapioutput/ -w 100

Installation using the Ant Build Tool

Finally, you can also install or update the SDK using the Ant Build Tool. This method also allows you to specify which services you'd like to deploy.

  1. Clone this repository from GitHub using the following command:

    git clone https://github.com/watson-developer-cloud/salesforce-sdk
  2. Edit install/build.properties to insert your Salesforce username and password. Since you will be using the API to access Salesforce, remember to append your Security Token to your password.

  3. Open your command line to the install folder, then deploy using Ant:

    ant deployWatson

    By default, this will deploy all of the classes. To specify a service, use the -Dservice flag. Here is an example of just deploying the Watson Assistant service:

    ant deployWatson -Dservice=assistant

    Valid service names are all services listed here written as one word (e.g. Visual Recognition becomes visualrecognition). The parameter is case-insensitive. To deploy multiple services, just run the command again with the next desired service flag.

Authentication

To access your Watson services through Apex, you'll need to authenticate with your service credentials. There are two ways to do this: using named credentials or specifying credentials in the Apex code.

Note: Previously, it was possible to authenticate using a token in a header called X-Watson-Authorization-Token. This method is deprecated. The token continues to work with Cloud Foundry services, but is not supported for services that use Identity and Access Management (IAM) authentication. See here for details.

Using Named Credentials

Named Credentials are the preferred way of authentication, since they allow you to keep sensitive information out of your code. However, they only work when using a username and password, so if you'd like to authenticate with an API key or IAM, you'll need to set that up in your Apex code.

When creating a service instance like with new Discovery(), each service loads the credentials from Named Credentials. The SDK will use the service name and API version to build the Named Credentials name.

For example

IBMDiscoveryV1 discovery = new IBMDiscoveryV1('2017-11-07');

Will look for the watson_discovery_v1 named credentials while:

IBMAssistantV1 assistant = new IBMAssistantV1('2018-02-16');

Will look for watson_assistant_v1.

In order to create Named Credentials:

  1. Go to Setup by clicking on the gear icon on the top right of the Salesforce dashboard
  2. Enter Named Credentials in the quick find box and select the highlighted entry
  3. Click on New Named Credential
  4. Enter the following values:
    • Label: A unique label that identifies your named credentials
    • Name: watson_<service_name_snake_case>_<api_version>, e.g: watson_assistant_v1
    • URL: <SERVICE_URL>, e.g: https://gateway.watsonplatform.net/assistant/api
    • Identity Type: Named Principial
    • Authentication Protocol: Password Authentication
    • Username: <USERNAME>
    • Password: <PASSWORD>
  5. Click on Save.

Specifying credentials in the Apex code

Setting credentials in the code is always an option, and in fact, it's the only option if you're authenticating with IAM.

You can always set these values directly in the constructor or with a method call after instantiating your service.

Note: You must set the service endpoint manually when setting your credentials this way. Otherwise, the SDK will default to searching for Named Credentials that you won't have set up.

Username and password

// in the constructor
IBMDiscoveryV1 discovery = new IBMDiscoveryV1('2017-11-07', 'USERNAME', 'PASSWORD');
discovery.setEndPoint('URL');
// after instantiation
IBMDiscoveryV1 discovery = new IBMDiscoveryV1('2017-11-07');
discovery.setEndPoint('URL');
discovery.setUsernameAndPassword('USERNAME', 'PASSWORD');

Using IAM

When authenticating with IAM, you have the option of passing in:

  • the IAM API key and, optionally, the IAM service URL
  • an IAM access token

Be aware that passing in an access token means that you're assuming responsibility for maintaining that token's lifecycle. If you instead pass in an IAM API key, the SDK will manage it for you.

// in the constructor, letting the SDK manage the IAM token
IBMWatsonIAMOptions options = new IBMWatsonIAMOptions.Builder()
  .apiKey('IAM_API_KEY')
  .url('IAM_URL') // optional - the default value is https://iam.bluemix.net/identity/token
  .build();
IBMDiscoveryV1 service = new IBMDiscoveryV1('2017-11-07', options);
service.setEndPoint('SERVICE_URL');
// after instantiation, letting the SDK manage the IAM token
IBMDiscoveryV1 service = new IBMDiscoveryV1('2017-11-07');
IBMWatsonIAMOptions options = new IBMWatsonIAMOptions.Builder()
  .apiKey('IAM_API_KEY')
  .build();
service.setIamCredentials(options);
service.setEndPoint('SERVICE_URL');
// in the constructor, assuming control of managing IAM token
IBMWatsonIAMOptions options = new IBMWatsonIAMOptions.Builder()
  .accessToken('ACCESS_TOKEN')
  .build();
IBMDiscoveryV1 service = new IBMDiscoveryV1('2017-11-07', options);
service.setEndPoint('SERVICE_URL');
// after instantiation, assuming control of managing IAM token
IBMDiscoveryV1 service = new IBMDiscoveryV1('2017-11-07');
IBMWatsonIAMOptions options = new IBMWatsonIAMOptions.Builder()
  .accessToken('ACCESS_TOKEN')
  .build();
service.setIamCredentials(options);
service.setEndPoint('SERVICE_URL');

If at any time you would like to let the SDK take over managing your IAM token, simply override your stored IAM credentials with an IAM API key by calling the setIamCredentials() method again.

Setting remote site settings

The final piece of setup to access Watson services from your Salesforce environment is setting your remote site settings. To do so:

  1. Go to Setup by clicking on the gear icon on the top right of the Salesforce dashboard
  2. Enter Remote Site Settings in the quick find box and select the highlighted entry
  3. Click New Remote Site
  4. Add whatever name you desire, with the following URL: https://gateway.watsonplatform.net/
  5. Click Save

If you're authenticating with IAM, you'll also need to add your IAM URL in your remote site settings. The default URL is https://iam.bluemix.net.

Examples

Getting started using a service is very simple! All services follow the same pattern of service instantiation, option building, and requesting. To get an idea, below is an example of using the Discovery service to get a list of your current environments:

// Will load credentials from the `watson_discovery_v1` named credential
IBMDiscoveryV1 discovery = new IBMDiscoveryV1('2017-11-07');

// configuring options for listing environments
IBMDiscoveryV1Models.ListEnvironmentsOptions options = new
  IBMDiscoveryV1Models.ListEnvironmentsOptionsBuilder()
  .build();

// making request
IBMDiscoveryV1Models.ListEnvironmentsResponse environmentList = discovery.listEnvironments(options);
System.debug(environmentList);

Similarly, here is an example of creating an intent in the Watson Assistant service:

// Will load credentials from the `watson_assistant_v1` named credential
IBMAssistantV1 assistant = new IBMAssistantV1('2018-02-16');

// configuring options for creating intent
IBMAssistantV1Models.CreateIntentOptions options = new
  IBMAssistantV1Models.CreateIntentOptionsBuilder()
  .workspaceId('<workspace_id>')
  .intentName('MyIntent')
  .description('This is an example of creating an intent!')
  .build();

// making request
IBMAssistantV1Models.Intent intent = assistant.createIntent(options);
System.debug(intent);

The manner of instantiating and using services should be consistent no matter which you decide to use, which should make it easy to explore the many capabilities Watson services have to offer.

Request and response headers

The SDK supports sending custom headers with any request as well as parsing headers that are returned by the service.

To send request headers, simply add them as a property when building up your Options model. Here's an example in the Discovery service:

IBMDiscoveryV1Models.QueryOptions options = new
  IBMDiscoveryV1Models.QueryOptionsBuilder()
  .environmentId('<environment_id>')
  .collectionId('<collection_id>')
  .naturalLanguageQuery('Articles about the Boston Celtics')
  .addHeader('Custon-Header', 'custom_value') // custom header added here
  .build();

To get headers returned by the service, call the getHeaders() method on a response model. This is what it looks like to get the headers returned after making the above call:

IBMDiscoveryV1Models.QueryResponse response = discovery.query(options);
Map<String, String> responseHeaders = response.getHeaders();

Using the SDK with Lightning

The Watson Salesforce SDK models are Lightning-ready, meaning that you can access model properties through Javascript for your Lightning apps. Everything should work as expected, but it's important to note that there are two ways to go about dealing with dynamic models through Javascript. These models are ones which may have properties unknown until runtime and which extend IBMWatsonDynamicModel.

Using the additionalProperties object

Dynamic models have an extra "AuraEnabled" property called additionalProperties, which is a map that holds all of the dynamic properties returned with the model. If you're dealing with a dynamic model in your Javascript code and want to access any dynamic properties, you can do the following:

action.setCallback(this, function(response) {
  var resp = response.getReturnValue(); // resp is a dynamic model
  console.log(resp.additionalProperties["enriched_text"]["concepts"][0]["text"]);
});
$A.enqueueAction(action);

Any properties within the additionalProperties object can be accessed exactly like a generic Javascript object.

Using the model string representation

If going through the additionalProperties object is undesired for any reason, you can still access the dynamic properties as top-level properties with a small workaround. All models in the SDK override the toString() method to output the models as pretty-printed JSON with the additional properties brought up to the top level. Therefore, if you tweak your server-side controller to return the string representation instead of the model object, as follows:

public class ServerSideController {
  @AuraEnabled
  public static String query(String environmentId, String collectionId) {
    IBMDiscoveryV1 discovery = new IBMDiscoveryV1('2017-11-07');
    IBMDiscoveryV1Models.QueryOptions options = new IBMDiscoveryV1Models.QueryOptionsBuilder(environmentId, collectionId)
      .naturalLanguageQuery('example query')
      .build();
    IBMDiscoveryV1Models.QueryResponse response = discovery.query(options);

    // IMPORTANT: return the string instead of the model
    return response.toString();
  }
}

you can use JSON.parse() to access the whole object as a generic JSON object. Here is an example of accessing the same property in Javascript using this aternate method:

action.setCallback(this, function(response) {
  var resp = response.getReturnValue(); // resp is now the string representation of our dynamic model
  console.log(JSON.parse(resp)["enriched_text"]["concepts"][0]["text"]);
});
$A.enqueueAction(action);

Both methods shown above will print out the same information.

WebSocket support

Both the Text to Speech and Speech to Text services have API endpoints which support the use of WebSockets. However, the use of WebSockets are not supported natively by Apex. If you would like to take advantage of this functionality, the recommendation would be to leverage the Watson Speech Javascript SDK.

The above library adds minimal overhead and will allow for the use of WebSockets. Also, being Javascript, this SDK can integrate smoothly with Lightning apps and be used alongside the Salesforce SDK.

ICP

If you're using this SDK to interact with a service on IBM Cloud Private (ICP), you'll need to add your self-signed certificate to your Salesforce organization. You can do this in the Certificate and Key Management section of the security settings.

Functional tests

The force-app/main/test folder contains the example calls for each service. These examples are used for functional testing of services. Developers can use them for reference and testing the installed SDK.

Contributing

If you're interested in helping to make this project better, see Contributing.md.

License

This library is licensed under the MIT license. Full license text is available in LICENSE.