How do I accomplish nesting a loop iteration in a variable inside a loop in CFML - coldfusion

I am trying to loops through a json response in CFML, and need to do one call to get the number of pages, then a call for each successive page, then loop through items to get my data. In doing so I have a nested loop that requires a nested variable. So, for example, my initial loop will result in:
#jsonarray.items.1.id#
#jsonarray.items.2.id#
#jsonarray.items.3.id#
#jsonarray.items.4.id#
and so on.
So in my inside loop, I am trying to replace that number by doing another loop like this:
<cfloop from="1" to="50" index="thisBatchItem">
</cfloop>
But then, I would have to nest the index inside my variable, and that of course does not work.
Can someone point to what I am missing here?
Here is the code I am working with, and you will see the obvious places where I have the problem. I put in #jsonarray.items.#thisBatchItem#.id# just to show where I am trying to make this happen. I know it does not work.
<cfhttp method="GET" result="httpResp" url="https://example.com/api?filter[batch.date][gte]=2022-02-01&filter[batch.date][lte]=2022-03-07&page=1&per-page=50" throwonerror="false">
<cfhttpparam type="header" name="Authorization" value="Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX">
<cfhttpparam type="header" name="Content-type" value="application/json">
</cfhttp>
<cfoutput>
<cfloop from="1" to="#pageCount#" index="thisPage">
<cfhttp method="GET" result="httpResp" url="https://example.com/api?filter[batch.date][gte]=2022-02-01&filter[batch.date][lte]=2022-03-07&page=#thisPage#&per-page=50" throwonerror="false">
<cfhttpparam type="header" name="Authorization" value="Bearer XXXXXXXXXXXXXXXXXXXXXXXXXX">
<cfhttpparam type="header" name="Content-type" value="application/json">
</cfhttp>
<cfset jsonarray = deserializeJson(httpResp.fileContent)>
<cfloop from="1" to="50" index="thisBatchItem">
<cfif StructKeyExists(jsonarray,"#jsonarray.items.#thisBatchItem#.id#")>
#jsonarray.items.[#thisBatchItem#].id#<br>
</cfif>
</cfloop>
</cfloop>
</cfoutput>

Since items is an array of structures, it's simpler and cleaner to use an "array" loop.
If some of the structure keys are optional, try using the safe navigation operator ?.. In the example below, it prevents an error from occurring when the code uses a non-existent key name like "lookMaNoErrors"?
<cfset jsonResponse = deserializeJson(httpResp.fileContent)>
<cfloop array="#jsonResponse.items#" index="currentItem">
id #currentItem.id#<br>
lookMaNoErrors #currentItem?.lookMaNoErrors#<br>
</cfloop>
Update:
To answer the original question, you need to use structure notation. Also, instead of hard coding the upper limit of the loop, use the array length instead:
<cfloop from="1" to="#arrayLen(jsonResponse.items)#" index="itemIndex">
id #jsonResponse.items[itemIndex].id#
</cfloop>
demo trycf.com

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>

Creating Combinations using CF List Loops

I'm working on a Coldfusion project and I seem to be stuck. I am a newbie so I hope I don't get too much slack. The purpose of my project is to create a password list by using nested loops. I am to design a template that combines all words in the list "cold, fusion, dynamic" with all words on the list "bert, ernie, oscar" to produce a bulleted list of vaild passwords. This template should process two URL prarameters named List1 and List2. I must use two list loops nested within each other to produce all possible combinations of words. (For example "coldbert", "coldernie", "coldoscar", "fusionbert" and so on..)
This is what I have so far:
<cfinclude template="header.cfm">
<body>
<h2>Loop List</h2>
<cfhttp url="looplist.cfm?List1=cold,fusion,dynamic&List2=bert,ernie,oscar" method="get">
<CFLOOP LIST="#URL.List1#"
INDEX="List1">
<UL><CFOUTPUT>#List1#</CFOUTPUT></UL><br>
</CFLOOP>
<cfinclude template="footer.cfm">
I want to ensure that I'm going in the right direction here. Thanks guys for any assistance.
Unless you're calling a page that doesn't exist on your site, I do not see a need to do a http call. You could just create a function in the template (although I'd prefer it to be in a separate cfc) and call that to get your password combos. Something like ...
<cffunction name="getPasswordCombos" returntype="string">
<cfargument name="list1" type="string" required="true" />
<cfargument name="list2" type="string" required="true" />
<cfset var passwordCombos = "" />
<cfset var i = "" />
<cfset var j = "" />
<!--- your combo generation logic might look something like --->
<cfloop list="#arguments.list1#" index="i">
<cfloop list="#arguments.list2#" index="j">
.....
<!--- set passwordCombos logic here --->
.....
</cfloop>
</cfloop>
<cfreturn passwordCombos />
</cffunction>
Then,
<cfset passwordCombos = getPasswordCombos("cold,fusion,dynamic", "bert,ernie,oscar") />
Then loop over the "passwordCombos"
<ul>
<cfloop list="#passwordCombos#" index="i">
<li>#i#</li>
</cfloop>
</ul>
Also, if you HAVE to user CFHTTP, use cfhttpparam to pass in arguments. It's much cleaner.
<cfhttp result="result" url="looplist.cfm" method="GET">
<cfhttpparam name="list1" type="url" value="cold,fusion,dynamic">
<cfhttpparam name="list2" type="url" value="bert,ernie,oscar">
</cfhttp>

Load ColdFusion values from Form scope when name includes hyphen/dash

We're upgrading to Google's reCaptcha and it adds a field with name "g-recaptcha-response" to the form during submit. We them need to verify the response to Google with code like this:
<cfhttp url="https://www.google.com/recaptcha/api/siteverify" method="post" result="captchaResult">
<cfhttpparam type="formfield" name="secret" value="SECRET">
<cfhttpparam type="formfield" name="response" value="#Form.g-recaptcha-response#">
<cfhttpparam type="formfield" name="remoteip" value="#CGI.REMOTE_ADDR#">
</cfhttp>
However, the #Form.g-recaptcha-response# gives Element G is undefined in FORM.
Is there another way to reference Form scope to allow hyphens?
That will give you an error if the form variable doesn't exist. Try this instead:
<CFSET Form.Response = "">
<CFIF StructKeyExists(Form, "g-recaptcha-response")>
<CFSET Form.Response = form["g-recaptcha-response"]>
</CFIF>
I wrote a reCAPTCHA v2 UDF yesterday. I wanted to support sToken so I could use the same API key on multiple websites.
http://gist.github.com/JamoCA/c4e83ca402bd6175a1d7
Just found a solution:
<cfhttpparam type="formfield" name="response" value="#Form["g-recaptcha-response"]#">

Looping over CFHTTP cookie and issues setting up cookie

I am trying to loop over a cookie coming from cfhttp but it is not displaying correct results.
Below is my code
<cfhttp url="#address#" method="get" throwOnError="Yes" resolveurl="false">
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfloop collection="#cookies#" item="k">
<cfset temp = REReplace(k, ";.*", "")>
<cfset cookieName = listfirst(temp,'=')>
<cfset cookievalue = listlast(temp,'=')>
<cfhttpparam type="cookie" name="#cookieName#" value="#cookievalue#">
</cfloop>
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
Second attempt:
From one call, I am getting cookies and I am putting them in a structure as follows:
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfset cookieStruct=StructNew()>
<cfloop collection="#cookies#" item="key">
<cfset cookieKeyAndValue = REReplace(key, ";.*", "")>
<cfset cookieKey = listfirst(cookieKeyAndValue,'=')>
<cfset cookieValue = listlast(cookieKeyAndValue,'=')>
<cfset StructInsert(cookieStruct,cookieKey,cookieValue)>
</cfloop>
<cfdump var="#cookieStruct#" abort>
<cfhttp url="#addr#" method="get" throwOnError="Yes" resolveurl="false" result="objAddress">
<cfloop collection="#cookieStruct#" item="key">
<cfhttpparam type="cookie" name="#key#" value="#cookieStruct[key]#">
</cfloop>
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
This is giving me an error:
Invalid collection ASPJGASGHSG=KBHFPN; path=/. Must be a valid structure or COM object. Loop error.
CFHTTP GET returns a result into the CFHTTP data structure. Having the loop inside the CFHTTP open/close tag results in trying to loop over something that does not exist yet.
<cfhttp url="#address#" method="get" throwOnError="Yes" resolveurl="false" >
<cfhttpparam type="Header" name="Accept-Encoding" value="deflate;q=0">
<cfhttpparam type="Header" name="TE" value="deflate;q=0">
</cfhttp>
<cfset cookies = cfhttp.responseHeader['Set-Cookie'] />
<cfloop collection="#cookies#" item="k">
<cfset temp = REReplace(k, ";.*", "")>
<cfset cookieName = listfirst(temp,'=')>
<cfset cookievalue = listlast(temp,'=')>
</cfloop>
I'm not sure what you are attempting to do here but if there is more than one cookie you're going to need different code in the loop.

ColdFusion oAuth Authorization header not passing

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>
?