More Related Content

Slideshows for you(20)

Similar to Deep Dive into Salesforce Integrations: Mapping Engines(20)


Deep Dive into Salesforce Integrations: Mapping Engines

  1. Deep dive into Salesforce Integrations: Mapping Engines Salesforce World Tour NYC June 18, 2015
  2. Ami Assayag Architect, CRM Science Developer UG Leader, PhillyForce @AmiAssayag Speakers
  3. Safe Harbor Safe harbor statement under the Private Securities Litigation Reform Act of 1995: This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or if any of the assumptions proves incorrect, the results of, inc. could differ materially from the results expressed or implied by the forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of our services. The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of intellectual property and other litigation, risks associated with possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our limited history reselling products, and utilization and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of, inc. is included in our annual report on Form 10-Q for the most recent fiscal quarter ended July 31, 2012. This documents and others containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site. Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are currently available., inc. assumes no obligation and does not intend to update these forward-looking statements.
  4. Agenda • What is a mapping engine? • Architecture overview? • Support infrastructure • Mapping types • Code, code and more code!
  5. What is a Mapping Engine? • For the user – User interface that provide flexibility to how the integration interacts with the org. – Allows pre-configuration and changes without development • Beind the scene – Supported by a code that handles outbound and inbound transactions.
  6. Why Use Mapping Engines for Integrations? • Allows for more generic integrations • Dynamic – builds callout bodies – Interprets incoming payloads • Changes to external service can be handled without additional development
  7. Architecture Overview (Outbound) Salesforce Org External System Mapping Data Callout Handler Inbound Mapping Engine Outbound Mapping Engine Org Data Apex REST Trigger/Job 1 2 3 4 5
  8. Architecture Overview (Inbound) Salesforce Org External System Mapping Data Callout Handler Inbound Mapping Engine Outbound Mapping Engine Org Data Apex REST Trigger/Job 12 3 4
  9. Pillars of Mapping Engine Code • Use object data type instead of specific types • Cast custom and standard objects to sobject • Build soql queries dynamically • Use DMLOptions
  10. Infrastructure Choices • Mapping engines are database driven • Data infrastructure options – Custom objects • Pro: easier to manage relationships • Con: more directly accessible to users – Custom setting • Pro: can be protected, cached • Con: difficult to manage relationships, requires data configuration – Custom Metadata Types • Pro: can include pre-configured data, can be protected, cached • Con : difficult to manage relationships
  11. Mapping Data Structure • Important: The integration terminology is the “Master” terminology because Salesforce mapping can be changed. Object Mapping Object Integration Object Salesforce Object Field Mapping Object Integration Object Integration Field Salesforce Field Mapping Type Inbound Outbound Value Mapping Object Integration Field Integration Value Salesforce Value Inbound Outbound
  12. Simplified Field Mapping Data Structure • Each field mapping record represents the mapping of a single field between the two systems. Field Mapping Object Integration Object Salesforce Object Integration Field Salesforce Field Mapping Type Inbound Outbound Values Example = prospect = Opportunity = person.first_name = Contact__r.Name = Value = true = true
  13. Mapping Types • Regular Value – single value of any acceptable data type • Constant – value that is defined by the mapping, not by the integration • Value Map – sub mapping for value conversion (normally for picklists) • Object – mapping of lookup relationships • Related List – mapping of related list mapping
  14. Integration Trigger // Trigger to handle all callouts for the Opportunity object trigger ContactIntegration on Contact(after insert) { ProcessTrigger.sendInsertedRecords('Person', Trigger.newMap.keySet()); } • Pass to static class – Integration object name (not salesforce name) – Triggered record ids (not actual objects because it is not likely to contain all the information needed.
  15. Prepare the Field Mapping // property to hold field mappings per object private map<string, list<FieldMapping__c>> fieldMappings; public list<FieldMapping__c> getFieldMappings(string obj) { // make sure the variable is instantiated if (fieldMappings == null) fieldMappings = new map<string, list<FieldMapping__c>>(); // check if the object mappings was already fetched if (fieldMappings.get(obj) == null) { // get the mappings for the object fieldMappings.put(obj, [SELECT Name, SalesforceObject__c, SalesforceField__c, IntegrationField__c, MappingType__c, FROM FieldMapping__c WHERE IntegrationObject__c = :obj AND Incoming__c = true //can make this dynamic LIMIT 1000]); } // return the list of mappings for the requested object return fieldMappings.get(obj); }
  16. Re-Query the Triggered Records // prepare a set of strings to hold all the field names (set prevents dups) set<string> fieldsToSelect = new set<string>(); // get the field mapping for the specified object and collect the fields that need to be queried for (FieldMapping__c mapping : getFieldMappings(objectName)) { // exclude related list and constant mapping if (mapping.MappingType__c != 'Related List' && mapping.MappingType__c != ‘Constant') fieldsToSelect.add(mapping.SalesforceField__c); } // start a query string and add the select fields string query = 'SELECT '; for (string fieldToSelect : fieldsToSelect) query += fieldToSelect + ','; // remove the last comma query = query.substring(0, query.length()-1); // add the from and where clause query += ' FROM ' + objectName + ' WHERE Id IN :' + recordIds; // dynamically retrieve records into list of sobject list<sObject> records = database.query(query);
  17. Loop Through Records and Mappings // recieve a list of records and create a json body to call the integration public list<map<string, object>> processMapping(string objectName, list<sObject> records) { // define a list of maps that contain value pairs that can be built // dynamically and serialized. // the list represents the array of sent records // each map represents a single record // the map keyset represent the integration field names // the map values represent the field values in sfdc list<map<string, object>> jsonStructure = new list<map<string, object>>(); object fieldValue; // get the records from the database and loop through the records for (sObject record : records) { // prepare a map to hold the value pairs for the current record map<string, object> outboundRecord = new map<string, object>(); // loop through each mapping record for this object for (FieldMapping__c mapping : getFieldMappings(objectName)) { // assemble value pairs } } return jsonStructure ; }
  18. While Looping – Get the Value (Simple Value) // check if any of the value need to be accessed through a lookup list<string> FieldParts = fieldName.split('.'); // loop through all the associated objects to drill down to the actual referenced object integer part = 0; while (part < FieldParts.size() - 1) { record = record.getSobject(FieldParts[part]); // go to next node if the record is valued if (record == null) break; else part += 1; } // make sure the record is not null (example: if accountId is null, will cause a an exception) if (record != null) { // get the value from the record retValue = record.get(FieldParts[part]); // the last part is the field itself, use a simple get to return the value as a string. // if value is null, don't process further (just return null) if (retValue != null) { // figure out the field type and do any conversion required by the integration } } }
  19. While Looping – Add Value Pair // split the integration field name - may need to add one or more nested objects before adding a value pair. list<string> node = mapping.IntegrationField__c.split('.'); // prepare a map to hold a parent object. start with the outbound record reference map<string, object> currentObject = outboundRecord; // prepare a map to hold a nested object map<string, object> nestedObject; // loop through the integration nodes in the path and place each nested object/value for (integer i = 0; i < node.size(); i++) { if (i == node.size()-1) { // this is the last node in the path (the value), so add it unless a dependent field is null currentObject.put(node[i], fieldValue); } else { // check if the nested object already exists if (currentObject.containsKey(node[i])) nestedObject = (map<string, object>)(currentObject.get(node[i])); else // if the nested object was not found, instantiate one. nestedObject = new map<string, object>(); // add the nested object to the current object currentObject.put(node[i], nestedObject); // make the nested object the next current object currentObject = nestedObject; } } }
  20. Finish the loop and Serialize . . . . // if any field values were added for this record, add it to the // outbound body if (!outboundRecord.isEmpty()) jsonStructure.add(outboundRecord); } // deserialize the list of map so it is ready to be sent. Remove empty objects if (!jsonStructure.isEmpty()) jsonBody = json.serialize(jsonStructure).remove('{}'); // returned the serialized structure return jsonBody;
  21. Ami Assayag Architect, CRM Science User Group Leader, PhillyForce @AmiAssayag Q & A
  22. Thank you
  23. Other Stuff
  24. While Looping – Get the Value (other types) // only process mappings that are not of type related list if (mapping.MappingType__c == 'Constant') { // this is a string value that is stored in the salesforce field fieldValue = mapping.SalesforceField__c; } else if (mapping.MappingType__c == 'Account' || mapping.MappingType__c == 'Contact') // this is a lookup to a related object // Get the Id and call processMapping recursively to get object’s value pairs; } else { // Single Value or Value map // get the field value from the record fieldValue = retrieveValue(record, mapping.SalesforceField__c); // check if the retrieved value needs to be mapped to other values if (mapping.MappingType__c == 'Value Map') // get the mapped value from the custom setting fieldValue = getValueMapping(mapping.Name.toLowerCase(), string.valueof(fieldValue)); }