Botpress - how to bring database values into the chat window? - node.js

I'm trying to build a chatbot using Botpress. I'm a beginner, looking for your help. One of the requirements is to query database to answer questions. This is what I have tried so far:
dbconnect.js
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
var db = function dbCall(sql, values) {
return new Promise(function(resolve, reject){
oracledb.getConnection(
{
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
},
function(err, connection) {
if (err) {
reject(err);
return;
}
connection.execute(
sql,
values,
{
maxRows: 1
},
function(err, result) {
if (err) {
console.error(err.message);
return;
}
resolve(result);
doRelease(connection);
}
);
});
});
}
// Note: connections should always be released when not needed
function doRelease(connection) {
connection.close(
function (err) {
if (err) {
console.error(err.message);
}
});
}
module.exports = db;
select.js
var dbConnect = require('../oracledb/dbconnect');
dbConnect('select code from table1' +
' where id=:id', {id:'value1'}).then(function (response) {
console.info(response.rows);
}).catch(function(error) {
console.info(error);
});
everything above works great, if I run select.js. How could I bring the response into the botpress chat window? I tried placing the select.js code in index.js event.reply, it doesn't work.
Thanks,
Babu.

I have resolved this by using the promise directly in the action.
return dbConnect('<SQL here>=:id', { id: Id })
.then(function(response) {
var res = response.rows
console.info(res);
const newState = { ...state, status: res}
return newState
})
.catch(function(error) {
console.info(error)
})
Note that response has the resultset.

Related

AdonisJS unable to return response within a function (SAP HANA)

I am using SAP HANA CLIENT for NodeJS in my AdonisJS project. I am unable to return the response as json from the the function that connects to the database. Here is the code
The controller method validateInvoice is first called
async validateInvoice({request, response}) {
const req = request.all()
const inv_num = req.invoice_num;
const data = await this.fetchInvStatus(inv_num);
return response.json({success: true, data: data});
}
This in turn calls the fetchInvStatus method which actually connects to HANA DB
var conn = hana.createConnection();
var conn_params = {
serverNode: '127.0.0.1:30015',
uid: 'CUST_USER_ROLE_ADMIN',
pwd: 'Welcome#1234',
database: 'DED'
};
conn.connect(conn_params, (err) => {
if(err) {
return err;
}
conn.exec("Select * FROM SAPDED.YACSF_RRHD where INVOICE_NUMBER = ?", ['BOMT000005'], (err, result) => {
if (err) {
return err;
}
console.log(result);
return result;
})
});
In the console I'm able to see the result but this result is not being passed to the validateInvoice method so that the API could return the response.
The line in the first method response.json() is executed even before the data from DB is returned. How can I overcome this problem? I've tried adding return statement to conn.connect and conn.exec but nothing helps!
You have to return promise in the fetchInvStatus method.
function fetchInvStatus() {
return new Promise(resolve, reject) {
var conn = hana.createConnection();
var conn_params = {
serverNode: '127.0.0.1:30015',
uid: 'CUST_USER_ROLE_ADMIN',
pwd: 'Welcome#1234',
database: 'DED'
};
conn.connect(conn_params, (err) => {
if(err) {
reject(err);
}
conn.exec("Select * FROM SAPDED.YACSF_RRHD where INVOICE_NUMBER = ?", ['BOMT000005'], (err, result) => {
if (err) {
reject(err);
}
console.log(result);
resolve(result);
})
});
}
}

model.update() not updating

I'm trying to add a new element to an array.
This is the code:
var findEditThenSave = function(personId, done) {
var foodToAdd = 'hamburger';
var foodArray = [];
Person.findById(personId, function (err, data) {
if (err) return console.log(err);
done(null, data);
foodArray = data.favoriteFoods;
console.log("foodArray inside findById: ", foodArray);
foodArray.push(foodToAdd);
var updateObj = {favoriteFoods: foodArray};
console.log(updateObj)
Person.update({_id: personId}, updateObj, function(err, raw) {
if (err) {
console.log("There was an error");
}
console.log("Updated successfully");
console.log("foodArray inside update function:", foodArray);
});
});
};
This is the whole code on Glitch: https://glitch.com/edit/#!/holly-maroon-pony?path=myApp.js%3A226%3A0
This is the console log for a POST request:
POST
foodArray inside findById: ["spaghetti"]
{ favoriteFoods: ["spaghetti","hamburger"] }
(node:8943) DeprecationWarning: collection.update is deprecated. Use updateOne, updateMany, or bulkWrite instead.
Updated successfully
foodArray inside update function: ["spaghetti","hamburger"]
You can use async and await while making these updates.
var findEditThenSave = async function(personId, done){
var foodToAdd = 'hamburger';
var foodArray = [];
var updateObj;
try{
var data=await Person.findById(personId);
done(null, data);
foodArray = data.favoriteFoods;
console.log("foodArray inside findById: ", foodArray);
foodArray.push(foodToAdd);
updateObj = {favoriteFoods: foodArray};
console.log(updateObj)
}catch(err){
console.log(err);
}
try{
await Person.update({_id: personId}, updateObj);
console.log("Updated successfully");
console.log("foodArray inside update function:", foodArray);
}catch(err){
console.log(err);
}
};
As you can see in your console:
(node:8943) DeprecationWarning: collection.update is
deprecated. Use updateOne, updateMany, or bulkWrite instead.
Updated successfully
So you can get through this using Person.updateOne instead of Person.update
Hope it helps
If you are just planning to update a document and return the updated document you can do something like this
function update(personId) {
var foodToAdd = "hamburger";
const person = Person.findByIdAndUpdate(
personId,
{ $push: { favoriteFoods: foodToAdd } },
{ new: true },
function (err, result) {
if (err) console.log(err);
else console.log(result);
}
);
};

TypeError: Cannot read property 'drop' of undefined

I am doing mocha testing. I have to connect to MongoDB in before function and I need to remove the documents in the collection in after function.
before("authenticate user", async () => {
mongoose.connect('mongodb://localhost:27017/mo-identity')
db = mongoose.connection;
db.once('open', function() {
console.log('We are connected to test `enter code here`database!')
})
.on('error', ()=>{console.error.bind(console, 'connection error')})
})
after(()=>{
db.User.drop()
})
Above is my code.
user is a collection. While executing this code I am getting this error TypeError: Cannot read property 'drop' of undefined. Help me out this error
I am afraid that you cannot drop collection like that:
db.User.drop()
If you want to drop collection then you should do something like this:
mongoose.connection.db.dropCollection('User', function(err, result) {...});
As #drinchev said, you can remove all documents by doing this :
Model.remove({}, function(err) {
console.log('collection removed')
});
In your case :
after(()=>{
db.User.remove({}, (err) => {
if (err) throw err;
});
})
Hope it helps.
/*import mongoose connection*/
const { mongodb } = require("./database/mongodb");
const collectionDrop = (collection) => {
return new Promise(function (resolve, reject) {
mongodb.connection.dropCollection(collection, function (err, result) {
var success = `\n🗑️ DropCollection '${collection}': Success!`;
var failure = `\n🗑️ DropCollection '${collection}' Error! ${err}`;
if (err) {
//if it doesn't exist, it's not an error.
if (err.message.includes("not found")) {
resolve(success);
} else {
reject(failure);
}
}
if (result) {
resolve(success);
}
resolve(success);
});
});
};
(() => {
try {
(async () => {
const collections = ["users", "aaaa"];
for (let i = 0; i < collections.length; i++) {
const result = await collectionDrop(collections[i]);
console.log(result);
}
/* In my case, I'm using it as a "pretest" script, in package.json.
Then I close the process to proceed with the test */
process.exit(0);
})();
} catch (error) {
console.trace(error.message);
}
})();

Node.js promises and call backs and asyncs

So I have done C++ Java and PHP and wanted to give a crack at Node.js. Below I have a very simple and unsecured SQL query.
module.exports = {
LoginCheck: function(data,cb){
console.log(data.password);
console.log(data.username);
if(data.username && data.password){
console.log('we have arrived');
var config = require(__dirname+'/../Config/config.json');
var mysql = require('mysql');
var con = mysql.createConnection({
host: config.LoginDBhost,
port: config.LoginDBport,
user: config.LoginDBusername,
password: config.LoginDBpassword,
database: config.LoginDB
});
con.connect(function(err) {
if (err) throw err;
console.log('we have connected');
con.query("SELECT * FROM `Users` WHERE `Username` = '"+data.username+"'", function (err, result) {
if (err) console.log(err);
if(result.Password){
if(result[0].Password){
if(result[0].Password == data.password){
console.log('true');
cb(true);
}
}
}
});
});
}
//either password is null or didnt match on file... if we are this far its a fail
console.log('false');
cb(false);
},
bar: function () {
// whateverss
}
};
The code does not wait for the sql connection and skips to being false. I been banging my head on a desk for days trying to figure out how promises and callbacks works after years of having my code follow steps layed.
Could someone convert this and explain how to force my code to be synchronous when I need it to be?
Try this code :
module.exports = {
LoginCheck: function(data,cb){
console.log(data.password);
console.log(data.username);
if(data.username && data.password){
console.log('we have arrived');
var config = require(__dirname+'/../Config/config.json');
var mysql = require('mysql');
var con = mysql.createConnection({
host: config.LoginDBhost,
port: config.LoginDBport,
user: config.LoginDBusername,
password: config.LoginDBpassword,
database: config.LoginDB
});
con.connect(function(err) {
// I think cb(false) here but you use throw err.
if (err) throw err;
console.log('we have connected');
con.query("SELECT * FROM `Users` WHERE `Username` = '"+data.username+"'", function (err, result) {
if (err) console.log(err);
if(result.Password){
if(result[0].Password){
if(result[0].Password == data.password){
console.log('true');
cb(true);
}
}
}
//either password is null or didn't match on file... if we are this far its a fail
console.log('false');
cb(false);
});
});
}
},
bar: function () {
// whateverss
}
};

Node.js reuse MongoDB reference

I am having trouble understanding node.js.
Example, MongoDB access, here's what I've got (mydb.js):
var mongodb = require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db = new mongodb.Db('mydb', server);
function authenticateAndGo(db, handle) {
db.authenticate('username', 'password', function(err) {
if (err) {
console.log(err);
return;
}
console.log('Database user authenticated');
var collection = new mongodb.Collection(db, 'test');
handle(collection);
});
}
function query(handle) {
db.open(function(err, db) {
if( err ) {
console.log(err);
return;
}
console.log('Database connected');
authenticateAndGo(db, handle);
});
};
exports.query = query;
So, if I want to use it later, I would
var mydb = require('./mydb');
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
But, If I do multiple calls, like so:
var mydb = require('./mydb');
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
mydb.query(function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log(docs);
});
});
I get an exception:
Error: db object already connecting, open cannot be called multiple times
I think that there is really something fundamental that I do not understand about all this and it is probable that this question is stupid ...
Anyway, all help is welcome.
Thanks in advance.
mydb.js:
var mongodb= require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db1 = new mongodb.Db('mydb', server);
// callback: (err, db)
function openDatabase(callback) {
db1.open(function(err, db) {
if (err)
return callback(err);
console.log('Database connected');
return callback(null, db);
});
}
// callback: (err, collection)
function authenticate(db, username, password, callback) {
db.authenticate(username, password, function(err, result) {
if (err) {
return callback (err);
}
if (result) {
var collection = new mongodb.Collection(db, 'test');
// always, ALWAYS return the error object as the first argument of a callback
return callback(null, collection);
} else {
return callback (new Error('authentication failed'));
}
});
}
exports.openDatabase = openDatabase;
exports.authenticate = authenticate;
use.js:
var mydb = require('./mydb');
// open the database once
mydb.openDatabase(function(err, db) {
if (err) {
console.log('ERROR CONNECTING TO DATABASE');
console.log(err);
process.exit(1);
}
// authenticate once after you opened the database. What's the point of
// authenticating on-demand (for each query)?
mydb.authenticate(db, 'usernsame', 'password', function(err, collection) {
if (err) {
console.log('ERROR AUTHENTICATING');
console.log(err);
process.exit(1);
}
// use the returned collection as many times as you like INSIDE THE CALLBACK
collection.find({}, {limit: 10})
.toArray(function(err, docs) {
console.log('\n------ 1 ------');
console.log(docs);
});
collection.find({}, {limit: 10})
.toArray(function(err, docs) {
console.log('\n------ 2 ------');
console.log(docs);
});
});
});
Result:
on success:
Database connected
Database user authenticated
------ 1 ------
[ { _id: 4f86889079a120bf04e48550, asd: 'asd' } ]
------ 2 ------
[ { _id: 4f86889079a120bf04e48550, asd: 'asd' } ]
on failure:
Database connected
{ [MongoError: auth fails] name: 'MongoError', errmsg: 'auth fails', ok: 0 }
[Original Answer]:
You're opening the db multiple times (once in each query). You should open the database just once, and use the db object in the callback for later use.
You're using the same variable name multiple times, and that might've caused some confusion.
var mongodb = require('mongodb'),
server = new mongodb.Server('staff.mongohq.com', 10030, {
auto_reconnect: true
}),
db1 = new mongodb.Db('mydb', server);
function authenticateAndGo(db, handle) {
db.authenticate('username', 'password', function(err) {
if (err) {
console.log(err);
return;
}
console.log('Database user authenticated');
var collection = new mongodb.Collection(db, 'test');
handle(collection);
});
}
function query(handle) {
db1.open(function(err, db2) {
if( err ) {
console.log(err);
return;
}
console.log('Database connected');
authenticateAndGo(db2, handle);
});
};
exports.query = query;
I've changed the above code a little (db1 for the original db, db2 for the opened db). As you can see, you're opening db1 multiple times, which is not good. extract the code for opening into another method and use it ONCE and use the db2 instance for all your queries/updates/removes/...
You can only call "open" once. When the open callback fires, you can then do your queries on the DB object it returns. So one way to handle this is to queue up the requests until the open completes.
e.g MyMongo.js
var mongodb = require('mongodb');
function MyMongo(host, port, dbname) {
this.host = host;
this.port = port;
this.dbname = dbname;
this.server = new mongodb.Server(
'localhost',
9000,
{auto_reconnect: true});
this.db_connector = new mongodb.Db(this.dbname, this.server);
var self = this;
this.db = undefined;
this.queue = [];
this.db_connector.open(function(err, db) {
if( err ) {
console.log(err);
return;
}
self.db = db;
for (var i = 0; i < self.queue.length; i++) {
var collection = new mongodb.Collection(
self.db, self.queue[i].cn);
self.queue[i].cb(collection);
}
self.queue = [];
});
}
exports.MyMongo = MyMongo;
MyMongo.prototype.query = function(collectionName, callback) {
if (this.db != undefined) {
var collection = new mongodb.Collection(this.db, collectionName);
callback(collection);
return;
}
this.queue.push({ "cn" : collectionName, "cb" : callback});
}
and then a sample use:
var MyMongo = require('./MyMongo.js').MyMongo;
var db = new MyMongo('localhost', 9000, 'db1');
var COL = 'col';
db.query(COL, function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log("First:\n", docs);
});
});
db.query(COL, function(collection) {
collection.find({}, {
limit: 10
}).toArray(function(err, docs) {
console.log("\nSecond:\n", docs);
});
});
I simply call the open function once directly after the db init:
var mongodb = require('mongodb');
var server = new mongodb.Server('foo', 3000, {auto_reconnect: true});
var db = new mongodb.Db('mydb', server);
db.open(function(){});
After that I do not have to care about that anymore because of auto_reconnect is true.
db.collection('bar', function(err, collection) { [...] };

Resources