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>
Related
Sending http call to stripe API for getting Auth-token, but following code throws error: The start tag must have a matching end tag. An explicit end tag can be provided by adding </cffunction>. If the body of the tag is empty, you can use the shortcut <cffunction .../>.....
<cffunction name="getAuthToken" access="private" output="false">
<cfhttp url="#variables.server#/v1/tokens" method="post" password="#variables.password#">
<cfargument name="req" required="true" type="any">
<cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="Accept-Language" value="en_US">
<cfhttpparam type="body" value="#req#">
</cfhttp>
<cfset response = deserializeJSON(cfhttp.FileContent)>
<cfreturn response>
</cffunction>
It looks to me like you have that <cfargument ... tag in the wrong place. The compiler is getting confused by that. Move it up under the <cffunction ... tag instead.
Like this:
<cffunction name="getAuthToken" access="private" output="false">
<cfargument name="req" required="true" type="any">
<cfhttp url="#variables.server#/v1/tokens" method="post" password="#variables.password#">
<cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="Accept-Language" value="en_US">
<cfhttpparam type="body" value="#req#">
</cfhttp>
<cfset response = deserializeJSON(cfhttp.FileContent)>
<cfreturn response>
</cffunction>
Unless it's intended to be another <cfhttpparam ... in which case change it to that.
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#" />
*NEW UPDATED FOR BETTER SECOND PART - NOW GETS TO "308 Resume Incomplete", even though file should be just one upload!
I am using the foundation of cfgoogle from Ray Camden. But Google has deprecated the code for document uploads. The new standard is Resumable Media Uploads.
I have this part working (up to and including the "Initiating a resumable upload request") in the above referenced Google document.
Calling Page:
<cfset application.cfc.Google = createObject('component','#path_cf_cfc#Google') />
<cfset application.cfc.GoogleDocs = createObject('component','#path_cf_cfc#GoogleDocs') />
<cfset gtoken = application.cfc.GoogleDocs.authenticate(emailaddress,password)>
<CFSET testdoc = "a\filepath\documentname.doc">
<CFSET FileType = "application/msword">
<CFSET FileTitle = "test_001">
<cfset temp = application.cfc.GoogleDocs.upload_auth("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle)>
<CFSET uploadpath = Listgetat(Listgetat(temp.header,ListContains(temp.header,"https://docs.google.com","#chr(10)#"),"#chr(10)#"),2," ") >
<cfset temp2 = application.cfc.GoogleDocs.upload_file("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle,uploadpath)>
The code works up to and including the cfset temp line (getting the unique upload URI)
Here is the code for upload_auth:
<cffunction name="upload_auth" access="public" returnType="any" hint="I get a uniqu URI from Google API." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="application/msword">
<cfargument name="myTitle" type="string" required="true" hint="name of doc">
<cfset GoogleUrl = "https://docs.google.com/feeds/upload/create-session/default/private/full">
<cfset GoogleVersion = 3>
<cfset FileSize = createObject("java","java.io.File").init(myFile).length()>
<cfhttp url="#GoogleUrl#" method="post" result="diditwork" resolveurl="no">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="0">
<cfhttpparam type="header" name="X-Upload-Content-Type" value="#myType#">
<cfhttpparam type="header" name="X-Upload-Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Slug" value="#myTitle#">
</cfhttp>
<cfreturn diditwork>
</cffunction>
OK - So Far So Good.
But here is where it breaks down:
Running upload_file returns "308 Resume Incomplete" (A lest it's not a 400!) from Google. Arrgh!!
Here is the upload_file -
<cffunction name="upload_file" access="public" returnType="any" hint="I upload the document." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="like application/msword">
<cfargument name="myTitle" type="string" required="true" hint="name of doc">
<cfargument name="myAuthPath" type="string" required="true" hint="call auth">
<cfset FileSize = GetFileInfo(myFile).size >
<CFSET tv.tostartwithzero = FileSize - 1>
<CFFILE action="read" file="#myfile#" variable="FileText">
<cfhttp url="#myAuthPath#" method="put" result="diditwork" resolveurl="no" multipart="yes" charset="utf-8" >
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#variables.GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Content-Range" value="bytes 0-#tv.tostartwithzero#/#FileSize#">
<cfhttpparam type="header" name="Content-Type" value="#myType#">
<cfhttpparam type="body" value="#trim(FileText)#">
</cfhttp>
<cfreturn diditwork>
</cffunction>
So, there we have it - where I am stuck.
I can get the unique URI, but (maybe because it is late at night) I'm brain dead on what I am doing wrong otherwise to complete the file upload.
All help is appreciated.
I strongly recommend taking a different approach here. There are two big issues with the path you're taking:
It appears the code is using the deprecated client login auth mechanism and is using the username/password instead of OAuth.
Using the deprecated documents list API instead of the newer Drive API.
Since you can call java, I'd suggest writing the upload code in plain Java then invoking it from your CF pages as needed.
My authorisation header looks like this (params altered slightly for security and line breaked for easier reading):
<cfset oAuthHeader = 'OAuth oauth_consumer_key="zz3u0Lf9XxkC2KX839r2MS0fDltvLquow3ZMLaOw",
oauth_nonce="9BD4FAE88D1B213F86D908FE183F0501C682EE2F",
oauth_signature="Zy91IhXWGcMxyuAVIlGX%2F3ULTWU%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1337169270",
oauth_version="1.0"'
My cfhttp call looks like this:
<cfhttp url="#oRequestReq.getNormalizedHttpURL()#" method="POST">
<cfhttpparam type="header" name="Authorization" value="#oAuthHeader#">
<cfloop collection="#arguments#" item="key">
<cfif key neq 'fieldnames'>
<cfhttpparam type="formfield" name="#key#" value="#arguments[key]#">
</cfif>
</cfloop>
</cfhttp>
Running <cfdump var="#GetHttpRequestData()#">, I get the following, which shows that my fields are passing through as formfield params OK, but my Authorization header is nowhere to be seen.
![enter image description here][1]
Shouldn't the Authorization header be included in the Headers struct?
[1]: http://i.stack.imgur.com/VbQQO.jpg
How are you getting the oauth_signature? It's not a hard-coded thing in OAuth - it's being generated each time.
I'd suggest using this library http://oauth.riaforge.org/
There are some examples there that should help you get started.
Shouldn't it be...
<cfset oAuthHeader = {
'oauth_consumer_key'="zz3u0Lf9XxkC2KX839r2MS0fDltvLquow3ZMLaOw",
'oauth_nonce'="9BD4FAE88D1B213F86D908FE183F0501C682EE2F",
'oauth_signature'="Zy91IhXWGcMxyuAVIlGX%2F3ULTWU%3D",
'oauth_signature_method'="HMAC-SHA1",
'oauth_timestamp'="1337169270",
'oauth_version'="1.0"
}>
<cfhttp url="#oRequestReq.getNormalizedHttpURL()#" method="POST">
<cfloop collection="#oAuthHeader#" item="key">
<cfhttpparam type="header" name="#key#" value="#oAuthHeader[key]#">
</cfloop>
<cfloop collection="#arguments#" item="key">
<cfif key neq 'fieldnames'>
<cfhttpparam type="formfield" name="#key#" value="#arguments[key]#">
</cfif>
</cfloop>
...
</cfloop>
?
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.