Integrating Social Relationship Management (SRM) with Oracle Business Intelligence Cloud Service (BICS)

Introduction

 

This article outlines how to integrate Social Relationship Management (SRM) with Oracle Business Intelligence Cloud Service (BICS).

Bringing the SRM records into BICS enables the data consumer to refine and focus on relevant data through prompts and filters. Additionally, once in BICS, the SRM data can be mashed with complementary datasets.

Three patterns are covered:

(a) Managing SRM authentication, authorization, and token refresh.

(b) Retrieving SRM data in JSON format using REST Web Services.

(c) Parsing and Inserting SRM data into the Schema Service database with Apex PL/SQL functions.


The article concludes at PL/SQL code snippet stage. Suggestions on how to schedule, trigger, and display results in BICS can be found in past A-Team BICS Blogs.

SRM screen prints in this article have been translated to English from Spanish and text may differ slightly from originals.

For those very familiar with the SRM API and the BICS API, it is possible to jump straight ahead to Step 4 – #4 “Final Code Snippet – add apex_json.parse code to PL/SQL”. That said, Steps 1-3 are valuable for assisting in understanding the solution and will be needed should de-bugging be required.

 

Main Article

 

Step 1 – Review SRM Developer Platform API Documentation

 

Begin by reviewing the SRM Developer Platform API documentation. This article covers Refreshing Tokens (oauth/token) and the List Messages Method (engage/v1/messages). There are many other objects and methods available in the API that may be useful for various integration scenarios.

Step 2 – Authentication

 

Detailed documented steps for Authentication can be found here. The following is a summary of what was found to be the minimal steps required for BICS.


Prerequisites:

* SRM Account must include message routing from from Listen to Engage within Social Engagement and Monitoring (SEM) for these to be available in the API.

* SRM Account must have administrator privileges to access the Account link.


1)    Register the Client Application

Go to: https://accounts.vitrue.com/#api_apps

Click: Create New Application

Enter Application Name & URL(URI) Call Back.

Make a note of the Application Name, Callback URL (URI), Client ID (Customer ID), and Client Secret.

Snap1

Snap2


2)    Request User Authorization and Obtain the Authorization Code

Authorize URL:

https://gatekeeper.vitrue.com/oauth/authorize?client_id=abc123&scope=engage&redirect_uri=https://accounts.vitrue.com&response_type=code

Replace client_id and redirect_uri with those associated with the API Application.

There are 3 different scopes:

engage = Access endpoints for messages and replies
publish = Access endpoints for posts
admin = Access endpoints for accounts, bundles, and resources

To determine what scope is required review the URL of the method being used. This example uses engage/v1/messages, thus scope = engage is required. When using admin/v1/accounts, scope = admin is required. If the incorrect scope is referenced, when running the method, the following error will be received:

{“Unauthorized.”,”detail”:”The user credentials provided with the request are invalid or do not provide the necessary level of access.”,”status”:401}

Copy the Authorize URL into a browser. Log into SRM.

Untitled1

Click “Authorize” to grant access. It is very important that the correct scope is displayed. In this case “Engage”.

Untitled3

If successful the browser will re-direct to the URL (URI) Call Back / Redirect URI. The code will be appended. Make a note of the code.

Untitled2


3)    Exchange Authorization Code for an Access Tokens

If necessary install Curl. Download Curl from here.

Run the following replacing client_id, client_secret, redirect_uri, code, and scope.

It is very important that the scope matches what was used to generate the code.

curl -X POST -d grant_type=authorization_code -d client_id=abc123 -d client_secret=abc123 -d redirect_uri=”https://accounts.vitrue.com” -d code=abc123 -d scope=engage https://gatekeeper.vitrue.com/oauth/token -k

If successful the access_token and refresh_token will be returned in JSON format. Copy the JSON containing the tokens and save it to notepad for future reference.

{“access_token”:”abc123″,”token_type”:”bearer”,”expires_in”:7200,”refresh_token”:”abc123″,”scope”:”engage”}

Tokens expire after two hours. If tokens expire generate new tokens by running grant_type=refresh_token.

curl -X POST -d grant_type=refresh_token -d refresh_token=abc123 -d client_id=abc123 -d client_secret=abc123 -d redirect_uri=”https://accounts.vitrue.com” https://gatekeeper.vitrue.com/oauth/token -k

If tokens get lost or out of sync obtain a new “authorization code” and repeat the process to get a new code and new access / refresh tokens. If tokens get out of sync the following error will be received:

{“error”:”invalid_request”,”error_description”:”The request is missing a require
d parameter, includes an unsupported parameter value, or is otherwise malformed.”}

If an attempt is made to re-authorize a code that is still active the following error will be received. Thus, the need to get a fresh code.

{“error”:”invalid_grant”,”error_description”:”The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.”}


4)    Save the refresh tokens to the BICS Database

CREATE TABLE REFRESH_TOKEN(CREATE_DATE TIMESTAMP, access_token VARCHAR(500), token_type VARCHAR(10), expires_in VARCHAR(10), refresh_token varchar(500), scope varchar(10));

Replace ‘abc123‘ with the current refresh token. Only the refresh token is needed at this stage.

INSERT INTO REFRESH_TOKEN(CREATE_DATE,refresh_token) VALUES (SYSDATE,’abc123‘);

5)    Refresh token from BICS

This code snippet provides a very basic example of how to store the refresh token in BICS. It should be used for demo purposes only. In production systems, more secure options of storing and linking refreshing token’s with the user’s record or profile should be considered.

Open SQL Workshop from Oracle Application Express

Snap2

Launch SQL Commands

Snap3

Use the code snippet below as a starting point to build the refresh token PL/SQL.

For a text version of the code snippet click here.

Replace the redirect_uri, client_id, client_secret.

Re-run the code snippet code to confirm that the new refresh code gets inserted into the REFRESH_TOKEN table each time.

The REFRESH_TOKEN table should only ever have one record in it at all times.

DECLARE
l_ws_response_clob CLOB;
l_refresh_token VARCHAR(500);
l_body VARCHAR(500);

SELECT MAX(refresh_token) INTO l_refresh_token FROM REFRESH_TOKEN;
dbms_output.put_line(‘Old Refresh Token: ‘ || dbms_lob.substr(l_refresh_token,12000,1));
apex_web_service.g_request_headers(1).name := ‘Content-Type’;
apex_web_service.g_request_headers(1).value := ‘application/json’;
l_body := ‘{
“refresh_token”: “‘ || l_refresh_token || ‘”,
“grant_type”: “refresh_token”,
“redirect_uri”: “https://accounts.vitrue.com“,
“client_id”: “abc123“,
“client_secret”: “abc123
}’;
l_ws_response_clob := apex_web_service.make_rest_request
(
p_url => ‘https://gatekeeper.vitrue.com/oauth/token’,
p_body => l_body,
p_http_method => ‘POST’
);
apex_json.parse(l_ws_response_clob);
DELETE FROM REFRESH_TOKEN;
INSERT INTO REFRESH_TOKEN(CREATE_DATE, access_token, token_type, expires_in, refresh_token, scope)
VALUES (
SYSDATE,
apex_json.get_varchar2(p_path => ‘access_token’),
apex_json.get_varchar2(p_path => ‘token_type’),
apex_json.get_varchar2(p_path => ‘expires_in’),
apex_json.get_varchar2(p_path => ‘refresh_token’),
apex_json.get_varchar2(p_path => ‘scope’)
);
dbms_output.put_line(‘New Refresh Token: ‘ || apex_json.get_varchar2(p_path => ‘refresh_token’));
COMMIT;

 

Step 3 – Run List Messages Method (engage/v1/messages)


For a text version of the code snippet click here.


Code Breakdown


Orange
– Refresh token code (from Step 2).

Blue – Reads Access Token from REFRESH_TOKEN table

Light Green – Rest Web Service for List Messages Method (engage/v1/messages). Returns data as a clob.

Bright Green – bundleId and resourceId parameters. These were identified through the “List Accounts Method” (admin/v1/accounts) and “List Bundles Method” (admin/v1/accounts/:account_id/bundles).

DECLARE
l_ws_response_clob CLOB;
l_refresh_token VARCHAR(500);
l_body VARCHAR(500);
l_access_token VARCHAR(500);
l_ws_response_clob2 CLOB;
l_ws_url VARCHAR2(500) := ‘https://public-api.vitrue.com/engage/v1/messages?bundleId=3876?resourceId=108641‘;

SELECT MAX(refresh_token) INTO l_refresh_token FROM REFRESH_TOKEN;
–dbms_output.put_line(‘Old Refresh Token: ‘ || dbms_lob.substr(l_refresh_token,12000,1));
apex_web_service.g_request_headers(1).name := ‘Content-Type’;
apex_web_service.g_request_headers(1).value := ‘application/json’;
l_body := ‘{
“refresh_token”: “‘ || l_refresh_token || ‘”,
“grant_type”: “refresh_token”,
“redirect_uri”: “https://accounts.vitrue.com”,
“client_id”: “abc123”,
“client_secret”: “abc123”
}’;
l_ws_response_clob := apex_web_service.make_rest_request
(
p_url => ‘https://gatekeeper.vitrue.com/oauth/token’,
p_body => l_body,
p_http_method => ‘POST’
);
apex_json.parse(l_ws_response_clob);
DELETE FROM REFRESH_TOKEN;
INSERT INTO REFRESH_TOKEN(CREATE_DATE, access_token, token_type, expires_in, refresh_token, scope)
VALUES (
SYSDATE,
apex_json.get_varchar2(p_path => ‘access_token’),
apex_json.get_varchar2(p_path => ‘token_type’),
apex_json.get_varchar2(p_path => ‘expires_in’),
apex_json.get_varchar2(p_path => ‘refresh_token’),
apex_json.get_varchar2(p_path => ‘scope’)
);
–dbms_output.put_line(‘New Refresh Token: ‘ || apex_json.get_varchar2(p_path => ‘refresh_token’));
–Get Access Token
SELECT MAX(access_token) INTO l_access_token FROM REFRESH_TOKEN;
dbms_output.put_line(dbms_lob.substr(l_access_token,12000,1));
–Set Headers
apex_web_service.g_request_headers(1).name := ‘Authorization’;
apex_web_service.g_request_headers(1).value := ‘Bearer ‘ || l_access_token;
apex_web_service.g_request_headers(2).name := ‘Accept’;
apex_web_service.g_request_headers(2).value := ‘application/json’;
–Get Message
l_ws_response_clob2 := apex_web_service.make_rest_request
(
p_url => l_ws_url,
p_http_method => ‘GET’
);
dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,1));
dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,12001));
dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,24001));
COMMIT;

 

Step 4 – Parse and Insert SRM messages into BICS


1. Validate the JSON


Only valid JSON can be parsed by Apex. If the code fails at the parsing stage, it is recommended to validate it.

There are many free online JSON validating tools such as: https://jsonformatter.curiousconcept.com/

For a sample SRM JSON payload click here.

Viewing the JSON in the JSON formatter allows for easily expand and collapse of the different elements – assisting with choosing the desired fields to bring into BICS.

For this example id, type, resourceName, resourceType, and body will be selected.

Snap3

2. Test JSON Path Expressions


When formulating the JSON path expression, it may be useful to use an online JSON Path Expression Tester such as https://jsonpath.curiousconcept.com.

The below example shows testing the “id” path.

Snap4

Snap5

JSON Path Expression’s of all required fields.

Value                       JSON Path Expression 
count                       count
id                          items[*].id
type                        items[*].type
resourceName                items[*].resource.resourceName
resourceType                items[*].resource.resourceType
body                        items[*].body

3. Create SRM_MESSAGE table in BICS

Create the table from Apex SQL Workshop -> SQL Commands.

CREATE TABLE SRM_MESSAGES(ID VARCHAR(100),TYPE VARCHAR(100),RESOURCE_NAME VARCHAR(100),RESOURCE_TYPE VARCHAR(100),BODY VARCHAR(1000));

 4. Final Code Snippet – add apex_json.parse code to PL/SQL

For a text version of the code snippet click here.

DECLARE
l_ws_response_clob CLOB;
l_refresh_token VARCHAR(500);
l_body VARCHAR(500);
l_access_token VARCHAR(500);
l_ws_response_clob2 CLOB;
l_ws_url VARCHAR2(500) := ‘https://public-api.vitrue.com/engage/v1/messages?bundleId=3876?resourceId=108641’;

SELECT MAX(refresh_token) INTO l_refresh_token FROM REFRESH_TOKEN;
–dbms_output.put_line(‘Old Refresh Token: ‘ || dbms_lob.substr(l_refresh_token,12000,1));
apex_web_service.g_request_headers(1).name := ‘Content-Type’;
apex_web_service.g_request_headers(1).value := ‘application/json’;
l_body := ‘{
“refresh_token”: “‘ || l_refresh_token || ‘”,
“grant_type”: “refresh_token”,
“redirect_uri”: “https://accounts.vitrue.com”,
“client_id”: “abc123“,
“client_secret”: “abc123
}’;
l_ws_response_clob := apex_web_service.make_rest_request
(
p_url => ‘https://gatekeeper.vitrue.com/oauth/token’,
p_body => l_body,
p_http_method => ‘POST’
);
apex_json.parse(l_ws_response_clob);
DELETE FROM REFRESH_TOKEN;
INSERT INTO REFRESH_TOKEN(CREATE_DATE, access_token, token_type, expires_in, refresh_token, scope)
VALUES (
SYSDATE,
apex_json.get_varchar2(p_path => ‘access_token’),
apex_json.get_varchar2(p_path => ‘token_type’),
apex_json.get_varchar2(p_path => ‘expires_in’),
apex_json.get_varchar2(p_path => ‘refresh_token’),
apex_json.get_varchar2(p_path => ‘scope’)
);
–dbms_output.put_line(‘New Refresh Token: ‘ || apex_json.get_varchar2(p_path => ‘refresh_token’));
–Get Access Token
SELECT MAX(access_token) INTO l_access_token FROM REFRESH_TOKEN;
–dbms_output.put_line(‘Access Token’ || dbms_lob.substr(l_access_token,12000,1));
–Set Headers
apex_web_service.g_request_headers(1).name := ‘Authorization’;
apex_web_service.g_request_headers(1).value := ‘Bearer ‘ || l_access_token;
apex_web_service.g_request_headers(2).name := ‘Accept’;
apex_web_service.g_request_headers(2).value := ‘application/json’;
–Get Message
l_ws_response_clob2 := apex_web_service.make_rest_request
(
p_url => l_ws_url,
p_http_method => ‘GET’
);
–dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,1));
–dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,12001));
–dbms_output.put_line(dbms_lob.substr(l_ws_response_clob2,12000,24001));
–Delete Messages
DELETE FROM SRM_MESSAGES;
–Parse Clob to JSON
apex_json.parse(l_ws_response_clob2);
–Insert data
IF apex_json.get_varchar2(p_path => ‘count’) > 0 THEN
for i in 1..apex_json.get_varchar2(p_path => ‘count’) LOOP
INSERT INTO SRM_MESSAGES(ID,TYPE,RESOURCE_NAME,RESOURCE_TYPE,BODY)
VALUES
(
apex_json.get_varchar2(p_path => ‘items[‘|| i || ‘].id’),
apex_json.get_varchar2(p_path => ‘items[‘|| i || ‘].type’),
apex_json.get_varchar2(p_path => ‘items[‘|| i || ‘].resource.resourceName’),
apex_json.get_varchar2(p_path => ‘items[‘|| i || ‘].resource.resourceType’),
apex_json.get_varchar2(p_path => ‘items[‘|| i || ‘].body’)
);
end loop;
END IF;
COMMIT;

Further Reading


Click here for the Application Express API Reference Guide – MAKE_REST_REQUEST Function.

Click here for the Application Express API Reference Guide – APEX_JSON Package.

Click here for the SRM Developer Platform API Guide.

Click here for more A-Team BICS Blogs.

Summary


This article provided a set of examples that leverage the APEX_WEB_SERVICE_API to integrate Social Relationship Management (SRM) with Oracle Business Intelligence Cloud Service (BICS) using the SRM Developer Platform.

The use case shown was for BICS and SRM. However, many of the techniques referenced could be used to integrate SRM with other Oracle and non-Oracle applications.

Similarly, the Apex MAKE_REST_REQUEST and APEX_JSON examples could be easily modified to integrate BICS or standalone Oracle Apex with any other REST web service that is accessed via a URL and returns JSON data.

Techniques referenced in this blog could be useful for those building BICS REST ETL connectors and plug-ins.

Add Your Comment