I created a singleton to store a pool connection during the lifetime of the application :
var { MongoClient } = require("mongodb");
var credentials = require("./../database/credentials.json");
class MongoInstance {
static _db;
static isInitialized() {
return MongoInstance._db ? true : false;
}
static async connect() {
const client = new MongoClient("mongodb://localhost:27017");
try {
await client.connect();
console.log("Connected successfully to server");
MongoInstance._db = client.db(credentials.dbName);
return "done";
} catch (e) {
console.error("MongoDB connection error: ", e);
}
}
static closeConnection() {
if (MongoInstance.isInitialized()) MongoInstance._db.client.close();
}
static get db() {
if (MongoInstance.isInitialized()) return MongoInstance._db;
else {
MongoInstance.connect().then(() => {
return MongoInstance._db;
});
}
}
}
module.exports = MongoInstance;
I call this singleton with the following method :
var MongoInstance = require("./../database/mongodb");
....
const find = async function (collection, limit) {
try {
const query = {};
const sort = { length: -1 };
return await MongoInstance.db
.collection(collection)
.find(query)
.sort(sort)
.limit(+limit)
.toArray();
} catch (e) {
console.error("ERROR MongoDB find(): ", e);
return null;
}
};
But I get this error :
ERROR MongoDB find(): TypeError: Cannot read property 'collection' of undefined
What does it mean ?
Link to the official doc : npm
Related
I wanted to get user information from my collection using their ID to send them notifications. This are my functions in index.ts
export const sendNotifications = functions.firestore
.document('messages/{groupId1}/{groupId2}/{message}')
.onCreate((snapshot, context) =>{
console.log('Starting sendNotification Function');
const doc = snapshot.data();
console.log(doc.content);
console.log(getUserData(doc.idFrom))
return true;
});
export async function getUserData(id: string){
try {
const snapshot = await admin.firestore().collection('users').doc(id).get();
const userData = snapshot.data();
if(userData){
return userData.nickname;
}
} catch (error) {
console.log('Error getting User Information:', error);
return `NOT FOUND: ${error}`
}
}
From my deploy, I get the console log messages, the 'Starting sendNotification Function', then the actual 'doc.content' then an error for my 'getUserData(doc.idFrom)'.
Promise {
<pending>,
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] } }
Thank you in advance!
You should call your async getUserData() function with await.
The following should do the trick (untested):
export const sendNotifications = functions.firestore
.document('messages/{groupId1}/{groupId2}/{message}')
.onCreate(async (snapshot, context) => {
try {
console.log('Starting sendNotification Function');
const doc = snapshot.data();
console.log(doc.content);
const nickname = await getUserData(doc.idFrom);
// Do something with the nickname value
return true;
} catch (error) {
// ...
}
});
async function getUserData(id: string) {
try {
const snapshot = await admin.firestore().collection('users').doc(id).get();
if (snapshot.exists) {
const userData = snapshot.data();
return userData.nickname;
} else {
//Throw an error
}
} catch (error) {
// I would suggest you throw an error
console.log('Error getting User Information:', error);
return `NOT FOUND: ${error}`;
}
}
Or, if you don't want to have the Cloud Function async, you can do as follows:
export const sendNotifications = functions.firestore
.document('messages/{groupId1}/{groupId2}/{message}')
.onCreate((snapshot, context) => {
console.log('Starting sendNotification Function');
const doc = snapshot.data();
console.log(doc.content);
return getUserData(doc.idFrom)
.then((nickname) => {
// Do something with the nickname value
return true;
})
.catch((error) => {
console.log(error);
return true;
});
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})
I've got a method in a class which does query an ActiveDirectory.
Therefore I'm using 'activedirectory2' npm package.
I successfully authenticated and successfully logged my result to console.
Now that I have instanciated my class and have tried to call the method, I'm not abled to get a non-empty result.
I tried it with getters/setters to make the _result value available after instaciating the class.
I tried to solve my issue with research on asynchronous calls, but obviously wasn't able to ask the right question.
class Activedirectory
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
var result = this._result;
this.config.entryParser = function(entry,raw,callback){
if(entry.hasOwnProperty('info')) {
result.push(entry.info);
this._result = result;
}
callback(entry);
}
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//some error handling
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
console.log('result: ', this._result);
return await this._result;
}
}
})
} else {
console.log("Authentication failed!");
}
});
}
//getter/setter
get result(){
return this._result;
}
set result(value) {
this._result.push(value);
}
}
module.exports = AuthenticateWithLDAP;
route module
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
let x = async ()=> {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
})
I expected to be able to use the _result value of AuthenticateWithLDAP class in my router.post method handler.
Actually i only get [] (empty array) in router.post.
Could you please tell me how to alter the value _result in a way, so that the instance of the class knows it and can use it outside the class itself.
Thank you very much.
Micha
I am not 100% sure but I think this should work.
In your code you cant return the result because the return is in a callback.
There are to ways to fix that.
Pass a callback to the auth() method (This is bad since callbacks suck)
Return a promise and that resolves to the result
I've decided to go for promises.
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
return new Promise((resolve, reject) => {
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//Call reject here
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
resolve(await this._result);
}
}
})
} else {
console.log("Authentication failed!");
}
});
});
}
}
module.exports = AuthenticateWithLDAP;
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
/* This code can be simplifed
let x = async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
*/
(async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
var res = await authwithldap.auth();
console.log('res: ', res);
})();
})
Could you try to add syntax "await" like this?
await x().then((res)=>{
console.log('res: ', res); //always []
})
As your "x" method is in async mode, maybe you have to wait for the Promise to be resolved...
I want to permanently store a result of a callback function, here is the code:
class constants {
constructor(){
this.acquire()
}
acquire() {
this.last_refresh = new Date().getDate();
require('./db_slave').then(conn => {
return conn.query("SELECT table_name FROM information_schema.tables where table_schema='gen'").then(result => {
this.tables = result
console.log(result)
});
}).catch(err => {
// handle error here
});
}
tables() {
this.refresh()
return this.tables
}
refresh() { if (new Date().getDate() != this.last_refresh) this.acquire() }
}
module.exports = constants
but this is obviously not working because this.tables = result doesnt do anything...
Here is the file that is being called by this promise:
var mysql = require('promise-mysql');
var db = require('./db')
module.exports = mysql.createConnection({
host: db.host,
user: db.user,
password: db.password
}).then(conn => {
console.log("connected to the DB");
return conn;
}).catch(err => {
console.log("error connecting to the DB", err);
throw err;
});
How do i permanently store the result in that callback?
here is how i used these:
var db_constants = require('./database_service/db_constants')
let cons = new db_constants()
console.log(cons.tables())
I have a problem with Express response and Promise.
My script call a MongoDB Database to get informations from the server. I use Promise to send the result only when my server answered me.
The problem is in my route. I get the response correctly, but the
res.send returns nothing. I can't figure why.
Let's review my code. I've truncated some of the files, as it is not important code.
I have first a server.js file with my router declaration :
let router = express.Router();
app.use('/mongo', MongoRouter);
Then here is my MongoRouter.js file :
import { Router } from 'express';
import { MongoController } from '../Controllers/MongoController';
class MongoRouter {
constructor() {
this.router = Router();
this.controller = new MongoController();
this.routes();
}
routes() {
let obj = this;
this.router.get('/global', (req, res) => {
let promise = obj.controller.globalInfos();
promise.then((value) => {
console.log('Resolve OK. Showing data');
console.log(value);
res.send(value);
}).catch((error) => {
res.status(500).send(error.toString());
});
});
this.router.get('*', (req, res) => {
res.json({
data: 'Invalid route'
});
});
}
}
export default new MongoRouter().router;
And the function globalInfos() from my MongoController :
globalInfos() {
var status = '';
var globalInfos = new Array();
var internalServerReached = 0;
var dbCommand = {'serverStatus': 1};
let obj = this;
return new Promise((resolve, reject) => {
obj.cnxStrings.forEach(element => {
MongoClient.connect(element['cnxString'], (err, db) => {
if (!err) {
if (element['replica'] == true) {
dbCommand = {'replSetGetStatus':1};
}
db.command(dbCommand, (err, results) => {
if (!err) {
status = results;
} else {
status = 'Status unknown';
}
globalInfos[element['name']] = status;
internalServerReached++;
db.close();
if (internalServerReached >= obj.cnxStrings.length) {
console.log('Waiting 3s for the resolve');
setTimeout(() => {
resolve(globalInfos);
},3000);
}
});
} else {
status = 'Unreachable';
}
});
});
});
}
When i go to http://localhost:3000/mongo my function is running, the resolve is called after 3sec, my data are ok in the console (in the .then() callback), but the res.send(..) show an empty array []
Thanks!