NodeJS in AWS Lambda to Mongo Atlas - connection fails with no logs - node.js

I am JS newbie so this may be some silly trouble. I have a lambda written in NodeJS 10.x and I am trying to add MongoDB Atlas insertion. I have started with this tutorial: https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/
This is my code:
const MongoClient = require('mongodb').MongoClient;
let cachedDb = null;
function connectToDatabase (uri) {
console.log('Connect to mongo database');
if (cachedDb) {
console.log('Using cached database instance');
return Promise.resolve(cachedDb);
}
return MongoClient.connect(uri)
.then(db => {
console.log('Successful connect');
cachedDb = db;
return cachedDb;
}).catch(err => {
console.log('Connection error occurred: ', err);
callback(err);
});
}
function insertUser(db, email) {
console.log('=> modify database');
return db.collection('users').insertOne({"email" : email})
.then(() => { callback(null, result); })
.catch(err => {
console.log('Insert error occurred: ', err);
callback(err);
});
}
exports.handler = (payload, context, callback) => {
const { email, password } = JSON.parse(payload.body);
context.callbackWaitsForEmptyEventLoop = false;
connectToDatabase(MONGODB_URI)
.then(db => {
console.log('Mongo connected')
insertUser(db, email);
})
.then(result => {
console.log('Mongo insert succeeded', result);
})
.catch(err => {
console.log('Mongo insert failed', err);
return responses.INTERNAL_SERVER_ERROR_500(err, callback, response);
});
console.log('finished mongo stuff');
I can see the following logs in CloudWatch:
START RequestId: 0338d336-7d33-40d5-abc7-1511f1c9ea4c Version: $LATEST
2020-01-11T12:18:00.808Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c INFO Connect to mongo database
2020-01-11T12:18:00.855Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c INFO finished mongo stuff
2020-01-11T12:18:01.416Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c ERROR (node:8) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
END RequestId: 0338d336-7d33-40d5-abc7-1511f1c9ea4c
The item is not inserted in Atlas. I have added more verbose logging but it is not shown. If the mongo connect failed there shall be an error. But it seems that the error handlers are ignored. Where is the problem?

You are very close. Few things are missing
callback function needs to be called from your handler function as the insert function doesn't know about callback.
When you do a .then on a promise and you use curly braces you need to return whatever is in there to send it to the next then (unless you do it in a single line).
db in insert method needs to be db.db()
I would recommend to to use async/await instead of callback style. Here is the equivalent code
exports.handler = async (payload) => {
try {
const { email, password } = JSON.parse(payload.body);
const db = await connectToDatabase(MONGODB_URI);
console.log("Mongo connected");
const result = await insertUser(db, email);
console.log("Mongo insert succeeded", result);
return result;
} catch(err) {
console.error(err);
}
};

Related

I cant fetch data from odoo calendar using intent of dialogflow and webhook

I want to list the event of odoo calendar in my chatbot created in dialogflow and using webhook.
This is my code :
function GetEventOdoo(agent) {
let username =agent.parameters.username;
var odooResult;
var odoo = new Odoo({
url: 'xxxxxxxxx',
port: 'xxxxxxxxxx',
db: 'xxxxxxxxxxxx',
username: 'xxxxxxxx#example.com',
password: 'xxxxxxxxxxxxx'
});
odooResult = JSON.stringify(odoo);
console.log('odooResult!!!:' + odooResult );
odoo.connect(function (err) {
if (err) {
return console.log('connection error: '+ JSON.stringify(err)); }
console.log('Connected to Odoo server.');
var inParams = [];
inParams.push([]); //where conditions
inParams.push(['name']); //fields
inParams.push(0); //offset
inParams.push(5); //limit
var params = [];
params.push(inParams);
odoo.execute_kw('calendar.event', 'search_read', params, function (err, value){
if (err) { return console.log(err); }
console.log(value);
});
})
return odooResult += JSON.stringify(value)
// eslint-disable-next-line promise/always-return
.then(res => {
const launchData = res.data;
agent.add(`Odoo event is: ${launchData[0].ID} `);
console.log(`Odoo event is: ${launchData[0].ID} `);
});
}
I can't connect with odoo to list data, i try out of dialogflow and works good, but here it's not working , maybe i have to correct the function to list this data from webhook to dialogflow.
Webhook call failed. Error: DEADLINE_EXCEEDED
Error: No handler for requested intent
at WebhookClient.handleRequest (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:327:29)
at exports.dialogflowFirebaseFulfillment.functions.https.onRequest (/srv/index.js:118:9)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:49:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
Edited:
I just uploaded my code like this*
function findeventlist(username) {
return new Promise((resolve, reject) => {
const odoo = new Odoo({
url: 'http://sssss',
port: '80',
db: 'ssss',
username: 'sssssssss',
password: 'sssssssss'
});
//odooResult = JSON.stringify(odoo);
odoo.connect(function (err) {
if (err) {
return console.log(err);
}
console.log('findeventlist Connected to Odoo server.');
const params = [];
params.push([]);
params.push(['name']);
params.push(0);
params.push(5);
params.push([params]);
odoo.execute_kw('calendar.event', 'search_read', params, function (err, value) {
if (err) {
return console.log(err);
}
console.log('Result: ', value);
resolve.send(JSON.stringify(value));
})
.catch(error => {
console.log(error);
reject(error);
});
});
});
}
function GetEventOdoo(agent) {
let username = agent.parameters.username;
console.log("GetEventOdoo");
return findeventlist().then(() => {
agent.add(`Event: ${username}, 😊`);
}).catch(() => {
agent.add(` 😔 `);
});
}
Error is : Error: memory limit exceeded. Function invocation was interrupted.
You are mixing up how you use Promises with other methods. This is certainly not returning what you expect, likely causing the response to not be sent back, and could be causing other issues such as memory leaks.
For example, in a few places in your code, inside the Promise, you have
if (err) {
return console.log(err);
}
All this does is quit out of the Promise without calling either resolve() or reject(), but it has returned that Promise to the Intent dispatcher, which is waiting for it to either resolve or reject, which never happens.
To fix this, instead of returning the console log (?), you should explicitly call reject() in these cases. So your code might look something like
if (err) {
reject( err );
}
This is only this complicated because you are wrapping calls to odoo in the Promise yourself. I don't know which library you're using, but you may wish to use one that works with Promises natively. For example, the odoo-api library lets you write things such as
function findeventlist(){
return odoo
.connect({
database: 'unicorn',
username: 'foo',
password: 'bar'
})
.then(client => {
return client.searchRead('product.product', [['list_price', '>', '50']], {limit: 1});
})
.then(products => {
console.log(products);
//=> [{list_price: 52, name: 'Unicorn'}]
agent.add( `I found ${products.length} results` );
});
};
without having to create or manage the Promise yourself. Using this library would even let you use async/await in the more recent versions of node, so this could even be written as something like (untested)
async function findeventlist(){
const client = await odoo.connect( connectionParameters );
const products = await client.searchRead('product.product', [['list_price', '>', '50']], {limit: 1});
console.log( products );
agent.add( `I found ${products.length} results` );
}

Nodejs - Wrong usage of promises

I have the following async method:
alreadyLoaded: async function (id) {
const pool = await poolPromise;
return pool.request()
.input('idParameter', id)
.query('SELECT count(*) AS value FROM dbo.partidos WHERE id=#idParameter')
.then(result => {
console.log(result.recordset[0].value)
result.recordset[0].value > 0
}).catch(function(err) {
console.log(err.message);
});
Invoked in another one:
processMatches: function(payload) {
payload.matches.forEach(p => {
if(partidosRepository.alreadyLoaded(p.id))
{
console.log("El partido ya fue cargado.");
return;
}
The alreadyLoaded method checks if some record is already inserted on database, and it's invoked inside another method to validate. The problem here is that the processMatches method continues processing records before the alreadyLoaded finishs with the current one.
I'm having a wrong manage of promises here, Can anyone help me solve this out?
Here's the database connection:
const poolPromise = new sql.ConnectionPool(config)
.connect()
.then(pool => {
console.log('Connected to localhost '+ config.database +' database');
return pool;
})
.catch(err => console.log('Database connection failed. Error: ', err));
module.exports = {
sql, poolPromise
}
Why don't you build your if statement like this
if(await partidosRepository.alreadyLoaded(p.id))
also keep in mind you are not returning boolean value here
}).catch(function(err) {
console.log(err.message);
});

Mongo makes new connection on each page refresh

When I load my page I want some products to be shown, so I make a GET request and it retrieves them from the database. However, when I refresh the page I notice the old connection remains. How to make sure the old connections close?
Here's my code:
const MongoClient = require('mongodb').MongoClient;
const connection = (closure) => {
return MongoClient.connect(config.connectionString, (err, client) => {
if (err) {
return winston.log('error', now() + err);
}
closure(client);
});
};
...
router.get('/products', (req, res) => {
connection((client) => {
client.db('dbname').collection('collectionname')
.find({})
.toArray()
.then((products) => {
response.data = products;
response.message = "Products retrieved successfully!"
res.json(response);
})
.catch((err) => {
winston.log('error', now() + err);
sendError(err, res);
});
});
});
Well, each time your /products route is called, you do create a new MongoClient instance. In that extent to limit the number of connection to your Database, you may either connect once, and save your MongoClient instance:
let client = undefined;
const connection = (closure) => {
// Return the client if any...
if(client) return closure(client);
return MongoClient.connect(config.connectionString, (err, c) => {
if (err) {
return winston.log('error', now() + err);
}
// Save the client.
client = c;
closure(client);
});
};
...or simply close the MongoClient connection you instantiated once you're done with it:
router.get('/products', (req, res) => {
connection((client) => {
client.db('dbname').collection('collectionname')
.find({})
.toArray()
.then((products) => {
response.data = products;
response.message = "Products retrieved successfully!"
// Close the MongoClient...
client.close();
res.json(response);
})
.catch((err) => {
winston.log('error', now() + err);
sendError(err, res);
// Close the MongoClient...
client.close();
});
});
});
I would advise you to go with the first solution: The MongoClient maintains a connection pool, so having multiple clients does not have any advantages. In addition, it allows you to check whether or not the DB is remotely available, before executing anything else as well (just connect to the DB on your app init(), and save the client instance, and you'll be done).

Sample code to query MongoDB in node.js (lambda) code

I'm new to node.js and trying to create a lambda function that queries a collection from MongoDB.
Here is a code I found as a starting point:
'use strict';
const MongoClient = require('mongodb').MongoClient;
const ATLAS_URI = "mongodb://lambdaUser:PASSWORD#cluster0-shard-00-00-ddlwo.mongodb.net:27017,cluster0-shard-00-01-ddlwo.mongodb.net:27017,cluster0-shard-00-02-ddlwo.mongodb.net:27017/mydb?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin";
let cachedDb = null;
function connectToDatabase(uri) {
console.log('=> connect to database');
if (cachedDb) {
console.log('=> using cached database instance');
return Promise.resolve(cachedDb);
}
return MongoClient.connect(uri)
.then(db => { cachedDb = db; return cachedDb; });
}
function queryDatabase(db) {
console.log('=> query database');
return db.collection('sensordata').find({}).toArray()
.then(() => { return { statusCode: 200, body: 'success' }; })
.catch(err => { return { statusCode: 500, body: 'error' }; });
}
exports.handler = (event, context, callback) => {
connectToDatabase(ATLAS_URI)
.then(db => queryDatabase(db))
.then(result => {
console.log('=> returning result: ', result);
context.succeed(result);
})
.catch(err => {
console.log('=> an error occurred: ', err);
context.failed(err);
});
};
This code works fine, but I don't know how to recover the data from the query...
Looking other code I see there is a function(err,data) inside the find(), but in this case I don't know how to insert that or modify the code to return the data instead of the {statuscode: 200, body: 'success'} json object.
I would appreciate any help.
Thanks
Gus

How to handle MongoDB Client connection error after it has been cached Mongo shuts down ?

I have created a mongodb native connection and saved it and then using findOne to query a document.
const Promise = require("bluebird");
const MongoClient = require('mongodb').MongoClient;
let mongoDB = undefined;
const getCollection = (collName) => {
if (mongoDB) {
return Promise.resolve(mongoDB.collection(collName));
} else {
return MongoClient.connect(EXT_CONFS.MONGODB_URL)
.then(db => {
mongoDB = db;
return Promise.resolve(mongoDB.collection(collName));
}).catch(e => {
console.error('Error in MongoDb connection');
});
}
};
const findOne = (collName, filter, options) => {
return getCollection(collName)
.then(collection => {
return collection.findOne(filter, options);
})
.then(doc => {
return Promise.resolve(doc);
}).catch(e => {
console.error(e);
return Promise.reject(e);
});
};
Now this all works fine, but if Mongo ShutsDown / Fails after db client is cached, There is no way to handle error. Error never goes to any catch handler :
console.error('Error in MongoDb connection');
or
console.error(e);
I even tried events :
mongoDB.on('connecting', function () {
console.log('connecting');
});
mongoDB.on('timeout', function (error) {
console.log('timeout!');
});
mongoDB.on('close', function (error) {
console.log('close!');
});
mongoDB.on('error', function (error) {
console.error('Error in MongoDb connection: ' + error);
});
mongoDB.on('connected', function () {
console.log('connected!');
});
mongoDB.on('connection', function () {
console.log('connected!');
});
mongoDB.on('connect', function () {
console.log('connected!');
});
mongoDB.once('open', function () {
console.log('connection open');
});
mongoDB.on('reconnected', function () {
console.log('reconnected');
});
mongoDB.on('disconnected', function () {
console.log('disconnected');
});
but no success still. Using NodeJS 4.5.0, MongoDB-Native driver 2.2.24
You should do something like console.error('Failed to connect to mongodb ',e); you are not outputting the error.
Also some events provide an additional parameter and you are outputting those either. In case of failing to connect to an mongodb server, your application should just notify you it's not the best approach to handle mongodb server start/restart from your application use daemons such as systemd or other process monitoring.
Some events are there to just notify the application that connection was lost or an reconnection is attempted, its up to you to handle what is going to be done when those events are emitted.
You can for example attempt to check mongodb status when an disconnect event is emitted an recreate connection object.
You could wrap the connect statement in a try-catch block.

Resources