Create Wufoo webhook with PUT request in ColdFusion - coldfusion

I'm having troubles with building correct PUT request to the Wufoo.
In all my attempts I see the same error:
404 A WebHook must contain a url parameter.
Here is the version with JSON data type:
<cfset local.action = "forms/#local.formHash#/webhooks.json" />
<cfset local.request = {"url" : local.webHookURL, "handshakeKey" : local.webHookKey} />
<cfset local.request["handshakeKey"] = local.webHookKey />
<cfhttp url="#local.baseURL##local.action#" method="put" username="#local.apiKey#" password="#local.apiPass#">
<cfhttpparam type="header" name="Content-Type" value="application/json; charset=UTF-8" />
<cfhttpparam type="body" value="#SerializeJSON(local.request)#" />
</cfhttp>
Same failure when using file:
<cfset local.action = "forms/#local.formHash#/webhooks.json" />
<cfset local.request = {"url" : local.webHookURL, "handshakeKey" : local.webHookKey} />
<cffile action="write" file="#GetTempDirectory()#webhook.json" output="#SerializeJSON(local.request)#">
<cfhttp url="#local.baseURL##local.action#" method="put" username="#local.apiKey#" password="#local.apiPass#">
<cfhttpparam type="header" name="Content-Type" value="application/json; charset=UTF-8" />
<cfhttpparam type="file" mimetype="application/json" name="json" file="#GetTempDirectory()#webhook.json" />
</cfhttp>
UPDATE:
To make the code working in ACF (my code works in Railo only) use this syntax for request:
<cfset local.request = {} />
<cfset local.request["url"] = local.webHookURL />
<cfset local.request["handshakeKey"] = local.webHookKey />
Both methods should produce same JSON with case-sensitive keys.
Also I've tried the XML data type:
<cfset local.action = "forms/#local.formHash#/webhooks.xml" />
<cfsavecontent variable="putXML">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<WebHookPutRequest>
<url>#XMLFormat(local.webHookURL)#</url>
<handshakeKey>#XMLFormat(local.webHookKey)#</handshakeKey>
</WebHookPutRequest>
</cfoutput>
</cfsavecontent>
<cffile action="write" file="#GetTempDirectory()#webhook.xml" output="#Trim(putXML)#">
<cfhttp url="#local.baseURL##local.action#" method="put" username="#local.apiKey#" password="#local.apiPass#">
<cfhttpparam type="header" name="Content-Type" value="application/xml; charset=UTF-8" />
<cfhttpparam type="body" value="#putXML#" />
</cfhttp>
Here I'm not sure if my XML is correct, though for JSON everything should be fine.
Any ideas what's wrong with my code?
Thanks in advance.

Wufoo asks for the parameters to be "be passed as post parameters to the Web Hook API". Try using the application/x-www-form-urlencoded encoding for the body of the request. In ColdFusion, you can do this with <cfhttpparam type="FormField" />.
<cfhttpparam type="FormField" name="url" value="#local.webHookURL#" />
<cfhttpparam type="FormField" name="handshakeKey" value="#local.webHookKey#" />
However, ColdFusion rejects this technique with PUT methods. You can encode the body yourself using:
<cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded; charset=UTF-8" />
<cfhttpparam type="body" value="url=#UrlEncode(local.webHookURL)#&handshakeKey=#UrlEncode(local.webHookKey)#" />

In ColdFusion, generally, variable names are case-insensitive and uppercase.
<cfset local.request = {
url = local.webHookURL,
handshakeKey = local.webHookKey
} />
This gives you a struct with keys URL and HANDSHAKEKEY.
On the Web, presumably including with the Wufoo REST API, keys are case-sensitive. In this case, Wufoo accepts keys url, handshakeKey, and metadata - in that casing.
In ColdFusion, associative-array notation with struct puts (assignments) lets you keep the precise casing you want.
<cfset local.request = { } />
<cfset local.request["url"] = local.webHookURL />
<cfset local.request["handshakeKey"] = local.webHookKey />
This gives you a struct with keys url and handshakeKey.

Not familiar with this api but should the url, handshakekey, etc be form post params?
The following parameters must be
passed as post parameters to the Web
Hook API
url - this required parameter
represents the URL on your server that
the Web Hook will call when a new
entry is submitted. We do validate the
URL and reject malformed URLs.
handshakeKey - this optional parameter
is described in the Web Hook
integration getting started
documentation.
metadata=true - this optional value
parameter the Web Hook to send along
form/field
The way I read that, it looks like they are asking
for each of the params.
The error is suggesting it can't find the URL param, maybe that is it.

Related

cfschedule: Fire onError function if url cannot be found

We have ColdFusion tasks defined like this:
<cfschedule
action="update"
task="Test"
operation="HTTPRequest"
url="#path.getFileFolder()#tasks/test.cfm"
startDate="#now()#"
startTime="00:00"
interval="daily"
resolveURL="no"
publish="yes"
file="test.txt"
path="#path.getLogFolder#"
eventHandler="tasks.eventHandler"
onException="invokeHandler">
The onError function in the eventHandler looks like this:
<cffunction name="onError" returntype="void">
<cfargument name="context" type="struct" required="false" />
<cfscript>
var slackHookURL = 'urlToOurSlackErrorChannel';
var slackMessage = 'ourSlackMessage';
</cfscript>
<cftry>
<cfhttp url="#slackHookURL#" method="post" result="httpResp" timeout="60">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
<cfhttpparam type="body" value="#slackMessage#" />
</cfhttp>
<cfcatch></cfcatch>
</cftry>
</cffunction>
We had the problem that after a server switch our config file missed a / in the file folder path. So the url referenced in all of our tasks pointed to something like https://ourPagetasks/test.cfm instead of https://ourPage/tasks/test.cfm. The onError function hasn't been fired. We just "accidentally" stumbled upon all our tasks not having been executed ever since.
In the test.txt log file however we found the message "Connection timeout". Shouldn't the onError function warn us if that happens? Or is there any workaround so I can check the text that's about to be written to the log file? The onTaskEnd function of the eventHandler is only allowed to have the parameter context which tells me nothing about what's going to be logged.
I hope I explained my problem somehow understandable. Thanks in advance!
I managed to implement a workaround. In our scheduledTasks.cfm I added the following lines at the end to check if any of the urls are invalid:
<!--- Check if the tasks are defined correctly --->
<cfschedule action="list" mode="server" result="tasks" />
<cfloop query="tasks">
<cfhttp method="head" url="#tasks.URL#" />
<cfif len(cfhttp.errorDetail)>
<cfscript>
slackHookURL = 'urlToOurSlackErrorChannel';
slackMessage = 'ourSlackMessage';
</cfscript>
<cftry>
<cfhttp url="#slackHookURL#" method="post" result="httpResp" timeout="60">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
<cfhttpparam type="body" value="#slackMessage#" />
</cfhttp>
<cfcatch></cfcatch>
</cftry>
</cfif>
</cfloop>

How to Send this API Call to Twilio Flow via CFHTTP

So I'm trying to setup the API call to Twilio Flow using CFHTTP but am having no luck. Keeps returning CFHTTP doesn't exist when I try to view the response.
I've already tried adjusting from formfields to body, setting the charset to utf-8, etc. I was successfully able to send an SMS using the Programmable SMS portion but am having no luck hitting the Flow.
<cfset twilioUsername = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<cfset twilioFlowSid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<cfset twilioPassword = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<cfset twilioFrom = "+1XXXXXXXXXX" />
<cfset twilioTo = "+1XXXXXXXXXX" />
<cfset bodyFields = {
"name" : "Tester",
"cases" : "This Case this Time"
} />
<cfset twilioFlowResource = (
"https://studio.twilio.com/v1/Flows/#twilioFlowSid#/Executions.json"
) />
<cfhttp result="POST" method="POST" charset="utf-8" url="#twilioFlowResource#" username="#twilioUsername#" password="#twilioPassword#">
<cfhttpparam type="formfield" name="From" value="twilioFrom" />
<cfhttpparam type="formfield" name="To" value="twilioTo" />
<cfhttpparam type="formfield" name="Parameters" value="#serializeJSON(bodyFields)#" />
</cfhttp>
All I keep receiving is variable CFHTTP doesn't exist when I try to view the contents of cfhttp.filecontent.
It is because you used cfhttp's "result" attribute which ".. lets you specify an alternate variable in which to receive a result". Meaning CF won't populate the default variable named cfhttp. So in your example, you should be dumping the variable named #POST#. (Though to avoid further confusion, I'd recommend using something else, like "response".)
<cfhttp result="response"
method="POST"
charset="utf-8"
url="#twilioFlowResource#"
username="#twilioUsername#"
password="#twilioPassword#">
... parameters ...
</cfhttp>
<cfdump var="#response#">
Also, perhaps it's just a typo, but ... if that's the actual code you're using, it's missing pound signs around the variables in the <cfhttpparam> declarations. So the code is actually sending the literal string "twilioFrom" instead of the variable value: +1XXXXXXXXXX. These lines:
<cfhttpparam type="formfield" name="From" value="twilioFrom" />
<cfhttpparam type="formfield" name="To" value="twilioTo" />
... should be changed to this:
<cfhttpparam type="formfield" name="From" value="#twilioFrom#" />
<cfhttpparam type="formfield" name="To" value="#twilioTo#" />

get content sent with cfhttp post

I have a cfc that makes a call to a remote service using cfhttp. The service is returning a failure code, which means that my call to the remote service is not formatted properly. Is there any way to capture the content of a cfhttp post I'm sending? I want to capture the raw post data so I can see where my formatting problem is. Here is a sample of what my code looks like:
<cfhttp url="https://www.webservice.com" method="POST" result="httpResponse">
<cfhttpparam type="formField" name="method" value="doSomething">
<cfhttpparam type="formField" name="user" value="myUserName">
<cfhttpparam type="formField" name="password" value="myPassword">
</cfhttp>
I'd like to do something like this:
<cfset result = structNew() />
<cfset result["response"] = httpResponse />
<cfset result["sentContent"] = cfhttp.sentContent />
Is it possible to get the content of a sent cfhttp without looking at the server logs. My server is offsite and getting the logs will be a PITA.
You could create a template to which you would POST to. That template could return the result of GetHttpRequestData() function.

Consuming Salesforce WSDL via Coldfusion Issue

Ive been given a class file from another group of SFDC developers that work in a separate instance that generates a SOAP based WSDL. I have imported that class file and generated the WSDL in our instance. The webservice is very basic, it just returns a set of values (in XML of course) and doesn't require any arguments to retrieve those values.
Just to test that the WSDL is working Ive downloaded the WSDL from our instance to my local PC and put it into SOAPUI and successfully returned the set of values.
NOW ONTO THE ISSUE - CONSUMING VIA THE URL
Im using Coldfusion's CFHTTP, so I have two CFHTTP calls, the first is to the login of our instance which returns a valid session ID. This session ID is used in my second call which is below:
<!--- token from from first cfhttp --->
<cfset variables.access_token_node = xmlSearch(XMLContent, "//*[name()='sessionId']") />
<cfset variables.access_token = variables.access_token_node[1].xmlText>
<cfset variables.wsdl_url2 = "https://cs12.salesforce.com/services/wsdl/class/WS_FAKE_WSDL">
<cfset packet = CreateObject("java", "java.lang.StringBuffer") />
<cfset packet.append('<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://soap.sforce.com/schemas/class/WS_FAKE_WSDL">') />
<cfset packet.append('<soapenv:Header>') />
<cfset packet.append('<ws:SessionHeader>') />
<cfset packet.append('<ws:sessionId>#variables.access_token#</ws:sessionId>') />
<cfset packet.append('</ws:SessionHeader>') />
<cfset packet.append('</soapenv:Header>') />
<cfset packet.append('<soapenv:Body>') />
<cfset packet.append('<ws:DescribesObjectFields/>') />
<cfset packet.append('</soapenv:Body>') />
<cfset packet.append('</soapenv:Envelope>') />
<cfhttp method="post" url="#variables.wsdl_url2#" result="findResponse2">
<cfhttpparam type="HEADER" name="Accept" value="application/soap+xml, application/xml, multipart/related, text/*">
<cfhttpparam type="HEADER" name="ACCEPT-ENCODING" value="application/soap+xml">
<cfhttpparam type="HEADER" name="CONNECTION" value="Keep-Alive">
<cfhttpparam type="HEADER" name="SOAPAction" value="dummy">
<cfhttpparam type="HEADER" name="Content-Type" value="text/xml; charset=utf-8">
<cfhttpparam type="HEADER" name="Must-Understand" value="1">
<cfhttpparam type="Header" name="Content-Length" value="#len(trim(packet.ToString()))#">
<cfhttpparam type="body" value="#packet.ToString()#" encoded="yes">
</cfhttp>
Below is what I am receiving from the WSDL..that code just takes me back to the login screen. With a Session ID I would assume I wouldn't need to log back in, correct?
All advice is appreciated.
You appear to be sending your APi call to the URL of the WSDL file, this is not the correct URL, you should be sending it to the service address that's detailed within the WSDL file, see the soap:address element in the service element at the bottom of the WSDL.

google analytics, hitting it from coldfusion server cfhttp

I have used this:
Generate Google Analytics events (__utm.gif requests) serverside
and this:
http://www.garyrgilbert.com/blog/index.cfm/2008/10/21/Tracking-Digital-Content
to build a cfhttp string so that when a user hits a page it calls google analytics. I'm doing it like this because the pages I'm serving are XML pages and I can't mix javascript with xml.
My problem is that google analytics is ignoring my requests. I have activated my bucket code on a normal html server, so it thinks/knows it exists, but now when i call any of my xml server pages and make the cfhttp request from coldfusion server, it doesn't get registered.
Update:
Following Sergii advice, I have done a dump to find out what the cfhttp is doing (i was previously missing a variable which was causing it to error), i am now getting a http return of 200, though analytics is not applying the request to my account.
Update the 2nd, including code:
<cfset var_utmac='UA-myUA'> <!--- enter the new urchin code --->
<cfset var_utmhn='www.myaddress.co.uk'>
<cfset var_utmn = RandRange(10000000,99999999)>
<cfset var_cookie = RandRange(10000000,99999999)>
<cfset var_random = RandRange(1000000000,2147483647)>
<cfset var_today = now()>
<cfset var_referer = #cgi.HTTP_REFERER#>
<cfset var_uservar = 'jevans'>
<cfset var_utmp= ''>
<cfset apiname = 'listings.getlistings'>
<cfhttp method="get" url="http://www.google-analytics.com/__utm.gif">
<cfhttpparam type="url" name="utmwv" value="1" />
<cfhttpparam type="url" name="utmn" value="#var_utmn#" />
<cfhttpparam type="url" name="utmsr" value="-" />
<cfhttpparam type="url" name="utmsc" value="-" />
<cfhttpparam type="url" name="utmul" value="-" />
<cfhttpparam type="url" name="utmje" value="0" />
<cfhttpparam type="url" name="utmfl" value="-" />
<cfhttpparam type="url" name="utmdt" value="#apiName#" />
<cfhttpparam type="url" name="utmhn" value="#var_utmhn#" />
<cfhttpparam type="url" name="utmr" value="#var_referer#" />
<cfhttpparam type="url" name="utmp" value="#var_utmp#" />
<cfhttpparam type="url" name="utmac" value="#var_utmac#" />
<cfhttpparam type="url" name="utmcc" value="__utma%3D#var_cookie#.#var_random#.#var_today#.#var_today#.#var_today#.2%3B%2B__utmb%3D#var_cookie#%3B%2B__utmc%3D#var_cookie#%3B%2B__utmz%3D#var_cookie#.#var_today#.2.2.utmccn%3D(direct)%7Cutmcsr%3D(direct)%7Cutmcmd%3D(none)%3B%2B__utmv%3D#var_cookie#.#var_uservar#%3B" />
</cfhttp>
any thoughts?
cheers
Looking at your code, I'm guessing that you need to replace &amp's in your code with regular & symbols. You only need to escape the ampersands to validate XML documents and such. If you send them over the URL, then they may not be recognized as separators.
I would actually construct it like so:
<cfhttp method="get" url="http://www.google-analytics.com/__utm.gif">
<cfhttpparam type="url" name="utmwv" value="5.1.2" />
<cfhttpparam type="url" name="utmn" value="#var_utmn#" />
... all your other URL variables
<cfhttp>
This will make your code a little easier to read, as well as make sure that all of your variables are sent over in the property format, without needing to concatenate a huge string.
It looks like several of your parameters should be of different types. You are sending them all as URL parameters. Should, for example, the HTTP_REFERER be sent as type="CGI".
Looking at my own GA HTTP, I see that in my URL string that I have utmr=-
But the request is also sending along a CGI header for Referer: http://12robots.com/
Maybe try adding another param with type="CGI" name="HTTP_REFERER" value="#CGI.HTTP_REFERER#"
You might want to look at how it is being done in this PHP class and see if you can adapt it to your ColdFusion code. It looks like it might be more than a few URl params that need setting. It is likely that you need to better simulate a real browser to make GA think you are a real browser.
http://code.google.com/p/serversidegoogleanalytics/
Pretty sure cfset var_today = now() is wrong. GA has no idea what a ColdFusion date/time object is