Modify field at any level with circe-optics - circe

I am trying to transform the "model" field at any level with circe-optics and I'm having trouble in achieving this.
Input:
{
"model":"ModelExample1",
"test": {
"model":"ModelExample2"
}
}
Expected Ouput:
{
"model":"AAAA-ModelExample1",
"test": {
"model":"AAAA-ModelExample2"
}
}

Circe optics do not provide a recursive modification function out of the box. However, you can make one:
import io.circe.optics.JsonPath._
val modifyModel: Json => Json = root.model.string.modify("AAAA-" + _)
def modifyAllModels(value: Json): Json =
root.each.json.modify(modifyAllModels)(modifyModel(value))
The modification will be applied to all keys, not just test - if you don't want that, swap each for test in modifyAllModels.

Related

Get value out of a tolist([ data object in Terraform

I have this terraform output:
output "cloud_connection" {
value = data.cloud_connection.current.connection[0]
}
$ terraform output
cloud_connection = tolist([
{
"id" = "123"
"settings" = toset([
{
"vlan_id" = 100
},
])
"connection_type" = "cloud"
},
])
I need to get the vlan_id to reuse it later on.
output "cloud_connection" {
value = data.cloud_connection.current.connection[0].settings[0].vlan_id
}
$ terraform output
cloud_connection = tolist([
tolist([
100,
]),
])
The problem is I can't seem to be able to get the vlan id out of a list.
When I try:
output "cloud_connection" {
value = data.connection.current.connection[0].settings[0].vlan_id[0]
}
I am getting this error:
│ Elements of a set are identified only by their value and don't have any separate index or key to select
│ with, so it's only possible to perform operations across all elements of the set.
How can I get the vlan_id alone?
Thanks
Assuming that you know that you have at least 1 element in each of the nested collections, the idiomatic way would be to use splat expression with some flattening:
output "cloud_connection" {
value = flatten(data.connection.current.connection[*].settings[*].vlan_id)[0]
}
You can use the function join, assuming you´re getting the following output:
$ terraform output
cloud_connection = tolist([
tolist([
100,
]),
])
For example:
output "cloud_connection" {
value = join(",",data.cloud_connection.current.connection[0].settings[0].vlan_id)
}
That returns:
cloud_connection = "100"

How to trigger Cmd from view in Elm

I'm new to Elm and I ran into this problem...
We get translations for our page using something like:
case (translate translation.id) of
Success: -> translation
Failure: -> translation.id
Where translate just finds translation.id in a dictionary and it may or may not be there.
There are no runtime errors because you get a string either way, but we would like to log the missing translation to a rest service logger. But Elm hates side effects in the view that doesn't stem from html events so I'm not sure how to handle this.
Obviously in regular JS you could just crowbar in a fetch inside the failure case block and then return a string afterwards but that doesn't seem to be possible in Elm?
You need to move the effects part of your code into the update function. In this case I suggest doing it when the translation arrives. There you will want something like
OnTranslation translation ->
( { model | translation = translation } -- attach to model
, case translate translation.id of
Ok _ ->
Cmd.none
Err err ->
-- register with the error logger
logMissingTranslation translation.id
)
#Odin Thorsen, use Html.node and Html.Attributes.attribute to reference a custom element in Elm:
-- VIEW
view : Model -> Html.Html ()
view model =
Html.div []
[ translation model "key"
, translation model "junk key"
]
translation : Model -> String -> Html.Html ()
translation { translations } id =
Html.node "translated-text"
[ Html.Attributes.attribute "translation-id" id
, Html.Attributes.attribute "translation-text" <| translate translations id
]
[]
translate : Dict.Dict String String -> String -> String
translate translations id =
case Dict.get id translations of
Just value ->
value
Nothing ->
""
And in Javascript, use customElements.define to create the custom element:
customElements.define("translated-text", class extends HTMLElement {
constructor() { super(); }
connectedCallback() { }
attributeChangedCallback() { this.setTextAndLogFailure(); }
static get observedAttributes() { return ["translation-id", "translation-text"]; }
setTextAndLogFailure() {
var id = this.getAttribute("translation-id");
var text = this.getAttribute("translation-text");
if (text === null) return;
this.textContent = text;
if (!text.length) alert("Unkown translation id: " + id);
}
});
You'd replace alert("Unkown translation id: " + id); with the logging fetch.
Here is an Ellie with a full solution.

Karate API : How to assert a json/response node value which returns null or an array object for different data

I've got a request that returns a response node(itemCanBe) value in two possible ways, depending on the 'itemNum'. How do I assert this ? below attempt is not working
* match res..itemCanBe == null || res..itemCanBe[*] contains ['Colgate']
itemCanBe returning null
{
"itemDetails": {
"1234": {
"itemNum": "1234",
"itemCanBe": null
}
}
}
itemCanBe returning array
{
"itemDetails": {
"4567": {
"itemNum": "4567",
"itemCanBe": [
"Colgate",
"Sensodine"
]
}
}
}
See this thread for details: https://github.com/intuit/karate/issues/1202#issuecomment-653632397
This should be good enough to solve your problem.
This will actually work:
* def temp = get[0] response..itemCanBe
* match temp == '##[]'
Also refer: https://stackoverflow.com/a/50350442/143475

Why does this documentation example fail? Is my workaround an acceptable equivalent?

While exploring the documented example raised in this perl6 question that was asked here recently, I found that the final implementation option - (my interpretation of the example is that it provides three different ways to do something) - doesn't work. Running this;
class HTTP::Header does Associative {
has %!fields handles <iterator list kv keys values>;
sub normalize-key ($key) { $key.subst(/\w+/, *.tc, :g) }
method EXISTS-KEY ($key) { %!fields{normalize-key $key}:exists }
method DELETE-KEY ($key) { %!fields{normalize-key $key}:delete }
method push (*#_) { %!fields.push: #_ }
multi method AT-KEY (::?CLASS:D: $key) is rw {
my $element := %!fields{normalize-key $key};
Proxy.new(
FETCH => method () { $element },
STORE => method ($value) {
$element = do given $value».split(/',' \s+/).flat {
when 1 { .[0] } # a single value is stored as a string
default { .Array } # multiple values are stored as an array
}
}
);
}
}
my $header = HTTP::Header.new;
say $header.WHAT; #-> (Header)
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header.push('Accept-Language' => "fr"); # like .push on a Hash
say $header<Accept-Language>.perl; #-> $["en", "fr"]
... produces the expected output. Note that the third last line with the X meta-operator assigns a literal list (built with angle brackets) to a hash slice (given a flexible definition of "hash"). My understanding is this results in three seperate calls to method AT-KEY each with a single string argument (apart from self) and therefore does not exersise the default clause of the given statement. Is that correct?
When I invent a use case that excersises that part of the code, it appears to fail;
... as above ...
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header{'Accept-Language'} = "en, fr, cz";
say $header<Accept-Language>.perl; #-> ["en", "fr", "cz"] ??
# outputs
(Header)
This Seq has already been iterated, and its values consumed
(you might solve this by adding .cache on usages of the Seq, or
by assigning the Seq into an array)
in block at ./hhorig.pl line 20
in method <anon> at ./hhorig.pl line 18
in block <unit> at ./hhorig.pl line 32
The error message provides an awesome explanation - the topic is a sequence produced by the split and is now spent and hence can't be referenced in the when and/or default clauses.
Have I correctly "lifted" and implemented the example? Is my invented use case of several language codes in the one string wrong or is the example code wrong/out-of-date? I say out-of-date as my recollection is that Seq's came along pretty late in the perl6 development process - so perhaps, this code used to work but doesn't now. Can anyone clarify/confirm?
Finally, taking the error message into account, the following code appears to solve the problem;
... as above ...
STORE => method ($value) {
my #values = $value».split(/',' \s+/) ;
$element = do given #values.flat {
when 1 { $value } # a single value is stored as a string
default { #values } # multiple values are stored as an array
}
}
... but is it an exact equivalent?
That code works now (Rakudo 2018.04) and prints
$["en", "fr", "cz"]
as intended. It was probably a bug which was eventually solved.

Couchdb views and many (thousands) document types

I'm studing CouchDB and I'm picturing a worst case scenario:
for each document type I need 3 view and this application can generate 10 thousands of document types.
With "document type" I mean the structure of the document.
After insertion of a new document, couchdb make 3*10K calls to view functions searching for right document type.
Is this true?
Is there a smart solution than make a database for each doc type?
Document example (assume that none documents have the same structure, in this example data is under different keys):
[
{
"_id":"1251888780.0",
"_rev":"1-582726400f3c9437259adef7888cbac0"
"type":'sensorX',
"value":{"ValueA":"123"}
},
{
"_id":"1251888780.0",
"_rev":"1-37259adef7888cbac06400f3c9458272"
"type":'sensorY',
"value":{"valueB":"456"}
},
{
"_id":"1251888780.0",
"_rev":"1-6400f3c945827237259adef7888cbac0"
"type":'sensorZ',
"value":{"valueC":"789"}
},
]
Views example (in this example only one per doc type)
"views":
{
"sensorX": {
"map": "function(doc) { if (doc.type == 'sensorX') emit(null, doc.valueA) }"
},
"sensorY": {
"map": "function(doc) { if (doc.type == 'sensorY') emit(null, doc.valueB) }"
},
"sensorZ": {
"map": "function(doc) { if (doc.type == 'sensorZ') emit(null, doc.valueC) }"
},
}
The results of the map() function in CouchDB is cached the first time you request the view for each new document. Let me explain with a quick illustration.
You insert 100 documents to CouchDB
You request the view. Now the 100 documents have the map() function run against them and the results cached.
You request the view again. The data is read from the indexed view data, no documents have to be re-mapped.
You insert 50 more documents
You request the view. The 50 new documents are mapped and merged into the index with the old 100 documents.
You request the view again. The data is read from the indexed view data, no documents have to be re-mapped.
I hope that makes sense. If you're concerned about a big load being generated when a user requests a view and lots of new documents have been added you could look at having your import process call the view (to re-map the new documents) and have the user request for the view include stale=ok.
The CouchDB book is a really good resource for information on CouchDB.
James has a great answer.
It looks like you are asking the question "what are the values of documents of type X?"
I think you can do that with one view:
function(doc) {
// _view/sensor_value
var val_names = { "sensorX": "valueA"
, "sensorY": "valueB"
, "sensorZ": "valueC"
};
var value_name = val_names[doc.type];
if(value_name) {
// e.g. "sensorX" -> "123"
// or "sensorZ" -> "789"
emit(doc.type, doc.value[value_name]);
}
}
Now, to get all values for sensorY, you query /db/_design/app/_view/sensor_value with a parameter ?key="sensorX". CouchDB will show all values for sensorX, which come from the document's value.valueA field. (For sensorY, it comes from value.valueB, etc.)
Future-proofing
If you might have new document types in the future, something more general might be better:
function(doc) {
if(doc.type && doc.value) {
emit(doc.type, doc.value);
}
}
That is very simple, and any document will work if it has a type and value field. Next, to get the valueA, valueB, etc. from the view, just do that on the client side.
If using the client is impossible, use a _list function.
function(head, req) {
// _list/sensor_val
//
start({'headers':{'Content-Type':'application/json'}});
// Updating this will *not* cause the map/reduce view to re-build.
var val_names = { "sensorX": "valueA"
, "sensorY": "valueB"
, "sensorZ": "valueC"
};
var row;
var doc_type, val_name, doc_val;
while(row = getRow()) {
doc_type = row.key;
val_name = val_names[doc_type];
doc_val = row.value[val_name];
send("Doc " + row.id + " is type " + doc_type + " and value " + doc_val);
}
}
Obviously use send() to send whichever format you prefer for the client (such as JSON).