Salesforce.com interface to directly access Salesforce.com
Dependencies: HTTPClient-SSL MbedJSONValue
Dependents: df-2014-salesforce-hrm-k64f
Fork of SalesforceInterface by
SalesforceInterface.cpp
- Committer:
- ansond
- Date:
- 2015-06-10
- Revision:
- 24:bc93af985435
- Parent:
- 22:3363752cd523
File content as of revision 24:bc93af985435:
/* Copyright C2014 ARM, MIT License * * Author: Doug Anson (doug.anson@arm.com) * * 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" // search tokens for URLS in salesforce token #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}" // HTTP response code to give for errored out conditions #define SF_GEN_ERR_HTTP_CODE 500 // include class definition #include "SalesforceInterface.h" // Supported DataTypes for HTTPClient #include "HTTPMap.h" #include "HTTPJson.h" // Logging #define LOG(...) { if (this->logger() != NULL) { this->logger()->log(__VA_ARGS__); } } #define LOG_CONSOLE(...) { if (this->logger() != NULL) { this->logger()->logConsole(__VA_ARGS__); } } // default constructor SalesforceInterface::SalesforceInterface(HTTPClient *http,RawSerial *pc) { Logger *logger = NULL; if (pc != NULL) { logger = new Logger(pc,NULL); this->init(http,logger,true); } else { this->init(http,NULL,false); } } // alternative constructor SalesforceInterface::SalesforceInterface(HTTPClient *http,Logger *logger) { this->init(http,logger,false); } // initialize void SalesforceInterface::init(HTTPClient *http,Logger *logger,bool logger_internal) { this->m_logger = logger; this->m_logger_internal = logger_internal; 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); RESET_SML_BUFFER(this->m_error_buffer); memset(this->m_salesforce_api,0,SALESFORCE_API_VERSION_LENGTH); strcpy(this->m_salesforce_api,SALESFORCE_API_VERSION); this->resetSalesforceToken(); } // destructor SalesforceInterface::~SalesforceInterface() { if (this->m_logger_internal == true && this->m_logger != NULL) delete this->m_logger; } // 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 Logger *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; } 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() { 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 = ""; if (this->http() != NULL) this->http()->oauthToken(NULL); } // 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; } LOG_CONSOLE("error: invalid or null OAUTH token fill attempt."); } // is our OAUTH token valid? bool SalesforceInterface::validOauthToken(bool fetch) { // make sure we have a valid OAUTH Token this->checkAndGetOauthToken(fetch); return this->m_oauth_token.valid; } // reset our salesforce token and OAUTH tokens void SalesforceInterface::resetSalesforceToken() { this->resetOauthToken(); RESET_BUFFER(this->m_salesforce_id); } // do we have a valid salesforce.com token? bool SalesforceInterface::haveSalesforceToken(bool fetch) { if (this->m_salesforce_id != NULL && strlen(this->m_salesforce_id) > 0) return true; if (fetch) { LOG("Fetching Salesforce Token..."); this->getSalesforceToken(); return this->haveSalesforceToken(false); } return false; } // check and get our OAUTH token void SalesforceInterface::checkAndGetOauthToken(bool fetch) { DEBUG("checking for valid OAUTH token..."); // reset the token structure for sanity... if (this->m_oauth_token.valid == false) this->resetOauthToken(); // should go fetch our token if we dont have one? // just pass through if we already have a token if (this->m_oauth_token.valid == true) { DEBUG("valid OAUTH token found."); } else if (this->m_oauth_token.valid == false && fetch == true) { // get our OAUTH token DEBUG("No OAUTH token found. Acquiring OAUTH token..."); ALLOC_BUFFER(output_buffer); char *token = this->getOauthToken(output_buffer,MAX_BUFFER_LENGTH); if (token != NULL && strlen(token) > 0) { // fill DEBUG("Saving OAUTH token..."); this->fillOauthToken(token); return; } else { // unable to get the token (reset for sanity) LOG_CONSOLE("error in acquiring OAUTH token http_code=%d status=%d",this->httpResponseCode(),this->httpStatus()); this->resetOauthToken(); } } // else report that we dont have a token else { LOG_CONSOLE("No OAUTH token found (fetch=false)."); } } // // 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->httpStatus() == HTTP_OK || this->httpResponseCode() == 200) return output_buffer; LOG_CONSOLE("acquire oauth FAILED. URL: %s http_code=%d status=%d",SF_OAUTH_TOKEN_URL,this->httpResponseCode(),this->httpStatus()); } else { // no credentials LOG_CONSOLE("no/incomplete salesforce.com credentials provided. Unable to acquire OAUTH2 token..."); } return NULL; } // Salesforce.com: Get our token char *SalesforceInterface::getSalesforceToken(bool fetch) { // proceed only if we have a valid OAUTH Token if (this->validOauthToken(fetch) == true) { // pull the token from salesforce 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) LOG_CONSOLE("Unable to get Salesforce Token: status=%d httpCode=%d",this->httpStatus(),this->httpResponseCode()); return id; } else { // unable to get token - no OAUTH token LOG_CONSOLE("Unable to get Salesforce Token: no valid OAUTH token."); } 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 token if (this->haveSalesforceToken()) { // 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... LOG_CONSOLE("query: error - unable to find query URL in salesforce token..."); } } else { // dont have a valid salesforce token LOG_CONSOLE("query: error - no valid salesforce token was found..."); } return NULL; } // CREATE: a record in Salesforce.com MbedJSONValue SalesforceInterface::createRecord(char *object_name,MbedJSONValue &record) { ALLOC_BUFFER(output_buffer); char *reply = this->createRecord(object_name,(char *)record.serialize().c_str(),output_buffer,MAX_BUFFER_LENGTH); MbedJSONValue response; if (reply != NULL && strlen(reply) > 0) parse(response,reply); return response; } // READ: a specific record in Salesforce.com MbedJSONValue SalesforceInterface::readRecord(char *object_name,char *record_id,char *record_value) { ALLOC_BUFFER(output_buffer); char *reply = this->readRecord(object_name,record_id,record_value,output_buffer,MAX_BUFFER_LENGTH); MbedJSONValue response; if (reply != NULL && strlen(reply) > 0) parse(response,reply); return response; } // UPDATE: a specific record in Salesforce.com bool SalesforceInterface::updateRecord(char *object_name,char *record_id,MbedJSONValue &record) { RESET_SML_BUFFER(this->m_error_buffer); return this->updateRecord(object_name,record_id,(char *)record.serialize().c_str(),this->m_error_buffer,MAX_SMALL_BUFFER_LENGTH); } // UPSERT: update/insert an External ID record in Salesforce.com bool SalesforceInterface::upsertRecord(char *object_name,char *external_id_field_name,char *external_id_field_value,MbedJSONValue &record) { RESET_SML_BUFFER(this->m_error_buffer); return this->upsertRecord(object_name,external_id_field_name,external_id_field_value,(char *)record.serialize().c_str(),this->m_error_buffer,MAX_SMALL_BUFFER_LENGTH); } // DELETE: a specific record in Salesforce.com bool SalesforceInterface::deleteRecord(char *object_name,char *record_id) { RESET_SML_BUFFER(this->m_error_buffer); return this->deleteRecord(object_name,record_id,this->m_error_buffer,MAX_SMALL_BUFFER_LENGTH); } // ERROR: get last error result MbedJSONValue SalesforceInterface::getLastError() { MbedJSONValue error; if (strlen(this->m_error_buffer) > 0) parse(error,this->m_error_buffer); return error; } // CREATE: a record in Salesforce.com char *SalesforceInterface::createRecord(char *object_name,char *json_data,char *output_buffer,int output_buffer_length) { // parameter check if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0 && output_buffer != NULL && output_buffer_length > 0) { // first we have to ensure that we have valid salesforce token if (this->haveSalesforceToken()) { // get the sobjects url ALLOC_BUFFER(url); char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); if (sf_url != NULL && strlen(sf_url) > 0) { // convert to string string str_url(sf_url); // add object name that we want to create a record in str_url += object_name; // DEBUG DEBUG("createRecord: URL: %s DATA: %s",str_url.c_str(),json_data); // now invoke with POST with JSON data type return this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); } } else { // dont have a valid salesforce token LOG_CONSOLE("createRecord: error - no valid salesforce token was found..."); } } else { // invalid or NULL parameters LOG_CONSOLE("createRecord: error - invalid or NULL parameters..."); } this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; return NULL; } // READ: a specific record in Salesforce.com char *SalesforceInterface::readRecord(char *object_name,char *record_id,char *record_value,char *output_buffer,int output_buffer_length) { // parameter check if (object_name != NULL && strlen(object_name) > 0 && record_id != NULL && strlen(record_id) > 0 && output_buffer != NULL && output_buffer_length > 0) { // first we have to ensure that we have valid salesforce token if (this->haveSalesforceToken()) { // get the sobjects url ALLOC_BUFFER(url); char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); if (sf_url != NULL && strlen(sf_url) > 0) { // convert to string string str_url(sf_url); // add object name that we want to create a record in str_url += object_name; // add the record token str_url += "/"; str_url += record_id; // add the record value (if present) if (record_value != NULL && strlen(record_value) > 0) { str_url += "/"; str_url += record_value; } // DEBUG DEBUG("readRecord: URL: %s",str_url.c_str()); // now invoke with GET with JSON data type return this->invoke(str_url.c_str(),output_buffer,output_buffer_length); } } else { // dont have a valid salesforce token LOG_CONSOLE("readRecord: error - no valid salesforce token was found..."); } } else { // invalid or NULL parameters LOG_CONSOLE("readRecord: error - invalid or NULL parameters..."); } this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; return NULL; } // UPDATE: a specific record in Salesforce.com bool SalesforceInterface::updateRecord(char *object_name,char *record_id,char *json_data,char *output_buffer,int output_buffer_length) { // reset the error buffer RESET_SML_BUFFER(this->m_error_buffer); // parameter check if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0) { // first we have to ensure that we have valid salesforce token if (this->haveSalesforceToken()) { // get the sobjects url ALLOC_BUFFER(url); char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); if (sf_url != NULL && strlen(sf_url) > 0) { // convert to string string str_url(sf_url); // add object name that we want to create a record in str_url += object_name; // add the record token str_url += "/"; str_url += record_id; // HTTPClient does not support PATCH, so we have to use POST with a special added parameter str_url += "?_HttpMethod=PATCH"; // DEBUG DEBUG("updateRecord: URL: %s DATA: %s",str_url.c_str(),json_data); // now invoke with POST with JSON data type char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); // DEBUG DEBUG("updateRecord: http status=%d",this->httpResponseCode()); // return our status if (this->httpResponseCodeInRange(200)) return true; // we are in error - so copy the result if we have one and return false if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); return false; } } else { // dont have a valid salesforce token LOG_CONSOLE("updateRecord: error - no valid salesforce token was found..."); } } else { // invalid or NULL parameters LOG_CONSOLE("updateRecord: error - invalid or NULL parameters..."); } this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; return false; } // UPSERT: update/insert a specific External record in Salesforce.com bool SalesforceInterface::upsertRecord(char *object_name,char *external_id_field_name,char *external_id_field_value,char *json_data,char *output_buffer,int output_buffer_length) { // reset the error buffer RESET_SML_BUFFER(this->m_error_buffer); // parameter check if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0) { // first we have to ensure that we have valid salesforce token if (this->haveSalesforceToken()) { // get the sobjects url ALLOC_BUFFER(url); char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); if (sf_url != NULL && strlen(sf_url) > 0) { // convert to string string str_url(sf_url); // add object name that we want to create a record in str_url += object_name; // add the external field name token str_url += "/"; str_url += external_id_field_name; // add the external field value token (if not NULL) if (external_id_field_value != NULL && strlen(external_id_field_value) > 0) { str_url += "/"; str_url += external_id_field_value; } // HTTPClient does not support PATCH, so we have to use POST with a special added parameter str_url += "?_HttpMethod=PATCH"; // DEBUG DEBUG("upsertRecord: URL: %s DATA: %s",str_url.c_str(),json_data); // now invoke with POST with JSON data type char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); // DEBUG DEBUG("upsertRecord: http status=%d",this->httpResponseCode()); // return our status if (this->httpResponseCodeInRange(200)) return true; // we are in error - so copy the result if we have one and return false if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); return false; } } else { // dont have a valid salesforce token LOG_CONSOLE("upsertRecord: error - no valid salesforce token was found..."); } } else { // invalid or NULL parameters LOG_CONSOLE("upsertRecord: error - invalid or NULL parameters..."); } this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; return false; } // DELETE: a specific record in Salesforce.com bool SalesforceInterface::deleteRecord(char *object_name,char *record_id,char *output_buffer,int output_buffer_length) { // reset the error buffer RESET_SML_BUFFER(this->m_error_buffer); // parameter check if (object_name != NULL && strlen(object_name) > 0 && record_id != NULL && strlen(record_id) > 0) { // first we have to ensure that we have valid salesforce token if (this->haveSalesforceToken()) { // get the sobjects url ALLOC_BUFFER(url); char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); if (sf_url != NULL && strlen(sf_url) > 0) { // convert to string string str_url(sf_url); // add object name that we want to create a record in str_url += object_name; // add the record token str_url += "/"; str_url += record_id; // DEBUG LOG_CONSOLE("deleteRecord: URL: %s",str_url.c_str()); // now invoke with DELETE ALLOC_SML_BUFFER(output_buffer); char *reply = this->invoke(str_url.c_str(),output_buffer,output_buffer_length,DELETE); // DEBUG DEBUG("deleteRecord: http status=%d",this->httpResponseCode()); // return our status if (this->httpResponseCodeInRange(200)) return true; // we are in error - so copy the result if we have one and return false if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); return false; } } else { // dont have a valid salesforce token LOG_CONSOLE("deleteRecord: error - no valid salesforce token was found..."); } } else { // invalid or NULL parameters LOG_CONSOLE("deleteRecord: error - invalid or NULL parameters..."); } this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; return false; } // Salesforce.com Invoke: defaults to GET char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_length) { return this->invoke(url,output_buffer,output_buffer_length,GET); } // Salesforce.com Invoke: GET or DELETE with simple output char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_length,HttpVerb verb) { char *response = NULL; switch(verb) { case GET: case DELETE: // GET and DELETE only require an output buffer... response = this->invoke(url,NUM_TYPES,NULL,0,output_buffer,output_buffer_length,verb); break; default: // wrong verb for this call interface... LOG_CONSOLE("invoke: invalid call: must be either GET or DELETE verb if only output buffer is provided"); break; } return response; } // 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_length) { return this->invoke(url,JSON,input_data,input_data_len,output_buffer,output_buffer_length); } // 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_length) { return this->invoke(url,input_type,input_data,input_data_len,output_buffer,output_buffer_length,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_length,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_length > 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_length); // now make the HTTP(S) request switch(verb) { case GET: DEBUG("invoke (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("invoke (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("invoke (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("invoke (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! LOG_CONSOLE("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("invoke (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("invoke (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! LOG_CONSOLE("invoke: ERROR HTTP(PUT) requested but no input data provided... returning NULL"); } break; default: // invalid HTTP verb LOG_CONSOLE("invoke: ERROR invalid HTTP verb (%d) provided... returning NULL",verb); break; } } else { // no OAUTH Token LOG_CONSOLE("unable to acquire OAUTH token for credentials provided. Unable to invoke API..."); } } else { // no credentials LOG_CONSOLE("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->httpResponseCodeInRange(300) /* REDIRECT */ && strlen(this->m_http_redirection_url) > 0) { // we have a redirect - so reset the output buffer memset(output_buffer,0,output_buffer_length); // 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_length,verb); } else if (this->httpResponseCodeInRange(300) /* REDIRECT */) { // error - got a redirect but have no URL LOG_CONSOLE("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 || this->httpStatus() == HTTP_REDIRECT) return output_buffer; else LOG_CONSOLE("invocation failed with HTTP error code=%d status=%d",this->httpResponseCode(),this->httpStatus()); return NULL; } // find the specific URL in our salesforce token 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 token 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 token 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 ); } } } // validate that a given HTTP result code is in the "n" range bool SalesforceInterface::httpResponseCodeInRange(int n) { int http_response = this->httpResponseCode(); int diff = http_response - n; if (diff >= 0 && diff < 100) return true; return false; } // min() method int SalesforceInterface::min(int value1,int value2) { if (value1 < value2) return value1; return value2; } // look for a specific key value pair in a json message bool SalesforceInterface::contains(char *json,char *key,char *value) { bool has_it = false; if (json != NULL && key != NULL && value != NULL) { // parse the json and look for a specific <key,value> pair... MbedJSONValue parsed; parse(parsed,json); char *answer = (char *)parsed[key].get<std::string>().c_str(); if (strcmp(answer,value) == 0) has_it = true; } return has_it; }