Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: HTTPClient-SSL MbedJSONValue
SalesforceInterface.cpp
- Committer:
- ansond
- Date:
- 2014-09-23
- Revision:
- 17:6c774354b599
- Parent:
- 16:3d160f224084
- Child:
- 18:7dc9b949bbc3
File content as of revision 17:6c774354b599:
/* 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"
// 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);
memset(this->m_salesforce_api,0,SALESFORCE_API_VERSION_LENGTH);
strcpy(this->m_salesforce_api,SALESFORCE_API_VERSION);
this->resetSalesforceToken();
}
// 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; }
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;
}
DEBUG("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) {
this->logger()->log("No Salesforce Token found... fetching...");
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)
this->logger()->log("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 {
this->logger()->log("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;
this->logger()->log("acquire oauth FAILED. URL: %s http_code=%d status=%d",SF_OAUTH_TOKEN_URL,this->httpResponseCode(),this->httpStatus());
}
else {
// no credentials
this->logger()->log("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) this->logger()->log("Unable to get Salesforce Token: status=%d httpCode=%d",this->httpStatus(),this->httpResponseCode());
return id;
}
else {
// unable to get token - no OAUTH token
this->logger()->log("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...
this->logger()->log("query: error - unable to find query URL in salesforce token...");
}
}
else {
// dont have a valid salesforce token
this->logger()->log("query: error - no valid salesforced 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) {
return this->updateRecord(object_name,record_id,(char *)record.serialize().c_str());
}
// 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) {
return this->upsertRecord(object_name,external_id_field_name,external_id_field_value,(char *)record.serialize().c_str());
}
// 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
this->logger()->log("createRecord: error - no valid salesforced token was found...");
}
}
else {
// invalid or NULL parameters
this->logger()->log("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
this->logger()->log("readRecord: error - no valid salesforced token was found...");
}
}
else {
// invalid or NULL parameters
this->logger()->log("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) {
// 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
ALLOC_SML_BUFFER(output_buffer);
char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,MAX_SMALL_BUFFER_LENGTH);
// DEBUG
DEBUG("updateRecord: http status=%d",this->httpResponseCode());
// return our status
if (this->httpResponseCodeInRange(200)) return true;
return false;
}
}
else {
// dont have a valid salesforce token
this->logger()->log("updateRecord: error - no valid salesforced token was found...");
}
}
else {
// invalid or NULL parameters
this->logger()->log("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) {
// 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
ALLOC_SML_BUFFER(output_buffer);
char *reply = this->invoke(str_url.c_str(),json_data,strlen(json_data)+1,output_buffer,MAX_SMALL_BUFFER_LENGTH);
// DEBUG
DEBUG("upsertRecord: http status=%d",this->httpResponseCode());
// return our status
if (this->httpResponseCodeInRange(200)) return true;
return false;
}
}
else {
// dont have a valid salesforce token
this->logger()->log("upsertRecord: error - no valid salesforced token was found...");
}
}
else {
// invalid or NULL parameters
this->logger()->log("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) {
// 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
DEBUG("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,MAX_SMALL_BUFFER_LENGTH,DELETE);
// DEBUG
DEBUG("deleteRecord: http status=%d",this->httpResponseCode());
// return our status
if (this->httpResponseCodeInRange(200)) return true;
return false;
}
}
else {
// dont have a valid salesforce token
this->logger()->log("deleteRecord: error - no valid salesforced token was found...");
}
}
else {
// invalid or NULL parameters
this->logger()->log("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...
this->logger()->log("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!
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("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!
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->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
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 || this->httpStatus() == HTTP_REDIRECT) return output_buffer;
else this->logger()->log("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;
}