output http request node.js in aws lambda - amazon-web-services

exports.handler = async (event) => {
// TODO implement
const https = require('https');
https.get('https://postman-echo.com/get?' +
'username =' +
'&password =' +
'&date=' +
'&cashSales=' + +
'&creditCardVisa=' +
'&creditCardMaster=' + +
'&creditCardAmex=' +
'&creditCardOthers=0',
res => {
//console.log(res.statusCode);
//console.log(res.headers);
let body = '';
res.on('data', data => {
body += data;
})
res.on('end', () => console.log(body));
})
const response = {
statusCode: 200,
body: JSON.stringify(https.get),
};
return response;
};
I can't seem to output the http request using this function, this can run in node.js but not in aws lambda, even after putting it in response function.

I have restructed the code a bit and wrapped it in a promise. You were not returning anything from the function. see here
const https = require("https");
exports.handler = event => {
return Promise((resolve, reject) => {
https.get(
"https://postman-echo.com/get?" +
"username =" +
"&password =" +
"&date=" +
"&cashSales=" +
+"&creditCardVisa=" +
"&creditCardMaster=" +
+"&creditCardAmex=" +
"&creditCardOthers=0",
resp => {
let data = "";
// A chunk of data has been recieved.
resp.on("data", chunk => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on("end", () => {
resolve(data);
});
resp.on("error", error => {
reject(error);
});
}
);
});
};

you were close. But I found two issues.
Your lambda function was not waiting for the http get to finish.
You were returning the incorrect value https.get. you should return the response payload of the https.get call.
I have fixed the issues above.
const https = require("https");
exports.handler = async (event) => {
const httpResponse = await new Promise((resolve, reject) => {
https.get(
"https://postman-echo.com/get?" +
"username =" +
"&password =" +
"&date=" +
"&cashSales=" +
+"&creditCardVisa=" +
"&creditCardMaster=" +
+"&creditCardAmex=" +
"&creditCardOthers=0",
resp => {
let body = '';
// A chunk of data has been recieved.
resp.on("data", chunk => {
body += chunk;
});
// The whole response has been received. Print out the result.
resp.on("end", () => {
resolve(body);
});
resp.on("error", error => {
reject(error);
});
}
);
});
const response = {
statusCode: 200,
body: JSON.stringify(httpResponse),
};
return response;
};
hope this helps.

I think others have already pointed out the problem in your code. You are not waiting for the http get call to complete before returning. Read this article which explains the incorrect handling of Promises in AWS Lambda functions and the solutions for that.
If you use request-promise library (a much more popular one on npm) instead of https and with the new async/await syntax, your code will become very simple.
exports.handler = async function(event) {
const request = require('request-promise');
const res = await request.get('https://postman-echo.com/get', {
params: {
username: '',
password: '',
date: '',
cashSales: '',
creditCardVisa: '',
creditCardMaster: '',
creditCardAmex: '',
creditCardOthers: '0'
}
});
const response = {
statusCode: 200,
body: JSON.stringify(res)
};
return response;
};

Related

Modify Cloudfront origin response with Lambda - read-only headers

I have a Cloudfront distribution with a single React site, which is hosting in S3. The origin is connected via REST api. To properly handle queries, I use custom error responses on status 403 and 404 to 200 and route them to root. The root object is index.html and everything seems to be fine.
Now I have a task to add to a distribution an another site, which should be accessible through a subdirectory.
To do this I have to set a root object for a subdirectory and to catch 404 and 403 responses and transfer them to a root object. I've already set up origin and behaviour.
I tried to use theese manuals:
example
source
but it seems that something went wrong
The first approach (CloudFrontSubdirectoryIndex) seems not working at all (the function is not invoked and no rewrite happens), so i tried CloudFront function and it seems to work fine.
The last step is to handle 404 and 403 responses.
Here is the function from the manual:
'use strict';
const http = require('https');
const indexPage = 'index.html';
exports.handler = async (event, context, callback) => {
const cf = event.Records[0].cf;
const request = cf.request;
const response = cf.response;
const statusCode = response.status;
// Only replace 403 and 404 requests typically received
// when loading a page for a SPA that uses client-side routing
const doReplace = request.method === 'GET'
&& (statusCode == '403' || statusCode == '404');
const result = doReplace
? await generateResponseAndLog(cf, request, indexPage)
: response;
callback(null, result);
};
async function generateResponseAndLog(cf, request, indexPage){
const domain = cf.config.distributionDomainName;
const appPath = getAppPath(request.uri);
const indexPath = `/${appPath}/${indexPage}`;
const response = await generateResponse(domain, indexPath);
console.log('response: ' + JSON.stringify(response));
return response;
}
async function generateResponse(domain, path){
try {
// Load HTML index from the CloudFront cache
const s3Response = await httpGet({ hostname: domain, path: path });
const headers = s3Response.headers ||
{
'content-type': [{ value: 'text/html;charset=UTF-8' }]
};
return {
status: '200',
headers: wrapAndFilterHeaders(headers),
body: s3Response.body
};
} catch (error) {
return {
status: '500',
headers:{
'content-type': [{ value: 'text/plain' }]
},
body: 'An error occurred loading the page'
};
}
}
function httpGet(params) {
return new Promise((resolve, reject) => {
http.get(params, (resp) => {
console.log(`Fetching ${params.hostname}${params.path}, status code : ${resp.statusCode}`);
let result = {
headers: resp.headers,
body: ''
};
resp.on('data', (chunk) => { result.body += chunk; });
resp.on('end', () => { resolve(result); });
}).on('error', (err) => {
console.log(`Couldn't fetch ${params.hostname}${params.path} : ${err.message}`);
reject(err, null);
});
});
}
// Get the app path segment e.g. candidates.app, employers.client etc
function getAppPath(path){
if(!path){
return '';
}
if(path[0] === '/'){
path = path.slice(1);
}
const segments = path.split('/');
// will always have at least one segment (may be empty)
return segments[0];
}
// Cloudfront requires header values to be wrapped in an array
function wrapAndFilterHeaders(headers){
const allowedHeaders = [
'content-type',
'content-length',
'last-modified',
'date',
'etag'
];
const responseHeaders = {};
if(!headers){
return responseHeaders;
}
for(var propName in headers) {
// only include allowed headers
if(allowedHeaders.includes(propName.toLowerCase())){
var header = headers[propName];
if (Array.isArray(header)){
// assume already 'wrapped' format
responseHeaders[propName] = header;
} else {
// fix to required format
responseHeaders[propName] = [{ value: header }];
}
}
}
return responseHeaders;
}
When i try to implement this solution (attach the function to origin response) I get
The Lambda function result failed validation: The function tried to add, delete, or change a read-only header.
Here is a list of restricted headers, but I'm not modifying any of them.
If I try not to attach any headers to a response at all, the message is the same.
If I try to attach all headers, CloudFront says that i'm modifying a black-listed header.
Objects in a bucket have only one customized Cache-Control: no-cache metadata.
It seemed to be a fast task, but I'm stuck for two days already.
Any help will be appreciated.
UPD: I've searched the logs and found
ERROR Validation error: Lambda function result failed validation, the function tried to delete read-only header, headerName : Transfer-Encoding.
I'm a little bit confused. This header is not present in origin response, but CF is telling that I deleted it...
I tried to find the value of the header "Transfer-Encoding" that should come from origin (S3) but it seems that it has been disappeared. And CloudFront says that this header is essential.
So I've just hard-coded it and everything becomes fine.
'use strict';
const http = require('https');
const indexPage = 'index.html';
exports.handler = async (event, context, callback) => {
const cf = event.Records[0].cf;
const request = cf.request;
const response = cf.response;
const statusCode = response.status;
// Only replace 403 and 404 requests typically received
// when loading a page for a SPA that uses client-side routing
const doReplace = request.method === 'GET'
&& (statusCode == '403' || statusCode == '404');
const result = doReplace
? await generateResponseAndLog(cf, request, indexPage)
: response;
callback(null, result);
};
async function generateResponseAndLog(cf, request, indexPage){
const domain = cf.config.distributionDomainName;
const appPath = getAppPath(request.uri);
const indexPath = `/${appPath}/${indexPage}`;
const response = await generateResponse(domain, indexPath);
console.log('response: ' + JSON.stringify(response));
return response;
}
async function generateResponse(domain, path){
try {
// Load HTML index from the CloudFront cache
const s3Response = await httpGet({ hostname: domain, path: path });
const headers = s3Response.headers ||
{
'content-type': [{ value: 'text/html;charset=UTF-8' }]
};
s3Response.headers['transfer-encoding'] = 'chunked';
return {
status: '200',
headers: wrapAndFilterHeaders(headers),
body: s3Response.body
};
} catch (error) {
return {
status: '500',
headers:{
'content-type': [{ value: 'text/plain' }]
},
body: 'An error occurred loading the page'
};
}
}
function httpGet(params) {
return new Promise((resolve, reject) => {
http.get(params, (resp) => {
console.log(`Fetching ${params.hostname}${params.path}, status code : ${resp.statusCode}`);
let result = {
headers: resp.headers,
body: ''
};
resp.on('data', (chunk) => { result.body += chunk; });
resp.on('end', () => { resolve(result); });
}).on('error', (err) => {
console.log(`Couldn't fetch ${params.hostname}${params.path} : ${err.message}`);
reject(err, null);
});
});
}
// Get the app path segment e.g. candidates.app, employers.client etc
function getAppPath(path){
if(!path){
return '';
}
if(path[0] === '/'){
path = path.slice(1);
}
const segments = path.split('/');
// will always have at least one segment (may be empty)
return segments[0];
}
// Cloudfront requires header values to be wrapped in an array
function wrapAndFilterHeaders(headers){
const allowedHeaders = [
'content-type',
'content-length',
'content-encoding',
'transfer-encoding',
'last-modified',
'date',
'etag'
];
const responseHeaders = {};
if(!headers){
return responseHeaders;
}
for(var propName in headers) {
// only include allowed headers
if(allowedHeaders.includes(propName.toLowerCase())){
var header = headers[propName];
if (Array.isArray(header)){
// assume already 'wrapped' format
responseHeaders[propName] = header;
} else {
// fix to required format
responseHeaders[propName] = [{ value: header }];
}
}
}
return responseHeaders;
}

Power BI REST API - Error: connect: exceeded the limit of 5 sockets

Hello dear PBI REST API experts, I would appreciate any support on the following issue.
Trying to send data from MongoDB Atlas to Power BI Push Dataset through Rest API.
However I met the following issue, when I'm making more than 5 post http requests to PBI Rest API it says 'Error: connect: exceeded the limit of 5 sockets'.
I found the following link on this website, however seems like solution was not found there: Close socket connection in request.js
function postDataToPowerBi(result) {
...
collection.findOne({"_id": item._id}).then(itemDoc => {
postOne(itemDoc, access_token);
});
...
}
function postOne(itemDoc, access_token){
var postData = JSON.stringify({
"rows": [
{
"Name": itemDoc.Name,
"name.1": itemDoc.name
}
]
});
// request option
var options = {
host: 'api.powerbi.com' ,
port: 443,
method: 'POST',
path: '/v1.0/myorg/groups/' + group_id + '/datasets/' + dataset_id + '/tables/' + table_name + '/rows',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + access_token,
// 'Content-Length': postData.length,
'Connection': 'Close'
}
};
// request object
var req = https.request(options, function (res) {
var result = '';
res.on('data', function (chunk) {
result += chunk;
});
res.on('end', function () {
console.log(res.statusCode);
});
res.on('error', function (err) {
console.log(err);
});
});
// req error
req.on('error', function (err) {
console.log(err);
});
req.on('close', function () {
console.log("request closed");
});
//send request witht the postData form
req.write(postData);
req.end();
}
Found explanation for the question posted. Power BI has 5 max pending POST rows requests per dataset for push datasets. More information here: https://learn.microsoft.com/en-us/power-bi/developer/embedded/push-datasets-limitations Hence the only and much better approach is to post rows in bulk instead of line by line. Example below:
function postBulk(docs, access_token, firstRun){
if(firstRun){
deleteRows(access_token);
}
var postData = '{"rows": ' + JSON.stringify(docs) + '}';
// console.log(postData);
// request option
var options = {
host: 'api.powerbi.com' ,
port: 443,
method: 'POST',
path: '/v1.0/myorg/groups/' + group_id + '/datasets/' + dataset_id + '/tables/' + table_name + '/rows',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + access_token,
// 'Content-Length': postData.length,
// 'Connection': 'Close'
}
};
// request object
var req = https.request(options, function (res) {
var result = '';
res.on('data', function (chunk) {
result += chunk;
});
res.on('end', function () {
console.log("postBulk: " + res.statusMessage);
});
res.on('error', function (err) {
console.log("postBulk: " + err);
});
});
// req error
req.on('error', function (err) {
console.log("postBulk: " + err);
});
// req.on('close', function () {
// console.log("request closed");
// });
//send request witht the postData form
req.write(postData);
req.end();
}

How to add GET request parameters in AWS canaries

I'm new in Cloudwatch synthetics. I want to test my API with Get request with parameters. Please suggest the configuration for adding parameters or provide blue print scripts which uses parameters in get request.
Thanks
Try This...
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');
const apiCanaryBlueprint = async function () {
// Handle validation for positive scenario
const validatePositiveCase = async function(res) {
return new Promise((resolve, reject) => {
if (res.statusCode < 200 || res.statusCode > 299) {
throw res.statusCode + ' ' + res.statusMessage;
}
let responseBody = '';
res.on('data', (d) => {
responseBody += d;
});
res.on('end', () => {
// Add validation on 'responseBody' here if required. For ex, your status code is 200 but data might be empty
resolve();
});
});
};
// Handle validation for negative scenario
const validateNegativeCase = async function(res) {
return new Promise((resolve, reject) => {
if (res.statusCode < 400) {
throw res.statusCode + ' ' + res.statusMessage;
}
});
};
let requestOptionsStep1 = {
'hostname': 'myproductsEndpoint.com',
'method': 'GET',
'path': '/test/product/validProductName',
'port': 443,
'protocol': 'https:'
};
let headers = {};
headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
requestOptionsStep1['headers'] = headers;
// By default headers, post data and response body are not included in the report for security reasons.
// Change the configuration at global level or add as step configuration for individual steps
let stepConfig = {
includeRequestHeaders: true,
includeResponseHeaders: true,
restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
includeRequestBody: true,
includeResponseBody: true
};
await synthetics.executeHttpStep('Verify GET products API with valid name', requestOptionsStep1, validatePositiveCase, stepConfig);
let requestOptionsStep2 = {
'hostname': ‘myproductsEndpoint.com',
'method': 'GET',
'path': '/test/canary/InvalidName(',
'port': 443,
'protocol': 'https:'
};
headers = {};
headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
requestOptionsStep2['headers'] = headers;
// By default headers, post data and response body are not included in the report for security reasons.
// Change the configuration at global level or add as step configuration for individual steps
stepConfig = {
includeRequestHeaders: true,
includeResponseHeaders: true,
restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
includeRequestBody: true,
includeResponseBody: true
};
await synthetics.executeHttpStep('Verify GET products API with invalid name', requestOptionsStep2, validateNegativeCase, stepConfig);
};
exports.handler = async () => {
return await apiCanaryBlueprint();
};

How to access the aws parameter store from a lambda using node.js and aws-sdk

I've created a lambda and cloud formation template which grants a lambda access to the parameter store and secrets manager. When I test the lambda I have the following functions outside of the export.handler function:
function getParameterFromStore(param){
let promise = new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
let parameterResult = promise.then(function(result){
console.log('---- result: '+ JSON.stringify(result));
return result;
});
return parameterResult;
};
servmgr is instantiated as var servmgr = new AWS.SSM();
When I call this function from the export.handler function I do so as:
myFirstParam = { Path : '/myPath/Service/servicesEndpoint'};
let endpointResult = getParameterFromStore(myFirstParam);
In the lambda I have the function retrieve the parameter defined outside of the export.handler function bt wrapped in a promise.
When I run/test this lambda the object returned is always undefined... I get Parameters[] back but no values.
2019-02-20T21:42:41.340Z 2684fe88-d552-4560-a477-6761f2de6717 ++ /myPath/Service/serviceEndpoint
2019-02-20T21:42:41.452Z 2684fe88-d552-4560-a477-6761f2de6717 ---- result: {"Parameters":[]}
How do you get parameter values returned back to a lambda at run time?
update
based upon the suggestion/answer from Thales I've simplified the lambda to just this:
const getParameterFromStoreAsync = (param) => {
return new Promise((resolve, reject) => {
servmgr.getParametersByPath(param, (err, data) => {
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
}
return resolve(data);
});
});
};
exports.handler = async(event, ctx, callback) => {
console.log('INFO[lambda]: Event: [' + JSON.stringify(event, null, 2) + ']');
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));
done = ()=>{}
callback(null, done());
};
I am still seeing an empty array being returned in my tests:
### endpoint path: {"Parameters":[]}
I've also moved the function into the callback as
exports.handler = (event,ctx, callback){
done = async()=>{
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));}
}
callback(null, done());
Same result ... empty array. Any additional things to try?
This is because your getParameterFromStore returns before your then() code is executed, thus parameterResult is undefined. If you don't want to change your code too much, I would return the Promise you create, like this:
function getParameterFromStore(param){
return new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
};
And finally, on your function's client, you can get the result like this:
const myFirstParam = { Path : '/myPath/Service/servicesEndpoint'}
getParameterFromStore(myFirstParam).then(console.log)
When coding in NodeJS, however, I highly recommend you use async/await instead, so you'll be able to escape the Promise Hell (chaninig Promise after Promise in order to achieve something "synchronously")
When using async/await, you can design your code as though it was synchronous. Here's a refactored version of your example, using async/await as well as arrow functions:
const getParameterFromStore = param => {
return new Promise((resolve, reject) => {
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, (err, data) => {
if (err) {
console.log('Error getting parameter: ' + err, err.stack)
return reject(err);
}
return resolve(data);
});
})
}
exports.handler = async (event) => {
const endpointResult = await getParameterFromStore(event.someAttributeFromTheEventThatYouWantToUse)
console.log(endpointResult)
};
EDIT: After the OP fixed the first issue, I created a working example on my own. It turned out that the way the OP was invoking the API was incorrect.
Here's the full working example:
'use strict';
const AWS = require('aws-sdk')
AWS.config.update({
region: 'us-east-1'
})
const parameterStore = new AWS.SSM()
const getParam = param => {
return new Promise((res, rej) => {
parameterStore.getParameter({
Name: param
}, (err, data) => {
if (err) {
return rej(err)
}
return res(data)
})
})
}
module.exports.get = async (event, context) => {
const param = await getParam('MyTestParameter')
console.log(param);
return {
statusCode: 200,
body: JSON.stringify(param)
};
};
Mind the Name attribute which must be provided as part of the API call to the ServiceManager.getAttribute method.
This attribute is stated in the official docs
I have run this myself and here's the output in CloudWatch Logs:
As you can see, the value was returned successfully.
Hope this helps!
If your lambda is deployed on VPC, make sure that Security Group is attached to it and outbound traffic is allowed. It will be able to access parameter store automatically.
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-vpc-parameter-store/
A simpler solution would be:
const getParameterFromStore = (params) => servmgr.getParametersByPath(params).promise();
const myFirstParam = { Path : '/myPath/Service'};
getParameterFromStore(myFirstParam).then(console.log);
As you can see, the SDK itself provides utility functinality that you can use depending on your needs to use in an async or syncronious fashion.
Hope it helps.

How to POST data for evaluation in middleware in loopback?

I want to use custom API to evaluate data which are posted by applications but remote methods are not accepted in middleware in loopback
module.exports = function () {
const http = require('https');
var request = require('request');
var { Lib } = require('Lib');
var lib = new Lib;
verification.checkID = function (ID, cb) {
cb(null, 'ID is :' + ID);
}
verification.remoteMethod('greet', {
accepts: {
arg: 'ID',
type: 'string'
},
returns: {
arg: 'OK',
type: 'string'
}
});
module.exports = function () {
const http = require('https');
var request = require('request');
var { Lib } = require('Lib');
var lib = new Lib;
verification.checkID = function (ID, cb) {
cb(null, 'ID is :' + ID);
}
verification.remoteMethod('greet', {
'http': { // add the verb here
'path': '/greet',
'verb': 'post'
},
accepts: {
arg: 'ID',
type: 'string'
},
returns: {
arg: 'OK',
type: 'string'
}
});
Update
module.exports = function(server) {
// Install a `/` route that returns server status
var router = server.loopback.Router();
router.get('/', server.loopback.status());
router.get('/ping', function(req, res) { // your middle ware function now you need to call the next() here
res.send('pong');
});
server.use(router);
};
To evaluate is something i am not getting please check this link too Intercepting error handling with loopback
Regarding to fallowing question How to make a simple API for post method?
I find my solution in fallowing way:
module.exports = function(server) {
const https = require('https');
var request = require('request');
return function verification(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', true);
var request;
var response;
var body = '';
// When a chunk of data arrives.
req.on('data', function (chunk) {
// Append it.
body += chunk;
});
// When finished with data.
req.on('end', function () {
// Show what just arrived if POST.
if (req.method === 'POST') {
console.log(body);
}
// Which method?
switch (req.method) {
case 'GET':
Verify url and respond with appropriate data.
handleGet(req, res);
Response has already been sent.
response = '';
break;
case 'POST':
// Verify JSON request and respond with stringified JSON response.
response = handlePost(body);
break;
default:
response = JSON.stringify({ 'error': 'Not A POST' });
break;
}
// Send the response if not empty.
if (response.length !== 0) {
res.write(response);
res.end();
}
// Paranoid clear of the 'body'. Seems to work without
// this, but I don't trust it...
body = '';
});
// If error.
req.on('error', function (err) {
res.write(JSON.stringify({ 'error': err.message }));
res.end();
});
//
};
function handlePost(body) {
var response = '';
var obj = JSON.parse(body);
// Error if no 'fcn' property.
if (obj['fcn'] === 'undefined') {
return JSON.stringify({ 'error': 'Request method missing' });
}
// Which function.
switch (obj['fcn']) {
// Calculate() requres 3 arguments.
case 'verification':
// Error if no arguments.
if ((obj['arg'] === 'undefined') || (obj['arg'].length !== 3)) {
response = JSON.stringify({ 'error': 'Arguments missing' });
break;
}
// Return with response from method.
response = verification(obj['arg']);
break;
default:
response = JSON.stringify({ 'error': 'Unknown function' });
break;
}
return response;
};
function verification(arg) {
var n1 = Number(arg[0]);
var n2 = Number(arg[1]);
var n3 = Number(arg[2]);
var result;
// Addem up.
result = n1 + n2 + n3;
// Return with JSON string.
return JSON.stringify({ 'result': result });
};
};