Quantcast
Channel: ATeam Chronicles
Viewing all articles
Browse latest Browse all 33

Integrating with Oracle Sales Cloud using SOAP web services and REST APIs (Part 1)

$
0
0

Sales Cloud provides several types of interfaces to facilitate integration with other applications within your enterprise or on the cloud, such as SOAP web services, REST APIs, Events, file-loaders, and BI Reports. The focus of this blog series is SOAP web services and REST APIs.

Sales Cloud provides SOAP web services and REST APIs for other applications to operate on core Sales Cloud functions. Sales Cloud also provides an extension framework that allows outbound invocation to other SOAP services within your on-premises systems or on the cloud.

In this blog series, I will cover the following topics briefly.

1.  Invoking Sales Cloud SOAP web services

a. Identifying the Sales Cloud SOAP web service to be invoked
b. Sample invocation of a web service
c. Sample invocation from an ADF application

2.  Invoking external SOAP Web Services from Sales Cloud (covered in Part 2)

3.  Invoking Sales Cloud REST APIs from other external applications (covered in Part 3)

4.  Invoking external REST APIs from Sales Cloud (currently unavailable but will soon be a feature )

1. Invoking Sales Cloud SOAP web services from external applications

There are two main types of services that Sales Cloud exposes

–          ADF Services – These services allow you to perform CRUD operations on Sales Cloud business objects. For example, Sales Party Service, Opportunity Service etc. Using these services you can typically perform operations such as get, find, create, delete, update etc on Sales Cloud objects.These services are typically useful for UI driven integrations such as looking up Sales Cloud information from external application UIs, using third party Interfaces to create/update data in Sales Cloud. They are also used in non-UI driven integration uses cases such as initial upload of business or setup data, synchronizing data with an external systems, etc.

–          Composite Services – These services involve more logic than CRUD and often involving human workflows, rules etc. These services perform a business function such as Orchestration Order Service and are used when building larger process based integration with external systems.These services are usually asynchronous in nature and are not typically used for UI integration patterns. These services will not be the focus of this blog

1a. Identifying the SOAP web service to be invoked

All Sales Cloud web service metadata is available through Developer Connect available within your Sales Cloud Instance. Note: HCM, ERP, and SCM web services are also available in the same location. To navigate to Developer Connect, log into your Sales Cloud instance > Navigator (the hamburger icon) > Tools > Developer Connect

DeveloperConnectNav1

Once you connect, you will be able to search any service that you are interested in. Before you proceed, click the Synchronize button to ensure that you have the latest definitions. It may take a few seconds for the Synchronization to complete. In the example below, I’m searching for the Opportunity Service. You can directly view the WSDL if you would like using the WSDL download link.

DevConnect2

You can drill down into the service and look for additional information about the service, such as the endpoint, security policies, operations, payload structures, and even sample code for invocation.

 

DevConnect3

 

DevConnect4

 

Note: The SOAP webservices can be used part of data integration or UI integration requirements.
When using Oracle Integration Cloud Service or Oracle SOA cloud Service or when using any data integration platform, these SOAP web services can be utilized to query and edit data in Sales Cloud.
When using Java Cloud Service, you can build an Oracle ADF application which uses the SOAP service to display and modify data on a custom built UI

1b. Sample invocation of a web service

Take a closer look at the WSDL or the details tabs in the developer connect. Couple of items to notice here

–          Most operations have synchronous as well as asynchronous variations. This blog will focus on synchronous only. For asynchronous, please refer – http://www.ateam-oracle.com/using-soapui-for-secure-asynchronous-web-service-invocations-in-fusion-applications.

–          Notice the security policy entry under binding. The synchronous operations are associated usually with wss11_saml_or_username_token_with_message_protection_service_policy

image003

The get operation has a simpler request payload since it typically uses only an ID column. The create and update are larger payloads but straightforward to execute. The focus of this blog will be the “Find” Operation, which is equivalent to an “Advanced Search” that you typically find in UIs.

The Find operation allows you to find one more entities based on a given combination of search criteria. Additionally, Find operations allow you to filter the resulting fields that you want to see. Imagine that you want to build a simple search feature in your custom application where you want the search results to display a valid set of Sales Accounts – as defined in Sales Cloud. You may want the results to display as a list of Accounts (organizations), with information just four key pieces of information – Organization Name, Address, City and Country.

image005If the service invocation with find operation results in providing ALL details for each Sales Party, then it makes it hard for the custom application to handle the output. There is an unnecessary performance overhead as well. Fortunately, the Find operation provides a set of parameters in the input payload that can be used to control the output.

For example, you can search for all customers of type organizations using the query below. The above request translates to – give me the top 10 hits for Organizations whose names start with “Art” and give me only the Name, Address, City and Country details of these Organizations.

Request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <typ:findSalesParty xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/types/">
         <typ:findCriteria xmlns:typ1="http://xmlns.oracle.com/adf/svc/types/">
            <typ1:fetchStart>0</typ1:fetchStart>
            <typ1:fetchSize>10</typ1:fetchSize>
            <typ1:filter>
               <typ1:group>
               <typ1:conjunction>And</typ1:conjunction>
                  <typ1:item>
                     <typ1:attribute>PartyName</typ1:attribute>
                     <typ1:operator>STARTSWITH</typ1:operator>
                     <typ1:value>Art</typ1:value>
                  </typ1:item>
                  <typ1:item>
                     <typ1:attribute>PartyType</typ1:attribute>
                     <typ1:operator>=</typ1:operator>
                     <typ1:value>ORGANIZATION</typ1:value>
                  </typ1:item>	
               </typ1:group>
            </typ1:filter>
            <typ1:findAttribute>PartyName</typ1:findAttribute>
            <typ1:findAttribute>OrganizationParty</typ1:findAttribute>
            <typ1:childFindCriteria>
            		<typ1:childAttrName>OrganizationParty</typ1:childAttrName>
            		<typ1:findAttribute>Address1</typ1:findAttribute>
            		<typ1:findAttribute>City</typ1:findAttribute>
            		<typ1:findAttribute>Country</typ1:findAttribute>
            </typ1:childFindCriteria>
         </typ:findCriteria>
      </typ:findSalesParty>
   </soapenv:Body>
</soapenv:Envelope>

When executed against my Sales Cloud instance, the response displays something like below

Response:

<env:Envelope ..>
   <env:Header>..</env:Header>
   <env:Body>
      <ns0:findSalesPartyResponse ..">
         <ns2:result ..>
            <ns1:PartyName>Artemis International Solutions Corp</ns1:PartyName>
            <ns1:OrganizationParty ..>
               <ns3:Address1>1000 Louisiana</ns3:Address1>
               <ns3:Country>US</ns3:Country>
               <ns3:City>Houston</ns3:City>
            </ns1:OrganizationParty>
         </ns2:result>
         <ns2:result ..>
            <ns1:PartyName>Artisan Press Ltd</ns1:PartyName>
            <ns1:OrganizationParty ..>
               <ns3:Address1>4  BOSTON ROAD</ns3:Address1>
               <ns3:Country>GB</ns3:Country>
               <ns3:City>Leeds</ns3:City>
            </ns1:OrganizationParty>
         </ns2:result>
         <ns2:result ..>
            <ns1:PartyName>Artwise Messe</ns1:PartyName>
            <ns1:OrganizationParty..>
               <ns3:Address1>Bergengrünstr. 9</ns3:Address1>
               <ns3:Country>DE</ns3:Country>
               <ns3:City>Essen</ns3:City>
            </ns1:OrganizationParty>
         </ns2:result>
      </ns0:findSalesPartyResponse>
   </env:Body>
</env:Envelope>

Note that the response is much smaller and manageable. They key here is to use the findAttribute to control the output. In my system, the response time for this request without the findAttribute was 2000 to 2500ms. With the findAttribute the response time improved to 500ms.

In general, you can play with the findCriteria to exactly define what you want to search and what you want in the output. Think of it as the web service equivalent of the find functionality that you see in many enterprise applications. This is a powerful feature and is present only for the Find Operation.

Another key point to note about web services is that they encapsulate details originating from more than one individual object in Sales Cloud. For example in the case of SalesParty, in addition to the basic details of the Sales Party, the service provides dependent information from OrganizationParty and SalesAccount objects. Each of these objects again bring in dependencies that they need.

This allows the consumers of the webservice, to get all relavant information in one go, without having to make multiple calls to retrieve related information. This makes ADF services, granular and right-sized business services, which is a corner store to building robust web services based integration architectures.

1c. Sample invocation from an ADF application

Let us see some techniques when invoking Sales Cloud services from a custom built ADF application. This is useful when you have standalone ADF based application in your organization, or if you are building ADF extensions to Sales Cloud, say on Oracle Java Cloud Service.

Some parts of these instructions can be useful when invoking Sales Cloud web services from any J2EE based application.

There are several ways to invoke Sales Cloud web services from ADF. We will look at two simple options

–          Using ADF Web Service Data Control
–          Using Web Service Proxy

ADF Web Service data control is a simple and declarative approach to invoke a web service from ADF pages. The web service data control is particularly useful when the end objective is to simply display the results of a web service invocation on an ADF Page.

The Web Service Proxy option provides more flexibility and control, and can be used in conjunction with a programmatic VO or a bean data control.

The blog entry https://blogs.oracle.com/jdevotnharvest/entry/which_option_to_choose_for provides guidelines to pick a suitable approach when accessing web services in ADF.

If you have JDeveloper 11g installed in your system, you can simply follow the steps below to invoke your Sales Cloud on premise or SaaS instance.

Using ADF Web Service Data Control

  • Create an Application of type Fusion Web Application. I named mine ‘SalesPartySample’. Ensure that you choose and shuttle ‘Web Services’ when on the Model project creation page. Accept all other defaults in the Wizard

image009

  • Under the Model Project, create a web service data control and provide the WSDL for the SalesParty web service. Select the getSalesParty Operation. Note that you can also include custom HTTP Headers. We won’t be using it in this example but it is one way of authenticating against an Sales Cloud service

image013image015

  • In the Endpoint authentication, provide the username/password

image017

  • At this point you should see something like this in the data controls. Notice the Parameters and results

image019

  • Now we will use this data control from a simple UI. In the View Control Project, create a new JSF page. I created mine as index.jspx
  • Drag and drop the partyId parameter from the Data Control into the jspx. Choose the display type as Text Input w/ Label. Underneath it, drag and drop the getSalesParty(Long). Choose to display it as an ADF button. Underneath it, drag and drop the PartyName as Text Output. Drag and drop Address1, City and Country from under Organization Party. Your jspx should look like below

image021

  • Simply right click your .jspx and Run.
  • In this page, plug in a Party ID. I entered the Party ID for ‘Artisan Press Ltd’ that I got from the previous SOAP UI exercise

image023

  • It’s that simple! No coding effort!! Of course the UI can be made much better looking and there could be more complex usecases such as using custom HTTP Headers and this will require some amount of coding.
  • At this point if you face a SSL related error, it is because your Jdev keystore doenst have the necessary SSL certificates imported.To fix this, navigate to your Sales Cloud instance >Export the SSL certificate as .pem using your favorite browser (plenty of instruction on the internet). Import it to your JDev keystore as follows

C:\Oracle\Middleware\jdk160_24\bin>keytool -importcert -alias fusionapps -file <locationtotheexportedPEM>\mypk.pem -trustcacerts -keystore C:\Oracle\Middleware\wlserver_10.3\server\lib\DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

Using Web Service Proxy

Next we will look at using a Web Service Proxy to invoke the Sales Party Service. This time we will not use the get operation but use the find operation. We will build the FindCriteria that we used in SOAPUI using Java.

  • Create a new Application and Project that will house your proxy exclusively (I will explain later why this makes sense)
  • Click New and select Web Service Proxy

image027

  • Choose JAX WS style
  • Enter the package name as oracle.sample.salesparty.proxy and oracle.sample.salesparty.proxy.types
  • Unselect generate as async and subsequently select Don’t Generate Async

image029

  • You should see SalesPartyServiceSoapHttpPortClient.java with the text, “Add your code to call the desired methods”
  • Best Practice: It is a good practice to not use the above Proxy Client directly. This is because the when the proxy gets regenerated the changes made to the above client changes will be be lost. Instead it is good to create a separate facade. The facade also allows you to control the input and output fields you would like to work with, instead of working with the entire payload. For example in our example we would like to only take in the input of ‘startswith’
  • Best Practice: Like I mentioned earlier, leave your proxy in its project. In fact you will treat as a standalone deployable entity. The facade and the rest of the application components such as UI, Data controls etc will constitute the main application which will include the proxy application as a dependency. This allows for distributed development as the same proxy can be used by multiple applications. This reduces redundancy and also ensure that if the proxy is regenerated, then all applications pull the latest code.
  • Create a facade like below. The idea is to build the same XML request payload using ‘findCriteria’ like we built in the earlier example. The getSalesPartyList method takes only “startsWith” as input from the user, builds the findcriteria and executes the service, returning a list of Sales Party records
public class SalesPartyFacade {
          private SalesPartyService_Service salesPartyService_Service;
          public List<SalesParty> getSalesPartyList(String startsWith)
          throws ServiceException
        {
          List<SalesParty> SalesParties;
          FindCriteria findCriteria = createFindCriteria(startsWith);
 
          SecurityPolicyFeature[] securityFeatures =
          new SecurityPolicyFeature[] { new SecurityPolicyFeature("oracle/wss_username_token_over_ssl_client_policy") };
          
          // salesPartyService_Service = new SalesPartyService_Service();
           try
           {
               salesPartyService_Service = new SalesPartyService_Service(new URL("https://your_sales_cloud_URL/crmCommonSalesParties/SalesPartyService?WSDL"),new QName("http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/","SalesPartyService") );
           } catch (MalformedURLException e) { 
               e.printStackTrace(); 
           }  
          SalesPartyService salesPartyService = salesPartyService_Service.getSalesPartyServiceSoapHttpPort(securityFeatures);

          
          WSBindingProvider wsbp = (WSBindingProvider)salesPartyService;
          wsbp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY,"User1");
          wsbp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,"Passwd1");

          FindSalesParty fSalesParty= new FindSalesParty();
          fSalesParty.setFindCriteria(findCriteria);
          SalesParties = salesPartyService.findSalesParty(fSalesParty).getResult();

          return SalesParties;
        }

    private static FindCriteria createFindCriteria(String startsWith)
    {
      FindCriteria findCriteria = new FindCriteria();
      ChildFindCriteria childFindCriteria = new ChildFindCriteria();
      findCriteria.setFetchStart(0);
      findCriteria.setFetchSize(10);

      ViewCriteria filter = new ViewCriteria();
      ViewCriteriaRow group1 = new ViewCriteriaRow();
      ViewCriteriaItem item1 = new ViewCriteriaItem();
      item1.setAttribute("PartyName");
      item1.setOperator("STARTSWITH");
      item1.getValue().add(startsWith);
      
      ViewCriteriaItem item2 = new ViewCriteriaItem();
      item2.setAttribute("PartyType");
      item2.setOperator("=");
      item2.getValue().add("ORGANIZATION");
      

      group1.getItem().add(item1);
      group1.getItem().add(item2);
      group1.setConjunction(Conjunction.AND);
      
      filter.getGroup().add(group1);
      findCriteria.setFilter(filter);
      
      findCriteria.getFindAttribute().add("PartyName");
      findCriteria.getFindAttribute().add("OrganizationParty");
     
/*    childFindCriteria.setChildAttrName("OrganizationParty");
      childFindCriteria.getFindAttribute().add("Address1");
      childFindCriteria.getFindAttribute().add("City");
      childFindCriteria.getFindAttribute().add("Country");
      findCriteria.getChildFindCriteria().add(childFindCriteria);
*/  
      return findCriteria;
    }

}
  • Best Practice: As shown in the code, you can use the WSBindingProvider to override the username password. This is especially useful when multiple projects are dependent on the same Proxy. Each project can then override the security policy in its facade to suit its needs.
  • Best Practice: When instantiating the proxy in the facade, instead of using the constructor with no arguments (which I intentionally commented out), use the constructor that accepts the WSDL. You will provide the WSDL of the SalesParty service in your Sales Cloud env here (although you already used the WSDL when building the proxy using the wizard). Reason for doing this again is as follows. At design time, you would’ve used a WSDL from a specific environment. However this proxy code can be ported to a different environment. At that time, it is not required for you to re-generate the proxy if you followed this approach. By simply providing the WSDL everytime in the facade, you are ensuring that your proxy is using the latest WSDL definitions. As an alternative approach, sometimes you see people overriding the endpoint alone using the ENDPOINT_ADDRESS_PROPERTY. However this alternative approach (of not overriding the WSDL but simply overriding the endpoint) has two pitfalls – the old WSDL may become unavailable and the old WSDL could have security related configuration that is incorrect for the new endpoint causing security issues.
  • Best Practice: You can also externalize the new WSDL URL by creating it as an entry in a property file and have the proxy reference it. Since you need to modify the property file only, you do not need access to the proxy source code. You just need the .ear of the file being deployed. The other advantage of this approach is that multiple proxies can leverage the same property in the property file allowing you to quickly modify applications during env migration. You can also use scripting to automate the end point changes in the property files to assist deployment automation.
  • Now that you have the facade, you can write a simple test class to test this facade.
        String filter = "Art"; 
        SalesPartyFacade spf = new SalesPartyFacade();
        List<SalesParty> salesparties = spf.getSalesPartyList(filter);
        for (SalesParty sp: salesparties)
        {System.out.print("Party Name = " + sp.getPartyName().getValue()+"\n");
         System.out.print("Address1 = " + sp.getOrganizationParty().get(0).getAddress1().getValue()+"\n");
         System.out.print("City = " + sp.getOrganizationParty().get(0).getCity().getValue()+"\n");
         System.out.print("Country = " + sp.getOrganizationParty().get(0).getCountry().getValue()+"\n");
         System.out.println("\n\n");   }
  • This facade can now be used in any manner to invoke the Sales Party Service from your UI.

This concludes part 1 of this two part blog. In part 2 of this post I’ll be talking about invoking external SOAP and REST services from Fusion Applications.

 

 

 


Viewing all articles
Browse latest Browse all 33

Trending Articles