Oh my god. It's full of code!

Posts tagged “coldFusion

While This is Awesome, There Must be a Better Way.

This is half an interesting post about how I solved a fairly complicated problem, and half me looking for a better way to do it. The problem I was solving in theory is fairly simple.

I want to import answers from an external survey software into our Salsforce org.

Pretty simple right? Standard import functionality. Nothing too complex. But now let’s build on this.

1) It should be automated. No manual triggering of the import.
2) It should be imported as it is created. Closer to real time than to ETL.
3) The place the data is coming from has no API. We only have direct ODBC access to it.
4) Since the process must be closer to real time, we should use a ‘push’ style mentality for import. The survey software should push the data into Salesforce as it is created, instead of Salesforce requesting it.
5) The survey software only gives me access to javascript to add functionality. No server side processing of any kind.
6) Solution should be as cloud based as possible. Ideally no code on our servers (we have a cold fusion server we use for a lot of data brokering, but we would like to get rid of it).

Suddenly got harder eh? Right off the bat, point 3 means we are going to need to use some kind of middle-ware (in this case our Coldfusion webserver that can talk to the database). Salesforce has no method to talk directly to a database, as it shouldn’t. Everything is done with API’s these days, nobody directly queries databases anymore. At least they shouldn’t. So my first task was to write an API that Salesforce could access to get it’s data. So I wrote up a simple ColdFusion webservice that queries the survey database, formats the data in a reliable way, and returns a JSON array. While not ideal because it does use our server, at least it’s fairly transparent. It’s just a webservice call that could possibly be replaced in the future if the survey software does release an API in the future.

With the webservice up and running, now I need some Apex code to call out to it, get the data and handle it. So I wrote a nice little Apex method that uses a simple HTTP get with some URL params to invoke my ColdFusion webservice. Now it can get the JSON data. Using the new awesome JSON parser in winter 12 I am able to parse the data into a list of custom objects. Hooray.

So now I need a Salesforce object to store this data right? Actually I am going to want a few. I ended up creating 3 in total. The first object ‘survey’, is just a simple container object with pretty much no fields. The second object, ‘survey entry’ is just an object that will contain all the answer objects for a person in a given survey. It of course has a lookup to the ‘survey’ object, as well as a lookup to a contact, and some other info (when they took the survey, etc). The third object ‘survey answer’ is where the real juicy data is. It has the ID of the question, the text of the question, the persons answer, and a lookup to the survey entry.

So now I modified my Apex class a bit to create the survey answers objects, and relate them to the proper ‘survey entry’ (and create one if one does not exist for this person in this survey yet). Boom, so now all the ‘hard’ work is done. I have a class that can be called that will import the survey data for a person into Salesforce. But wait, how do I actually call this thing? I don’t want this on a scheduler, I want it to get called when new data is available. So I need to make this class itself into a webservice of some kind.

I have a bit of experience with Apex REST so I decided that would be a fitting way to handle this. This class only needs the ID of the survey, and the person for whom it needs to import data. That information is easily included in the URL or in POST fields, so I quickly modified my class to be an Apex REST service. Now it was ready to begin being accessed from the outside world. The question now is, how do I invoke the service itself?

First I used the apigee app console to make sure it was working as required. Apigee handles the oAuth and lets you specify params so testing your Apex REST service couldn’t be easier. Once I had verified that it worked, I needed some method to allow the survey software to invoke it. Problem is of course, if you remember that the survey software only supports JavaScript. JavaScript is still subject to that cross domain security policy BS. Normally you could use the script injection technique to make a callout to a different domain, but I need to set headers and such in the request, as well as making it a post request, so that wasn’t going to fly. On top of that I have would have no idea how to let JavaScript start using oAuth or get a valid session ID. So here is where things get a little murky.

How could I allow a javascript only application invoke my Apex REST service? Looks like I would again have to turn to my ColdFusion middleware box. I wrote another webservice which can invoke the desired Apex method from ColdFusion. You can call Apex REST services using a session ID instead of having to deal with oAuth so I went that route. I already have integration between Salesforce and ColdFusion through use of the awesome CFC library provided at RIA Forge (I actually helped contribute a bit to that). So I just wrote up what basically amounts to be a wrapper. You can invoke it from a simple get request and it will wrap the request with the required headers (authorization, content-length, content-type) and send it off to Salesforce. ColdFusion web services have the awesome feature of being able to be called via a URL, instead of having to use a WSDL or whatever. Come to think of it, they are almost a forerunner for REST, but I digress.

So now I have a URL that when called (with some arguments/params) will invoke my Apex REST service that goes and gets the survey data and imports it. So now I still need to get the survey software to call this URL. Here I use the classic script injection technique to make my cross domain request (because the survey software and my ColdFusion box live on different domains) and it much to my surprise it all worked. If you are curious, the code to do that looks like this.


function loadJSON(url)
{
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = url;
headID.appendChild(newScript);
}
var survey = '12345';
var token = 'mkwcgvixvxskchn';
var contact = '003GASDFADFAFDAS';
newUrl = 'http://XXXXXXXXXXXXX/webservice.cfc?method=importData&survey='+survey+'&token&='+token+'&contact='+contact;
loadJSON(newUrl);

So in the end, this is the process I came up with.

1) User takes online survey from 3rd party site (lets call it survey.com)
2) survey.com invokes javascript which calls the ColdFusion webservice (which includes survey id and person id in the request)
3) ColdFusion receives the request, and ‘wraps’ it with the needed authorization information to make a valid HTTP request.
4) Salesforce custom Apex REST class receives the request (with survey id and person id still included)
5) Salesforce sends request BACK to a different ColdFusion webservice, which requests the actual question/answer data.
6) ColdFusion receives request. Queries survey database and encodes database info as an array of JSON encoded objects.
7) JSON encoded data is returned to the calling Apex, where it is parsed into custom objects and committed to the database.

Seems kind of obtuse eh? Yet I can’t think of any way to make it leaner. I really would like to eliminate the 2nd and 3rd step and just have the survey software invoke the Apex REST directly somehow, or at least make the call totally cloud based. I suppose I could host a visualforce page that does the same thing, and have the JavaScript call that…

So anyway, here you can see an interesting case study of integrating a lot of different crap using some fairly new methods and techniques. I am totally open to suggestions on how to refine this. Right now there are just so many points of failure that it makes me nervous but again it seems to be about the best I can do. Thoughts and feedback welcome in the comments.


Coldfusion describe sObject function

Hey all!

So this time I have another handy function for interacting with Salesforce from Coldfusion. If you don’t know, I’m a huge fan of working with the Salesforce API with Coldfusion using the library by Tom De Manicor http://www.escapekeys.com/blog/index.cfm/.

I’ve been using it so much, and so heavily that here and there I make some small add ons and other neat stuff. This function in particular is one I really like. It allows you to describe an sObject and store all data about the fields in an easy to reference object. So say on your ColdFusion powered website you want to make a picklist with the same values as a picklist in Salesforce, and not have to hard code them, you can describe the sObject, find the fieldname key, and iterate over the values to build your picklist. Of course describe sObject is kind of an expensive call, so I’d recommend caching the results in application or server scope. I generally do this when my application starts and have a manual refresh when needed (how often does the schema of your object change, honestly?). Here is the function.

<cffunction name="CacheSFObject" access="public" hint="GetInfo About an Object from XML and store in an easy to use object">
    <cfargument name="ObjectName" type="string" required="yes">
    <cfset var Object = server.OSF.describeObject(arguments.ObjectName)>
    <cfset var ObjectInfo = Object.RawSoap>
    <cfset var returnStruct = structnew()>
    <cftry>
        <cfloop
                    From="1"
                    To="#arraylen(ObjectInfo.Envelope.Body.describeSobjectResponse.result.fields)#"
                    Index="i">
            <cfset ObjectFieldReference = ObjectInfo.Envelope.Body.describeSobjectResponse.result.fields[i]>
            <cfset returnStruct[ObjectFieldReference.name.xmltext] = structnew()>
            <cfset returnStruct[ObjectFieldReference.name.xmltext].name=ObjectFieldReference.name.xmltext>
            <cfset returnStruct[ObjectFieldReference.name.xmltext].type=ObjectFieldReference.type.xmltext>
            <cfset returnStruct[ObjectFieldReference.name.xmltext].label=ObjectFieldReference.label.xmltext>
            <cfset returnStruct[ObjectFieldReference.name.xmltext].updateable=ObjectFieldReference.updateable.xmltext>
            <cfset returnStruct[ObjectFieldReference.name.xmltext].calculated=ObjectFieldReference.calculated.xmltext>
            <cfif trim(ObjectFieldReference.type.xmltext) EQ "reference">
                <cfset returnStruct[ObjectFieldReference.name.xmltext].refto=ObjectFieldReference.referenceto.xmltext>
            </cfif>
            <cfif trim(ObjectFieldReference.type.xmltext) EQ "picklist" or trim(ObjectFieldReference.type.xmltext) EQ "multipicklist">
                <cfset returnStruct[ObjectFieldReference.name.xmltext].values = arraynew(1)>
                <cfloop from="1" To="#arraylen(ObjectFieldReference.picklistValues)#" index="j">
                    <cfset returnStruct[ObjectFieldReference.name.xmltext].values[j] = ObjectFieldReference.picklistValues[j].label.xmlText>
                </cfloop>
            </cfif>
        </cfloop>
        
        <cfcatch type="any">
        
        </cfcatch>
    </cftry>    
    <cfreturn returnStruct>
</cffunction>

The function does reference the Salesforce component by Tom De Manicor, so you’ll need to download that and have it set up before my function works. You’ll have to change the object reference near the top as well unless you happen to reference it exactly the same way I do. So then to use it, just call

<cfset server.SalesforceFields.Contact = CacheSFObject('Contact')>

Now if you dump out of the contents of server.SalesforceFields.contact you’ll get something that looks like.

Sample of contact describe dump

Sample of contact describe dump

So there ya go, another handy function. Hope someone digs this as much as I do 😉


Coldfusion serializeJson preserve case

Hey this is just a quick tip for any of you ColdFusion programmers who may be interacting with javascript while using JSON. ColdFusion being a non case sensative language has an annoying habit of changing the case of your structure key names when being fed through the serializeJson function, or most other functions that translate ColdFusion datastructures. The official statment is that you just need to change your javascript to expect all uppercase key names, but in some cases you cannot do that. There is a workaround however. Instead of declairing your structure keys like

<cfset myStruct = structnew()>
<cfset myStruct.keyName = "I won't preserve this key's case!">
<!---- this will be all uppercase. Sad face 😦 ----->
<cfoutput>#serializeJson(myStruct)#</cfoutput>

use

<cfset myStruct = structnew()>
<cfset myStruct['keyName'] = "I WILL preserve this key's case!">
<!---- this will preserve case. yay! ----->
<cfoutput>#serializeJson(myStruct)#</cfoutput>

so even after sending that structure through serializeJson the case of key name in the second example is preserved. Hope this helps someone, cause I know it was driving me crazy for a while!