Salesforce.com interface to directly access Salesforce.com

Dependencies:   HTTPClient-SSL MbedJSONValue

Dependents:   df-2014-salesforce-hrm-k64f

Fork of SalesforceInterface by Doug Anson

SalesforceInterface.cpp

Committer:
ansond
Date:
2014-09-21
Revision:
7:97ea5ef906f7
Parent:
1:a7dca096e47d
Child:
8:47db53cd5884

File content as of revision 7:97ea5ef906f7:

/* Copyright C2014 ARM, MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files the "Software", to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
  
 // Tuneables
 #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"
 
 // include class definition
 #include "SalesforceInterface.h"
 
 // Supported DataTypes for HTTPClient
 #include "HTTPMap.h"
 #include "HTTPJson.h"
 
 // default constructor
 SalesforceInterface::SalesforceInterface(ErrorHandler *logger,HTTPClient *http) {
     this->m_logger = logger;
     this->m_http = http;
     this->m_username = NULL;
     this->m_password = NULL;
     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
 SalesforceInterface::~SalesforceInterface() {
 }
 
 // set credentials
 void SalesforceInterface::setCredentials(char *username,char *password,char *client_id,char *client_secret) {
     this->m_username = NULL;
     this->m_password = NULL;
     this->m_client_id = NULL;
     this->m_client_secret = NULL;
     this->m_have_creds = false;
     
     if (username != NULL) {
        this->m_username = username;
        if (password != NULL) {
            this->m_password = password;
            if (client_id != NULL) {
                this->m_client_id = client_id;
                if (client_secret != NULL) {
                    this->m_client_secret = client_secret;
                    this->m_have_creds = true;
                }
            }
        }
     }
 }
 
 // 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: 
 // https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com#Obtaining_a_Token_in_an_Autonomous_Client_.28Username_and_Password_Flow.29
 //
 char *SalesforceInterface::getOauthToken(char *output_buffer,int output_buffer_length) {
     if (this->haveCreds()) { 
         // construct the OAUTH2 Token request body
         HTTPMap input;
         
         //
         // FORMAT: Taken from URL above method signature:
         //
         // grant_type=password&client_id=<your_client_id>&client_secret=<your_client_secret>&username=<your_username>&password=<your_password>
         //
         // ContentType: application/x-www-form-urlencoded
         //
         input.put("grant_type","password");
         input.put("client_id",this->m_client_id);
         input.put("client_secret",this->m_client_secret);
         input.put("username",this->m_username);
         input.put("password",this->m_password);
                  
         // prepare the output buffer
         HTTPText output(output_buffer,output_buffer_length);
         
         // HTTP POST call to gett he token 
         DEBUG("Getting OAUTH Token...");
         this->m_http_status = this->http()->post(SF_OAUTH_TOKEN_URL,input,&output);

         // check the result and return the token
         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
         this->logger()->log("no/incomplete salesforce.com credentials provided. Unable to acquire OAUTH2 token...");
     }
     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(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(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(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(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());
            
            // 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:
                    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:
                    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) {
                            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 {
                            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 {
                        // no input buffer!
                        this->logger()->log("invoke: ERROR HTTP(POST) requested but no input data provided... returning NULL");
                    }
                    break;
                case PUT:
                    if (input_data != NULL && input_data_len > 0) {
                        if (input_type == JSON) {
                            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 {
                            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 {
                        // no input buffer!
                        this->logger()->log("invoke: ERROR HTTP(PUT) requested but no input data provided... returning NULL");
                    }
                    break;
                default:
                    // invalid HTTP verb
                    this->logger()->log("invoke: ERROR invalid HTTP verb (%d) provided... returning NULL",verb);
                    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...");
     }
     
     // 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 (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;
 }