Thursday, 14 February 2013

SQL Access to Salesforce data

In this post I will talk about an ODBC/JDBC driver solution I discovered lately which enables you to Access your Salesforce data using the standard SQL query language.

The company which provides these drivers is called Progress|DataDirect 

Their JDBC/ODBC Salesforce Connect XE drivers, acts as translators between SQL and SOQL (The Salesforce proprietary query language). So you can write your joins, use expressions like SUBSTRING on your Salesforce data as if the data was in a relational database.

I found the concept quite interesting.  If you already know SQL -and there are many people which do- you can just query data in any Salesforce standard objects like Account, Contact and custom objects with the use of a SQL Client tool.

For my post I used SQuireL SQL Client and the JDBC driver. You can easily point your own SQL Client tool to these drivers too.  ODBC and JDBC are data access standards many tools comply with.

To get started

1. Download Progress|DataDirect JDBC driver the file will be called something like this PROGRESS_DATADIRECT_CONNECT_JDBC_5.1.0.jar  ( I have downloaded the 15 day trial version)

2. Install the driver as per instructions found here and more generic info like User Guide is here

3. Configure SQuireL SQL Client to use the driver (to install SQuireL go here)

Once you have downloaded the driver and you have installed it. Start the SQuireL Client tool and register the JDBC driver with SQuireL like this:

Go to Drivers tab on the right and click the + sign.

Add a driver by using the plus sign (+) and fill in the driver details like below. You can find out the Class Name and the Website URLs, Extra Class Payt for the Progress|DataDirect JDBC drivers here. You can find out more about connection properties here DataDirect Connect Series for JDBC User's Guide

Once you have configured the driver is time to add an Alias SQuireL connection to your Salesforce Org. I used my developer org below. Just follow the wizard to add the alias. It will ask you for the driver to use. Choose the Salesforce driver you have crated above. 

Once you have created the alias (connection) is time to test your alias and connect to Salesforce with the new JDBC driver. Provide your Salesforce credentials like this:

Once you connect then you can issue a SQL join and write any SQL statement to your Salesforce Org as below.  Below I am writing a SQL join between Account with Contact Salesforce standard objects.

What the driver really does is to translate your SQL to SOQL.

To read more about the JDBC and other drivers go to the company site

There is potential here, imagine all the data profiling, data quality operations and data integrations, you can do 'in place' in SaaS and Cloud systems with SQL, without having to move the data around a lot. 

More resources

More blogs and resources on SQL Access to Salesforce can be found below. There is a very interesting blog post which shows you how you can access the Salesforce data from within Oracle directly here too.

Thursday, 7 February 2013

Send tweets from Oracle with OAuth

Twitter is a great real time social platform. Timelines and hashtags are a great way to communicate to an audience of subscribers relevant information.

There are lots of websites and applications which use twitter to provide content to their users. There are programming languages, Java, PHP, Python which have build Twitter API libraries to quickly send and receive content from Twitter but I haven't come accross a fully fledged library in PL/SQL, which would enable you to send a Tweet from PL/SQL using Twitter's latest oAuth security protocol. 

There are blogs out there which show you how to send a tweet from Oracle, but not many using the new oAuth security protocol.

The only blog post I have seen so far which uses PL/SQL and oAuth to send a tweet from Oracle is of Some coding hero's heroic mumblings an Oracle consultant. 

I have spend time reviewing his code with the comments made on his post which he has wrote in 2010 and managed to trim it down to use only one of his PL/SQL procedures.  The procedure below sends the 140 characters tweet  for you using oAuth. To be able to do this, as Some Coding Hero says, you will have to create a Twitter Application for your twitter handle. Actually you don't create an application you just sign up and obtain 4 security codes! Once you register your application with Twitter,  you are given 4 oAuth security Codes as follows:

With the above 4 oAuth security codes you can use Somecodingheros last block of code only to send a tweet from Oracle - I used Oracle Apex - like this:

new Twitter API 1.1 update and the use of Oracle Wallet is added to the script in green below on  21-JUN-2013

-- Twitter API 1.1 update
  l_oauth_request_token_url CONSTANT VARCHAR2 (500) := '';   
  l_oauth_consumer_key CONSTANT VARCHAR2 (500) := 'xxxxxxxxx';  
  l_oauth_token  CONSTANT VARCHAR2 (500) := 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx';  
  l_oauth_secret CONSTANT VARCHAR2 (500) := 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';  
  l_oauth_nonce VARCHAR2 (500);  
  l_oauth_signature_method CONSTANT VARCHAR2 (10) := urlencode ('HMAC-SHA1');  
  l_oauth_timestamp VARCHAR2 (100);  
  l_oauth_version CONSTANT VARCHAR2 (5) := urlencode ('1.0');  
  l_oauth_consumer_secret CONSTANT VARCHAR2 (500) := 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';  
  l_http_method VARCHAR2 (5) := 'POST';  
  l_oauth_base_string VARCHAR2 (2000);  
  l_oauth_key VARCHAR2 (500) := l_oauth_consumer_secret || '&' || l_oauth_secret ;  
  l_sig_mac RAW (2000);  
  l_base64_sig_mac VARCHAR2 (100);  
  http_req UTL_HTTP.req;  
  http_resp UTL_HTTP.resp;  
  l_update_send VARCHAR2(2000);  
  l_oauth_header  VARCHAR2(2000);  
  l_line  VARCHAR2(1024);  
  resp_name  VARCHAR2(256);  
  resp_value VARCHAR2(1024);  
-- put the tweet in the urlencode function below 
  l_content varchar2(140) := urlencode('@somecodinghero thank you');  
  l_random varchar2(25);  

 -- Oracle Wallet
    utl_http.set_wallet('file:/home/app/oracle/product/11.2.0/dbhome_1/owm/wallets/oracle', 'putyourwalletpasswordhere');

 -- Get the timestamp  
  SELECT urlencode ((SYSDATE - TO_DATE ('01-01-1970', 'DD-MM-YYYY'))  * (86400)) 
  INTO l_oauth_timestamp  
  -- RANDOM oauth_nonce  
  SELECT dbms_random.string('A',25)  
  INTO l_random  
  SELECT urlencode (UTL_ENCODE.base64_encode(UTL_I18N.string_to_raw (l_random, 'AL32UTF8')))  
  INTO l_oauth_nonce  
  l_oauth_base_string := l_http_method   
                          || '&'  
                          || urlencode (l_oauth_request_token_url)  
                          || '&'  
                          || urlencode ( 'oauth_consumer_key'  
                              || '='  
                              || l_oauth_consumer_key  
                              || '&'  
                              || 'oauth_nonce'  
                              || '='  
                              || l_oauth_nonce  
                              || '&'  
                              || 'oauth_signature_method'  
                              || '='  
                              || l_oauth_signature_method  
                              || '&'  
                              || 'oauth_timestamp'  
                              || '='  
                              || l_oauth_timestamp  
                              || '&'  
                              || 'oauth_token'  
                              || '='  
                              || l_oauth_token  
                              || '&'  
                              || 'oauth_version'  
                              || '='  
                              || l_oauth_version  
                              || '&'  
                              || 'status'  
                              || '='  
                              || l_content);  
  DBMS_OUTPUT.put_line (l_oauth_base_string);  
  l_sig_mac := DBMS_CRYPTO.mac (  UTL_I18N.string_to_raw (l_oauth_base_string, 'AL32UTF8')  
                                , DBMS_CRYPTO.hmac_sh1  
                                , UTL_I18N.string_to_raw (l_oauth_key, 'AL32UTF8'));  
  DBMS_OUTPUT.put_line ('Combined sig: ' || l_oauth_key);  
  l_base64_sig_mac := UTL_RAW.cast_to_varchar2 (UTL_ENCODE.base64_encode (l_sig_mac));  
  DBMS_OUTPUT.put_line ('MAC Signature (Base64-encoded): ' ||  l_base64_sig_mac);  
  l_update_send := l_oauth_request_token_url || '?status=' || l_content;  
    http_req := UTL_HTTP.begin_request (  l_update_send  
                                        , l_http_method  
                                        , UTL_HTTP.http_version_1_1);  
   DBMS_OUTPUT.put_line ('UPDATE URL ' || l_update_send);  
   UTL_HTTP.set_response_error_check (TRUE);  
   UTL_HTTP.set_detailed_excp_support (TRUE);  
    l_oauth_header := 'OAuth oauth_nonce="' || l_oauth_nonce || '", '  
                      || 'oauth_signature_method="'|| l_oauth_signature_method || '", '  
                      || 'oauth_timestamp="'|| l_oauth_timestamp || '", '  
                      || 'oauth_consumer_key="'|| l_oauth_consumer_key || '", '  
                      || 'oauth_token="' || l_oauth_token || '", '  
                      || 'oauth_signature="' || urlencode (l_base64_sig_mac) || '", '  
                      || 'oauth_version="' || l_oauth_version || '"';  
    utl_http.set_header ( r => http_req,   
                          NAME => 'Authorization', VALUE => l_oauth_header);  
    DBMS_OUTPUT.put_line  ('HEADER: ' || l_oauth_header);                          
    utl_http.write_text(  r => http_req, DATA => l_content);   
    http_resp := utl_http.get_response(r => http_req);  
   FOR i IN 1..utl_http.get_header_count(http_resp) LOOP  
    utl_http.get_header(http_resp, i, resp_name, resp_value);  
    dbms_output.put_line(resp_name || ': ' || resp_value);  
   END LOOP;  
  DBMS_OUTPUT.put_line('Getting content:');  
        utl_http.read_line(http_resp, resp_value, TRUE);  
      END LOOP;  
      WHEN utl_http.end_of_body THEN  
        DBMS_OUTPUT.put_line('No more content.');  
   utl_http.end_response(r => http_resp);  
    when others then  
      DBMS_OUTPUT.put_line('HTTP ERROR: ' || utl_http.get_detailed_sqlerrm);  

Thank you Somecodinghero!