Skip to content

Instantly share code, notes, and snippets.

@johndstein
Created November 14, 2017 11:06
Show Gist options
  • Save johndstein/d226f0803d5a800ab2fa3717767b67bb to your computer and use it in GitHub Desktop.
Save johndstein/d226f0803d5a800ab2fa3717767b67bb to your computer and use it in GitHub Desktop.
Luminate Soap Service
/*
Handles all the communication with Luminate Online web service SOAP API.
*/
public class LuminateSoapService {
public static final String FORMAT = 'yyyy-MM-dd\'T\'HH:mm:ssZ';
public class MyException extends Exception {}
public class SoapResponse {
public String requestBody;
public String requestName;
public SObject[] records = new List<SObject>();
SoapResponse(String requestName, String requestBody) {
this.requestName = requestName;
this.requestBody = requestBody;
}
}
public String loginSessionId;
public String syncSessionId;
public Luminate_Connection__c connection;
public Luminate_Sync_Session__c syncSession;
public String pullDonationInsertSoap(Integer page) {
String[] fields = new List<String>();
fields.add('TransactionId');
fields.add('CampaignId');
fields.add('Organization');
fields.add('Payment');
fields.add('Donor');
fields.add('ReceiptNumber');
return pullSoap('Inserts', 'Donation', fields, page);
}
public String pullDonationUpdateSoap(Integer page) {
String[] fields = new List<String>();
fields.add('TransactionId');
fields.add('CampaignId');
fields.add('Organization');
fields.add('Payment');
fields.add('Donor');
fields.add('ReceiptNumber');
return pullSoap('Updates', 'Donation', fields, page);
}
public String pullConstituentInsertSoap(Integer page) {
String[] fields = new List<String>();
fields.add('ConsId');
fields.add('ConsName');
fields.add('HomeAddress');
return pullSoap('Inserts', 'Constituent', fields, page);
}
public String pullConstituentUpdateSoap(Integer page) {
String[] fields = new List<String>();
fields.add('ConsId');
fields.add('ConsName');
fields.add('HomeAddress');
return pullSoap('Updates', 'Constituent', fields, page);
}
public String pullSoap(String insUpDel, String recordType,
String[] fields, Integer page) {
String soap = '';
soap += '<?xml version=\'1.0\' encoding=\'UTF-8\' ?>';
soap += '<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\'>';
soap += ' <soap:Header>';
soap += ' <Session xmlns=\'urn:soap.convio.com\'>';
soap += ' <SessionId>' + this.loginSessionId + '</SessionId>';
soap += ' </Session>';
soap += ' </soap:Header>';
soap += ' <soap:Body>';
soap += ' <GetIncremental' + insUpDel + ' xmlns=\'urn:soap.convio.com\'>';
soap += ' <PartitionId>' + this.connection.Partition_Id__c + '</PartitionId>';
soap += ' <RecordType>' + recordType + '</RecordType>';
soap += ' <Page>' + page + '</Page>';
soap += ' <PageSize>' + this.connection.Page_Size__c + '</PageSize>';
for (String fieldName : fields) {
soap += '<Field>' + fieldName + '</Field>';
}
soap += ' </GetIncremental' + insUpDel + '>';
soap += ' </soap:Body>';
soap += '</soap:Envelope>';
return soap;
}
// <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
// <soap:Body>
// <GetIncrementalInsertsResponse xmlns="urn:soap.convio.com" xmlns:ens="urn:object.soap.convio.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
// <Record xsi:type="ens:Constituent">
// <ens:ConsId>1164881</ens:ConsId>
// <ens:ConsName>
// <ens:FirstName>Cynthia</ens:FirstName>
// <ens:LastName>Osborne</ens:LastName>
// </ens:ConsName>
// <ens:Active>ACTIVE</ens:Active>
// <ens:HomeAddress>
// <ens:Street1 xsi:nil="true" />
// <ens:Street2 xsi:nil="true" />
// <ens:City xsi:nil="true" />
// <ens:State xsi:nil="true" />
// <ens:Zip xsi:nil="true" />
// </ens:HomeAddress>
// </Record>
//
// Parses records out of the soap body and returns them as a string.
public String[] parseRecords(Dom.Document doc) {
Dom.XmlNode[] bodies = doc.getRootElement().getChildElements();
Dom.XmlNode[] responses = bodies[0].getChildElements();
Dom.XmlNode[] records = responses[0].getChildElements();
String[] a = new List<String>();
for (Dom.XmlNode n : records) {
a.add(toXmlString(n));
}
return a;
}
// We assume all nodes EITHER have text OR have more nodes.
// Nothing else.
public static String toXmlString(DOM.XMLNode node) {
String result = '';
if (node.getNodeType() == DOM.XMLNodeType.ELEMENT) {
result += '\n<' + node.getName() + '>';
if (node.getText().trim() != '') {
result += node.getText().trim();
return result + '</' + node.getName() + '>';
} else {
for (Dom.XMLNode child : node.getChildElements()) {
result += toXmlString(child);
}
// Don't output empty elements.
if (result == '\n<' + node.getName() + '>') {
result = '';
} else {
result += '\n</' + node.getName() + '>';
}
return result;
}
}
throw new MyException('Should never reach here.');
}
public SoapResponse[] pullDonationUpdates() {
SoapResponse[] soapResponses = new List<SoapResponse>();
SoapResponse soapResponse;
Integer page = 1;
String[] records;
do {
String requestBody = pullDonationUpdateSoap(page);
soapResponse =
new SoapResponse('GetIncrementalUpdates Donation', requestBody);
soapResponses.add(soapResponse);
Dom.Document responseDoc = doSoapDoc(requestBody);
records = parseRecords(responseDoc);
for (String rec : records) {
soapResponse.records.add(
new Luminate_Record__c(
Sync_Session__c = this.syncSession.Id,
Record__c = rec
));
}
page++;
} while (soapResponse.records.size() == this.connection.Page_Size__c);
return soapResponses;
}
public SoapResponse[] pullDonationInserts() {
SoapResponse[] soapResponses = new List<SoapResponse>();
SoapResponse soapResponse;
Integer page = 1;
String[] records;
do {
String requestBody = pullDonationInsertSoap(page);
soapResponse =
new SoapResponse('GetIncrementalInserts Donation', requestBody);
soapResponses.add(soapResponse);
Dom.Document responseDoc = doSoapDoc(requestBody);
records = parseRecords(responseDoc);
for (String rec : records) {
soapResponse.records.add(
new Luminate_Record__c(
Sync_Session__c = this.syncSession.Id,
Record__c = rec
));
}
page++;
} while (soapResponse.records.size() == this.connection.Page_Size__c);
return soapResponses;
}
public SoapResponse[] pullConstituentInserts() {
SoapResponse[] soapResponses = new List<SoapResponse>();
SoapResponse soapResponse;
Integer page = 1;
String[] records;
do {
String requestBody = pullConstituentInsertSoap(page);
soapResponse =
new SoapResponse('GetIncrementalInserts Constituent', requestBody);
soapResponses.add(soapResponse);
Dom.Document responseDoc = doSoapDoc(requestBody);
records = parseRecords(responseDoc);
for (String rec : records) {
soapResponse.records.add(
new Luminate_Record__c(
Sync_Session__c = this.syncSession.Id,
Record__c = rec
));
}
page++;
} while (soapResponse.records.size() == this.connection.Page_Size__c);
return soapResponses;
}
public SoapResponse[] pullConstituentUpdates() {
SoapResponse[] soapResponses = new List<SoapResponse>();
SoapResponse soapResponse;
Integer page = 1;
String[] records;
do {
String requestBody = pullConstituentUpdateSoap(page);
soapResponse =
new SoapResponse('GetIncrementalUpdates Constituent', requestBody);
soapResponses.add(soapResponse);
Dom.Document responseDoc = doSoapDoc(requestBody);
records = parseRecords(responseDoc);
for (String rec : records) {
soapResponse.records.add(
new Luminate_Record__c(
Sync_Session__c = this.syncSession.Id,
Record__c = rec
));
}
page++;
} while (soapResponse.records.size() == this.connection.Page_Size__c);
return soapResponses;
}
public void endSyncSession() {
String soap = '';
soap += '<?xml version=\'1.0\' encoding=\'UTF-8\' ?>';
soap += '<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\'>';
soap += ' <soap:Header>';
soap += ' <Session xmlns=\'urn:soap.convio.com\'>';
soap += ' <SessionId>' + this.loginSessionId + '</SessionId>';
soap += ' </Session>';
soap += ' </soap:Header>';
soap += ' <soap:Body>';
soap += ' <EndSynchronization xmlns=\'urn:soap.convio.com\'>';
soap += ' <PartitionId>' + this.connection.Partition_Id__c + '</PartitionId>';
soap += ' </EndSynchronization>';
soap += ' </soap:Body>';
soap += '</soap:Envelope>';
// See LuminateSoapMock.END_SYNC_RESPONSE
doSoapString(soap);
}
public Luminate_Sync_Session__c
getLatestSuccessfulSyncSession(String targetSystem) {
Luminate_Sync_Session__c lss = null;
for (Luminate_Sync_Session__c s :
[
select End_Time__c
from Luminate_Sync_Session__c
where Success_Time__c != null
order by End_Time__c desc
]) {
lss = s;
break;
}
if (lss == null) {
Datetime dt = Datetime.now();
dt = Datetime.newInstance(dt.year(), dt.month(), 1);
lss = new Luminate_Sync_Session__c(
End_Time__c = dt
);
}
return lss;
}
public void startSyncSession(String targetSystem) {
Luminate_Sync_Session__c lss = getLatestSuccessfulSyncSession(targetSystem);
Datetime dtNow = Datetime.now();
String startTime = lss.End_Time__c.format(FORMAT);
String endTime = dtNow.format(FORMAT);
String soap = '';
soap += '<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\'>';
soap += ' <soap:Header>';
soap += ' <Session xmlns=\'urn:soap.convio.com\'>';
soap += ' <SessionId>' + this.loginSessionId + '</SessionId>';
soap += ' </Session>';
soap += ' </soap:Header>';
soap += ' <soap:Body>';
soap += ' <StartSynchronization xmlns=\'urn:soap.convio.com\'>';
soap += ' <PartitionId>' + this.connection.Partition_Id__c + '</PartitionId>';
soap += ' <Start>' + startTime + '</Start>';
soap += ' <End>' + endTime + '</End>';
soap += ' </StartSynchronization>';
soap += ' </soap:Body>';
soap += '</soap:Envelope>';
// See LuminateSoapMock.START_SYNC_RESPONSE
String result = doSoapString(soap);
System.debug('RESULT ' + result);
try {
Dom.Document doc = new Dom.Document();
doc.load(result);
this.syncSessionId =
doc
.getRootElement()
.getChildElement('Body', 'http://schemas.xmlsoap.org/soap/envelope/')
.getChildElement('StartSynchronizationResponse', 'urn:soap.convio.com')
.getChildElement('Result', 'urn:soap.convio.com')
.getChildElement('SyncId', 'urn:soap.convio.com')
.getText();
this.syncSession = new Luminate_Sync_Session__c(
Name = this.syncSessionId,
End_Time__c = dtNow,
Start_Time__c = lss.End_Time__c
);
insert this.syncSession;
} catch (Exception e) {
sendErrorEmail('StartSynchronization', e);
throw e;
}
}
public void ensureConnection() {
if (this.connection == null) {
this.connection =
[ select Endpoint__c,
Error_Email_Address__c,
Name,
Partition_Id__c,
Password__c,
Username__c,
Page_Size__c
from Luminate_Connection__c
where Name = 'Prod'][0];
}
}
public void login() {
ensureConnection();
String soap = '';
soap += '<?xml version=\'1.0\' encoding=\'UTF - 8\' ?>';
soap += '<soap:Envelope xmlns:soap=\'http://schemas.xmlsoap.org/soap/envelope/\'>';
soap += ' <soap:Body>';
soap += ' <Login xmlns=\'urn: soap.convio.com\'>';
soap += ' <UserName>' + this.connection.username__c + '</UserName>';
soap += ' <Password>' + this.connection.password__c + '</Password>';
soap += ' </Login>';
soap += ' </soap:Body>';
soap += '</soap:Envelope>';
// See LuminateSoapMock.LOGIN_RESPONSE
try {
this.loginSessionId =
doSoapDoc(soap)
.getRootElement()
.getChildElement('Body', 'http://schemas.xmlsoap.org/soap/envelope/')
.getChildElement('LoginResponse', 'urn:soap.convio.com')
.getChildElement('Result', 'urn:soap.convio.com')
.getChildElement('SessionId', 'urn:soap.convio.com')
.getText();
} catch (Exception e) {
sendErrorEmail('Login', e);
throw e;
}
}
public Dom.Document doSoapDoc(String requestBody) {
return (Dom.Document) doSoapPost(requestBody, true);
}
public String doSoapString(String requestBody) {
return (String) doSoapPost(requestBody, false);
}
public Object doSoapPost(String requestBody, Boolean domDoc) {
HttpRequest req = new HttpRequest();
req.setEndpoint(this.connection.Endpoint__c);
req.setMethod('POST');
req.setBody(requestBody);
HttpResponse res = new Http().send(req);
if (res.getStatusCode() != 200) {
Exception e = new MyException(
'Bad HTTP status code: ' + res.getStatusCode()
+ ' ' + res.getStatus());
sendErrorEmail(e.getMessage(), e);
throw e;
}
if (domDoc) {
return res.getBodyDocument();
} else {
return res.getBody();
}
}
// TODO need to verify this works. Haven't got email from sandbox yet.
public void sendErrorEmail(String action, Exception err) {
ensureConnection();
if (err == null) {
err = new MyException('Fake exception');
}
System.debug(System.LoggingLevel.ERROR, err);
try {
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {this.connection.Error_Email_Address__c};
mail.setToAddresses(toAddresses);
mail.setSenderDisplayName('Luminate Online Sync');
mail.setSubject('Error : ' + action + ' ' + err.getMessage());
mail.setPlainTextBody('Error: ' + action + '\n\n' + err.getStackTraceString());
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
} catch (Exception e) {
System.debug(System.LoggingLevel.ERROR, e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment