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:
7:97ea5ef906f7
Parent:
1:a7dca096e47d
Child:
8:47db53cd5884
--- a/SalesforceInterface.cpp	Fri Sep 19 22:07:44 2014 +0000
+++ b/SalesforceInterface.cpp	Sun Sep 21 07:08:51 2014 +0000
@@ -20,10 +20,6 @@
  #define  SF_OAUTH_TOKEN_URL    "https://login.salesforce.com/services/oauth2/token"
  #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"
- #define  MAX_BUFFER_LENGTH     1024
- 
- // convenience macros
- #define  ALLOC_BUFFER(x)       char x[MAX_BUFFER_LENGTH+1];memset(x,0,MAX_BUFFER_LENGTH+1);
  
  // include class definition
  #include "SalesforceInterface.h"
@@ -41,6 +37,10 @@
      this->m_client_id = NULL;
      this->m_client_secret = NULL;
      this->m_have_creds = false;
+     this->m_http_status = HTTP_OK;
+     this->m_http_response_code = -1;
+     RESET_BUFFER(this->m_http_redirection_url);
+     this->resetOauthToken();
  }
  
  // destructor
@@ -73,7 +73,72 @@
  // convenience accessors
  ErrorHandler *SalesforceInterface::logger() { return this->m_logger; }
  HTTPClient *SalesforceInterface::http() { return this->m_http; }
+ OauthToken *SalesforceInterface::oauth() { return &this->m_oauth_token; }
  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; }
+ 
+ // reset our oauth token
+ void SalesforceInterface::resetOauthToken() {
+     //DEBUG("resetting OAUTH token...");
+     this->m_oauth_token.valid              = false;
+     this->m_oauth_token.id                 = "";
+     this->m_oauth_token.issued_at          = "";
+     this->m_oauth_token.token_type         = "";
+     this->m_oauth_token.instance_url       = "";
+     this->m_oauth_token.signature          = "";
+     this->m_oauth_token.access_token       = "";
+ }
+ 
+ // fill our oauth token
+ void SalesforceInterface::fillOauthToken(char *token) {
+     if (token != NULL && strlen(token) > 0) {
+         // parse JSON
+         MbedJSONValue parsed_token;
+         parse(parsed_token,token);
+         
+         // fill our OAUTH token
+         this->m_oauth_token.id             = parsed_token["id"].get<std::string>();
+         this->m_oauth_token.issued_at      = parsed_token["issued_at"].get<std::string>();
+         this->m_oauth_token.token_type     = parsed_token["token_type"].get<std::string>();
+         this->m_oauth_token.instance_url   = parsed_token["instance_url"].get<std::string>();
+         this->m_oauth_token.signature      = parsed_token["signature"].get<std::string>();
+         this->m_oauth_token.access_token   = parsed_token["access_token"].get<std::string>();
+         
+         // we have an OAUTH token now
+         this->m_oauth_token.valid = true;
+         DEBUG("valid OAUTH token acquired.");
+         return;
+     }
+     DEBUG("error: invalid or null OAUTH token fill attempt.");
+ }
+ 
+ // is our OAUTH token valid?
+ bool SalesforceInterface::validOauthToken() {         
+    // make sure we have a valid OAUTH Token
+    this->checkAndGetOauthToken();
+    
+    // TODO: we currently only return fill status. Later we may want to check dates too...
+    return this->m_oauth_token.valid; 
+}
+ 
+ // check and get our OAUTH token
+ void SalesforceInterface::checkAndGetOauthToken() {
+     DEBUG("checking for valid OAUTH token...");
+     if (this->m_oauth_token.valid == false) {
+         // re-initialize token
+         this->resetOauthToken();
+         
+         // get our Token
+         ALLOC_BUFFER(output_buffer);
+         char *token = this->getOauthToken(output_buffer,MAX_BUFFER_LENGTH);
+         
+         // fill
+         this->fillOauthToken(token);
+         return;
+     }
+     DEBUG("valid OAUTH token found.");
+ }
  
  //
  // get OAUTH2 Token - taken from here: 
@@ -101,12 +166,12 @@
          HTTPText output(output_buffer,output_buffer_length);
          
          // HTTP POST call to gett he token 
-         DEBUG("getOauthToken: Calling %s...",SF_OAUTH_TOKEN_URL);
-         HTTPResult status = this->http()->post(SF_OAUTH_TOKEN_URL,input,&output);
-         
+         DEBUG("Getting OAUTH Token...");
+         this->m_http_status = this->http()->post(SF_OAUTH_TOKEN_URL,input,&output);
+
          // check the result and return the token
-         DEBUG("getOauthToken: status(%d) response: %s",status,output_buffer);
-         if (status == 0) return output_buffer;
+         if (this->m_http_status == HTTP_OK) return output_buffer;
+         this->logger()->log("oauth invocation failed. URL: %s",SF_OAUTH_TOKEN_URL);
      }
      else {
          // no credentials
@@ -115,66 +180,84 @@
      return NULL;
  }
  
+ // Salesforce.com: Get our ID
+ char *SalesforceInterface::getSalesforceID(char *output_buffer,int output_buffer_length) {
+    // 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);
+        
+        // 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());
+        return id;
+    }
+    else {
+        // unable to get ID - no OAUTH token
+        this->logger()->log("Unable to get Salesforce ID: no valid OAUTH token.");
+    }
+    return NULL;
+ }
+ 
  // Salesforce.com Invoke: defaults to GET
- char *SalesforceInterface::invoke(char *url,char *output_buffer,int output_buffer_len) { 
+ 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); 
  }
  
  // Salesforce.com Invoke: defaults to POST with JSON input data type                                                  
- char *SalesforceInterface::invoke(char *url,char *input_data,int input_data_len,char *output_buffer,int output_buffer_len) { 
+ char *SalesforceInterface::invoke(const char *url,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len) { 
     return this->invoke(url,JSON,input_data,input_data_len,output_buffer,output_buffer_len); 
  }
  
  // Salesforce.com Invoke: defaults to POST with variable input data type                                                  
- char *SalesforceInterface::invoke(char *url,InputDataTypes input_type,char *input_data,int input_data_len,char *output_buffer,int output_buffer_len) { 
+ char *SalesforceInterface::invoke(const char *url,const InputDataTypes input_type,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_len) { 
     return this->invoke(url,input_type,input_data,input_data_len,output_buffer,output_buffer_len,POST); 
  }
  
  // Salesforce.com Invoke: full fidelity method
- char *SalesforceInterface::invoke(char *url,InputDataTypes input_type,char *input_data,int input_data_len,char *output_buffer,int output_buffer_len,HttpVerb verb) {     
-     // initialize our invocation status
-     HTTPResult status = HTTP_ERROR;
+ char *SalesforceInterface::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) {     
+     // initialize our invocation status and response code
+     this->m_http_response_code = -1;
+     this->m_http_status = HTTP_ERROR;
+                 
+     // param check: make sure that we at least have an output buffer and URL
+     if (url != NULL && strlen(url) > 0 && output_buffer != NULL && output_buffer_len > 0) {         
+        // proceed only if we have a valid OAUTH Token
+        if (this->validOauthToken() == true) {                  
+            // use OAUTH headers
+            this->http()->oauthToken(this->oauth()->access_token.c_str());
             
-     if (this->haveCreds() && output_buffer != NULL && output_buffer_len > 0) {
-        // first we have to get our OAUTH2 Token
-        ALLOC_BUFFER(token_buffer);
-     
-        // get the OAUTH2 token
-        char *token = this->getOauthToken(token_buffer,sizeof(token_buffer));
-        if (token != NULL) {
-            // Parse the OAUTH2 token
-            MbedJSONValue parsed_token;
-            parse(parsed_token,token);
-            
-            // extract the OAUTH2 token
-            string access_token = parsed_token["access_token"].get<std::string>();
-            
-            // DEBUG
-            this->logger()->log("OAUTH: %s",access_token.c_str());
-            
-            // use OAUTH headers
-            this->http()->oauthToken(access_token.c_str());
-            
+            // reset the redirection url buffer in case we get a redirect...
+            RESET_BUFFER(this->m_http_redirection_url);
+            this->http()->setLocationBuf((char *)this->m_http_redirection_url,MAX_BUFFER_LENGTH);
+
             // create our output/response buffer
             HTTPText output(output_buffer,output_buffer_len);
             
             // now make the HTTP(S) request
             switch(verb) {
                 case GET:
-                    status = this->http()->get(url,&output);
+                    DEBUG("invoking(GET) URL: %s...",url);
+                    this->m_http_status = this->http()->get(url,&output);
+                    this->m_http_response_code = this->http()->getHTTPResponseCode();
                     break;
                 case DELETE:
-                    status = this->http()->del(url,&output);
+                    DEBUG("invoking(DEL) URL: %s...",url);
+                    this->m_http_status = this->http()->del(url,&output);
+                    this->m_http_response_code = this->http()->getHTTPResponseCode();
                     break;
                 case POST:
                     if (input_data != NULL && input_data_len > 0) {
                         if (input_type == JSON) {
-                            HTTPJson input_json(input_data,input_data_len);
-                            status = this->http()->post(url,input_json,&output);
+                            DEBUG("invoking(POST-JSON) URL: %s...",url);
+                            HTTPJson input_json((char *)input_data,(int)input_data_len);
+                            this->m_http_status = this->http()->post(url,input_json,&output);
+                            this->m_http_response_code = this->http()->getHTTPResponseCode();
                         }
                         else {
-                            HTTPText input_text(input_data,input_data_len);
-                            status = this->http()->post(url,input_text,&output);
+                            DEBUG("invoking(POST-TEXT) URL: %s...",url);
+                            HTTPText input_text((char *)input_data,(int)input_data_len);
+                            this->m_http_status = this->http()->post(url,input_text,&output);
+                            this->m_http_response_code = this->http()->getHTTPResponseCode();
                         }
                     }
                     else {
@@ -185,12 +268,16 @@
                 case PUT:
                     if (input_data != NULL && input_data_len > 0) {
                         if (input_type == JSON) {
-                            HTTPJson input_json(input_data,input_data_len);
-                            status = this->http()->put(url,input_json,&output);
+                            DEBUG("invoking(PUT-JSON) URL: %s...",url);
+                            HTTPJson input_json((char *)input_data,(int)input_data_len);
+                            this->m_http_status = this->http()->put(url,input_json,&output);
+                            this->m_http_response_code = this->http()->getHTTPResponseCode();
                         }
                         else {
-                            HTTPText input_text(input_data,input_data_len);
-                            status = this->http()->put(url,input_text,&output);
+                            DEBUG("invoking(PUT-TEXT) URL: %s...",url);
+                            HTTPText input_text((char *)input_data,(int)input_data_len);
+                            this->m_http_status = this->http()->put(url,input_text,&output);
+                            this->m_http_response_code = this->http()->getHTTPResponseCode();
                         }
                     }
                     else {
@@ -204,18 +291,41 @@
                     break;
             }
         }
+        else {
+            // no OAUTH Token
+            this->logger()->log("unable to acquire OAUTH token for credentials provided. Unable to invoke API...");
+        }
      }
      else {
          // no credentials
          this->logger()->log("no/incomplete salesforce.com credentials provided. Unable to invoke API...");
      }
      
-     // DEBUG
-     if (status == 0) this->logger()->log("invoke: SUCCESS!");
-     else this->logger()->log("invoke: FAILURE (%d)",status);
-     
+     // process any return results that we have
+     if (this->httpStatus() == HTTP_OK || this->httpStatus() == HTTP_REDIRECT) {
+         // do we have any redirections?
+         if (this->httpResponseCode() == 302 /* REDIRECT */ && strlen(this->m_http_redirection_url) > 0) {
+            // we have a redirect - so reset the output buffer
+            memset(output_buffer,0,output_buffer_len);
+            
+            // we have to make a copy of the redirection URL - this is because the subsequent invoke() will wipe our current one clean
+            ALLOC_BUFFER(redirect_url);
+            strcpy(redirect_url,this->m_http_redirection_url);
+                        
+            // repeat with the redirection URL      
+            DEBUG("invoke: redirecting to: %s",redirect_url);  
+            return this->invoke((const char *)redirect_url,input_type,input_data,input_data_len,output_buffer,output_buffer_len,verb);
+         }
+         else if (this->httpResponseCode() == 302 /* REDIRECT */) {
+            // error - got a redirect but have no URL
+            this->logger()->log("invoke error: received redirect but no URL...");
+            this->m_http_status = HTTP_ERROR;
+         }
+     }
+          
      // return the response in the output buffer
-     if (status == 0) return output_buffer;
+     if (this->httpStatus() == HTTP_OK) return output_buffer;
+     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