How do I show the content of two entities in Firebase? - node.js

I use node.js with express.js and only show me the data of the book, but I also want to show the authors but I do not know how to do it.
Any ideas ?
Note :English is not my mother tongue; please excuse any errors on my part
My Controller :
'use strict'
var Libro = require('../models/libro');
var admin = require('firebase-admin');
const firebaseDb = admin.database();
function getLibros(req,res){
firebaseDb.ref('libros').on('value',gotData,errData);
}
function gotData(data){
console.log(data.val());
}
function errData(){
console.log('TestErr');
}
Result:
{ '-LGeoSBuikhIuSicBH6I':
{
autor: '-LGc4ijGtUtQPoIyduOs',
descripcion: 'Libro que trata sobre temas de memorias2 ',
imagenRuta: 'assets/imagenes/HumbertoBallesteros.jpg',
isbn: '978-958-42-6142-4',
paginas: '200',
precio: '50',
titulo: 'Juego de Memoria'
}
}
My database Firebase
Database Firebase

function getLibros(req, res) {
let todo_libros = []; // array of books
// return Promise
return firebaseDb.ref('libros').once('value')
// libros es una array
.then(libros => {
let autor_promises = [];
for (libro in libros) {
todo_libros.push(libro); // libro
autor_promises.push(firebaseDb.ref('autores').child(libro.autor).once('value')); // author
}
// Array de [autor]
return Promise.all(autor_promises);
})
.then(autores => {
for (let i = 0; i < autores.length; ++i)
todo_libros[i].autor = autores[i];
return null;
})
.then(() => {
gotData(todo_libros);
res.status(200); // send OK response
res.json(todo_libros);
return null;
})
.catch(err => {
errData();
console.log(err); // print error
res.status(503); // Send Error Response
res.send(err);
})
}

Related

How to have rows from related tables as nested json objects in a node.js API

I'm trying to build my first Rest-API using node.js, swagger and mysql.
I have two tables in mysql database: Banco (0:1) --> Conta (1:N).
I want my API to return an array of a JSON objects of Banco rows, each containing a field "Conta" containing all related Conta objects, like:
{
"bancos": [
{
"id": 1,
"nome": "Banco 1",
"Conta": [{"nome": "Conta 1"},{"nome": "Conta 2"}]
},
{
"id": 2,
"nome": "Banco 2",
"Conta": [{"nome": "Conta 3"},{"nome": "Conta 4"}]
}
]}
I'm using mysql pooling object.
// const sql = require('tedious');
const dbConfig = require('./dbConn').config;
const pool = new sql.createPool(dbConfig);
exports.execSql = function(sqlquery, params) {
// var params = [];
// params.push(id);
sqlquery = sql.format(sqlquery, params);
return new Promise(function(res, rej) {
pool.getConnection(function(err, connection) {
if (err) rej(err);
connection.query(sqlquery, function(err, rows) {
if (err) rej(err);
else res(rows);
connection.release();
});
});
});
}
This is my Banco controller, that uses the Banco and Conta models. Each one return a promise with the their queries (simple selects).
'use strict';
var Banco = require('../models/banco');
var Conta = require('../models/conta');
module.exports = {getBancos};
function getBancos(req, res, next) {
let idUsuario = 1; //example
Banco.getAll().then(result => {
if (!result.error){
if (result.length){
result.map(banco => {
Conta.getAll(idUsuario, banco.id)
.then(resultado => {
banco.Conta = resultado
// res.json({bancos: resultado}); --> note1: if I use this, the result is quiet ok, but it raises an error of resending the headers.
})
});
res.json({bancos: result}); //--> note2 --> here results just the Bancos objects with empty .Contas [], because it does not wait the promise to be resolved
}
else
res.status(204).send();
}
else
res.status(result.error.status || 500).send(result);
});
}
Assuming my structure is ok, I wonder how to manage the flow as I could have the objects Bancos with their Contas atributes populated with the result of Conta.getAll()?
Any idea?
Thanks for helping...
Rafael
(let me know if more information are required)
I've tried more stuff and could realize how to solve this: basically it is about to put the .map() inside a Promisse.all() and an await for the function that brings the rows. It ends up like this:
function getBancos(req, res, next) {
let idUsuario = 1;
Banco.getAll().then(result => {
if (!result.error){
if (result.length){
Promise.all(result.map(async banco => {
banco.Contas = await Conta.getAll(idUsuario, banco.id)
.then(resultado => {
if (!resultado.error && resultado.length >0){
return resultado;
}
else return []
});
})).then(() =>{
res.json({bancos: result});
});
}
else
res.status(204).send();
}
else
res.status(result.error.status || 500).send(result);
});
}

Feathersjs validation method using ldapjs module problem

I want to write a function that adds the params of an ldap server after checking if the binding with server is right otherwise the data won't get added in the db ; i have to use feathers js. I tried to write a couple of lines to validate the params before adding them to the db :
const ldap=require("ldapjs"); but the problem is that when i do console.log(errors) inside and outside of ldap.bind function : i noticed that the result is only visible inside the function but not to the rest of the code ! I need it to be visible to the other parts as well so that I can use it , I want to know how to fix that.
this is the code and what the console.log has as a result.
module.exports = (ldapConnexion, mode = "create") => {
const errors = {
url_path: [],
browsingAccount: [],
password: [],
userSearchBase:[],
};
const client=ldap.createClient({url:ldapConnexion.url_path})
client.bind(ldapConnexion.browsingAccount,ldapConnexion.password,function(err){
if (err){errors.browsingAccount.push("unvalid credentials:"+err);console.log(err)}})
console.log(errors)
const hasErrors = Object.keys(errors).reduce(
(res, errorType) =>
res || (errors[errorType] && errors[errorType].length > 0),
false
);
return { errors, hasErrors };
}
to do that we can implement the code this way : if this can interrest someone
in a different file do :
const ldap = require('ldapjs');
module.exports = (ldapConnexion, mode = "create",cb) => {
const errors = {
url_path: [],
browsingAccount: [],
password: [],
userSearchBase: [],
};
var opts ={
rejectUnauthorized: false, // To force the client not to check self-signed certificates
}
//create the ldap client with the list of informations mentionned in the form
var client = ldap.createClient({
url:ldapConnexion.url_path, tlsOptions:opts
});
client.bind(ldapConnexion.browsingAccount, ldapConnexion.password, function (err) {
if (err) {
errors.browsingAccount.push("unvalid credentials:" + err.message);
//console.log(errors);
cb(errors,true);
client.unbind(function(err){if(err){console.log(err)}});
} client.unbind(function(err){if(err){console.log(err)}});
})
// console.log(errors)
if (!ldapConnexion.url_path) {
errors.url_path.push("Url obligatoire");
}
if (!ldapConnexion.browsingAccount) {
errors.browsingAccount.push("le compte lecteur est obligatoire");
}
if (!ldapConnexion.password) {
errors.password.push("le mot de passe est obligatoire");
}
const hasErrors = Object.keys(errors).reduce(
(res, errorType) =>
res || (errors[errorType] && errors[errorType].length > 0),
false
);
cb(errors, hasErrors)
}
in the hooks :
const validateConnexionHook = function (mode = "create") {
return context => {
return new Promise((resolve, reject) => {
const ldapConnexion = context.data || {};
validateConnexion(ldapConnexion, mode, (errors, hasErrors) => {
console.log('**************')
// console.log(errors);
console.log(hasErrors);
if (hasErrors) {
context.error = errors;
reject(errors)
}
else setTimeout(function () { resolve(); }, 3000);
})
})
}
}

Undefined in Firebase Functions with Realtime Database

I have two functions in Firebase. The first one is ok:
exports.sendPush = functions.database.ref('/itemADividir/{itemId}').onCreate((snapshot, context) =>{
const oque = snapshot.val();
const quem = oque.userOrigem;
const itemADV = oque.itemAdividir;
const quanto = oque.itemValor;
return pegaTokenUser(oque.userDestino).then(userTk =>{
let payload ={
notification: {
title: 'Solicitação - Notificação InfSocial',
body: `${quem} quer compartilhar ${itemADV} no valor de ${quanto} com vc`,
sound: 'default',
badge: '1'
}
};
console.log(`${quem} quer compartilhar ${itemADV} no valor de ${quanto} com vc`)
console.log(`token ${userTk}`)
return admin.messaging().sendToDevice(userTk, payload)
})
})
The second is returning undefined in userRecusado:
exports.itemRecusado = functions.database.ref('/ItemDividirNegado/{itemId}').onCreate((snapshot, context) => {
const itemId = context.params.itemId;
const oRecusado = snapshot.val();
const userRecusado = oRecusado.userOrigem;
console.log(`item ${itemId} recusado ${userRecusado}`)
return pegaTokenUser(userRecusado).then(userTk =>{
let payload ={
notification: {
title: 'Solicitação - Notificação InfSocial',
body: `${oRecusado.userDestino} recusou compartilhar ${oRecusado.itemADV} com vc`,
sound: 'default',
badge: '1'
}
};
console.log(`${oRecusado.userDestino} recusou compartilhar ${oRecusado.itemADV} com vc`)
console.log(`token ${userTk}`)
return admin.messaging().sendToDevice(userTk, payload)
})
})
Here is the function to get the token from users:
function pegaTokenUser(userGet){
let userGet2 = userGet.replace ('.','')
let dbRef = admin.database().ref('/UserTokens');
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
let userTk;
for (var user in data) {
if(data[user].user === userGet2){
userTk = (data[user].token);
}
}
resolve (userTk)
},(err) => {
reject(err);
});
});
return defer
}
Does anyone know why this is happening? This two functions do the same thing, but the second one does not get the property of the created record.
The full file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.sendPush = functions.database.ref('/itemADividir/{itemId}').onCreate((snapshot, context) =>{
const oque = snapshot.val();
const quem = oque.userOrigem;
const itemADV = oque.itemAdividir;
const quanto = oque.itemValor;
return pegaTokenUser(oque.userDestino).then(userTk =>{
let payload ={
notification: {
title: 'Solicitação - Notificação InfSocial',
body: `${quem} quer compartilhar ${itemADV} no valor de ${quanto} com vc`,
sound: 'default',
badge: '1'
}
};
console.log(`${quem} quer compartilhar ${itemADV} no valor de ${quanto} com vc`)
console.log(`token ${userTk}`)
return admin.messaging().sendToDevice(userTk, payload)
})
})
exports.itemRecusado = functions.database.ref('/ItemDividirNegado/{itemId}').onCreate((snapshot, context) => {
const itemId = context.params.itemId;
const oRecusado = snapshot.val();
const userRecusado = oRecusado.userOrigem;
console.log(`item ${itemId} recusado ${userRecusado}`)
// the output
//itemRecusado
//item 1586992687992 recusado **undefined**
return pegaTokenUser(userRecusado).then(userTk =>{
let payload ={
notification: {
title: 'Solicitação - Notificação InfSocial',
body: `${oRecusado.userDestino} recusou compartilhar ${oRecusado.itemADV} com vc`,
sound: 'default',
badge: '1'
}
};
console.log(`${oRecusado.userDestino} recusou compartilhar ${oRecusado.itemADV} com vc`)
console.log(`token ${userTk}`)
return admin.messaging().sendToDevice(userTk, payload)
})
})
function pegaTokenUser(userGet){
let userGet2 = userGet.replace ('.','')
let dbRef = admin.database().ref('/UserTokens');
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
let userTk;
for (var user in data) {
if(data[user].user === userGet2){
userTk = (data[user].token);
}
}
resolve (userTk)
},(err) => {
reject(err);
});
});
return defer
}
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
exports.TrocaEmoki = functions.database.ref('/Chat/{pathStr}/{tStamp}').onCreate((snapshot, context) =>{
const pathStr = context.params.pathStr;
const tStamp = context.params.tStamp;
console.log(`Nova mensagem ${tStamp} na mesa ${pathStr}`)
const mensagem = snapshot.val()
const text = addEmoji(mensagem.text)
return snapshot.ref.update({text})
})
function addEmoji(text) {
//return text.replace(/\bpizza\b/g, '🍕')
var regex1 = /pizza/,
regex2 = /hotdog/,
regex3 = /vinho/,
regex4 = /hamburguer/,
regex5 = /taco/,
regex6 = /fritas/,
regex7 = /sushi/,
regex8 = /churrasco/,
regex9 = /pipoca/,
regex10 = /peixe/,
regex11 = /cerveja/,
regex12 = /sorvete/,
alimento = text;
switch(true){
case regex1.test(alimento) :
return text.replace(/\bpizza\b/g, '🍕');
break;
case regex2.test(alimento):
return text.replace(/\bhotdog\b/g, '🌭');
break;
case regex3.test(alimento):
return text.replace(/\bvinho\b/g, '🍷');
break;
case regex4.test(alimento):
return text.replace(/\bhamburguer\b/g, '🍔');
break;
case regex5.test(alimento):
return text.replace(/\btaco\b/g, '🌮');
break;
case regex6.test(alimento):
return text.replace(/\bfritas\b/g, '🍟');
break;
case regex7.test(alimento):
return text.replace(/\bsushi\b/g, '🍣');
break;
case regex8.test(alimento):
return text.replace(/\bchurrasco\b/g, '🍖');
break;
case regex9.test(alimento):
return text.replace(/\bpipoca\b/g, '🍿');
break;
case regex10.test(alimento):
return text.replace(/\bpeixe\b/g, '🐟');
break;
case regex11.test(alimento):
return text.replace(/\bcerveja\b/g, '🍺');
break;
case regex12.test(alimento):
return text.replace(/\bsorvete\b/g, '🍨');
break;
default:
return text;
}
}
I found the problem. My code was inserting values one by one in Realtime database (name, than id, than item, than value...).
I did the same that was doing in the first case (make an object with values and than update the database with this object) and than update the database. That's it.

firebase Nodejs async cloud function not finishing execution

I am building a firebase cloud function that when I change a value in my db, I get UIDs of all users in db then check a node called wishlists to see if the user has products in one of his wishlists. if the wishlists of the users do not have products the cloud function sends a notification to the user with a msg to fill his wishlist. The function works for one user but when I iterate the user's nodes and call the work that I do for one user, it finishes before completing all the work. I believe that the probléme comes from not handling properly the async functions. Any help is appreciated. Thanks!
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const runtimeOpts = {
timeoutSeconds: 180,
memory: '1GB'
}
admin.initializeApp(functions.config().firebase);
exports.sendAdminNotificationForEmptyWishlists = functions.runWith(runtimeOpts)
.database.ref('/dev/fireNotifCloudFunc')
.onWrite(async () => {
// get all users uids
const ref = admin.database().ref().child('Influencers')
return ref.once("value")
.then(function(snapshot) {
snapshot.forEach( async function(child) {
await checkUserWishlists(child.key)
})
})
.catch(function(error){
console.log(error);
});
});
//check users wishlists
async function checkUserWishlists(uid){
var ref = admin.database().ref().child('Articles').child(uid)
ref.once("value")
.then(function(snapshot) {
if(snapshot.exists()){
const wishlistsNumber = snapshot.numChildren();
let wishlistCount = 0
let userHasProduct = false
var BreakException = {};
try {
snapshot.forEach(function(child) {
wishlistCount = wishlistCount + 1
//wishlist node
if(child.child("articles").exists()){
//user have not empty wishlist
userHasProduct = true
throw BreakException
}
});
}
catch(err){
if (err !== BreakException) throw err;
}
if((wishlistCount === wishlistsNumber) && !userHasProduct){
let fcmToken = (async () => {
fcmToken = await getUserFirestoreFCM(uid).then(()=>{
sendPushNotificationToUser(fcmToken,"emptyWishlists")
return fcmToken
})
.catch((error)=>{
console.log("error getting fcm token " + error);
})
})();
}
}
else{
console.log("user did not exist in articles node" );
return
}
})
.catch(function(error){
console.log(error);
});
}
async function getUserFirestoreFCM(uid){
let documentRef = admin.firestore().doc("users_table/" + uid);
documentRef.get().then(documentSnapshot => {
if (documentSnapshot.exists) {
console.log('Document exist')
return documentSnapshot.get('fcmToken')
}
else {
console.log('Document dosen\'t exist')
return
}
})
.catch(function(error){
console.log('error geting fcm token from firestore ' + error);
})
}
async function sendPushNotificationToUser(fcm,type){
//get fcm token
if(type === "emptyWishlists"){
//notification content
const payloadEmptyWishlists = {
notification: {
title: `Vos wishlists sont vides !`,
body: "Hello veuillez ajouter des produits dans vos wishlists pour joindre les
influenceurs dans le fil d'acceil, cheers ! ",
icon: "default",
sound: "default"
}
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
//send notif
return admin.messaging().sendToDevice(fcm,
payloadEmptyWishlists,options).then(function(response){
console.log('Notification sent successfully:',response);
return
})
.catch(function(error){
console.log('Notification sent failed:',error);
});
}
}
this is a screeshot from the logs
I think you should not use async/await in forEach loop callaback. This generally is tricky. Searching for good explanation I have found this article. I think its very short and clear.
In this situation I don't think that you need this async/await, but I would have to implement it to be sure. Anyway, if it appear that you still need async there is workaround in mentioned article.

Nested insert in Mongodb with Node.js

I'm receiving a CSV file which contains a list of products. Each product has a category. When I save a product I have to find if the category already exists in the database and if it does not I have to create it (and save it) before saving the product.
module.exports.do_import = function (req, res) {
var final_json = {};
fs.createReadStream(req.files.file.path)
.pipe(csv({
separator: ';'
}))
.on('data', function (data) {
mongoose.model("Category").findOne({nombre: data.NOMBRE}, function (err, category) {
if (category == null) {
var created_category = new Category({nombre: data.NOMBRE});
created_category.save(function (err, __category) {
var product = new Product({
nombre: data.NOMVDA,
categoria: __category,
codigo: data.CODVAR
});
product.save(function (err, prod) {
if (err) {
final_json.success = false;
res.json(final_json);
}
});
});
} else {
var product = new Product({
nombre: data.NOMVDA,
categoria: category,
codigo: data.CODVAR
});
product.save(function (err, prod) {
if (err) {
final_json.success = false;
res.json(final_json);
}
});
}
});
}).on('finish', function () {
final_json.success = true;
}).on('error', function () {
final_json.success = false;
}).on('end', function () {
res.json(final_json);
});
So, the trouble I've got here is that the .save() function of the category is executed asynchronously, so when the next record of the file is requested, the category of the previous one isn't saved yet, so it creates a the same category twice. How can I make the function wait until the save operation is completed?
Finally a friend of mine help me and thanks to this https://nodejs.org/api/stream.html#stream_readable_pause we can make the flow stop until the product has already been created.
Here is the final code:
module.exports.do_import = function (req, res) {
var final_json = {};
var readable = fs.createReadStream(req.files.file.path)
.pipe(csv({separator: ';'}));
function saveErrorHandler(err, prod) {
if (err) {
final_json.success = false;
res.json(final_json);
}
readable.resume();
console.log('next category');
}
readable.on('data', function (data) {
console.log('data');
readable.pause();
console.log('pausa');
mongoose.xmodel("Category").findOne({nombre: data.NOMBRE}, function (err, category) {
// Manejá el error que no lo tenes.
if (category) {
// esto se podría sacar ya que esta duplicado, pasale la categoría a una función
var product = new Product({
nombre: data.NOMVDA,
categoria: category,
codigo: data.CODVAR
});
product.save(saveErrorHandler);
} else {
var created_category = new Category({nombre: data.NOMBRE});
created_category.save(function (err, __category) {
var product = new Product({
nombre: data.NOMVDA,
categoria: __category,
codigo: data.CODVAR
});
product.save(saveErrorHandler);
});
}
});
});
readable.on('finish', function () {
final_json.success = true;
});
readable.on('error', function () {
final_json.success = false;
});
readable.on('end', function () {
res.json(final_json);
});

Resources