Interface for invoking Salesforce.com REST calls over SSL with OAUTH authentication. This interface is designed to simplify the interaction between mbed devices and salesforce.com web services.
Dependencies: HTTPClient-SSL MbedJSONValue
Diff: SalesforceInterface.cpp
- 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