Salesforce.com interface to directly access Salesforce.com

Dependencies:   HTTPClient-SSL MbedJSONValue

Dependents:   df-2014-salesforce-hrm-k64f

Fork of SalesforceInterface by Doug Anson

Files at this revision

API Documentation at this revision

Comitter:
ansond
Date:
Mon Sep 22 04:28:02 2014 +0000
Parent:
7:97ea5ef906f7
Child:
9:a254fcd904be
Commit message:
fixes to enable query to work

Changed in this revision

SalesforceInterface.cpp Show annotated file Show diff for this revision Revisions of this file
SalesforceInterface.h Show annotated file Show diff for this revision Revisions of this file
--- 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
--- a/SalesforceInterface.h	Sun Sep 21 07:08:51 2014 +0000
+++ b/SalesforceInterface.h	Mon Sep 22 04:28:02 2014 +0000
@@ -30,14 +30,25 @@
  
  // verbose debugging
  #if ENABLE_DEBUG_LOGGING
-    #define DEBUG(...) { this->logger()->logConsole(__VA_ARGS__); }
+    #define DEBUG(...)                  { this->logger()->logConsole(__VA_ARGS__); }
  #else
     #define DEBUG(...)
  #endif 
  
  // convenience macros
- #define  RESET_BUFFER(x)       memset(x,0,MAX_BUFFER_LENGTH+1)
- #define  ALLOC_BUFFER(x)       char x[MAX_BUFFER_LENGTH+1];RESET_BUFFER(x)
+ #define  DEFINE_BUFFER(x)              char x[MAX_BUFFER_LENGTH+1]
+ #define  RESET_BUFFER(x)               memset(x,0,MAX_BUFFER_LENGTH+1)
+ #define  ALLOC_BUFFER(x)               DEFINE_BUFFER(x);RESET_BUFFER(x)
+ 
+ #define  DEFINE_SML_BUFFER(x)          char x[MAX_SMALL_BUFFER_LENGTH+1]
+ #define  RESET_SML_BUFFER(x)           memset(x,0,MAX_SMALL_BUFFER_LENGTH+1)
+ #define  ALLOC_SML_BUFFER(x)           DEFINE_SML_BUFFER(x);RESET_SML_BUFFER(x)
+ 
+ // Default salesforce API version (must be in XX.Y format and must be a string)
+ #define SALESFORCE_API_VERSION_LENGTH  10
+ #ifndef SALESFORCE_API_VERSION
+    #define SALESFORCE_API_VERSION      "28.0"
+ #endif
  
  // HTTP Verbs
  typedef enum {
@@ -81,6 +92,8 @@
         HTTPResult       m_http_status;
         int              m_http_response_code;
         char             m_http_redirection_url[MAX_BUFFER_LENGTH+1];
+        char             m_salesforce_id[MAX_BUFFER_LENGTH+1];
+        char             m_salesforce_api[SALESFORCE_API_VERSION_LENGTH];
         
     public:
         // construction/destruction
@@ -91,19 +104,28 @@
         void setCredentials(char *username,char *password,char *client_id,char *client_secret);
         
         // get our ID structure from Salesforce
-        char *getSalesforceID(char *output_buffer,int output_buffer_length);
-                        
-        // invoke REST calls into Salesforce.com
-        char *invoke(const char *url,char *output_buffer,int output_buffer_len);                                                                               // defaults to GET
-        char *invoke(const char *url,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len);                                           // defaults to POST with JSON input data type                                                                              // defaults to GET
-        char *invoke(const char *url,const InputDataTypes input_type,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len);                  // defaults to POST with variable input data type
+        char *getSalesforceID();
+        bool haveSalesforceID();
+        
+        // set/get our salesforce API version
+        void setSalesforceAPIVersion(int version);
+        void setSalesforceAPIVersion(char *version);
+        char *getSalesforceAPIVersion();
+        
+        // QUERY: into Salesforce.com
+        char *query(char *query_str,char *output_buffer,int output_buffer_length);
+                     
+        // raw invocation of REST calls into Salesforce.com
+        char *invoke(const char *url,char *output_buffer,int output_buffer_len);                                                                                                        // defaults to GET
+        char *invoke(const char *url,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len);                                                        // defaults to POST with JSON input data type                                                                              // defaults to GET
+        char *invoke(const char *url,const InputDataTypes input_type,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len);                        // defaults to POST with variable input data type
         char *invoke(const char *url,const InputDataTypes input_type,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len,const HttpVerb verb);    // full fidelity method
-        
-    private:
+ 
+ private:       
         // get our OAUTH Token
         void checkAndGetOauthToken();
         char *getOauthToken(char *output_buffer,int output_buffer_length);
-        
+     
         // convenience accessors
         ErrorHandler *logger();
         HTTPClient *http();
@@ -116,6 +138,15 @@
         void resetOauthToken();
         void fillOauthToken(char *token);
         bool validOauthToken();
+        
+        // get the specified URL from our Salesforce ID
+        char *getSalesforceURL(char *key,char *url_buffer,int url_buffer_length);
+        
+        // simple char array replacement (modifies input string!)
+        void replace(char *str,char orig_char,char new_char);
+        
+        // needed to replace substrings within std::string
+        void replace(string& line, string& oldString, string& newString);
  };
  
  #endif // _SALESFORCE_INTERFACE_H_
\ No newline at end of file