Velocity in AWS Api Gateway: how to access array of objects - aws-api-gateway

So in AWS Api Gateway, I'm querying my DynamoDB and gets this JSON as reply:
https://pastebin.com/GpQady4Z
So, Items is an array of 3 objects. I need to extract the properties of those objects: TS, Key and CamID.
I'm using Velocity in the Integration Response. Here is my Mapping Template:
#set($count = $input.json('$.Count'))
#set($items = $input.json('$.Items'))
{
"count" : $count,
"items" : $items,
"first_item": $items[0]
},
The result from API Gateway:
{
"count" : 3,
"items" : [{"TS":{"N":"1599050893346"},"Key":{"S":"000000/000000_2020-08-02-12.48.13.775-CEST.mp4"},"CamID":{"S":"000000"}},{"TS":{"N":"1599051001832"},"Key":{"S":"000000/000000_2020-08-02-12.50.01.220-CEST.mp4"},"CamID":{"S":"000000"}},{"TS":{"N":"1599051082769"},"Key":{"S":"000000/000000_2020-08-02-12.51.22.208-CEST.mp4"},"CamID":{"S":"000000"}}],
"first_item":
}
first_item always returns empty value
While in a pure array like this:
#set($foo = [ 42, "a string", 21, $myVar ])
"test" : $foo[0]
"test" returns 42
Why is my code not working on array of objects?

$items is a JSON string (not JSON object), so $items[0] doesn't make sense.
If you want to access the first item, use $input.json('$.Items[0]').
If you want to iterate over them, you can convert the JSON string to object first using $util.parseJson()

Related

How to document a mixed typed array structures in requests/responses with Spring REST Docs

Given the following exemplary JSON document, which is a list of polymorphic objects of type A and B:
[ {
"a" : 1,
"type" : "A"
}, {
"b" : true,
"type" : "B"
}, {
"b" : false,
"type" : "B"
}, {
"a" : 2,
"type" : "A"
} ]
How would I be able to select the As and the Bs to be able to document them differently.
I put an example project on github: https://github.com/dibog/spring-restdocs-polymorphic-list-demo
Here is an excerpt of me trying to document the fetch method:
.andDo(document("fetch-tree",
responseFields(
beneathPath("[0]").withSubsectionId("typeA"),
fieldWithPath("type")
.type(JsonFieldType.STRING)
.description("only node types 'A' and 'B' are supported"),
fieldWithPath("a")
.type(JsonFieldType.NUMBER)
.description("specific field for node type A")
),
responseFields(
beneathPath("[1]").withSubsectionId("typeB"),
fieldWithPath("type")
.type(JsonFieldType.STRING)
.description("only node types 'A' and 'B' are supported"),
fieldWithPath("b")
.type(JsonFieldType.BOOLEAN)
.description("specific field for node type A")
)))
But I get the following error message:
org.springframework.restdocs.payload.PayloadHandlingException: [0] identifies multiple sections of the payload and they do not have a common structure. The following non-optional uncommon paths were found: [[0].a, [0].b]
It looks like that [0] or [1] does not work and is interpreted as [].
What would be the best way to handle this situation?
Thanks,
Dieter
It looks like that [0] or [1] does not work and is interpreted as [].
That's correct. Adding support for indices is being tracked by this issue.
What would be the best way to handle this situation?
The beneathPath method that you've tried to use above returns an implementation of a strategy interface, PayloadSubsectionExtractor. You could provide your own implementation of this interface and, in the extractSubsection(byte[], MediaType) method, extract the JSON for a particular element in the array and return it as a byte[].

Specifying Numbers in VTL on AMS API Gateway

Doc Ref: http://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html
In AMS VTL one specifies dictionary fields in a model schema thus:
"field1" : {"type":"string"},
"field2" : {"type":"number"},
and so a mapping template can populate such fields thus:
#set($inputRoot = $input.path('$'))
"questions" :
[
#foreach($elem in $inputRoot)
{
"field1" : "$elem.field1",
"field2" : $elem.field2
}#if($foreach.hasNext),#end
#end
]
However... my iOS app complains the received data isn't in JSON format. If I add quotes around $elem.field2 then iOS accepts the JSON and converts all fields to strings.
My Lambda function is returning is returning a standard JSON list of dictionaries with field2 defined as an integer.
But APIG returns strings for all my fields, delimited with {} and a prefix:
{S=some text}
{N=10000000500}
So I can see that field2 isn't a number but a string {N=10000000500}.
How do I handle numbers in this system?
Undocumented but you can simply specify the type after the field name in a mapping template:
#set($inputRoot = $input.path('$'))
"questions" :
[
#foreach($elem in $inputRoot)
{
"field1" : "$elem.field1.S",
"field2" : $elem.field2.N
}#if($foreach.hasNext),#end
#end
]
Note that string fields need to be delimited in quotes.

Find query result to List

I have got a database filled with documents like the following :
{
"_id" : ObjectId("56zeffb2abcf7ff24b46"),
"id_thing" : -1,
"data" : {
"info1" : 36.0709427,
"date" : ISODate('2005-11-01T00:33:21.987+07:00'),
"info2" : 24563.87148077
}
}
My find method returns a List which I operate some operations over:
for (d <- result_of_find_method_here)
{
val l_d = d("data")
}
But I would like to l_d a List which is currently not, and the toList method does not work.
How do I retrieve all the fields and their value of the data container as a list?
EDIT:
I have tried multiple methods, and none work because neither applies to AnyRef which is what I get when I iterate through the l_d with a foreach loop.
Find method returns a list because there are more objects returned.
l_d is not a list, because d['data'] is not a list is a key value store: a dictionary, json or map in Scala. The question is how do you want to represent this data?
Maybe you want to take out the values from the map as a list.
You can convert map to list using: l_d.toList or map values to list: l_d.values.toList

How fetch data from mongo?

In my Meteor application, on the server side, I need to get data from Mongo, analyse and update it in Mongo if neсessary. In the database, I have an array of objects:
{ "0" : { "title" : "This is title", "description" : "This is description", "link" : "http://123435", "pubDate" : "16 Oct 2014 20:46:00 +0400" }, "1" : { }, "3" : { } etc
I'm try get data from Mongo: var savedNews = News.find().fetch(), there News is my collection. And I see in debugger array of this type:
0: "_id"="uuTOncmcIoIkfc",
1: "_id"="mcmroidewiuIpf"
etc
I understand this is _id's, but I need access to the fields of objects. In Mongo console I can see my real objects if I type db.news.find(); How I can get data in the form of array or object?
Thank you.
Fetch() return an array, those records you get can get probably expanded, if not, maybe you don't publish all of this data.
For example, you should be able to get to data like this:
temp = Collection.find({_id:"1"}).fetch();
temp[0].variable
Or simply
temp = Collection.find({_id:"1"}).fetch()[0].variable;
If you need just one record try using findOne, this return just one array, so no use of fetch() or [] is required

RMongo dbGetQueryForKeys(), what is the structure of "keys," and how do I sub-key them?

I am trying to query a mongo database from R using RMongo and return the values of a couple nested documents.
Looking through the documentation for RMongo, I understand the following query:
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo":1}')
Where the arguments are...
db = mongo
collection = 'test_data'
query = '{"foo": "bar"}'
keys = 'Specify a set of keys to return.'
What is the 1 in '{"foo":1}'? What is the structure of this key set? Checking against this blog post, I found a format like:
result < - dbGetQueryForKeys(mongo, "items", "{'publish_date' : { '$gte' : '2011-04-01', '$lt' : '2011-05-01'}}", "{'publish_date' : 1, 'rank' : 1}")
So, apparently, the keys need the value 1?
How would I get keys for nested documents? If I wanted something like...
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo1.foo2.foo3.foo4":1,"foo1.foo2.foo3.bar4":1}')
For nested keys, I'm currently returning something more like...
X_id
1 50fabd42a29d6013864fb9d7
foo1
1 { "foo2" : { "foo3" : { "foo4" : "090909" , "bar4" : "1"}}}
...where output[,2] is a looooong string, rather than as two separate variables for the values associated with the keys foo4 and bar4, ("090909", "1") as I would have expected.
What is the 1 in '{"foo":1}'? What is the structure of this key set?
These keys are the query projections to return for read operations in MongoDB. A value of "1" means to include a specific field and "0" excludes. The default behaviour is to include all fields in the projection.
How would I get keys for nested documents?
For nested keys, I'm currently returning something more like...
1 { "foo2" : { "foo3" : { "foo4" : "090909" , "bar4" : "1"}}}
...where output[,2] is a looooong string, rather than as two
separate variables for the values associated with the keys foo4
and bar4, ("090909", "1") as I would have expected.
The RMongo driver is returning data including the embedding hiearchy.
You can reshape & flatten the result output using the RMongo dbAggregate() command and the $project operator which is part of the Aggregation Framework in MongoDB 2.2+.
If your end goal is to extract the values from the nested object for some type of downstream processing in R this will get you there. It avoids having to build an aggregation pipeline and is a simple solution to your problem. Instead of trying to get deep into the nested structure and access bar4 directly, extract the top level of the object which will provide the long string that you've referenced.
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo1.foo2.foo3.foo4":1,"foo1":1}')
Since the output is a data.frame, you can use the 'jsonlite' library to get to your data:
library(jsonlite)
foo1 <- fromJSON(output$foo1)
bar4 <- foo1$foo2$foo3$bar4