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
00001 /* Copyright C2014 ARM, MIT License 00002 * 00003 * Author: Doug Anson (doug.anson@arm.com) 00004 * 00005 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 00006 * and associated documentation files the "Software", to deal in the Software without restriction, 00007 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 00008 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 00009 * furnished to do so, subject to the following conditions: 00010 * 00011 * The above copyright notice and this permission notice shall be included in all copies or 00012 * substantial portions of the Software. 00013 * 00014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 00015 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00016 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 00017 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00018 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00019 */ 00020 00021 // Tuneables 00022 #define SF_OAUTH_TOKEN_URL "https://login.salesforce.com/services/oauth2/token" 00023 #define SF_OAUTH_REQUEST_BODY "grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s" 00024 #define SF_HTTP_AUTH_HEADER "Authorization: Bearer %s" 00025 00026 // search tokens for URLS in salesforce token 00027 #define SF_URLS_START_TOKEN "\"urls\":" 00028 #define SF_URLS_STOP_TOKEN "}," 00029 00030 // salesforce QUERY specifier within URL 00031 #define SF_QUERY_URL_SPECIFIER "q=" 00032 00033 // salesforce URL API version token 00034 #define SF_URL_API_VER_TOKEN "{version}" 00035 00036 // HTTP response code to give for errored out conditions 00037 #define SF_GEN_ERR_HTTP_CODE 500 00038 00039 // include class definition 00040 #include "SalesforceInterface.h" 00041 00042 // Supported DataTypes for HTTPClient 00043 #include "HTTPMap.h" 00044 #include "HTTPJson.h" 00045 00046 // Logging 00047 #define LOG(...) { if (this->logger() != NULL) { this->logger()->log(__VA_ARGS__); } } 00048 #define LOG_CONSOLE(...) { if (this->logger() != NULL) { this->logger()->logConsole(__VA_ARGS__); } } 00049 00050 // default constructor 00051 SalesforceInterface::SalesforceInterface(HTTPClient *http,RawSerial *pc) { 00052 Logger *logger = NULL; 00053 if (pc != NULL) { 00054 logger = new Logger(pc,NULL); 00055 this->init(http,logger,true); 00056 } 00057 else { 00058 this->init(http,NULL,false); 00059 } 00060 } 00061 00062 // alternative constructor 00063 SalesforceInterface::SalesforceInterface(HTTPClient *http,Logger *logger) { 00064 this->init(http,logger,false); 00065 } 00066 00067 // initialize 00068 void SalesforceInterface::init(HTTPClient *http,Logger *logger,bool logger_internal) { 00069 this->m_logger = logger; 00070 this->m_logger_internal = logger_internal; 00071 this->m_http = http; 00072 this->m_username = NULL; 00073 this->m_password = NULL; 00074 this->m_client_id = NULL; 00075 this->m_client_secret = NULL; 00076 this->m_have_creds = false; 00077 this->m_http_status = HTTP_OK; 00078 this->m_http_response_code = -1; 00079 RESET_BUFFER(this->m_http_redirection_url); 00080 RESET_SML_BUFFER(this->m_error_buffer); 00081 memset(this->m_salesforce_api,0,SALESFORCE_API_VERSION_LENGTH); 00082 strcpy(this->m_salesforce_api,SALESFORCE_API_VERSION); 00083 this->resetSalesforceToken(); 00084 } 00085 00086 // destructor 00087 SalesforceInterface::~SalesforceInterface() { 00088 if (this->m_logger_internal == true && this->m_logger != NULL) delete this->m_logger; 00089 } 00090 00091 // set credentials 00092 void SalesforceInterface::setCredentials(char *username,char *password,char *client_id,char *client_secret) { 00093 this->m_username = NULL; 00094 this->m_password = NULL; 00095 this->m_client_id = NULL; 00096 this->m_client_secret = NULL; 00097 this->m_have_creds = false; 00098 00099 if (username != NULL) { 00100 this->m_username = username; 00101 if (password != NULL) { 00102 this->m_password = password; 00103 if (client_id != NULL) { 00104 this->m_client_id = client_id; 00105 if (client_secret != NULL) { 00106 this->m_client_secret = client_secret; 00107 this->m_have_creds = true; 00108 } 00109 } 00110 } 00111 } 00112 } 00113 00114 // convenience accessors 00115 Logger *SalesforceInterface::logger() { return this->m_logger; } 00116 HTTPClient *SalesforceInterface::http() { return this->m_http; } 00117 OauthToken *SalesforceInterface::oauth() { return &this->m_oauth_token; } 00118 bool SalesforceInterface::haveCreds() { return this->m_have_creds; } 00119 HTTPResult SalesforceInterface::httpStatus() { return this->m_http_status; } 00120 int SalesforceInterface::httpResponseCode() { return this->m_http_response_code; } 00121 void SalesforceInterface::setSalesforceAPIVersion(int version) { sprintf(this->m_salesforce_api,"%d",version); } 00122 void SalesforceInterface::setSalesforceAPIVersion(char *version) { if (version != NULL && strlen(version) > 0 && strlen(version) < SALESFORCE_API_VERSION_LENGTH) strcpy(this->m_salesforce_api,version); } 00123 char *SalesforceInterface::getSalesforceAPIVersion() { return this->m_salesforce_api; } 00124 00125 // reset our oauth token 00126 void SalesforceInterface::resetOauthToken() { 00127 DEBUG("resetting OAUTH token..."); 00128 this->m_oauth_token.valid = false; 00129 this->m_oauth_token.id = ""; 00130 this->m_oauth_token.issued_at = ""; 00131 this->m_oauth_token.token_type = ""; 00132 this->m_oauth_token.instance_url = ""; 00133 this->m_oauth_token.signature = ""; 00134 this->m_oauth_token.access_token = ""; 00135 if (this->http() != NULL) this->http()->oauthToken(NULL); 00136 } 00137 00138 // fill our oauth token 00139 void SalesforceInterface::fillOauthToken(char *token) { 00140 if (token != NULL && strlen(token) > 0) { 00141 // parse JSON 00142 MbedJSONValue parsed_token; 00143 parse(parsed_token,token); 00144 00145 // fill our OAUTH token 00146 this->m_oauth_token.id = parsed_token["id"].get<std::string>(); 00147 this->m_oauth_token.issued_at = parsed_token["issued_at"].get<std::string>(); 00148 this->m_oauth_token.token_type = parsed_token["token_type"].get<std::string>(); 00149 this->m_oauth_token.instance_url = parsed_token["instance_url"].get<std::string>(); 00150 this->m_oauth_token.signature = parsed_token["signature"].get<std::string>(); 00151 this->m_oauth_token.access_token = parsed_token["access_token"].get<std::string>(); 00152 00153 // we have an OAUTH token now 00154 this->m_oauth_token.valid = true; 00155 DEBUG("valid OAUTH token acquired."); 00156 return; 00157 } 00158 LOG_CONSOLE("error: invalid or null OAUTH token fill attempt."); 00159 } 00160 00161 // is our OAUTH token valid? 00162 bool SalesforceInterface::validOauthToken(bool fetch) { 00163 // make sure we have a valid OAUTH Token 00164 this->checkAndGetOauthToken(fetch); 00165 return this->m_oauth_token.valid; 00166 } 00167 00168 // reset our salesforce token and OAUTH tokens 00169 void SalesforceInterface::resetSalesforceToken() { 00170 this->resetOauthToken(); 00171 RESET_BUFFER(this->m_salesforce_id); 00172 } 00173 00174 // do we have a valid salesforce.com token? 00175 bool SalesforceInterface::haveSalesforceToken(bool fetch) { 00176 if (this->m_salesforce_id != NULL && strlen(this->m_salesforce_id) > 0) return true; 00177 if (fetch) { 00178 LOG("Fetching Salesforce Token..."); 00179 this->getSalesforceToken(); 00180 return this->haveSalesforceToken(false); 00181 } 00182 return false; 00183 } 00184 00185 // check and get our OAUTH token 00186 void SalesforceInterface::checkAndGetOauthToken(bool fetch) { 00187 DEBUG("checking for valid OAUTH token..."); 00188 00189 // reset the token structure for sanity... 00190 if (this->m_oauth_token.valid == false) this->resetOauthToken(); 00191 00192 // should go fetch our token if we dont have one? 00193 // just pass through if we already have a token 00194 if (this->m_oauth_token.valid == true) { 00195 DEBUG("valid OAUTH token found."); 00196 } 00197 else if (this->m_oauth_token.valid == false && fetch == true) { 00198 // get our OAUTH token 00199 DEBUG("No OAUTH token found. Acquiring OAUTH token..."); 00200 ALLOC_BUFFER(output_buffer); 00201 char *token = this->getOauthToken(output_buffer,MAX_BUFFER_LENGTH); 00202 if (token != NULL && strlen(token) > 0) { 00203 // fill 00204 DEBUG("Saving OAUTH token..."); 00205 this->fillOauthToken(token); 00206 return; 00207 } 00208 else { 00209 // unable to get the token (reset for sanity) 00210 LOG_CONSOLE("error in acquiring OAUTH token http_code=%d status=%d",this->httpResponseCode(),this->httpStatus()); 00211 this->resetOauthToken(); 00212 } 00213 } 00214 00215 // else report that we dont have a token 00216 else { 00217 LOG_CONSOLE("No OAUTH token found (fetch=false)."); 00218 } 00219 } 00220 00221 // 00222 // get OAUTH2 Token - taken from here: 00223 // 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 00224 // 00225 char *SalesforceInterface::getOauthToken(char *output_buffer,int output_buffer_length) { 00226 if (this->haveCreds()) { 00227 // construct the OAUTH2 Token request body 00228 HTTPMap input; 00229 00230 // 00231 // FORMAT: Taken from URL above method signature: 00232 // 00233 // grant_type=password&client_id=<your_client_id>&client_secret=<your_client_secret>&username=<your_username>&password=<your_password> 00234 // 00235 // ContentType: application/x-www-form-urlencoded 00236 // 00237 input.put("grant_type","password"); 00238 input.put("client_id",this->m_client_id); 00239 input.put("client_secret",this->m_client_secret); 00240 input.put("username",this->m_username); 00241 input.put("password",this->m_password); 00242 00243 // prepare the output buffer 00244 HTTPText output(output_buffer,output_buffer_length); 00245 00246 // HTTP POST call to gett he token 00247 DEBUG("Getting OAUTH Token..."); 00248 this->m_http_status = this->http()->post(SF_OAUTH_TOKEN_URL,input,&output); 00249 00250 // check the result and return the token 00251 if (this->httpStatus() == HTTP_OK || this->httpResponseCode() == 200) return output_buffer; 00252 LOG_CONSOLE("acquire oauth FAILED. URL: %s http_code=%d status=%d",SF_OAUTH_TOKEN_URL,this->httpResponseCode(),this->httpStatus()); 00253 } 00254 else { 00255 // no credentials 00256 LOG_CONSOLE("no/incomplete salesforce.com credentials provided. Unable to acquire OAUTH2 token..."); 00257 } 00258 return NULL; 00259 } 00260 00261 // Salesforce.com: Get our token 00262 char *SalesforceInterface::getSalesforceToken(bool fetch) { 00263 // proceed only if we have a valid OAUTH Token 00264 if (this->validOauthToken(fetch) == true) { 00265 // pull the token from salesforce 00266 RESET_BUFFER(this->m_salesforce_id); 00267 char *id = this->invoke(this->oauth()->id.c_str(),this->m_salesforce_id,MAX_BUFFER_LENGTH); 00268 00269 // log any error status and return what we have... 00270 if (this->httpStatus() != HTTP_OK) LOG_CONSOLE("Unable to get Salesforce Token: status=%d httpCode=%d",this->httpStatus(),this->httpResponseCode()); 00271 return id; 00272 } 00273 else { 00274 // unable to get token - no OAUTH token 00275 LOG_CONSOLE("Unable to get Salesforce Token: no valid OAUTH token."); 00276 } 00277 return NULL; 00278 } 00279 00280 // QUERY: Salesforce.com 00281 char *SalesforceInterface::query(char *query_str,char *output_buffer,int output_buffer_length) { 00282 // first we have to ensure that we have valid salesforce token 00283 if (this->haveSalesforceToken()) { 00284 // get the query url 00285 ALLOC_BUFFER(url); 00286 char *sf_url = this->getSalesforceURL("query",url,MAX_BUFFER_LENGTH); 00287 if (sf_url != NULL && strlen(sf_url) > 0) { 00288 // make sure that the query string is ready to ship... 00289 ALLOC_SML_BUFFER(tmp_query); 00290 00291 // replace all spaces in query with "+" 00292 strcpy(tmp_query,query_str); 00293 this->replace(tmp_query,' ','+'); // will modify tmp_query directly... 00294 00295 // customize the URL with our (formatted) query string 00296 string str_url(sf_url); 00297 str_url[str_url.length()-1] = '?'; // remove the slash and add a ? 00298 str_url = str_url + SF_QUERY_URL_SPECIFIER + tmp_query; // add the query specifier 00299 00300 // DEBUG - show the query URL 00301 DEBUG("query URL: %s",str_url.c_str()); 00302 00303 // invoke with GET 00304 return this->invoke((const char *)str_url.c_str(),output_buffer,output_buffer_length); 00305 } 00306 else { 00307 // unable to find the query URL... 00308 LOG_CONSOLE("query: error - unable to find query URL in salesforce token..."); 00309 } 00310 } 00311 else { 00312 // dont have a valid salesforce token 00313 LOG_CONSOLE("query: error - no valid salesforce token was found..."); 00314 } 00315 return NULL; 00316 } 00317 00318 // CREATE: a record in Salesforce.com 00319 MbedJSONValue SalesforceInterface::createRecord(char *object_name,MbedJSONValue &record) { 00320 ALLOC_BUFFER(output_buffer); 00321 char *reply = this->createRecord(object_name,(char *)record.serialize().c_str(),output_buffer,MAX_BUFFER_LENGTH); 00322 MbedJSONValue response; 00323 if (reply != NULL && strlen(reply) > 0) parse(response,reply); 00324 return response; 00325 } 00326 00327 // READ: a specific record in Salesforce.com 00328 MbedJSONValue SalesforceInterface::readRecord(char *object_name,char *record_id,char *record_value) { 00329 ALLOC_BUFFER(output_buffer); 00330 char *reply = this->readRecord(object_name,record_id,record_value,output_buffer,MAX_BUFFER_LENGTH); 00331 MbedJSONValue response; 00332 if (reply != NULL && strlen(reply) > 0) parse(response,reply); 00333 return response; 00334 } 00335 00336 // UPDATE: a specific record in Salesforce.com 00337 bool SalesforceInterface::updateRecord(char *object_name,char *record_id,MbedJSONValue &record) { 00338 RESET_SML_BUFFER(this->m_error_buffer); 00339 return this->updateRecord(object_name,record_id,(char *)record.serialize().c_str(),this->m_error_buffer,MAX_SMALL_BUFFER_LENGTH); 00340 } 00341 00342 // UPSERT: update/insert an External ID record in Salesforce.com 00343 bool SalesforceInterface::upsertRecord(char *object_name,char *external_id_field_name,char *external_id_field_value,MbedJSONValue &record) { 00344 RESET_SML_BUFFER(this->m_error_buffer); 00345 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); 00346 } 00347 00348 // DELETE: a specific record in Salesforce.com 00349 bool SalesforceInterface::deleteRecord(char *object_name,char *record_id) { 00350 RESET_SML_BUFFER(this->m_error_buffer); 00351 return this->deleteRecord(object_name,record_id,this->m_error_buffer,MAX_SMALL_BUFFER_LENGTH); 00352 } 00353 00354 // ERROR: get last error result 00355 MbedJSONValue SalesforceInterface::getLastError() { 00356 MbedJSONValue error; 00357 if (strlen(this->m_error_buffer) > 0) parse(error,this->m_error_buffer); 00358 return error; 00359 } 00360 00361 // CREATE: a record in Salesforce.com 00362 char *SalesforceInterface::createRecord(char *object_name,char *json_data,char *output_buffer,int output_buffer_length) { 00363 // parameter check 00364 if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0 && output_buffer != NULL && output_buffer_length > 0) { 00365 // first we have to ensure that we have valid salesforce token 00366 if (this->haveSalesforceToken()) { 00367 // get the sobjects url 00368 ALLOC_BUFFER(url); 00369 char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); 00370 if (sf_url != NULL && strlen(sf_url) > 0) { 00371 // convert to string 00372 string str_url(sf_url); 00373 00374 // add object name that we want to create a record in 00375 str_url += object_name; 00376 00377 // DEBUG 00378 DEBUG("createRecord: URL: %s DATA: %s",str_url.c_str(),json_data); 00379 00380 // now invoke with POST with JSON data type 00381 return this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); 00382 } 00383 } 00384 else { 00385 // dont have a valid salesforce token 00386 LOG_CONSOLE("createRecord: error - no valid salesforce token was found..."); 00387 } 00388 } 00389 else { 00390 // invalid or NULL parameters 00391 LOG_CONSOLE("createRecord: error - invalid or NULL parameters..."); 00392 } 00393 this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; 00394 return NULL; 00395 } 00396 00397 // READ: a specific record in Salesforce.com 00398 char *SalesforceInterface::readRecord(char *object_name,char *record_id,char *record_value,char *output_buffer,int output_buffer_length) { 00399 // parameter check 00400 if (object_name != NULL && strlen(object_name) > 0 && record_id != NULL && strlen(record_id) > 0 && output_buffer != NULL && output_buffer_length > 0) { 00401 // first we have to ensure that we have valid salesforce token 00402 if (this->haveSalesforceToken()) { 00403 // get the sobjects url 00404 ALLOC_BUFFER(url); 00405 char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); 00406 if (sf_url != NULL && strlen(sf_url) > 0) { 00407 // convert to string 00408 string str_url(sf_url); 00409 00410 // add object name that we want to create a record in 00411 str_url += object_name; 00412 00413 // add the record token 00414 str_url += "/"; 00415 str_url += record_id; 00416 00417 // add the record value (if present) 00418 if (record_value != NULL && strlen(record_value) > 0) { 00419 str_url += "/"; 00420 str_url += record_value; 00421 } 00422 00423 // DEBUG 00424 DEBUG("readRecord: URL: %s",str_url.c_str()); 00425 00426 // now invoke with GET with JSON data type 00427 return this->invoke(str_url.c_str(),output_buffer,output_buffer_length); 00428 } 00429 } 00430 else { 00431 // dont have a valid salesforce token 00432 LOG_CONSOLE("readRecord: error - no valid salesforce token was found..."); 00433 } 00434 } 00435 else { 00436 // invalid or NULL parameters 00437 LOG_CONSOLE("readRecord: error - invalid or NULL parameters..."); 00438 } 00439 this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; 00440 return NULL; 00441 } 00442 00443 // UPDATE: a specific record in Salesforce.com 00444 bool SalesforceInterface::updateRecord(char *object_name,char *record_id,char *json_data,char *output_buffer,int output_buffer_length) { 00445 // reset the error buffer 00446 RESET_SML_BUFFER(this->m_error_buffer); 00447 00448 // parameter check 00449 if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0) { 00450 // first we have to ensure that we have valid salesforce token 00451 if (this->haveSalesforceToken()) { 00452 // get the sobjects url 00453 ALLOC_BUFFER(url); 00454 char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); 00455 if (sf_url != NULL && strlen(sf_url) > 0) { 00456 // convert to string 00457 string str_url(sf_url); 00458 00459 // add object name that we want to create a record in 00460 str_url += object_name; 00461 00462 // add the record token 00463 str_url += "/"; 00464 str_url += record_id; 00465 00466 // HTTPClient does not support PATCH, so we have to use POST with a special added parameter 00467 str_url += "?_HttpMethod=PATCH"; 00468 00469 // DEBUG 00470 DEBUG("updateRecord: URL: %s DATA: %s",str_url.c_str(),json_data); 00471 00472 // now invoke with POST with JSON data type 00473 char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); 00474 00475 // DEBUG 00476 DEBUG("updateRecord: http status=%d",this->httpResponseCode()); 00477 00478 // return our status 00479 if (this->httpResponseCodeInRange(200)) return true; 00480 00481 // we are in error - so copy the result if we have one and return false 00482 if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); 00483 return false; 00484 } 00485 } 00486 else { 00487 // dont have a valid salesforce token 00488 LOG_CONSOLE("updateRecord: error - no valid salesforce token was found..."); 00489 } 00490 } 00491 else { 00492 // invalid or NULL parameters 00493 LOG_CONSOLE("updateRecord: error - invalid or NULL parameters..."); 00494 } 00495 this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; 00496 return false; 00497 } 00498 00499 // UPSERT: update/insert a specific External record in Salesforce.com 00500 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) { 00501 // reset the error buffer 00502 RESET_SML_BUFFER(this->m_error_buffer); 00503 00504 // parameter check 00505 if (object_name != NULL && strlen(object_name) > 0 && json_data != NULL && strlen(json_data) > 0) { 00506 // first we have to ensure that we have valid salesforce token 00507 if (this->haveSalesforceToken()) { 00508 // get the sobjects url 00509 ALLOC_BUFFER(url); 00510 char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); 00511 if (sf_url != NULL && strlen(sf_url) > 0) { 00512 // convert to string 00513 string str_url(sf_url); 00514 00515 // add object name that we want to create a record in 00516 str_url += object_name; 00517 00518 // add the external field name token 00519 str_url += "/"; 00520 str_url += external_id_field_name; 00521 00522 // add the external field value token (if not NULL) 00523 if (external_id_field_value != NULL && strlen(external_id_field_value) > 0) { 00524 str_url += "/"; 00525 str_url += external_id_field_value; 00526 } 00527 00528 // HTTPClient does not support PATCH, so we have to use POST with a special added parameter 00529 str_url += "?_HttpMethod=PATCH"; 00530 00531 // DEBUG 00532 DEBUG("upsertRecord: URL: %s DATA: %s",str_url.c_str(),json_data); 00533 00534 // now invoke with POST with JSON data type 00535 char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,output_buffer_length); 00536 00537 // DEBUG 00538 DEBUG("upsertRecord: http status=%d",this->httpResponseCode()); 00539 00540 // return our status 00541 if (this->httpResponseCodeInRange(200)) return true; 00542 00543 // we are in error - so copy the result if we have one and return false 00544 if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); 00545 return false; 00546 } 00547 } 00548 else { 00549 // dont have a valid salesforce token 00550 LOG_CONSOLE("upsertRecord: error - no valid salesforce token was found..."); 00551 } 00552 } 00553 else { 00554 // invalid or NULL parameters 00555 LOG_CONSOLE("upsertRecord: error - invalid or NULL parameters..."); 00556 } 00557 this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; 00558 return false; 00559 } 00560 00561 // DELETE: a specific record in Salesforce.com 00562 bool SalesforceInterface::deleteRecord(char *object_name,char *record_id,char *output_buffer,int output_buffer_length) { 00563 // reset the error buffer 00564 RESET_SML_BUFFER(this->m_error_buffer); 00565 00566 // parameter check 00567 if (object_name != NULL && strlen(object_name) > 0 && record_id != NULL && strlen(record_id) > 0) { 00568 // first we have to ensure that we have valid salesforce token 00569 if (this->haveSalesforceToken()) { 00570 // get the sobjects url 00571 ALLOC_BUFFER(url); 00572 char *sf_url = this->getSalesforceURL("sobjects",url,MAX_BUFFER_LENGTH); 00573 if (sf_url != NULL && strlen(sf_url) > 0) { 00574 // convert to string 00575 string str_url(sf_url); 00576 00577 // add object name that we want to create a record in 00578 str_url += object_name; 00579 00580 // add the record token 00581 str_url += "/"; 00582 str_url += record_id; 00583 00584 // DEBUG 00585 LOG_CONSOLE("deleteRecord: URL: %s",str_url.c_str()); 00586 00587 // now invoke with DELETE 00588 ALLOC_SML_BUFFER(output_buffer); 00589 char *reply = this->invoke(str_url.c_str(),output_buffer,output_buffer_length,DELETE); 00590 00591 // DEBUG 00592 DEBUG("deleteRecord: http status=%d",this->httpResponseCode()); 00593 00594 // return our status 00595 if (this->httpResponseCodeInRange(200)) return true; 00596 00597 // we are in error - so copy the result if we have one and return false 00598 if (reply != NULL && strlen(reply) > 0) strncpy(this->m_error_buffer,reply,this->min(strlen(reply),MAX_SMALL_BUFFER_LENGTH)); 00599 return false; 00600 } 00601 } 00602 else { 00603 // dont have a valid salesforce token 00604 LOG_CONSOLE("deleteRecord: error - no valid salesforce token was found..."); 00605 } 00606 } 00607 else { 00608 // invalid or NULL parameters 00609 LOG_CONSOLE("deleteRecord: error - invalid or NULL parameters..."); 00610 } 00611 this->m_http_response_code = SF_GEN_ERR_HTTP_CODE; 00612 return false; 00613 } 00614 00615 // Salesforce.com Invoke: defaults to GET 00616 char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_length) { 00617 return this->invoke(url,output_buffer,output_buffer_length,GET); 00618 } 00619 00620 // Salesforce.com Invoke: GET or DELETE with simple output 00621 char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_length,HttpVerb verb) { 00622 char *response = NULL; 00623 switch(verb) { 00624 case GET: 00625 case DELETE: 00626 // GET and DELETE only require an output buffer... 00627 response = this->invoke(url,NUM_TYPES,NULL,0,output_buffer,output_buffer_length,verb); 00628 break; 00629 default: 00630 // wrong verb for this call interface... 00631 LOG_CONSOLE("invoke: invalid call: must be either GET or DELETE verb if only output buffer is provided"); 00632 break; 00633 } 00634 return response; 00635 } 00636 00637 // Salesforce.com Invoke: defaults to POST with JSON input data type 00638 char *SalesforceInterface::invoke(const char *url,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_length) { 00639 return this->invoke(url,JSON,input_data,input_data_len,output_buffer,output_buffer_length); 00640 } 00641 00642 // Salesforce.com Invoke: defaults to POST with variable input data type 00643 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) { 00644 return this->invoke(url,input_type,input_data,input_data_len,output_buffer,output_buffer_length,POST); 00645 } 00646 00647 // Salesforce.com Invoke: full fidelity method 00648 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) { 00649 // initialize our invocation status and response code 00650 this->m_http_response_code = -1; 00651 this->m_http_status = HTTP_ERROR; 00652 00653 // param check: make sure that we at least have an output buffer and URL 00654 if (url != NULL && strlen(url) > 0 && output_buffer != NULL && output_buffer_length > 0) { 00655 // proceed only if we have a valid OAUTH Token 00656 if (this->validOauthToken() == true) { 00657 // use OAUTH headers 00658 this->http()->oauthToken(this->oauth()->access_token.c_str()); 00659 00660 // reset the redirection url buffer in case we get a redirect... 00661 RESET_BUFFER(this->m_http_redirection_url); 00662 this->http()->setLocationBuf((char *)this->m_http_redirection_url,MAX_BUFFER_LENGTH); 00663 00664 // create our output/response buffer 00665 HTTPText output(output_buffer,output_buffer_length); 00666 00667 // now make the HTTP(S) request 00668 switch(verb) { 00669 case GET: 00670 DEBUG("invoke (GET) URL: %s...",url); 00671 this->m_http_status = this->http()->get(url,&output); 00672 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00673 break; 00674 case DELETE: 00675 DEBUG("invoke (DEL) URL: %s...",url); 00676 this->m_http_status = this->http()->del(url,&output); 00677 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00678 break; 00679 case POST: 00680 if (input_data != NULL && input_data_len > 0) { 00681 if (input_type == JSON) { 00682 DEBUG("invoke (POST-JSON) URL: %s...",url); 00683 HTTPJson input_json((char *)input_data,(int)input_data_len); 00684 this->m_http_status = this->http()->post(url,input_json,&output); 00685 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00686 } 00687 else { 00688 DEBUG("invoke (POST-TEXT) URL: %s...",url); 00689 HTTPText input_text((char *)input_data,(int)input_data_len); 00690 this->m_http_status = this->http()->post(url,input_text,&output); 00691 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00692 } 00693 } 00694 else { 00695 // no input buffer! 00696 LOG_CONSOLE("invoke: ERROR HTTP(POST) requested but no input data provided... returning NULL"); 00697 } 00698 break; 00699 case PUT: 00700 if (input_data != NULL && input_data_len > 0) { 00701 if (input_type == JSON) { 00702 DEBUG("invoke (PUT-JSON) URL: %s...",url); 00703 HTTPJson input_json((char *)input_data,(int)input_data_len); 00704 this->m_http_status = this->http()->put(url,input_json,&output); 00705 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00706 } 00707 else { 00708 DEBUG("invoke (PUT-TEXT) URL: %s...",url); 00709 HTTPText input_text((char *)input_data,(int)input_data_len); 00710 this->m_http_status = this->http()->put(url,input_text,&output); 00711 this->m_http_response_code = this->http()->getHTTPResponseCode(); 00712 } 00713 } 00714 else { 00715 // no input buffer! 00716 LOG_CONSOLE("invoke: ERROR HTTP(PUT) requested but no input data provided... returning NULL"); 00717 } 00718 break; 00719 default: 00720 // invalid HTTP verb 00721 LOG_CONSOLE("invoke: ERROR invalid HTTP verb (%d) provided... returning NULL",verb); 00722 break; 00723 } 00724 } 00725 else { 00726 // no OAUTH Token 00727 LOG_CONSOLE("unable to acquire OAUTH token for credentials provided. Unable to invoke API..."); 00728 } 00729 } 00730 else { 00731 // no credentials 00732 LOG_CONSOLE("no/incomplete salesforce.com credentials provided. Unable to invoke API..."); 00733 } 00734 00735 // process any return results that we have 00736 if (this->httpStatus() == HTTP_OK || this->httpStatus() == HTTP_REDIRECT) { 00737 // do we have any redirections? 00738 if (this->httpResponseCodeInRange(300) /* REDIRECT */ && strlen(this->m_http_redirection_url) > 0) { 00739 // we have a redirect - so reset the output buffer 00740 memset(output_buffer,0,output_buffer_length); 00741 00742 // we have to make a copy of the redirection URL - this is because the subsequent invoke() will wipe our current one clean 00743 ALLOC_BUFFER(redirect_url); 00744 strcpy(redirect_url,this->m_http_redirection_url); 00745 00746 // repeat with the redirection URL 00747 DEBUG("invoke: redirecting to: %s",redirect_url); 00748 return this->invoke((const char *)redirect_url,input_type,input_data,input_data_len,output_buffer,output_buffer_length,verb); 00749 } 00750 else if (this->httpResponseCodeInRange(300) /* REDIRECT */) { 00751 // error - got a redirect but have no URL 00752 LOG_CONSOLE("invoke error: received redirect but no URL..."); 00753 this->m_http_status = HTTP_ERROR; 00754 } 00755 } 00756 00757 // return the response in the output buffer 00758 if (this->httpStatus() == HTTP_OK || this->httpStatus() == HTTP_REDIRECT) return output_buffer; 00759 else LOG_CONSOLE("invocation failed with HTTP error code=%d status=%d",this->httpResponseCode(),this->httpStatus()); 00760 return NULL; 00761 } 00762 00763 // find the specific URL in our salesforce token 00764 char *SalesforceInterface::getSalesforceURL(char *key,char *url_buffer,int url_buffer_length) { 00765 // due to MbedJSONValue limitations - we have to manually pull out the specific JSON from our salesforce token 00766 int start = (int)strstr(this->m_salesforce_id,SF_URLS_START_TOKEN); 00767 if (start >= 0) { 00768 start += strlen(SF_URLS_START_TOKEN); 00769 int stop = (int)strstr((char *)start,SF_URLS_STOP_TOKEN); 00770 if (stop >= 0 && stop > start) { 00771 // calculate the length 00772 int length = stop - start + 1; 00773 00774 // copy over the "urls" json from the salesforce token 00775 ALLOC_BUFFER(urls); 00776 int start_index = (start - (int)this->m_salesforce_id); 00777 for(int i=0;i<length;++i) urls[i] = this->m_salesforce_id[start_index+i]; 00778 00779 // use MbedJSONValue to parse the "urls" json 00780 MbedJSONValue parsed_urls; 00781 parse(parsed_urls,urls); 00782 00783 // find the appropriate URL and copy it 00784 string target_url = parsed_urls[key].get<std::string>(); 00785 00786 // replace the version of the string with our selected salesforce API version 00787 string sf_version(this->getSalesforceAPIVersion()); 00788 string version_tag(SF_URL_API_VER_TOKEN); 00789 this->replace(target_url,version_tag,sf_version); 00790 00791 // copy the final URL to our putput 00792 memset(url_buffer,0,url_buffer_length); 00793 strcpy(url_buffer,target_url.c_str()); 00794 00795 // return the URL 00796 return url_buffer; 00797 } 00798 } 00799 return NULL; 00800 } 00801 00802 // simple char array replacement (modifies input string!) 00803 void SalesforceInterface::replace(char *str,char orig_char,char new_char) { 00804 int length = strlen(str); 00805 for(int i=0;i<length;++i) if (str[i] == orig_char) str[i] = new_char; 00806 } 00807 00808 // 00809 // substring replacement 00810 // Credit: http://stackoverflow.com/questions/4643512/replace-substring-with-another-substring-c 00811 // 00812 void SalesforceInterface::replace(string& line, string& oldString, string& newString) { 00813 const size_t oldSize = oldString.length(); 00814 00815 // do nothing if line is shorter than the string to find 00816 if( oldSize > line.length() ) return; 00817 00818 const size_t newSize = newString.length(); 00819 for( size_t pos = 0; ; pos += newSize ) { 00820 00821 // Locate the substring to replace 00822 pos = line.find( oldString, pos ); 00823 if( pos == string::npos ) return; 00824 if( oldSize == newSize ) { 00825 00826 // if they're same size, use std::string::replace 00827 line.replace( pos, oldSize, newString ); 00828 } 00829 else { 00830 00831 // if not same size, replace by erasing and inserting 00832 line.erase( pos, oldSize ); 00833 line.insert( pos, newString ); 00834 } 00835 } 00836 } 00837 00838 // validate that a given HTTP result code is in the "n" range 00839 bool SalesforceInterface::httpResponseCodeInRange(int n) { 00840 int http_response = this->httpResponseCode(); 00841 int diff = http_response - n; 00842 if (diff >= 0 && diff < 100) return true; 00843 return false; 00844 } 00845 00846 // min() method 00847 int SalesforceInterface::min(int value1,int value2) { 00848 if (value1 < value2) return value1; 00849 return value2; 00850 } 00851 00852 // look for a specific key value pair in a json message 00853 bool SalesforceInterface::contains(char *json,char *key,char *value) { 00854 bool has_it = false; 00855 00856 if (json != NULL && key != NULL && value != NULL) { 00857 // parse the json and look for a specific <key,value> pair... 00858 MbedJSONValue parsed; 00859 parse(parsed,json); 00860 char *answer = (char *)parsed[key].get<std::string>().c_str(); 00861 if (strcmp(answer,value) == 0) has_it = true; 00862 } 00863 00864 return has_it; 00865 }
Generated on Sun Jul 17 2022 20:47:56 by 1.7.2