UNPKG

jive-sdk

Version:

Node.js SDK for Jive Software to assist with the development of add-ons

339 lines (301 loc) 12.7 kB
var jive = require("jive-sdk"); var url = require('url'); var util = require('util'); var sfdc_helpers = require('./sfdc_helpers'); var q = require('q'); var metadataCollection = "sfdcActivityMetadata"; var metadataStore = jive.service.persistence(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Public /** * Pulls chatter activity from SFDC since the last time it was pulled, for the opportunity encoded in the * passed in external stream instance object. The 'ticketID' in the instance object is used to * look up the SFDC access token that will be used to query SFDC for the activity. * @param extstreamInstance * @return array of activity objects */ exports.pullActivity = function(extstreamInstance) { return exports.getLastTimePulled(extstreamInstance, 'activity').then(function (lastTimePulled) { var opportunityID = extstreamInstance.config.opportunityID; var ticketID = extstreamInstance.config.ticketID; //First query text posts var queryTextPosts = util.format("SELECT Id, Type, CreatedDate, CreatedBy.Name, Parent.Name, IsDeleted, Body, (SELECT Id, FieldName, OldValue, NewValue" + " FROM FeedTrackedChanges ) FROM OpportunityFeed" + " WHERE ParentId = '%s' AND CreatedDate > %s ORDER BY CreatedDate ASC", opportunityID, getDateString(lastTimePulled)); var uri1 = util.format("/query?q=%s", encodeURIComponent(queryTextPosts)); return sfdc_helpers.querySalesforceV27(ticketID, uri1).then(function (response) { var entity = response['entity']; return convertToActivities(entity, lastTimePulled, extstreamInstance); }); }).catch(function (err) { jive.logger.error('Error querying salesforce', err); }); }; /** * Pulls chatter comments from SFDC since the last time it was pulled, for the opportunity encoded in the * passed in external stream instance object. he 'ticketID' in the instance object is used to * look up the SFDC access token that will be used to query SFDC for the activity. * @param extstreamInstance * @return array of comment objects */ exports.pullComments = function(extstreamInstance) { return exports.getLastTimePulled(extstreamInstance, 'comment').then(function (lastTimePulled) { var opportunityID = extstreamInstance.config.opportunityID; var query = util.format("SELECT Id, CommentType, CreatedDate, CreatedBy.Name, CreatedBy.Email, FeedItemId, IsDeleted, CommentBody" + " FROM FeedComment WHERE ParentId = '%s' AND CreatedDate > %s ORDER BY CreatedDate ASC", opportunityID, getDateString(lastTimePulled)); var uri = util.format("/query?q=%s", encodeURIComponent(query)); var ticketID = extstreamInstance.config.ticketID; return sfdc_helpers.querySalesforceV27(ticketID, uri).then(function (response) { var entity = response['entity']; return convertToComments(entity, lastTimePulled, extstreamInstance); }, function (err) { jive.logger.error('Error converting comments', err); }); }); }; /** * Returns the timestamp of the last time the tile instance was pulled, for a particular pull type (eg. comment, activity) * from SFDC, allowing us to avoid unnecessarily query SFDC for records spanning all time. * @param instance * @param type * @return long timestamp */ exports.getLastTimePulled = function(instance, type) { return getMetadataByInstance(instance).then(function (metadata) { var lastTimePulled = metadata && metadata.lastTimePulled && metadata.lastTimePulled[type]; if (!lastTimePulled) { lastTimePulled = 1; //start date as 1 ms after the epoch, so that instance pulls all existing data for an opportunity return exports.updateLastTimePulled(instance, lastTimePulled, type).thenResolve(lastTimePulled); } return lastTimePulled; }); }; /** * Updates the pull timestamp for the instance and pull type (activity, comment) * @param instance * @param lastTimePulled * @param type * @return instance pull metadata record (JSON) */ exports.updateLastTimePulled = function(instance, lastTimePulled, type) { return getMetadataByInstance(instance).then(function (metadata) { var changed = false; if (!metadata) { metadata = { "instanceID": instance['id'] }; } if (!metadata.lastTimePulled) { metadata.lastTimePulled = {}; } if (!metadata.lastTimePulled[type]) { metadata.lastTimePulled[type] = lastTimePulled; changed = true; } else { if (metadata.lastTimePulled[type] < lastTimePulled) { changed = true; metadata.lastTimePulled[type] = lastTimePulled; } } if (changed) { return metadataStore.save(metadataCollection, instance['id'], metadata); } return metadata; }); }; exports.recordSyncFromJive = function(instance, sfCommentID) { return getMetadataByInstance(instance).then(function (metadata) { if (!metadata) { metadata = {"instanceID": instance['id'], "syncs": []}; } if (!metadata.syncs) { metadata.syncs = []; } var changed = false; if (metadata.syncs.indexOf(sfCommentID) < 0) { metadata.syncs.push(sfCommentID); changed = true; } if (changed) { return metadataStore.save(metadataCollection, instance['id'], metadata); } return metadata; }); }; exports.pullOpportunity = function(tileInstance){ var opportunityID = tileInstance.config.opportunityID; var uri = util.format("/sobjects/Opportunity/%s", opportunityID); var ticketID = tileInstance.config.ticketID; return sfdc_helpers.querySalesforceV27(ticketID, uri).then(function(response) { var opportunity = response['entity']; return convertToListTileData(opportunity); }).catch(function(err){ jive.logger.error('Error querying salesforce', err); }); }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Private function convertToListTileData(opportunity) { return { data: { "title": opportunity['Name'], "contents": [ { "text": util.format("Stage Name: %s", opportunity['StageName']), "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Stage Name" }, { "text": util.format("Type: %s", opportunity['Type']), "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Type" }, { "text": util.format("Probability: %s", opportunity['Probability']) + "%", "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Close Date" }, { "text": util.format("Amount: $%d", opportunity['Amount']), "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Amount" }, { "text": util.format("Expected Revenue: $%d", opportunity['ExpectedRevenue']), "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Expected Revenue" }, { "text": util.format("Close Date: %s", opportunity['CloseDate']), "icon": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "linkDescription": "Close Date" }, { "text": new Date().toString().slice(0, 40) } ], "config": { "listStyle": "contentList" } } }; } function convertToActivities(entity, lastTimePulled, instance) { var records = entity['records']; var activities = records.map(function (record) { var json = getActivityJSON(record); if (!isNaN(json['sfdcCreatedDate'])) { lastTimePulled = Math.max(lastTimePulled, json['sfdcCreatedDate']); } return json; }); return exports.updateLastTimePulled(instance, lastTimePulled, 'activity').thenResolve(activities); } function convertToComments(entity, lastTimePulled, instance) { var records = entity['records']; var comments = []; var promise = q.resolve(null); records.forEach(function (record) { var sfCommentID = record['Id']; promise = promise.thenResolve( wasSynced(instance, sfCommentID).then(function (wasItSynced) { if (wasItSynced) { return; } var json = getCommentJSON(record); if (!isNaN(json['sfdcCreatedDate'])) { lastTimePulled = Math.max(lastTimePulled, json['sfdcCreatedDate']); } comments.push(json); }) ); }); return promise.then(function() { return exports.updateLastTimePulled(instance, lastTimePulled, 'comment').thenResolve(comments); }); } function getActivityJSON(record) { var actor = record.CreatedBy && record.CreatedBy.Name || 'Anonymous'; var oppName = record.Parent && record.Parent.Name || 'Some Opportunity'; var externalID = record.Id; var createdDate = new Date(record.CreatedDate).getTime(); var body = null; if (record.Type == 'TextPost') { body = record.Body; } else if (record.Type == 'TrackedChange') { var changes = record.FeedTrackedChanges && record.FeedTrackedChanges.records; if (changes && changes.length > 0) { var lastChange = changes[changes.length - 1]; body = actor + ' changed ' + lastChange.FieldName.replace('Opportunity\.', '') + ' from ' + lastChange.OldValue + ' to ' + lastChange.NewValue + '.'; } } body = body || 'Empty post'; return { "sfdcCreatedDate": createdDate, "activity": { "action": { "name": "posted", "description": body }, "actor": { "name": actor, "email": "actor@email.com" }, "object": { "type": "website", "url": "http://www.salesforce.com", "image": "http://farm6.staticflickr.com/5106/5678094118_a78e6ff4e7.jpg", "title": oppName, "description": body }, "externalID": externalID } } } function getCommentJSON(record) { var actor = record.CreatedBy && record.CreatedBy.Name || 'Anonymous'; var firstLast = actor.split(' '); var first = firstLast[0], last = ''; if (firstLast.length >= 2) { last = firstLast[1]; } var email = record.CreatedBy && record.CreatedBy.Email || 'anonymous@example.com'; var body = record.CommentBody || 'Empty comment'; var externalID = record.Id; var createdDate = new Date(record.CreatedDate).getTime(); return { "sfdcCreatedDate": createdDate, "author": { "name": { "givenName": first, "familyName": last }, "email": email }, "content": {"type": "text/html", "text": "<p>" + body + "</p>"}, "type": "comment", "externalID": externalID, "externalActivityID": record.FeedItemId //Need this to use /extstreams/{id}/extactivities/{externalActivityID}/comments endpoint } } function getDateString(time) { return new Date(time).toISOString().replace(/Z$/, '+0000'); } function getMetadataByInstance(instance) { return metadataStore.find(metadataCollection, {'instanceID': instance['id']}).then(function (results) { if (results.length <= 0) { return null; } return results[0]; }); } function wasSynced(instance, sfCommentID) { return getMetadataByInstance(instance).then(function (metadata) { return metadata && metadata.syncs && metadata.syncs.indexOf(sfCommentID) >= 0; }); }