Skip to content

Instantly share code, notes, and snippets.

@TheRealBenSmith
Last active August 29, 2015 14:07
Show Gist options
  • Select an option

  • Save TheRealBenSmith/d4ff161248d112a50bb0 to your computer and use it in GitHub Desktop.

Select an option

Save TheRealBenSmith/d4ff161248d112a50bb0 to your computer and use it in GitHub Desktop.
Simple Apex Client for HubSpot's Create Contact API Endpoint
You'll want to create a custom setting in Salesforce to store API keys, portal IDs and product__c mappings.
The settings should be named SendToHubSpot (SendToHubSpot__c) and have the following fields:
API Key 1 (API_Key_1__c), text(50)
API Key 2 (API_Key_2__c), text(50)
Portal ID 1 (Portal_ID_1__c), text(10)
Portal ID 2 (Portal_ID_2__c), text(10)
Portal 1 Product Values (Portal_1_Product_Values__c), text(255)
Portal 2 Product Values (Portal_2_Product_Values__c), text(255)
Manage the setting and add one named SendToHubSpot and put in the api keys and portal IDs and matching products (products should be ; delimited)
@isTest
global class HubSpotAPICalloutMock implements HttpCalloutMock {
/**
* A simple HttpCalloutMock for testing the HubSpotAPIClient class
*/
global HTTPResponse respond(HTTPRequest req) {
HTTPResponse resp = new HTTPResponse();
resp.setStatusCode(200);
return resp;
}
}
public class HubSpotAPIClient {
/**
* HubSpotAPIClient is a simple Apex implementation of the Create Contact endpoint of
* HubSpot's Contacts API: http://developers.hubspot.com/docs/methods/contacts/create_contact
*
* Example:
* String body = '{"properties": [{"property": "email","value": "testingapis@hubspot.com"},{"property": "firstname","value": "test"},{"property": "lastname","value": "person"}]}';
* String apikey = 'a valid HubSpot API key';
* try {
* HubSpotAPIClient client = new HubSpotAPIClient(apikey);
* client.postNewContact(body);
* } catch (HubSpotAPIClient.APIException e) {
* ...handle the error...
* }
* Unit testing of this class requires the HubSpotAPICalloutMock class be present as well
*/
public class APIException extends Exception {}
public static String apiKey;
public static String API_DOMAIN = 'https://api.hubapi.com';
public static String ADD_CONTACT_ENDPOINT = '/contacts/v1/contact';
public static String CONTENT_TYPE = 'application/JSON';
public HubSpotAPIClient(String hubspotAPIKey) {
if (String.isBlank(hubspotAPIKey)) {
throw new APIException('HubSpot API Key is required');
}
apiKey = hubspotAPIKey;
}
public String postNewContact(String body) {
if (String.isBlank(body)) {
throw new APIException('Request body is required');
}
Http h = new Http();
HttpRequest request = getPostRequest(body);
try {
return evaluateResponse(sendRequest(h,request));
} catch (APIException e ) {
throw new APIException(e.getMessage());
}
}
private HttpRequest getPostRequest(String body) {
HttpRequest req = new HttpRequest();
req.setEndpoint(getPostURL());
req.setMethod('POST');
req.setHeader('Content-Type',CONTENT_TYPE);
req.setBody(body);
return req;
}
private HttpResponse sendRequest(Http http, HttpRequest req) {
try{
return http.send(req);
} catch (System.Calloutexception e){
throw new APIException(e.getMessage());
}
}
private String evaluateResponse(HttpResponse resp) {
if (resp.getStatusCode() < 400) {
return 'success';
} else if (resp.getBody().contains('Contact already existed')) {
return 'success';
} else {
throw new APIException(resp.getBody());
}
}
private String getPostURL() {
return API_DOMAIN + ADD_CONTACT_ENDPOINT + '?hapikey=' + apiKey;
}
public static testmethod void testPositive() {
Test.setMock(HttpCalloutMock.class, new HubSpotAPICalloutMock());
String key = 'test';
HubSpotAPIClient hc = new HubSpotAPIClient(key);
String goodURL = 'https://api.hubapi.com/contacts/v1/contact?hapikey=test';
//Verify getPostURL
system.assertEquals(goodURL,hc.getPostURL());
//Verify getPostRequest
HttpRequest pr = hc.getPostRequest('test');
system.assertEquals(goodURL, pr.getEndpoint());
system.assertEquals('POST', pr.getMethod());
system.assertEquals('test', pr.getBody());
//Verify evaluateResponse
HttpResponse resp = new HttpResponse();
resp.setStatusCode(200);
system.assertEquals('success',hc.evaluateResponse(resp));
//Verify postNewContact
try {
hc.postNewContact('body');
} catch (APIException e) {
system.assertNotEquals(null, e.getMessage());
}
}
public static testmethod void testNegative() {
Test.setMock(HttpCalloutMock.class, new HubSpotAPICalloutMock());
//Test no api key
try {
HubSpotAPIClient hcerror = new HubSpotAPIClient('');
} catch (APIException e) {
system.assertEquals('HubSpot API Key is required', e.getMessage());
}
HubSpotAPIClient hc = new HubSpotAPIClient('test');
// Test no body
try {
hc.postNewContact('');
} catch (APIException e) {
system.assertEquals('Request body is required', e.getMessage());
}
// Test bad response
HttpResponse resp = new HttpResponse();
resp.setStatusCode(404);
try {
hc.evaluateResponse(resp);
} catch (APIException e) {
system.assertNotEquals(null, e.getMessage());
}
}
}
trigger Lead_Trigger on Lead (after delete, after insert, after undelete, after update, before delete, before insert, before update) {
if(trigger.isInsert){
if(trigger.isBefore){
LeadTrigger.beforeInsert(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}else if(trigger.isAfter){
LeadTrigger.afterInsert(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}
}
if(trigger.isUpdate){
if(trigger.isBefore){
LeadTrigger.beforeUpdate(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}else if(trigger.isAfter){
LeadTrigger.afterUpdate(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}
}
if(trigger.isDelete){
if(trigger.isBefore){
LeadTrigger.beforeDelete(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}else if(trigger.isAfter){
LeadTrigger.afterDelete(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}
}
if(trigger.isUndelete){
if(trigger.isAfter){
LeadTrigger.afterUndelete(trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
}
}
}
public with sharing class LeadTrigger {
public static void beforeInsert(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//Implement business logic
}
public static void beforeUpdate(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//Implement business logic
}
public static void beforeDelete(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//Implement business logic
}
public static void afterInsert(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//If new leads have product__c valued, send them to HubSpot
List<Lead> leadsToSend = new List<Lead>();
for (Lead l : newList) {
if (!String.isBlank(l.product__c)) {
leadsToSend.add(l);
}
}
if (leadsToSend.size()>0) {
sendLeadsToHubSpot(leadsToSend);
}
}
public static void afterUpdate(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//If lead gets changed to have product__c, send them to HubSpot
List<Lead> leadsToSend = new List<Lead>();
for (Lead l : newList) {
if ((!String.isBlank(l.product__c)) && (String.isBlank(oldMap.get(l.Id).product__c))) {
leadsToSend.add(l);
}
}
if (leadsToSend.size()>0) {
sendLeadsToHubSpot(leadsToSend);
}
}
public static void afterDelete(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//Implement business logic
}
public static void afterUnDelete(List<Lead> newList,List<Lead> oldList, Map<Id,Lead > newMap, Map<Id,Lead> oldMap){
//Implement business logic
}
//Helper methods
public static void sendLeadsToHubspot(List<Lead> leads) {
//Get settings
try {
Map<String,String> portalIdsByProduct = getPortalIdsByProduct();
Map<String,String> apiKeysByPortal = getAPIKeysByPortal();
//Get existing HubSpot Contact Data
Map<String,Map<String,Boolean>> existingHubSpotContacts = getExistingHubSpotContacts(leads);
Map<String,List<Lead>> newHubSpotContactsByAPIKey = new Map<String,List<Lead>>();
for (String key : apiKeysByPortal.keySet()) {
newHubSpotContactsByAPIKey.put(apiKeysByPortal.get(key),new List<Lead>());
}
for (Lead l : leads) {
for (String s : getNecessaryLeadPortals(l)) {
if (existingHubSpotContacts.containsKey(l.Id)) {
if (!existingHubSpotContacts.get(l.Id).containsKey(s)) {
newHubSpotContactsByAPIKey.get(apiKeysByPortal.get(s)).add(l);
}
} else {
newHubSpotContactsByAPIKey.get(apiKeysByPortal.get(s)).add(l);
}
}
}
//Send the necessary leads
for (String s : newHubSpotContactsByAPIKey.keySet()) {
if (newHubSpotContactsByAPIKey.get(s).size() > 0) {
SendToHubSpot sth = new SendToHubSpot(newHubSpotContactsByAPIKey.get(s),s);
}
}
} catch (Exception e) {
//Do Nothing. Put in place to allow silent failure if settings to not exist.
}
}
public static Set<String> getNecessaryLeadPortals(Lead l) {
Set<String> necessaryLeadPortals = new Set<String>();
for (String s : l.Product__c.split(';')) {
if (!String.isBlank(s)) {
necessaryLeadPortals.add(getPortalIdsByProduct().get(s));
}
}
return necessaryLeadPortals;
}
public static Map<String,Map<String,Boolean>> getExistingHubSpotContacts(List<Lead> leads) {
Map<String, Map<String,Boolean>> existingHubSpotContacts = new Map<String, Map<String,Boolean>>();
List<String> leadIds = new List<String>();
for (Lead l : leads) {
leadIds.add(l.Id);
}
for (HubSpot_Inc__HubSpot_Intelligence__c hsi : [select id, HubSpot_Inc__Portal_ID__c, HubSpot_Inc__Lead__c from HubSpot_Inc__HubSpot_Intelligence__c where HubSpot_Inc__Lead__c in : leadIds]) {
Map<String,Boolean> leadPortals = new Map<String,Boolean>();
if (existingHubSpotContacts.containsKey(hsi.HubSpot_Inc__Lead__c)) {
leadPortals = existingHubSpotContacts.get(hsi.HubSpot_Inc__Lead__c);
}
leadPortals.put(hsi.HubSpot_Inc__Portal_ID__c,true);
existingHubSpotContacts.put(hsi.HubSpot_Inc__Lead__c, leadPortals);
}
return existingHubSpotContacts;
}
public static Map<String,String> getPortalIdsByProduct() {
Map<String,String> portalIdsByProduct = new Map<String,String>();
SendToHubSpot__c setting = getCustomSettings();
for (String product : setting.portal_1_product_values__c.split(';')) {
if (!String.isBlank(product)) {
portalIdsByProduct.put(product,setting.portal_id_1__c);
}
}
for (String product : setting.portal_2_product_values__c.split(';')) {
if (!String.isBlank(product)) {
portalIdsByProduct.put(product,setting.portal_id_2__c);
}
}
return portalIdsByProduct;
}
public static Map<String,String> getAPIKeysByPortal() {
Map<String,String> apiKeysByPortal = new Map<String,String>();
SendToHubSpot__c setting = getCustomSettings();
apiKeysByPortal.put(setting.portal_id_1__c,setting.api_key_1__c);
apiKeysByPortal.put(setting.portal_id_2__c,setting.api_key_2__c);
return apiKeysByPortal;
}
public static SendToHubSpot__c getCustomSettings() {
return SendToHubSpot__c.getValues('SendToHubSpot');
}
@isTest(SeeAllData=true)
public static void testInsertAndUpdate() {
Test.setMock(HttpCalloutMock.class, new HubSpotAPICalloutMock());
String portal1id = '1234';
String portal2id = '4321';
String portal1api = 'abcd';
String portal2api = 'dcba';
SendToHubSpot__c settings = new SendToHubSpot__c(name='SendToHubSpot',
portal_id_1__c=portal1id,
portal_id_2__c=portal2id,
api_key_1__c=portal1api,
api_key_2__c=portal2api,
portal_1_product_values__c='a;b',
portal_2_product_values__c='c;d;');
try {
insert settings;
} catch (Exception e) {
settings = SendToHubSpot__c.getValues('SendToHubSpot');
}
String portal1product = settings.portal_1_product_values__c.split(';')[0];
String portal2product = settings.portal_2_product_values__c.split(';')[0];
Lead l1 = new Lead(firstname='test1',
lastname='lead',
email='test1@example.com',
company='test co.',
website='www.test.com',
product__c=portal1product);
insert l1;
HubSpot_Inc__HubSpot_Intelligence__c hsi = new HubSpot_Inc__HubSpot_Intelligence__c(HubSpot_Inc__Lead__c = l1.id,
HubSpot_Inc__Portal_Id__c = settings.portal_id_2__c,
HubSpot_Inc__GUID__c = 'randomness');
insert hsi;
l1.visionweb_product__c = portal2product;
update l1;
}
public static testmethod void testDeleteAndUndelete() {
Lead l1 = new Lead(firstname='test1',
lastname='lead',
email='test1@example.com',
company='test co.',
website='www.test.com');
insert l1;
delete l1;
undelete l1;
}
}
public class SendToHubSpot {
/**
* SendToHubSpot is meant to be executed by a trigger on Leads or Contacts to send those records
* to HubSpot as Contacts via the HubSpotAPIClient Class.
*
* A text field must exist on the Lead and Contact object named hubspot_send_status__c as the class will log
* the result of the send to HubSpot's API there.
*
* Fields to be sent to HubSpot can be modified in the convertLeadsToBodies and convertContactsToBodies methods.
*
* This class requires the HubSpotAPIClient class and HubSpotAPICalloutMock class
*
*/
public SendToHubSpot(List<Lead> leads, String apiKey) {
Map<String,String> bodies = convertLeadsToBodies(leads);
for (String id : bodies.keySet()) {
system.debug('Sending lead ' + id);
system.debug('With body ' + bodies.get(id));
system.debug('With apikey ' + apiKey);
sendIt(id, bodies.get(id), 'lead', apiKey);
}
}
public SendToHubSpot(List<Contact> contacts, String apiKey) {
Map<String,String> bodies = convertContactsToBodies(contacts);
for (String id : bodies.keySet()) {
sendIt(id, bodies.get(id), 'contact', apiKey);
}
}
public Map<String,Account> getContactAccounts(List<Contact> contacts) {
Map<String,Account> contactAccounts = new Map<String,Account>();
List<String> accountIds = new List<String>();
for (Contact c : contacts) {
accountIds.add(c.AccountId);
}
List<Account> accounts = [select id, website, name from Account where id in :accountIds];
for (Account a : accounts) {
contactAccounts.put(a.Id, a);
}
return contactAccounts;
}
@future(callout=true)
public static void sendIt(String recordId, String body, String recordType, String apiKey) {
String result = '';
try {
HubSpotAPIClient hc = new HubSpotAPIClient(apiKey);
result = hc.postNewContact(body);
} catch (HubSpotAPIClient.APIException e) {
result = e.getMessage();
}
if (recordType == 'contact') {
Contact c = new Contact(id = recordId,
hubspot_send_status__c = result);
update c;
}
if (recordType == 'lead') {
Lead l = new Lead(id = recordId,
hubspot_send_status__c = result);
update l;
}
}
public Map<String,String> convertLeadsToBodies(List<Lead> records) {
Map<String, String> idsAndBodies = new Map<String,String>();
for (Lead r : records) {
String body = '{"properties":[';
//email
body += '{"property":"email","value":"' + r.email + '"},';
//firstname
if (!String.isBlank(r.firstname)) {
body += '{"property":"firstname","value":"' + r.firstname + '"},';
}
//lastname
if (!String.isBlank(r.lastname)) {
body += '{"property":"lastname","value":"' + r.lastname + '"},';
}
//company
if (!String.isBlank(r.company)) {
body += '{"property":"company","value":"' + r.company + '"},';
}
//phone
if (!String.isBlank(r.phone)) {
body += '{"property":"phone","value":"' + r.phone + '"},';
}
//state
if (!String.isBlank(r.state)) {
body += '{"property":"state","value":"' + r.state + '"},';
}
//city
if (!String.isBlank(r.city)) {
body += '{"property":"city","value":"' + r.city + '"},';
}
//zip
if (!String.isBlank(r.PostalCode)) {
body += '{"property":"zip","value":"' + r.PostalCode+ '"},';
}
//address
if (!String.isBlank(r.street)) {
body += '{"property":"address","value":"' + r.street + '"},';
}
//website
if (!String.isBlank(r.website)) {
body += '{"property":"website","value":"' + r.website + '"},';
}
/*
* To pass other fields to HubSpot, simply the proper if stmt to verify the field is valued
* and append the property and value to body
*
* NON DATE FIELDS
* if (!String.isBlank(r.some_field__c)) {
* body += '{"property":"some_field","value":"' + r.some_field__c + '"},';
* }
*
* DATE FIELDS
* if (r.My_Date_Field__c <> null) {
* body += '{"property":"my_date_Field","value":"' + hubspotifyDate(r.My_Date_Field__c) + '"},';
* }
*/
//lifecyclestage
body += '{"property":"lifecyclestage","value":"lead"}';
body += ']}';
idsAndBodies.put(r.Id, body);
}
return idsAndBodies;
}
public Map<String,String> convertContactsToBodies(List<Contact> records) {
Map<String,Account> contactAccounts = getContactAccounts(records);
Map<String, String> idsAndBodies = new Map<String,String>();
for (Contact r : records) {
String body = '{"properties":[';
//email
body += '{"property":"email","value":"' + r.email + '"},';
//firstname
if (!String.isBlank(r.firstname)) {
body += '{"property":"firstname","value":"' + r.firstname + '"},';
}
//lastname
if (!String.isBlank(r.lastname)) {
body += '{"property":"lastname","value":"' + r.lastname + '"},';
}
//company
if (contactAccounts.containsKey(r.AccountId)) {
body += '{"property":"company","value":"' + contactAccounts.get(r.AccountId).name + '"},';
}
//phone
if (!String.isBlank(r.phone)) {
body += '{"property":"phone","value":"' + r.phone + '"},';
}
//state
if (!String.isBlank(r.mailingstate)) {
body += '{"property":"state","value":"' + r.mailingstate + '"},';
}
//city
if (!String.isBlank(r.mailingcity)) {
body += '{"property":"city","value":"' + r.mailingcity + '"},';
}
//zip
if (!String.isBlank(r.mailingpostalcode)) {
body += '{"property":"zip","value":"' + r.mailingpostalcode + '"},';
}
//address
if (!String.isBlank(r.mailingstreet)) {
body += '{"property":"address","value":"' + r.mailingstreet + '"},';
}
//website
if (contactAccounts.containsKey(r.AccountId)) {
body += '{"property":"website","value":"' + contactAccounts.get(r.AccountId).website + '"},';
}
/*
* To pass other fields to HubSpot, simply the proper if stmt to verify the field is valued
* and append the property and value to body
*
* NON DATE FIELDS
* if (!String.isBlank(r.some_field__c)) {
* body += '{"property":"some_field","value":"' + r.some_field__c + '"},';
* }
*
* DATE FIELDS
* if (r.My_Date_Field__c <> null) {
* body += '{"property":"my_date_Field","value":"' + hubspotifyDate(r.My_Date_Field__c) + '"},';
* }
*/
//lifecyclestage
body += '{"property":"lifecyclestage","value":"lead"}';
body += ']}';
idsAndBodies.put(r.Id, body);
}
return idsAndBodies;
}
public static String hubspotifyDate(Date d) {
Datetime dt = Datetime.newInstance(d.year(), d.month(), d.day(), 0, 0, 0);
return hubspotifyDate(dt);
}
public static String hubspotifyDate(Datetime dt) {
if (dt == null) {
return '';
}
try {
return String.valueOf(dt.getTime());
} catch (Exception e) {
return '';
}
}
static testMethod void leadsTest(){
Test.setMock(HttpCalloutMock.class, new HubSpotAPICalloutMock());
Lead l = new Lead(email='unittest@test.com',
firstname='Unit',
lastname='Test',
company='Test co.',
phone='123-123-1234',
state='MA',
city='test town',
PostalCode='12322',
street='123 my st.',
website='www.test.com');
insert l;
List<Lead> ls = new List<Lead>();
ls.add(l);
Test.startTest();
SendToHubSpot sth = new SendToHubSpot(ls,'test');
Test.stopTest();
l = [select id, hubspot_send_status__c from Lead where id = :l.id];
system.assertNotEquals(null, l.hubspot_send_status__c);
}
static testMethod void contactsTest(){
Test.setMock(HttpCalloutMock.class, new HubSpotAPICalloutMock());
Account a = new Account(name='test');
insert a;
contact c = new Contact(firstname='Unit',
lastname='Test',
email='unittest@test.com',
phone='123-123-1234',
accountId=a.Id,
mailingstreet='123 my st.',
mailingcity='test town',
mailingstate='MA',
mailingpostalcode='12322');
insert c;
List<Contact> cs = new List<Contact>();
cs.add(c);
Test.startTest();
SendToHubSpot sth = new SendToHubSpot(cs,'test');
Test.stopTest();
c = [select id, hubspot_send_status__c from Contact where id = :c.id];
system.assertNotEquals(null, c.hubspot_send_status__c);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment