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
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; }