Adding a key/value pair to an object in VTL (for API Gateway) - aws-api-gateway

I am writing a mapping template for an AWS API Gateway integration response. I would like to add a key/value pair to the JSON object returned my Lambda function.
My function returns some JSON like this:
{
"id": "1234",
"name": "Foo Barstein"
}
I would like the template to output something like this:
{
"id": "1234",
"name": "Foo Barstein",
"href": "https://example.tld/thingy/1234"
}
And my mapping template looks like this:
#set($thingy = $input.json('$'))
#set($thingy.href = "https://example.tld/thingy/$thingy.id")
$thingy
However, my template outputs the unmodified $thingy, without the href I have tried to add.
I've read the VTL user guide, but to no avail.

Something like this has worked for me:
#set($body = $input.path('$'))
#set($body.href = "https://example.tld/thingy/$body.id")
$input.json('$')

There is no easy way to achieve this but you can workaround it:
## Mapping template
#set($body = $input.body)
#set($id = $input.json('$.id'))
{
"custom": {
"href" : "https://example.tld/thingy/$id"
},
"body": $body
}
And then merge all the keys in AWS.Lambda (if you use Lambda):
## Lambda handler
exports.handler = function(event, context) {
const requestParams = Object.assign({}, event.body, event.custom);
// ... function code
}
And requestParams will be what you want.

Following could do the trick. Beware, untested!
{
#set($payload = $util.parseJson($input.json('$')))
#set($body = "{
#foreach ($mapEntry in $payload.entrySet())
""$mapEntry.key"": ""$mapEntry.value"",
#end
""href"": ""$payload.id""
}")
$body
}

Related

How to decrypt mongodb objectId on Nodejs CosomosDB Trigger

I am retrieving my azure cosmosdb/mongodb document from a custom trigger to azure functions.. But my objectId seems to be encrypted.. How to get the correct objectid..
for example ObjectId("5df88e60d588f00c32a3c9ce") is coming as ]øŽ`Õˆð2£ÉÎ
or ObjectId("5df88f92d588f00c32a3c9d1") is coming as ]ø’Õˆð2£ÉÑ
Is there a way to retrieve objectid in nodejs/python or any script if i give ]ø’Õˆð2£ÉÑ as input.
This is my function.json used in the azure function
{
"scriptFile": "__init__.py",
"bindings": [
{
"type": "cosmosDBTrigger",
"name": "documents",
"direction": "in",
"leaseCollectionName": "leases1",
"connectionStringSetting": "devcosmosdb_DOCUMENTDB",
"databaseName": "devcosmosdb",
"collectionName": "newCollection",
"createLeaseCollectionIfNotExists": "true"
}
]
}
This is my nodejs code..
module.exports = async function (context, documents) {
if (!!documents && documents.length > 0) {
context.log('Document Id: ', documents[0].id);
context.log(documents[0]);
}
}
This is my output and this is where i am not getting the objectid properly..
2020-06-16T17:16:38Z [Information] Executing 'Functions.changeTrigger' (Reason='New changes on collection newCollection at 2020-06-16T17:16:38.2618864Z', Id=adc9556a-133f-4e85-b533-5574283a5a7d)
2020-06-16T17:16:38Z [Information] Document Id: NWRmODhkZGRkNTg4ZjAwYzMyYTNjOWNj
2020-06-16T17:16:38Z [Information] {
id: 'NWRmODhkZGRkNTg4ZjAwYzMyYTNjOWNj',
_rid: 'KEcnAO163B4EAAAAAAAAAA==',
_self: 'dbs/KEcnAA==/colls/KEcnAO163B4=/docs/KEcnAO163B4EAAAAAAAAAA==/',
_ts: 1592327797,
_etag: '"0000c1d2-0000-0300-0000-5ee8fe750000"',
'$t': 3,
'$v': {
_id: { '$t': 7, '$v': ']øÝÕð\f2£ÉÌ' },
name: { '$t': 2, '$v': 'myname' },
email: { '$t': 2, '$v': 'my email' },
},
_lsn: 537
}
Please go to Azure portal to check the content of your document. I have done a test on my side, it just works fine.
Here is the document I used to test.
{
"id": "testid1",
"test1":"testvalue1",
"test2":{
"test21":"test21value",
"objectId":"5df88f92d588f00c32a3c9d1"
}
}
After clicking save button, the function will be triggered.
Here is my testing code.
def main(documents: func.DocumentList) -> str:
if documents:
logging.info('Document id: %s', documents[0]['id'])
logging.info('%s',documents[0].to_json())
The output is as below.
Update:
Currently only SQL API base is supported in Azure function cosmosdb trigger. You can also find the feature under Settings part.
The URI should be something like https://testbowman.documents.azure.com:443/
If you create a mongodb api cosmosdb account, you won't find the 'add to function' feature. And the URI should be something like https://tonycosmosdb.mongo.cosmos.azure.com:443/

How to perform PATCH operation in Firebase APi?

The firebase doc sys this is how it is supposed to be done:
curl -X PATCH -d '{"last":"Jones"}' \
'https://[PROJECT_ID].firebaseio.com/users/jack/name/.json'
But I dont know how to convert this to a rest based request.
TO be clear I need to send a web request from javascript/java, hence I want to know what should be the body , and header and operation type for this request.
Can someone please help?
If you use the documentation for curl, you can figure out what that command line you showed is trying to tell you.
The HTTP method is: PATCH
The request body is: {"last":"Jones"}
The url is: https://[PROJECT_ID].firebaseio.com/users/jack/name/.json
Where PROJECT_ID is the name of your project. That's all there is to it.
You need teh following structure:
HTTP Request:
https://firestore.googleapis.com/v1/projects/*YOUPROJECT_ID*/databases/(default)/documents/users_admin/*DOCUMENT_ID*?**updateMask.fieldPaths=user_name&updateMask.fieldPaths=permisos.Administrador&updateMask.fieldPaths=user_email**
JSON Body (must be exactly the same structure and type as your database):
{
"fields": {
"user_name": { "stringValue": "Test Actualización 2" },
"permisos": {
"mapValue": {
"fields": {
"Administrador": {
"booleanValue": true
}
}
}
},
"user_email": { "stringValue": "veviboj548#eyeremind.com" }
}
}

How to validate response in Postman?

I am trying to validate response body including errors in postman. How can I validate the response and text below?
{
"responseHeader": {
"publisherId": "12345",
"responseId": "abbcb15d79d54f5dbc473e502e2242c4abbcb15d79d54f5dbc473e502e224264",
"errors": [
{
"errorCode": "1004",
"errorMessage": "XXXX Not Found"
}
]
}
}
These are my tests which are failing:
tests['response json contains responseHeader'] = _.has(responseJSON, 'responseHeader');
tests['response json contains errors'] = _.has(responseJSON, 'responseHeader.publisherId');
tests["Response has publisher id"] = responseJSON.publisherId === 10003;
In the "Test" tab, parse your response body into an object, then use JavaScript to perform your tests.
var data = JSON.parse(responseBody);
tests["publisherId is 12345"] = data.responseHeader.publisherId === "12345";
Take a look at the test examples at the Postman site:
https://www.getpostman.com/docs/postman/scripts/test_scripts
https://www.getpostman.com/docs/postman/scripts/test_examples

Pagination Links in Yii2 Rest API Custom Action Response

I have created my own object/index action in a yii rest controller which does some simple things:
public function actionIndex($name){
$query = Object::find();
$count = $query->count();
$pagination = new Pagination(['totalCount'=>$count]);
$objectList = $query->offset($pagination->offset)->limit($pagination->limit)->all();
return $objectList;
}
When I make a request to: http://localhost:8443/v0/objects?name=warehouse&page=1&per-page=1 I receive the following response:
[
{
"id": 2,
"data": {
"city": "Test",
"name": "ABC Warehouse",
"postal_code": "M1F 4F2",
"street_address": "1234 Street",
"owner": 76,
"created_at": "2016-09-23 15:10:20",
"updated_at": "2017-07-27 11:56:15",
"created_by": 9,
"updated_by": 13
},
"displayData": []
}
]
I would like to include the pagination link information as shown here but am not sure how to go about this
http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html
HTTP/1.1 200 OK
...
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
<http://localhost/users?page=2>; rel=next,
<http://localhost/users?page=50>; rel=last
I assume you're using the yii\rest\ActiveController
A quick look into the yii2 source suggests (to me) that whatever is returned need to implement the yii\data\DataProviderInterface. Right now your code does not return the Pagination object at all to be handled.
Assuming you're Object extends ActiveRecord, try this in your action...
public function actionIndex($name){
$query = Object::find(); // Assumes Object extends ActiveRecord
$countQuery = clone $query; // Good idea to clone query
$count = $countQuery->count();
$pagination = new Pagination(['totalCount'=>$count]);
$query->offset($pagination->offset)->limit($pagination->limit);
return new ActiveDataProvider([
'query' => $query,
'pagination' => $pagination,
]);
}
Note, this code is NOT tested. Hopefully it will help anyway.
--- Edit ---
Also in the yii\rest\ActiveController set this property (as suggested by Scott)
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];

remote autocomplete by typeahead works only on unique queries

I am having problem setting up typeahead with bloodhound on two fields - symbol and name. You can try live version on my DGI portfolio manager and autocomplete remote source here.
Typeahead sometimes works and sometimes it does not.
If I type symbols like "jnj", "mcd", "aapl" it works.
However, when I type string from name like "corporation" and "inc" that have around 3000 objects with this name, it does not work. I doubt it is because it is loading, since json file is served quickly(under 250ms on localhost).
Firstly, I thought symbols work correctly and names are ignored. But I do get proper typeahead for some names: "apple" and "homestreet" for instance.
I believe it only works if there are 1 or 2 results. But I don't understand, json file serves always max 5 results.
Here are my codes:
views.py for autocomplete url:
from haystack.query import SearchQuerySet
import json
def autocomplete(request):
if request.GET.get('q', '') == '':
array = []
else:
sqs = SearchQuerySet().models(Stock)
sqs_symbol = sqs.filter(symbol_auto=request.GET.get('q', ''))
sqs_name = sqs.filter(name_auto=request.GET.get('q', ''))
sqs_result = sqs_symbol | sqs_name
array = []
print sqs_result.count()
for result in sqs_result[:5]:
data = {"symbol": str(result.symbol),
"name": str(result.name),
"tokens": str(result.name).split()
}
array.insert(0, data)
print array
return HttpResponse(json.dumps(array), content_type='application/json')
I added print so I know when it does not work.
search_indexes.py file:
from haystack import indexes
from stocks.models import Stock
class StockIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
symbol = indexes.CharField(model_attr='symbol')
name = indexes.CharField(model_attr='name')
# We add this for autocomplete.
symbol_auto = indexes.EdgeNgramField(model_attr='symbol')
name_auto = indexes.EdgeNgramField(model_attr='name')
def get_model(self):
return Stock
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
And in my template html file:
<script type="text/javascript">
$(function(){
var stocks = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.tokens);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 5,
remote: {
url: "/search/autocomplete/",
replace: function(url, query) {
return url + "?q=" + query;
},
filter: function(stocks) {
return $.map(stocks, function(data) {
return {
tokens: data.tokens,
symbol: data.symbol,
name: data.name
}
});
}
}
});
stocks.initialize();
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: 'name',
minLength: 1, // send AJAX request only after user type in at least X characters
source: stocks.ttAdapter(),
templates: {
suggestion: function(data){
return '<p>' + data.name + ' (<strong>' + data.symbol + '</strong>)</p>';
}
}
}).on('typeahead:selected', function (obj, stock) {
window.location.href = "/stocks/detail/" + stock.symbol;
});
});
</script>
EDIT: Some Examples
Json response:
[{"tokens": ["Johnson", "&", "Johnson"], "symbol": "JNJ", "name": "Johnson & Johnson"}]
Not working for "sto":
json response:
[{"tokens": ["QKL", "Stores,", "Inc."], "symbol": "QKLS", "name": "QKL Stores, Inc."}, {"tokens": ["SPDR", "DJ", "STOXX", "50"], "symbol": "FEU", "name": "SPDR DJ STOXX 50 "}, {"tokens": ["Statoil", "ASA"], "symbol": "STO", "name": "Statoil ASA"}, {"tokens": ["STORE", "Capital", "Corporation"], "symbol": "STOR", "name": "STORE Capital Corporation"}, {"tokens": ["StoneMor", "Partners", "L.P."], "symbol": "STON", "name": "StoneMor Partners L.P."}]
It is typeahead.js's bug. It should be fixed in version 0.11.2.