Interface for invoking Salesforce.com REST calls over SSL with OAUTH authentication. This interface is designed to simplify the interaction between mbed devices and salesforce.com web services.
Dependencies: HTTPClient-SSL MbedJSONValue
Diff: SalesforceInterface.cpp
- Revision:
- 9:a254fcd904be
- Parent:
- 8:47db53cd5884
- Child:
- 10:845ea6d00b65
--- a/SalesforceInterface.cpp Mon Sep 22 04:28:02 2014 +0000 +++ b/SalesforceInterface.cpp Tue Sep 23 05:40:18 2014 +0000 @@ -1,5 +1,7 @@ /* 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, @@ -50,10 +52,9 @@ this->m_http_status = HTTP_OK; this->m_http_response_code = -1; RESET_BUFFER(this->m_http_redirection_url); - RESET_BUFFER(this->m_salesforce_id); memset(this->m_salesforce_api,0,SALESFORCE_API_VERSION_LENGTH); strcpy(this->m_salesforce_api,SALESFORCE_API_VERSION); - this->resetOauthToken(); + this->resetSalesforceID(); } // destructor @@ -96,7 +97,7 @@ // reset our oauth token void SalesforceInterface::resetOauthToken() { - //DEBUG("resetting OAUTH token..."); + DEBUG("resetting OAUTH token..."); this->m_oauth_token.valid = false; this->m_oauth_token.id = ""; this->m_oauth_token.issued_at = ""; @@ -104,6 +105,7 @@ 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 @@ -130,37 +132,64 @@ } // is our OAUTH token valid? - bool SalesforceInterface::validOauthToken() { + bool SalesforceInterface::validOauthToken(bool fetch) { // make sure we have a valid OAUTH Token - this->checkAndGetOauthToken(); - - // TODO: we currently only return fill status. Later we may want to check dates too... + this->checkAndGetOauthToken(fetch); return this->m_oauth_token.valid; } + + // reset our salesforce ID and OAUTH tokens + void SalesforceInterface::resetSalesforceID() { + this->resetOauthToken(); + RESET_BUFFER(this->m_salesforce_id); + } // do we have a valid salesforce.com ID? - bool SalesforceInterface::haveSalesforceID() { + bool SalesforceInterface::haveSalesforceID(bool fetch) { if (this->m_salesforce_id != NULL && strlen(this->m_salesforce_id) > 0) return true; + if (fetch) { + this->logger()->log("No Salesforce ID found... fetching..."); + this->getSalesforceID(); + return this->haveSalesforceID(false); + } return false; } // check and get our OAUTH token - void SalesforceInterface::checkAndGetOauthToken() { + void SalesforceInterface::checkAndGetOauthToken(bool fetch) { DEBUG("checking for valid OAUTH token..."); - if (this->m_oauth_token.valid == false) { - // re-initialize token - this->resetOauthToken(); - - // get our 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); - - // fill - this->fillOauthToken(token); - return; + 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(); + } } - DEBUG("valid OAUTH token found."); - } + + // else report that we dont have a token + else { + this->logger()->log("No OAUTH token found (fetch=false)."); + } + } // // get OAUTH2 Token - taken from here: @@ -192,8 +221,8 @@ this->m_http_status = this->http()->post(SF_OAUTH_TOKEN_URL,input,&output); // check the result and return the token - if (this->m_http_status == HTTP_OK) return output_buffer; - this->logger()->log("oauth invocation failed. URL: %s",SF_OAUTH_TOKEN_URL); + 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 @@ -203,9 +232,9 @@ } // Salesforce.com: Get our ID - char *SalesforceInterface::getSalesforceID() { + char *SalesforceInterface::getSalesforceID(bool fetch) { // proceed only if we have a valid OAUTH Token - if (this->validOauthToken() == true) { + if (this->validOauthToken(fetch) == true) { // pull the ID from salesforce RESET_BUFFER(this->m_salesforce_id); char *id = this->invoke(this->oauth()->id.c_str(),this->m_salesforce_id,MAX_BUFFER_LENGTH); @@ -225,7 +254,7 @@ char *SalesforceInterface::query(char *query_str,char *output_buffer,int output_buffer_length) { // first we have to ensure that we have valid salesforce ID if (this->haveSalesforceID()) { - // get the QUERY URL + // 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) { @@ -259,29 +288,240 @@ return NULL; } + // CREATE: a field in Salesforce.com + MbedJSONValue SalesforceInterface::createField(char *object_name,MbedJSONValue &field) { + ALLOC_BUFFER(output_buffer); + char *reply = this->createField(object_name,(char *)field.serialize().c_str(),output_buffer,MAX_BUFFER_LENGTH); + MbedJSONValue response; + if (reply != NULL && strlen(reply) > 0) parse(response,reply); + return response; + } + + // READ: a specific field in Salesforce.com + MbedJSONValue SalesforceInterface::readField(char *object_name,char *object_id) { + ALLOC_BUFFER(output_buffer); + char *reply = this->readField(object_name,object_id,output_buffer,MAX_BUFFER_LENGTH); + MbedJSONValue response; + if (reply != NULL && strlen(reply) > 0) parse(response,reply); + return response; + } + + // UPDATE: a specific field in Salesforce.com + bool SalesforceInterface::updateField(char *object_name,char *object_id,MbedJSONValue &field) { + return this->updateField(object_name,object_id,(char *)field.serialize().c_str()); + } + + // CREATE: a field in Salesforce.com + char *SalesforceInterface::createField(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 ID + if (this->haveSalesforceID()) { + // 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 field in + str_url += object_name; + + // DEBUG + DEBUG("createField: 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 ID + this->logger()->log("createField: error - no valid salesforced ID was found..."); + } + } + else { + // invalid or NULL parameters + this->logger()->log("createField: error - invalid or NULL parameters..."); + } + return NULL; + } + + // READ: a specific field in Salesforce.com + char *SalesforceInterface::readField(char *object_name,char *object_id,char *output_buffer,int output_buffer_length) { + // parameter check + if (object_name != NULL && strlen(object_name) > 0 && object_id != NULL && strlen(object_id) > 0 && output_buffer != NULL && output_buffer_length > 0) { + // first we have to ensure that we have valid salesforce ID + if (this->haveSalesforceID()) { + // 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 field in + str_url += object_name; + + // add the field ID + str_url += "/"; + str_url += object_id; + + // DEBUG + DEBUG("readField: 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 ID + this->logger()->log("readField: error - no valid salesforced ID was found..."); + } + } + else { + // invalid or NULL parameters + this->logger()->log("readField: error - invalid or NULL parameters..."); + } + return NULL; + } + + // UPDATE: a specific field in Salesforce.com + bool SalesforceInterface::updateField(char *object_name,char *object_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 ID + if (this->haveSalesforceID()) { + // 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 field in + str_url += object_name; + + // add the field ID + str_url += "/"; + str_url += object_id; + + // HTTPClient does not support PATCH, so we have to use POST with a special added parameter + str_url += "?_HttpMethod=PATCH"; + + // DEBUG + DEBUG("updateField: 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("updateField: http status=%d",this->httpResponseCode()); + + // return our status + if (this->httpResponseCode() == 204) return true; + return false; + } + } + else { + // dont have a valid salesforce ID + this->logger()->log("updateField: error - no valid salesforced ID was found..."); + } + } + else { + // invalid or NULL parameters + this->logger()->log("updateField: error - invalid or NULL parameters..."); + } + return false; + } + + // DELETE: a specific field in Salesforce.com + bool SalesforceInterface::deleteField(char *object_name,char *object_id) { + // parameter check + if (object_name != NULL && strlen(object_name) > 0 && object_id != NULL && strlen(object_id) > 0) { + // first we have to ensure that we have valid salesforce ID + if (this->haveSalesforceID()) { + // 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 field in + str_url += object_name; + + // add the field ID + str_url += "/"; + str_url += object_id; + + // DEBUG + DEBUG("deleteField: 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("deleteField: http status=%d",this->httpResponseCode()); + + // return our status + if (this->httpResponseCode() == 204) return true; + return false; + } + } + else { + // dont have a valid salesforce ID + this->logger()->log("deleteField: error - no valid salesforced ID was found..."); + } + } + else { + // invalid or NULL parameters + this->logger()->log("deleteField: error - invalid or NULL parameters..."); + } + return false; + } + // Salesforce.com Invoke: defaults to GET - char *SalesforceInterface::invoke(const char *url,char *output_buffer,int output_buffer_len) { - return this->invoke(url,NUM_TYPES,NULL,0,output_buffer,output_buffer_len,GET); + 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_len) { - return this->invoke(url,JSON,input_data,input_data_len,output_buffer,output_buffer_len); + 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_len) { - return this->invoke(url,input_type,input_data,input_data_len,output_buffer,output_buffer_len,POST); + 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_len,const HttpVerb verb) { + 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_len > 0) { + 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 @@ -292,30 +532,30 @@ this->http()->setLocationBuf((char *)this->m_http_redirection_url,MAX_BUFFER_LENGTH); // create our output/response buffer - HTTPText output(output_buffer,output_buffer_len); + HTTPText output(output_buffer,output_buffer_length); // now make the HTTP(S) request switch(verb) { case GET: - DEBUG("invoking(GET) URL: %s...",url); + 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("invoking(DEL) URL: %s...",url); + 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("invoking(POST-JSON) URL: %s...",url); + 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("invoking(POST-TEXT) URL: %s...",url); + 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(); @@ -329,13 +569,13 @@ case PUT: if (input_data != NULL && input_data_len > 0) { if (input_type == JSON) { - DEBUG("invoking(PUT-JSON) URL: %s...",url); + 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("invoking(PUT-TEXT) URL: %s...",url); + 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(); @@ -367,7 +607,7 @@ // do we have any redirections? if (this->httpResponseCode() == 302 /* REDIRECT */ && strlen(this->m_http_redirection_url) > 0) { // we have a redirect - so reset the output buffer - memset(output_buffer,0,output_buffer_len); + 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); @@ -375,7 +615,7 @@ // repeat with the redirection URL DEBUG("invoke: redirecting to: %s",redirect_url); - return this->invoke((const char *)redirect_url,input_type,input_data,input_data_len,output_buffer,output_buffer_len,verb); + return this->invoke((const char *)redirect_url,input_type,input_data,input_data_len,output_buffer,output_buffer_length,verb); } else if (this->httpResponseCode() == 302 /* REDIRECT */) { // error - got a redirect but have no URL @@ -385,7 +625,7 @@ } // return the response in the output buffer - if (this->httpStatus() == HTTP_OK) return 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; } @@ -463,4 +703,4 @@ line.insert( pos, newString ); } } -} \ No newline at end of file + } \ No newline at end of file