使用 IBM Integration Bus 通过 REST API 集成 Salesforce.com
本文旨在演示 IBM Integration Bus 和 Salesforce.com 之间通过 REST API 的集成。
引言
Salesforce.com (简称 SFDC) 是全球领先的软件即服务 (SaaS) 应用程序,主要提供客户关系管理 (CRM) 软件。SFDC 在这个应用程序云普及的时代做出了巨大贡献。近年来,它取得了惊人的增长,已有超过 10 万(且还在不断增加)的客户。
IBM Integration Bus (简称 IIB) 是 IBM 在 ESB/中间件领域的解决方案。它以前称为 Websphere Message Broker。直到最近,中间件平台主要用于集成本地应用程序,特别是在复杂的应用程序环境中,并作为更成熟的 SOA(业务、应用程序和基础设施三个学科)的基础。
如今,中间件也用于在混合 IT 环境中集成本地应用程序和云应用程序。
让我们再回到 SFDC,尽管对于业务用户而言,SFDC 是一个业务应用程序,但其核心功能和强大之处实际上在于托管该应用程序本身的底层平台。
这个平台叫做 Force.com,它提供了多种方式供消费应用程序(包括中间件)与该应用程序集成,本文将重点介绍其中一种方式:REST API。
本文分为以下两部分:
- SFDC 设置
- 通过 IIB 消费 SFDC
SFDC 设置
在转向 IIB 之前,需要对 SFDC 进行适当的配置,以便平台能够处理基于 REST 的请求。
- Connected App(已连接应用程序):提供了一种机制,允许外部客户端通过 API 与 Salesforce 集成,并通过 OAuth2.0 进行客户端身份验证。
- 要创建已连接应用程序,请按照以下步骤操作:
- Setup(设置)-> Build(构建)-> Create(创建)-> Apps(应用程序)-> Connected Apps(已连接应用程序)-> New(新建)(按钮)??
- 要创建已连接应用程序,请按照以下步骤操作:
- 请在给定的字段中提供以下信息(出于本文的范围,仅需要最基本的信息):
- Connected App Name(已连接应用程序名称):Test Connected App
- API Name(API 名称):
Test_Connected_App
(这是应用程序的技术名称,可以在给定的客户端应用程序内部引用,它默认为与已连接应用程序名称相同,但去掉空格) - Contact Email(联系电子邮件):SFDC 使用此电子邮件与您或您的支持团队联系。
- 在 API (Enable OAuth Settings)(API(启用 OAuth 设置))部分下:
- 选择 Enable OAuth Settings(启用 OAuth 设置)。
- Callback URL(回调 URL):此字段包含 Salesforce 成功身份验证后可以回调的应用程序 URL。通常,这是用户成功身份验证后浏览器将被重定向到的 URL。此字段在我们的场景中不是必需的,因此我在此必填字段中输入了一个测试/虚拟值。
- 从“Available OAuth Scopes”(可用 OAuth 范围)选择框中选择“Access and manage your data (api)”(访问和管理您的数据(API))。
- 点击此页面底部的 Save(保存)。
- 以下是创建已连接应用程序页面的屏幕截图,供参考:
- 您的已连接应用程序现已创建。请等待 2-10 分钟使其生效。在此期间,请记下此新创建的已连接应用程序上可用的以下两个参数:
- Consumer Key(消费者密钥)
- Consumer Secret(消费者密钥)
- 这些参数在运行时用于客户端身份验证。事实上,在尝试使用用户名-密码 OAuth 流成功登录时,需要以下 5 个参数:
- Username(用户名):您的 Salesforce 用户 ID。
- Password(密码):您的 Salesforce 账户密码 + 与您账户关联的用户令牌(实际上是这两个值的串联)。
- Consumer Key(消费者密钥):将在登录的 HTTP 请求中作为
client_id
参数提供。 - Consumer Secret(消费者密钥):将在登录的 HTTP 请求中作为
client_secret
参数提供。 - 字符串值
password
。
SFDC 的前期准备工作已完成,现在我们开始 IIB 部分。
通过 IIB 消费 SFDC
为了方便本文的读者,我在 IIB 中开发了一个示例应用程序来演示与 SFDC 的集成部分。
该应用程序执行以下功能:
- 接收输入队列中的请求消息。
- 使用用户名-密码 OAuth 模式登录 SFDC。
- 根据输入队列中收到的请求消息的内容,在 SFDC 中创建一个客户(Account)。
- 如果客户创建成功,则将创建的记录 ID 和请求状态(由 SFDC 返回的两个字段)放入一个传出的 XML 消息中,并将其放入输出队列。
这是 XML 请求:
<SFDC_Create_Source>
<Account>
<AccountName>Test Account 1</AccountName>
<AccountNumber>123</AccountNumber>
<Type>Prospect</Type>
</Account>
</SFDC_Create_Source>
您应该可以在随附的项目文件中找到它。这是屏幕截图,供参考。
此 XML 中的 Account/AccountName、Account/AccountNumber 和 Account/Type 元素是我们将在发送到 SFDC 的创建消息中提供的参数,以便在那里创建一个具有提供信息的 Account 记录。
消息流如下所示:
设计时设置
此消息流使用了以下四个用户定义属性:
UserName
(用户名):包含 SFDC 账户用户名。Password
(密码):组合值,将密码和与给定账户关联的安全令牌合并。ClientSecret
(客户端密钥):从之前在 SFDC 中创建的已连接应用程序获取的消费者密钥。ClientID
(客户端 ID):从之前在 SFDC 中创建的已连接应用程序获取的消费者密钥。
在 Fire SFDC login http node(触发 SFDC 登录 HTTP 节点)中,指定此 URL:https://login.salesforce.com/services/oauth2/token(这是需要尝试登录的 URL)。
对于两个 HTTP 节点(Fire SFDC login 和 Fire Account Create),将 HTTP Method(HTTP 方法)设置为 POST。
对于两个 HTTP 节点(Fire SFDC login 和 Fire Account Create),将 SSL 协议设置为 TLS。
对于两个 HTTP 节点(Fire SFDC login 和 Fire Account Create),在 Response Message Parsing(响应消息解析)选项卡中,将 Message domian(消息域)设置为 JSON:用于 JavaScript Object Notation 消息。
对于 Process Login(处理登录)和 Prepare Create Command(准备创建命令)计算节点,将 Compute mode(计算模式)设置为 All(全部)(或至少 LocalEnvironment 和 Message)。
代码
初始化和登录准备
CREATE COMPUTE MODULE Init_And_Prepare_Login
DECLARE UserName EXTERNAL CHAR;
DECLARE Password EXTERNAL CHAR;
DECLARE ClientSecret EXTERNAL CHAR;
DECLARE ClientID EXTERNAL CHAR;
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
--Init, preserving the contents of the source message in
--Environment tree using the utlity methods defined above
-- These contents are needed at different steps in the downstream processing
-- The reason why these special copy functions are used is
-- because the header trees (mqmd, mqrfh2 etc.) cannot be simply assigned
-- to the Environment root only to be reassigned
-- to any other tree in a given compute node.
-- The reason is that these headers use
-- special parsers [DOMAIN keyword with the parser name,
-- e.g. DOMAIN(MQMD) or DOMAIN(MQRFH2) is used while 'creating' parsers and
-- then the fields inside them
-- More info on this topic can be found in this post:
-- https://villein.wordpress.com/copyfolders-by-esql-reference/
DECLARE rInput REFERENCE TO InputRoot;
DECLARE rEnvironment REFERENCE TO Environment;
--The following methods copies the source message tree to the environment root.
--The contents are needed in
--the subsequent processing steps
CALL CopyAllFolders(rInput,rEnvironment,'TOP');
--Now building the http post request for loging into SFDC.
--Please keep in mind that we are using Username-Password OAuth
--Authentication Flow in this case
DECLARE _param1 CHAR ''; DECLARE _param2 CHAR ''; _
DECLARE _param3 CHAR ''; DECLARE _param4 CHAR ''; DECLARE _param5 CHAR '';
DECLARE _totalString CHAR '';
SET _param1 = 'grant_type=password';
SET _param2 = '&client_id=' || ClientID;
SET _param3 = '&client_secret=' || ClientSecret;
SET _param4 = '&username=' || UserName;
SET _param5 = '&password=' || Password;
--Combining all the above params
SET _totalString = _param1 || _param2 || _param3 || _param4 || _param5;
--Setting the URL
--SET OutputLocalEnvironment.Destination.HTTP.RequestURL =
--'https://login.salesforce.com/services/oauth2/token';
--Setting the Content Type
SET OutputRoot.HTTPRequestHeader."Content-Type" = 'application/x-www-form-urlencoded';
SET OutputRoot.BLOB.BLOB = CAST(_totalString AS BLOB CCSID InputRoot.MQMD.CodedCharSetId);
-- The command should assume this form:
/*
grant_type=password&client_id=3MVG9lKcPoNINVBIPJjdw1J9LLM82Hn
FVVX19KY1uA5mu0QqEWhqKpoW3svG3XHrXDiCQjK1mdgAvhCscA9GE&client_secret=
1955279925675241571&username=testuser%40salesforce.com&password=mypassword
*/
-- CALL CopyMessageHeaders();
-- CALL CopyEntireMessage();
RETURN TRUE;
END;
CREATE PROCEDURE CopyMessageHeaders() BEGIN
DECLARE I INTEGER 1;
DECLARE J INTEGER;
SET J = CARDINALITY(InputRoot.*[]);
WHILE I < J DO
SET OutputRoot.*[I] = InputRoot.*[I];
SET I = I + 1;
END WHILE;
END;
CREATE PROCEDURE CopyEntireMessage() BEGIN
SET OutputRoot = InputRoot;
END;
END MODULE;
处理登录响应并准备创建 Account 命令
CREATE COMPUTE MODULE Process_login_and_prepare_create_command
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
DECLARE _objectURI CHAR InputRoot.JSON.Data.instance_url; --URL of the instance
--associated with the given SFDC account
DECLARE _oauthToken CHAR InputRoot.JSON.Data.access_token; --access token to unlock
--the associated instance
SET _objectURI = _objectURI || '/services/data/v30.0/sobjects/Account/'; -- setting the
--API version and the object on which the
--insert/create operation needs to be performed, to the URL
-- Setting HTTP params dynamically
SET OutputRoot.HTTPRequestHeader."Content-Type" = 'application/json';
SET OutputRoot.HTTPRequestHeader."Authorization" = 'Bearer ' || _oauthToken;
SET OutputLocalEnvironment.Destination.HTTP.RequestURL = _objectURI;
SET OutputRoot.JSON.Data.Name = Environment.XMLNSC.SFDC_Create_Source.Account.AccountName;
SET OutputRoot.JSON.Data.AccountNumber = _
Environment.XMLNSC.SFDC_Create_Source.Account.AccountNumber;
SET OutputRoot.JSON.Data.Type = Environment.XMLNSC.SFDC_Create_Source.Account.Type;
-- CALL CopyMessageHeaders();
-- CALL CopyEntireMessage();
RETURN TRUE;
END;
CREATE PROCEDURE CopyMessageHeaders() BEGIN
DECLARE I INTEGER 1;
DECLARE J INTEGER;
SET J = CARDINALITY(InputRoot.*[]);
WHILE I < J DO
SET OutputRoot.*[I] = InputRoot.*[I];
SET I = I + 1;
END WHILE;
END;
CREATE PROCEDURE CopyEntireMessage() BEGIN
SET OutputRoot = InputRoot;
END;
END MODULE;
将创建 Account 响应处理到传出的响应 XML 消息中
CREATE COMPUTE MODULE Process_Create_Result
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
-- Restoring Properties and MQMD from Environment to OutputRoot.
DECLARE rOutput REFERENCE TO OutputRoot;
DECLARE rEnvironment REFERENCE TO Environment;
CALL CopyAllFoldersButLast(rEnvironment,rOutput,'BOT');
SET OutputRoot.XMLNSC.Response.Id = InputRoot.JSON.Data.id;
SET OutputRoot.XMLNSC.Response.Status = InputRoot.JSON.Data.success;
-- CALL CopyMessageHeaders();
-- CALL CopyEntireMessage();
RETURN TRUE;
END;
CREATE PROCEDURE CopyMessageHeaders() BEGIN
DECLARE I INTEGER 1;
DECLARE J INTEGER;
SET J = CARDINALITY(InputRoot.*[]);
WHILE I < J DO
SET OutputRoot.*[I] = InputRoot.*[I];
SET I = I + 1;
END WHILE;
END;
CREATE PROCEDURE CopyEntireMessage() BEGIN
SET OutputRoot = InputRoot;
END;
END MODULE;
就是这样。您可以下载项目文件并以这种方式浏览解决方案。您也可以根据自己的需求扩展此项目。
重要链接
- Connected App Overview(已连接应用程序概述):https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_overview.htm
- Digging Deeper into OAuth 2.0 on Force.com(深入了解 Force.com 上的 OAuth 2.0):https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com
- REST API:https://developer.salesforce.com/page/REST_API
- Force.com REST API Developer's Guide(Force.com REST API 开发者指南):https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/
- IBM Integration Bus 9 Info Center(IBM Integration Bus 9 信息中心):http://www-01.ibm.com/support/knowledgecenter/SSMKHH_9.0.0/mapfiles/help_home_msgbroker.html?lang=en
- HTTPRequest Node(HTTP 请求节点):https://www-01.ibm.com/support/knowledgecenter/SSMKHH_10.0.0/com.ibm.etools.mft.doc/ac04595_.htm
- ESQL Reference(ESQL 参考):https://www-01.ibm.com/support/knowledgecenter/SSMKHH_9.0.0/com.ibm.etools.mft.doc/ak04860_.htm