Nitrogen get content of table - nitrogen

So, I have a nitrogen page, index.erl, which contains codes like this:
body() ->
[#table{
id = mytable,
rows=[
#tablerow{
cells=[#tableheader{text="column a"},
#tableheader{text="column b"},
#tableheader{text="column c"},
#tableheader{text="column d"}]
},
#custom_row{ %% just a wrapper around #tablerow
column_a = "a",
column_b = "b",
column_c = "c",
column_d = "d"
}
%% ... more #custom_rows omitted
]
},
#button{text="submit", postback=store_table}
].
event(store_table) ->
TableData = something_like_querySelector(mytable),
insert_into_database(TableData).
How do I get the content of mytable, does nitrogen has something like querySelector?

There isn't something as nice and clean as querySelector, but it is possible to retrieve the contents of an arbitrary DOM element though use of Nitrogen's #api{} action.
Using your code above, we could do the following:
body() ->
wf:wire(#api{name=send_table_contents, tag=some_tag}),
[#table{
id = mytable,
rows=[
#tablerow{
cells=[#tableheader{text="column a"},
#tableheader{text="column b"},
#tableheader{text="column c"},
#tableheader{text="column d"}]
},
#custom_row{ %% just a wrapper around #tablerow
column_a = "a",
column_b = "b",
column_c = "c",
column_d = "d"
}
%% ... more #custom_rows omitted
]
},
#button{text="submit", click="page.send_table_contents(objs('mytable').html())"}
].
api_event(send_table_contents, some_tag, [TableHTML]) ->
insert_into_database(TableHTML).
It's not as clean as being able to request the contents like you can with wf:q, but it does get the job done.
The quick explanation here is that the #api{} action creates a new function on the page called page.api_name_attribute (so if you look above, you'll see the name attribute was send_table_contents and the javascript function called in #button.click was also send_table_contents. The contents are then captured in an api_event(NameOfAPI, Tag, ListOfArgs) postback function.
That said, I've added this feature to the to-do list because it seems like a useful thing.

Related

Dynamic CSV with Header only once

I have a request to parse a JSON payload and then create columns dynamically based on a condition.
There should be a Groups column with header. For any additional groups the employee is in, they will be in a column with no header.
If the member is in one group it makes sense I can do something like the following:
%dw 2.0
output application/csv separator=","
var employeesPayload = payload
---
employeesPayload filter ($.workEmail != '' and $.companyEmploymentType.isContractor == false) map (employee) -> {
"Employee ID": employee.employeeNumber,
"Name": employee.preferredFirstName default employee.firstName ++ ' ' ++ if (employee.preferredLastName == '' or employee.preferredLastName == null) employee.lastName else employee.preferredLastName,
"Email": employee.workEmail,
"Groups": employee.workState
}
i.e the table should look similar to the following:
But, how do I add additional columns without headers?
i.e if I want to add a user like Tito (row 9) in the screenshot, how can I build this dynamically?
You can add additional fields dynamically by mapping the payload. If you want the header to be empty you can set the key to an empty string. Note that you can not skip columns, if there is no content you need to at least output an empty string.
Example:
%dw 2.0
output application/csv
---
payload map {
($), // just reusing the input payload as is
d: $$, // some calculated field with a header
"": if (isEven($$)) $$ else "", // calculated field with an empty name, only on some condition
"": $$ // another calculated field with an empty name
}
Input:
[
{
"a": "a1",
"b": "b1",
"c": "c1"
},
{
"a": "a2",
"b": "b2",
"c": "c2"
},
{
"a": "a3",
"b": "b3",
"c": "c3"
}
]
Output:
a,b,c,d,,
a1,b1,c1,0,0,0
a2,b2,c2,1,,1
a3,b3,c3,2,2,2

Matching dynamic keys in karate

I am trying to match a JSON array with a predefined expected json. The problem is that one of the key values in actual JSON is a set of strings delimited by "|". Here is how it looks :
actualJSON = [
{
"a": "varA",
"mix: "X|Y|Z",
},
{
"b": "B",
"c": "C"
} ]
expectedJSON = [
{
"a": "varA",
"mix: "Y|Z|X",
},
{
"b": "B",
"c": "C"
} ]
Issue here is the mix key that represents a set of strings and the value can be any combination of "X|Y|Z" without any specific order like "Z|Y|X" etc. When the value of mix is Y|Z|X then
* match actualJSON contains expectedJSON
works fine but in other cases it fails as expected. Is there a way to do the matching when key value is dynamic?
My first suggestion is as far as possible to write tests where the response is 100% predictable and don't waste time on these weird cases. Also refer: https://stackoverflow.com/a/50350442/143475
That said this is easy to do if you write a JS function:
* def response = { foo: 'X|Y|Z' }
* def hasXyz = function(x){ return x.includes('X') && x.includes('Y') && x.includes('Z') }
* match response == { foo: '#? hasXyz(_)' }
I leave it to you to figure out better logic if you want. Refer: https://github.com/karatelabs/karate#self-validation-expressions

Filter Payload with Dataweave for a Set Variable component

For the Mulesoft 4.2 Set Variable component, I want to assign a simple String value pulled from a single specific record from an incoming JSON payload.
In my case, taking just the value from the 'country' field from below example:
[
{
"salesID": "4404Bob Builder210032011-02-18T15:52:21+0.00",
"id": "4404",
"firstName": "Bob",
"lastName": "Builder",
"address": "181 Construction Road, Adelaide, NSW",
"postal": "21003",
"country": "New Zealand",
"creationDate": "2011-02-18T15:52:21+0.00",
"accountType": "personal",
"miles": 17469
}
]
A non-dynamic way to do this is like:
payload[0].country
I believe the best way to do this is with the filter function. The below option gives me the entire object, but I just want the country field.
payload filter ($.id == "4404")
Map function seems to be overkill for this since I only want the value, itself. I must be missing the syntax to get at the country field.
I did some more investigating, and this solution got me close enough to what I wanted:
https://stackoverflow.com/a/43488566/11995149
For my code example using filter, I had to surround the whole expression in parenthesis, and then I can access the field with a dot reference,
but below code gives String value as a single record within an array:
(payload filter ($.id == "4404")).country
[
"New Zealand"
]
In my case, I know that just one result will be returned from the filtered payload, so I could get just the String value with:
(payload filter ($.id == "4404"))[0].country
enter image description here
Can you try any of below options:
1) (payload groupBy ((item, index) -> item.id))["4404"][0].country
OR
2) (payload map ((item, index) -> if(item.id == "4404") item.country else ""))[0]
Thanks,
Ashish

Adding new key-value pair into json using karate

My payload looks like this :
{
"override_source": "DS",
"property_code": "0078099",
"stay_date": "2018-11-26T00:00:00.000000",
"sku_prices": [
],
"persistent_override": false
}
There is an array dblist ["2","3"] , it would consists of numbers from 1 to 4. Based on the elements present in the list, I want to add key-values {"sku_price":"1500","sku_code":"2"} to my payload. I am using the following code :
* eval if(contains("3",dblist)) karate.set('pushRatesFromDS.sku_prices[]','{ "sku_price": "1500","sku_code":"3" }')
When I execute my feature file, I do not get any errors but, key-values are not added to my payload. However if I move this code to a new feature file and call it, key-value pairs get added to my payload. The code in my new feature file looks like : * set pushRatesFromDS.sku_prices[] = { "sku_price": "1500","sku_code":"2" }
Try this:
* def foo =
"""
{
"override_source": "DS",
"property_code": "0078099",
"stay_date": "2018-11-26T00:00:00.000000",
"sku_prices": [
],
"persistent_override": false
}
"""
* eval karate.set('foo', '$.sku_prices[]', { foo: 'bar' })

How to retrieve a function from a series of functions and call it

I'm trying to create a dispatcher of functions in Rebol 3, so that for each string the program receives there's an associated function to be called.
For example:
handlers: make map! [
"foo" foo-func
"bar" bar-func
]
where foo-func and bar-func are functions:
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
The idea is to select the function starting from the string, so:
f: select handlers "foo"
so that executing f is the same as executing foo-func and then call f with some arguments:
f param1 param2
I tried quoting the words in the map!, or using get-words but without success.
Using a get-word! at the console, without passing through a map! it works:
>> a: func [] [ print "Hello world!" ]
>> a
Hello world!
>> b: :a
>> b
Hello world!
Any help appreciated.
select handlers "foo" only get the word foo-func:
f: select handlers "foo"
probe f ;will get: foo-func
You need to get its content:
f: get f
f 1 2 ;will print "foo"
Or more compact:
f: get select handlers "foo"
It's better to actually have the reference to the function in the map, rather than a word that refers to the function. If you store a word then you have to make sure the word is bound to an object which has a reference to that function, like this:
handlers: object [
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
]
handler-names: map [
"foo" foo-func
"bar" bar-func
]
apply get in handlers select handler-names name args
But if you just have a reference to the function in your map, you don't have to do the double indirect, and your code looks like this:
handlers: map reduce [
"foo" func [ a b ] [ print "foo" ]
"bar" func [ a b ] [ print "bar" ]
]
apply select handlers name args
Cleaner code, and more efficient too. Or if you're careful enough, like this:
handlers/(name) a b
The path method above will also work if you want the code to do nothing if there is no handler - common in cases where you have optional handlers, such as in GUIs.
You can even have more than one reference to the same function with different key names. You don't have to assign functions to words, they're just values. You can also use the path method to collect the handlers in the first place, saving a reduce.
handlers: make map! 10 ; preallocate as many entries as you expect
handlers/("foo"): func [ a b ] [ print "foo" ]
handlers/("bar"): func [ a b ] [ print "bar" ]
handlers/("baz"): select handlers "bar" ; multiple references
That path syntax is just another way to call poke, but some prefer it. We have to put the string values in parens because of a (hopefully temporary) syntax conflict, but within those parens the string keys work. It's a faster alternative to do select or poke.
foo-func in your map is just an unevaluated word
>> type? select handlers "foo"
== word!
You should first create your functions and then reduce the block, you use for creating your handler map so
handlers: make map! reduce [
"foo" :foo-func
"bar" :bar-func
]
then you have functions inside your map
>> type? select handlers "foo"
== function!
Try:
....
f: do select handlers "foo"
....