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
Dependents: df-2014-salesforce-hrm-k64f
Fork of SalesforceInterface by
SalesforceInterface.h
- Committer:
- ansond
- Date:
- 2015-06-05
- Revision:
- 23:43958e776695
- Parent:
- 22:3363752cd523
File content as of revision 23:43958e776695:
/* 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.
*/
#ifndef _SALESFORCE_INTERFACE_H_
#define _SALESFORCE_INTERFACE_H_
// Logger
#include "Logger.h"
// SSL-based HTTP support
#include "HTTPClient.h"
// JSON parsing support
#include "MbedJSONValue.h"
// convenience macros
#define DEFINE_BUFFER(x) char x[MAX_BUFFER_LENGTH+1]
#define RESET_BUFFER(x) memset(x,0,MAX_BUFFER_LENGTH+1)
#define ALLOC_BUFFER(x) DEFINE_BUFFER(x);RESET_BUFFER(x)
#define DEFINE_SML_BUFFER(x) char x[MAX_SMALL_BUFFER_LENGTH+1]
#define RESET_SML_BUFFER(x) memset(x,0,MAX_SMALL_BUFFER_LENGTH+1)
#define ALLOC_SML_BUFFER(x) DEFINE_SML_BUFFER(x);RESET_SML_BUFFER(x)
// Default salesforce API version (must be in XX.Y format and must be a string)
#define SALESFORCE_API_VERSION_LENGTH 10
#ifndef SALESFORCE_API_VERSION
#define SALESFORCE_API_VERSION "28.0"
#endif
// verbose debugging within SalesforceInterface
#if ENABLE_DEBUG_LOGGING
#define DEBUG(...) { LOG_CONSOLE(__VA_ARGS__); }
#else
#define DEBUG(...) { ; }
#endif
// HTTP Verbs
typedef enum {
GET,
PUT,
POST,
DELETE,
NUM_VERBS
} HttpVerb;
// Supported input data types for PUT and POST (Defined by HTTPClient-SSL/data support...)
typedef enum {
JSON, // ContentType: application/json
PLAIN_TEXT, // ContentType: plain/text
FORM_MAPPED, // ContentType: application/x-www-form-urlencoded
NUM_TYPES
} InputDataTypes;
// OAUTH structure
typedef struct {
bool valid;
string id;
string issued_at;
string token_type;
string instance_url;
string signature;
string access_token;
} OauthToken;
/**
* Salesforce Interface
* SalesforceInterface provides a simple C++ API into the REST-based Salesforce.com APIs
*
* Example Project: http://mbed.org/users/ansond/code/df-2014-salesforce-testharness-k64f/
*
* @code
#include "Definitions.h" // definitions including platform specifics...
#include "Logger.h"
// include salesforce.com credentials
#include "sf_creds.h"
// our Serial port
#include "BufferedSerial.h"
BufferedSerial pc(USBTX, USBRX);
// Ethernet
#include "EthernetInterface.h"
EthernetInterface ethernet;
// HTTP
#include "HTTPClient.h"
HTTPClient http;
// Salesforce.com Interface
#include "SalesforceInterface.h"
// test case persistence
char *object_name = NULL;
char *account_name = NULL;
char *updated_account_name = NULL;
char *external_id_field_name = NULL;
char *external_id_field_value = NULL;
DEFINE_SML_BUFFER(record_id);
// *************** Test Cases ************************
void Test_getSalesforceToken(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nGetting Salesforce Token...");
logger->turnLEDPurple();
// get the salesforce token
char *id = sf->getSalesforceToken();
if (id != NULL && strlen(id) > 0)
logger->log("Saleforce token: %s",id);
else
logger->log("Unable to get Saleforce token");
logger->turnLEDGreen();
}
void Test_query(Logger *logger,SalesforceInterface *sf,char *query_str) {
logger->log("\r\n\r\nExecuting test query: %s",query_str);
logger->turnLEDPurple();
if (query_str != NULL && strlen(query_str) > 0) {
ALLOC_BUFFER(response);
char *answer = sf->query(query_str,response,MAX_BUFFER_LENGTH);
if (answer != NULL) logger->log("query result: %s",answer);
else logger->log("query - NULL result");
}
else {
logger->log("Unable to perform query as we do not have our salesforce token");
}
logger->turnLEDGreen();
}
void Test_create(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting create()");
logger->turnLEDPurple();
// create a new record
MbedJSONValue new_record;
new_record["name"] = account_name;
// DEBUG
logger->log("Create: new record: %s",new_record.serialize().c_str());
// create...
MbedJSONValue response = sf->createRecord(object_name,new_record);
// display the result
char *result = (char *)response.serialize().c_str();
if (result != NULL && strlen(result) > 0 && strcmp(result,"null") != 0) {
// save off the token if we succeeded
logger->log("Create: result: %s",result);
logger->log("Create: http_code=%d",sf->httpResponseCode());
RESET_SML_BUFFER(record_id);
strcpy(record_id,(char *)response["id"].get<std::string>().c_str());
}
else {
// failure
logger->log("Create: FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_read(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting read()");
logger->turnLEDPurple();
// DEBUG
logger->log("Read: reading: %s from %s",record_id,object_name);
// read...
MbedJSONValue response = sf->readRecord(object_name,record_id);
// display the result
char *result = (char *)response.serialize().c_str();
if (result != NULL && strlen(result) > 0 && strcmp(result,"null") != 0) {
// save off the token if we succeeded
logger->log("Read: result: %s",result);
logger->log("Read: http_code=%d",sf->httpResponseCode());
}
else {
// failure
logger->log("Read: FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_create_external_id(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting create(ExternalID)");
logger->turnLEDPurple();
// create a new record
MbedJSONValue new_record;
new_record[external_id_field_name] = external_id_field_value;
// DEBUG
logger->log("create(ExternalID): new record: %s",new_record.serialize().c_str());
// create...
MbedJSONValue response = sf->createRecord(object_name,new_record);
// display the result
char *result = (char *)response.serialize().c_str();
if (result != NULL && strlen(result) > 0 && strcmp(result,"null") != 0) {
// save off the token if we succeeded
logger->log("create(ExternalID): result: %s",result);
logger->log("create(ExternalID): http_code=%d",sf->httpResponseCode());
RESET_SML_BUFFER(record_id);
strcpy(record_id,(char *)response["id"].get<std::string>().c_str());
}
else {
// failure
logger->log("create(ExternalID): FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_read_by_external_id_and_value(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting read(externalID)...");
logger->turnLEDPurple();
// DEBUG
logger->log("read(externalID): reading: %s from %s with value %s",object_name,external_id_field_name,external_id_field_value);
// read (external ID)...
MbedJSONValue response = sf->readRecord(object_name,external_id_field_name,external_id_field_value);
// display the result
char *result = (char *)response.serialize().c_str();
if (result != NULL && strlen(result) > 0 && strcmp(result,"null") != 0) {
// save off the token if we succeeded
logger->log("read(externalID): result: %s",result);
logger->log("read(externalID): http_code=%d",sf->httpResponseCode());
}
else {
// failure
logger->log("read(externalID): FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_update(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting update()");
logger->turnLEDPurple();
// update am existing record - assume "name" is the proper key for the record you wish to update...
MbedJSONValue changed_record;
changed_record["name"] = updated_account_name;
// DEBUG
logger->log("Update: updated record: %s",changed_record.serialize().c_str());
// update...
bool updated = sf->updateRecord(object_name,record_id,changed_record);
// display the result
if (updated) {
// SUCCESS
logger->log("Update: successful! http_code=%d",sf->httpResponseCode());
}
else {
// failure
logger->log("Update: FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_upsert_external_id(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting upsert(ExternalID)");
logger->turnLEDPurple();
// update am existing record - assume "name" is the proper key for the record you wish to update...
MbedJSONValue changed_record;
changed_record["name"] = updated_account_name;
// DEBUG
logger->log("upsert(ExternalID): upserted record: %s",changed_record.serialize().c_str());
// Upsert...
bool updated = sf->upsertRecord(object_name,external_id_field_name,external_id_field_value,changed_record);
// display the result
if (updated) {
// SUCCESS
logger->log("upsert(ExternalID): successful! http_code=%d",sf->httpResponseCode());
}
else {
// failure
logger->log("upsert(ExternalID): FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_delete(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nExecuting delete()");
logger->turnLEDPurple();
// DEBUG
logger->log("Delete: deleting: %s from %s",record_id,object_name);
// delete...
bool deleted = sf->deleteRecord(object_name,record_id);
// display the result
if (deleted) {
// SUCCESS
logger->log("Delete: successful! http_code=%d",sf->httpResponseCode());
}
else {
// failure
logger->log("Delete: FAILED http_code=%d",sf->httpResponseCode());
}
logger->turnLEDGreen();
}
void Test_reset_auth(Logger *logger,SalesforceInterface *sf) {
logger->log("\r\n\r\nForcing API to reset OAUTH token and Salesforce Token...");
logger->turnLEDPurple();
sf->resetSalesforceToken();
logger->turnLEDGreen();
}
// *************** Test Cases ************************
// Main Task...
void mainTask(void const *v) {
// create our object instances
Logger logger(&pc,NULL);
SalesforceInterface *sf = NULL;
// announce
logger.log("\r\n\r\nARM Salesforce Interface Testharness v%s",APP_VERSION);
logger.turnLEDBlue();
// initialize Ethernet
logger.log("Initializing Ethernet...");
ethernet.init();
// get a DHCP address and bring the network interface up
logger.log("Getting IP Address...");
logger.turnLEDOrange();
if (ethernet.connect() == 0) {
// log our IP address (DHCP)
logger.log("IP Address: %s",ethernet.getIPAddress());
// allocate the Salesforce.com interface
logger.log("Allocating Saleforce.com interface...");
sf = new SalesforceInterface(&http,&logger);
// set our Salesforce.com credentials
sf->setCredentials(username,password,client_id,client_secret);
// *************** BEGIN TEST CASES *****************
// configuration for the test cases
object_name = "Account"; // use the account object
account_name = "ARM"; // add this record (name)
updated_account_name = "ARM Holdings"; // update the existing record's name to this
external_id_field_name = "Device__c"; // External ID field name
external_id_field_value = "ABC123"; // External ID field value
RESET_SML_BUFFER(record_id); // buffer for the record's token
// Perform a Create
Test_create(&logger,sf);
// Perform a Read
Test_read(&logger,sf);
// Perform a Query
Test_query(&logger,sf,"SELECT Id,Name FROM Account LIMIT 5");
// Perform an Update
Test_update(&logger,sf);
// Perform a second Read to visually confirm the update above...
Test_read(&logger,sf);
// Perform a Create (External ID)
Test_create_external_id(&logger,sf);
// Perform an Upsert
Test_upsert_external_id(&logger,sf);
// Perform a read of the external ID'ed specified by a given value
Test_read_by_external_id_and_value(&logger,sf);
// force the API to re-acquire the OAUTH token and Salesforce Token
Test_reset_auth(&logger,sf);
// Perform a Read (should re-acquire the OAUTH token and Salesforce Token)
Test_read(&logger,sf);
// Perform a Delete
Test_delete(&logger,sf);
// reset the record token buffer
// RESET_SML_BUFFER(record_id);
// Perform a Read - should error out
Test_read(&logger,sf);
// reset the record token buffer
RESET_SML_BUFFER(record_id);
// *************** BEGIN TEST CASES *****************
// entering main loop
logger.log("All tests complete...\r\nExiting...");
logger.turnLEDBlue();
exit(0);
}
else {
logger.log("No Network... Exiting...");
logger.turnLEDRed();
exit(1);
}
}
// main entry
int main() {
Thread workerTask(mainTask, NULL, osPriorityNormal, STACK_SIZE);
while (true) {
Thread::wait(10*WAIT_TIME_MS);
}
}
* @endcode
*
*/
class SalesforceInterface {
private:
Logger *m_logger;
bool m_logger_internal;
HTTPClient *m_http;
char *m_username;
char *m_password;
char *m_client_id;
char *m_client_secret;
bool m_have_creds;
OauthToken m_oauth_token;
HTTPResult m_http_status;
int m_http_response_code;
char m_http_redirection_url[MAX_BUFFER_LENGTH+1];
char m_salesforce_id[MAX_BUFFER_LENGTH+1];
char m_salesforce_api[SALESFORCE_API_VERSION_LENGTH];
char m_error_buffer[MAX_SMALL_BUFFER_LENGTH+1];
public:
/**
Default constructor
@param http HTTPClient instance
@param pc optional RawSerial for debugging output. If NULL, there will be no debugging output
*/
SalesforceInterface(HTTPClient *http,RawSerial *pc = NULL);
/**
Alternative constructor
@param http HTTPClient instance
@param logger optional Logger instance (See Logger for more info). If NULL, there will be no debugging output
*/
SalesforceInterface(HTTPClient *http,Logger *logger = NULL);
/**
Default destructor
*/
virtual ~SalesforceInterface();
/**
Establish salesforce.com credentials
@param username salesforce.com account user name
@param password salesforce.com account password. The password must be of the form [password][security token]
@param client_id salesforce.com connected application "customer key" value
@param client_secret salesforce.com connected application client secret value
*/
void setCredentials(char *username,char *password,char *client_id,char *client_secret);
/**
Get our salesforce.com token
@param fetch boolean that will direct the interface to fetch the token if not already done (default = true)
@return our salesforce token in JSON format or NULL if in error
*/
char *getSalesforceToken(bool fetch = true);
/**
Force the interface to re-acquire the OAUTH token and salesforce token
*/
void resetSalesforceToken();
/**
Set our salesforce.com API version
@param version integer value (positive)
*/
void setSalesforceAPIVersion(int version);
/**
Set our salesforce.com API version
@param version string value (format "X.Y")
*/
void setSalesforceAPIVersion(char *version);
/**
Get our salesforce.com API version
@return string containing our salesforce.com API version or NULL if in error
*/
char *getSalesforceAPIVersion();
/**
Salesforce.com API SOQL QUERY method to invoke ad-hoc SOQL queries into salesforce.com
@param query_str character string with the SOQL query to invoke
@param output_buffer allocated result buffer to use
@param output_buffer_length allocated result buffer length
@return result of the SOQL query in JSON format or NULL if in error
*/
char *query(char *query_str,char *output_buffer,int output_buffer_length);
/**
Salesforce.com API record creation method to create a new record within a salesforce.com object
@param object_name name of the salesforce.com object to create the record in (i.e. "Account")
@param record MbedJSONValue json structure that the new record will be comprised with
@return MbedJSONValue structure with the results of the creation operation in JSON format
*/
MbedJSONValue createRecord(char *object_name,MbedJSONValue &record);
/**
Salesforce.com API record read method to read a record within a salesforce.com object
@param object_name name of the salesforce.com object to read the record in (i.e. "Account")
@param record_id salesforce.com id of the record instance to read
@param record_value salesforce.com id value of the record instance to read (for external ID usage - default is NULL for non-external IDs)
@return MbedJSONValue structure with the results of the read operation in JSON format
*/
MbedJSONValue readRecord(char *object_name,char *record_id,char *record_value = NULL);
/**
Salesforce.com API record update method to update a record within a salesforce.com object
@param object_name name of the salesforce.com object to update the record in (i.e. "Account")
@param record_id salesforce.com id of the record instance to read
@param record MbedJSONValue instance with updated data for the record
@return true - success, false - failure
*/
bool updateRecord(char *object_name,char *record_id,MbedJSONValue &record);
/**
Salesforce.com API record upsert (update/insert) method to update a record within a salesforce.com object for External IDs
@param object_name name of the salesforce.com External object to upsert the record in (i.e. "FooBar_c")
@param external_id_field_name salesforce.com id of the External record instance to upsert
@param external_id_field_value salesforce.com id value of the External record instance to upsert
@param record MbedJSONValue instance with updated data for the record
@return true - success, false - failure
*/
bool upsertRecord(char *object_name,char *external_id_field_name,char *external_id_field_value,MbedJSONValue &record);
/**
Salesforce.com API record delete method to delete a record within a salesforce.com object
@param object_name name of the salesforce.com object to delete the record in (i.e. "Account")
@param record_id salesforce.com id of the record instance to delete
@return true - success, false - failure
*/
bool deleteRecord(char *object_name,char *record_id);
/**
Salesforce.com API invocation HTTP response code to aid in debugging error conditions
@return http response code
*/
// HTTP Error code access
int httpResponseCode();
/**
Retrieve the last executed update, upsert, or delete response error detail
@return http response code from last update, upsert, or delete operation
*/
MbedJSONValue getLastError();
protected:
// do we have a valid salesforce token and OAUTH token?
bool haveSalesforceToken(bool fetch = true);
// CREATE: a record in Salesforce.com
char *createRecord(char *object_name,char *json_data,char *output_buffer,int output_buffer_length);
// READ: a specific record in Salesforce.com
char *readRecord(char *object_name,char *record_id,char *record_value,char *output_buffer,int output_buffer_length);
// UPDATE: a specific record in Salesforce.com
bool updateRecord(char *object_name,char *record_id,char *json_data,char *output_buffer,int output_buffer_length);
// UPSERT: update/insert a specific External record in Salesforce.com
bool upsertRecord(char *object_name,char *external_id_field_name,char *external_id_field_value,char *json_data,char *output_buffer,int output_buffer_length);
// DELETE: delete a specific record in Salesforce.com
bool deleteRecord(char *object_name,char *record_id,char *output_buffer,int output_buffer_length);
// raw invocation of REST calls into Salesforce.com
char *invoke(const char *url,char *output_buffer,int output_buffer_length); // defaults to GET
char *invoke(const char *url,char *output_buffer,int output_buffer_length,HttpVerb verb); // GET or DELETE with simple output
char *invoke(const char *url,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_length); // defaults to POST with JSON input data type
char *invoke(const char *url,const InputDataTypes input_type,const char *input_data,const int input_data_len,char *output_buffer,int output_buffer_length); // defaults to POST with variable input data type
char *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); // full fidelity method
// get our OAUTH Token
void checkAndGetOauthToken(bool fetch = true);
char *getOauthToken(char *output_buffer,int output_buffer_length);
// convenience accessors
Logger *logger();
HTTPClient *http();
OauthToken *oauth();
HTTPResult httpStatus();
// internal checkers
bool haveCreds();
void resetOauthToken();
void fillOauthToken(char *token);
bool validOauthToken(bool fetch = true);
// get the specified URL from our Salesforce Token
char *getSalesforceURL(char *key,char *url_buffer,int url_buffer_length);
// simple char array replacement (modifies input string!)
void replace(char *str,char orig_char,char new_char);
// needed to replace substrings within std::string
void replace(string& line, string& oldString, string& newString);
// validate that http status is in the "n" range
bool httpResponseCodeInRange(int n);
// min() method
int min(int value1,int value2);
// look for a specific key value pair in a json message
bool contains(char *json,char *key,char *value);
// initialize
void init(HTTPClient *http,Logger *logger,bool logger_internal);
};
#endif // _SALESFORCE_INTERFACE_H_
