Salesforce.com interface to directly access Salesforce.com

Dependencies:   HTTPClient-SSL MbedJSONValue

Dependents:   df-2014-salesforce-hrm-k64f

Fork of SalesforceInterface by Doug Anson

Revision:
8:47db53cd5884
Parent:
7:97ea5ef906f7
Child:
9:a254fcd904be
--- a/SalesforceInterface.cpp	Sun Sep 21 07:08:51 2014 +0000
+++ b/SalesforceInterface.cpp	Mon Sep 22 04:28:02 2014 +0000
@@ -21,6 +21,16 @@
  #define  SF_OAUTH_REQUEST_BODY "grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s"
  #define  SF_HTTP_AUTH_HEADER   "Authorization: Bearer %s"
  
+ // search tokens for URLS in salesforce ID
+ #define SF_URLS_START_TOKEN    "\"urls\":"
+ #define SF_URLS_STOP_TOKEN     "},"
+ 
+ // salesforce QUERY specifier within URL
+ #define SF_QUERY_URL_SPECIFIER "q="
+ 
+ // salesforce URL API version token
+ #define SF_URL_API_VER_TOKEN   "{version}"
+ 
  // include class definition
  #include "SalesforceInterface.h"
  
@@ -40,6 +50,9 @@
      this->m_http_status = HTTP_OK;
      this->m_http_response_code = -1;
      RESET_BUFFER(this->m_http_redirection_url);
+     RESET_BUFFER(this->m_salesforce_id);
+     memset(this->m_salesforce_api,0,SALESFORCE_API_VERSION_LENGTH);
+     strcpy(this->m_salesforce_api,SALESFORCE_API_VERSION);
      this->resetOauthToken();
  }
  
@@ -77,6 +90,9 @@
  bool SalesforceInterface::haveCreds() { return this->m_have_creds; }
  HTTPResult SalesforceInterface::httpStatus() { return this->m_http_status; }
  int SalesforceInterface::httpResponseCode() { return this->m_http_response_code; }
+ void SalesforceInterface::setSalesforceAPIVersion(int version) { sprintf(this->m_salesforce_api,"%d",version); }
+ void SalesforceInterface::setSalesforceAPIVersion(char *version) { if (version != NULL && strlen(version) > 0 && strlen(version) < SALESFORCE_API_VERSION_LENGTH) strcpy(this->m_salesforce_api,version); }
+ char *SalesforceInterface::getSalesforceAPIVersion() { return this->m_salesforce_api; }
  
  // reset our oauth token
  void SalesforceInterface::resetOauthToken() {
@@ -120,7 +136,13 @@
     
     // TODO: we currently only return fill status. Later we may want to check dates too...
     return this->m_oauth_token.valid; 
-}
+ }
+
+ // do we have a valid salesforce.com ID?
+ bool SalesforceInterface::haveSalesforceID() {
+     if (this->m_salesforce_id != NULL && strlen(this->m_salesforce_id) > 0) return true;
+     return false;
+ }
  
  // check and get our OAUTH token
  void SalesforceInterface::checkAndGetOauthToken() {
@@ -181,11 +203,12 @@
  }
  
  // Salesforce.com: Get our ID
- char *SalesforceInterface::getSalesforceID(char *output_buffer,int output_buffer_length) {
+ char *SalesforceInterface::getSalesforceID() {
     // proceed only if we have a valid OAUTH Token
     if (this->validOauthToken() == true) {
         // pull the ID from salesforce
-        char *id = this->invoke(this->oauth()->id.c_str(),output_buffer,output_buffer_length);
+        RESET_BUFFER(this->m_salesforce_id);
+        char *id = this->invoke(this->oauth()->id.c_str(),this->m_salesforce_id,MAX_BUFFER_LENGTH);
         
         // log any error status and return what we have...
         if (this->httpStatus() != HTTP_OK) this->logger()->log("Unable to get Salesforce ID: status=%d httpCode=%d",this->httpStatus(),this->httpResponseCode());
@@ -198,6 +221,44 @@
     return NULL;
  }
  
+ // QUERY: Salesforce.com
+ char *SalesforceInterface::query(char *query_str,char *output_buffer,int output_buffer_length) {
+     // first we have to ensure that we have valid salesforce ID
+     if (this->haveSalesforceID()) {        
+        // get the QUERY URL
+        ALLOC_BUFFER(url);
+        char *sf_url = this->getSalesforceURL("query",url,MAX_BUFFER_LENGTH);
+        if (sf_url != NULL && strlen(sf_url) > 0) {
+            // make sure that the query string is ready to ship...
+            ALLOC_SML_BUFFER(tmp_query);
+                                      
+            // replace all spaces in query with "+"
+            strcpy(tmp_query,query_str);
+            this->replace(tmp_query,' ','+');   // will modify tmp_query directly...
+                         
+            // customize the URL with our (formatted) query string
+            string str_url(sf_url);
+            str_url[str_url.length()-1] = '?';                           // remove the slash and add a ?
+            str_url = str_url + SF_QUERY_URL_SPECIFIER + tmp_query;      // add the query specifier
+            
+            // DEBUG - show the query URL
+            DEBUG("query URL: %s",str_url.c_str());
+            
+            // invoke with GET
+            return this->invoke((const char *)str_url.c_str(),output_buffer,output_buffer_length);
+        }
+        else {
+            // unable to find the query URL...
+            this->logger()->log("query: error - unable to find query URL in salesforce ID...");
+        }
+     }
+     else {
+         // dont have a valid salesforce ID
+         this->logger()->log("query: error - no valid salesforced ID was found...");
+     }
+     return NULL;
+ }
+ 
  // Salesforce.com Invoke: defaults to GET
  char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_len) { 
     return this->invoke(url,NUM_TYPES,NULL,0,output_buffer,output_buffer_len,GET); 
@@ -328,4 +389,78 @@
      else this->logger()->log("invocation failed with HTTP error code=%d status=%d",this->httpResponseCode(),this->httpStatus());
      return NULL;
  }
- 
\ No newline at end of file
+ 
+ // find the specific URL in our salesforce ID
+ char *SalesforceInterface::getSalesforceURL(char *key,char *url_buffer,int url_buffer_length) {
+     // due to MbedJSONValue limitations - we have to manually pull out the specific JSON from our salesforce ID
+     int start = (int)strstr(this->m_salesforce_id,SF_URLS_START_TOKEN);
+     if (start >= 0) {
+         start += strlen(SF_URLS_START_TOKEN);
+         int stop = (int)strstr((char *)start,SF_URLS_STOP_TOKEN);
+         if (stop >= 0 && stop > start) {
+             // calculate the length
+             int length = stop - start + 1;
+            
+             // copy over the "urls" json from the salesforce ID
+             ALLOC_BUFFER(urls);
+             int start_index = (start - (int)this->m_salesforce_id);
+             for(int i=0;i<length;++i) urls[i] = this->m_salesforce_id[start_index+i];
+                          
+             // use MbedJSONValue to parse the "urls" json 
+             MbedJSONValue parsed_urls;
+             parse(parsed_urls,urls);
+             
+             // find the appropriate URL and copy it
+             string target_url = parsed_urls[key].get<std::string>();
+             
+             // replace the version of the string with our selected salesforce API version
+             string sf_version(this->getSalesforceAPIVersion());
+             string version_tag(SF_URL_API_VER_TOKEN);
+             this->replace(target_url,version_tag,sf_version);
+             
+             // copy the final URL to our putput
+             memset(url_buffer,0,url_buffer_length);
+             strcpy(url_buffer,target_url.c_str()); 
+             
+             // return the URL
+             return url_buffer;
+         }
+     }
+     return NULL;
+ }
+ 
+ // simple char array replacement (modifies input string!)
+ void SalesforceInterface::replace(char *str,char orig_char,char new_char) {
+    int length = strlen(str);
+    for(int i=0;i<length;++i) if (str[i] == orig_char) str[i] = new_char;
+ }
+ 
+ //
+ // substring replacement
+ // Credit: http://stackoverflow.com/questions/4643512/replace-substring-with-another-substring-c
+ //
+ void SalesforceInterface::replace(string& line, string& oldString, string& newString) {
+    const size_t oldSize = oldString.length();
+
+    // do nothing if line is shorter than the string to find
+    if( oldSize > line.length() ) return;
+
+    const size_t newSize = newString.length();
+    for( size_t pos = 0; ; pos += newSize ) {
+
+        // Locate the substring to replace
+        pos = line.find( oldString, pos );
+        if( pos == string::npos ) return;
+        if( oldSize == newSize ) {
+
+            // if they're same size, use std::string::replace
+            line.replace( pos, oldSize, newString );
+        } 
+        else {
+
+            // if not same size, replace by erasing and inserting
+            line.erase( pos, oldSize );
+            line.insert( pos, newString );
+        }
+    }
+} 
\ No newline at end of file