How to structure my data in Firebase using NodeJS - javascript

How I want my data to be structured is as follows:
Student -> Reg_num -> someindex (that will start from 1 - like an
unique key) -> course details.
However, the code I wrote gives me an incorrect structure. Can someone help me sort it out.
var db = admin.database();
var ref = db.ref("Students");
var newMessageRef = ref.push();
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
var uniqueID = 97888888888888;
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return newMessageRef.set({
[uniqueID]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
...
Note: The -LC-lS2HPMbZW9AdT19K is a code that was automatically added from the code. This is because I used ref.push()

Do not use ref.push() or ref.set(), but ref.update() as follows:
const db = admin.database();
const ref = db.ref("Students");
//var newMessageRef = ref.push(); <- Don't do that
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const uniqueID = 97888888888888; <- Student ID
const uniqueCourseKey = 0; <- uniqueCourseKey
if (req.method !== 'POST') {
return res.status(500).json({
message: 'Not allowed'
})
} else {
return ref.child(uniqueID).update({
[uniqueCourseKey]: {
course: req.body.course,
credits: req.body.credit,
lecturer : 'Prof. Lee'
}
}).then(() => {
res.status(200).json({
message: "okkkkasss"
});
....
Then you can call again the Cloud Function with e.g. const uniqueCourseKey = 1; and the new node will be correctly added under the StudentID node.
The doc for the update method is here: https://firebase.google.com/docs/reference/js/firebase.database.Reference#update

Related

node.js send notification to specific user in controller

index.js file
var users = [];
let addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
let removeUser = (socketId) => {
users = users.filter((item) => item.socketId !== socketId);
};
const getUser = (userId) => {
console.log("inside function", users);
return users.find((item) => item.userId === userId);
};
io.on("connection", (socket) => {
socket.on("addUser", async (userId) => {
await addUser(userId, socket.id);
io.emit("getUsers", users);
console.log(users) // print array of users like this
// [{userId:'userId',socketId: 'socket id'}]
});
socket.on("disconnect", () => {
removeUser(socket.id);
io.emit("getUsers", users);
});
});
const socketIoObject = io;
const usersInObject = users;
module.exports.ioObject = { socketIoObject, usersInObject };
controller file
exports.createNotifications = async (req, res) => {
try {
const { userId, title, type = "default", isPublic } = req.body;
if (!title) {
return res.status(401).send("Data is required");
}
const notification = await notificationsModel.create({
userId,
title,
type,
isPublic: userId ? false : true,
});
console.log("socket", socket.ioObject.usersInObject); // return empty
// array [] !!!!
return res.status(200).send("sent");
} catch (err) {
return res.status(400).send(err.message);
}
};
why I can't get the users list in the controller, I got an empty array !!
I need to share the users list in all files to can get the user by function getUser to get the socketId of a specific user to can send a notification to him
Maybe, you import socket in controller file incorrect

Cannot read property 'count' of undefined Express API

When i call Create Option api it is working fine but when i call list api get error: Cannot read property 'count' of undefined Express (Node + MongoDB) API.here is my Option Controller File code.
i have Log DB.ProductDoption ,getting result but count function not working.
const _ = require('lodash');
const Joi = require('joi');
exports.create = async (req, res, next) => {
try {
const validateSchema = Joi.object().keys({
name: Joi.string().required(),
key: Joi.string().required(),
description: Joi.string().allow(['', null]).optional(),
options: Joi.array().items(Joi.object().keys({
key: Joi.string().required(),
displayText: Joi.string().required()
})).required()
});
const validate = Joi.validate(req.body, validateSchema);
if (validate.error) {
return next(PopulateResponse.validationError(validate.error));
}
const key = Helper.String.createAlias(req.body.key);
console.log(DB.ProductDoption);
const count = await DB.ProductDoption.count({ key });
if (count || validate.value.key === '_custom') {
return next(PopulateResponse.error({
message: 'Please add unique name for key'
}));
}
const option = new DB.ProductDoption(validate.value);
await option.save();
res.locals.option = option;
return next();
} catch (e) {
return next(e);
}
};
exports.list = async (req, res, next) => {
const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip()
const take = parseInt(req.query.take, 10) || 10;
try {
const query = Helper.App.populateDbQuery(req.query, {
text: ['name', 'key', 'description']
});
const sort = Helper.App.populateDBSort(req.query);
const count = await DB.ProductDoption.count(query);
const items = await DB.ProductDoption.find(query)
.collation({ locale: 'en' })
.sort(sort).skip(page * take)
.limit(take)
.exec();
res.locals.optionList = {
count,
items
};
next();
} catch (e) {
next(e);
}
};
collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead

Route.post() requires a callback function but got a [object Undefined] In ExpressJs

I'm aware this question has been asked before. I tried all the solutions that were provided but still I'm stuck. Please have a look at my code:
1) ReviewController.js
exports.setTourUserIds = (req, res, next) => {
// allow nested route
if (!req.body.tour) req.body.tour = req.params.tourId
if (!req.body.user) req.body.user = req.user.id
next()
}
exports.createReview = handlerFactory.createOne(Review)
exports.updateReview = handlerFactory.updateOne(Review)
exports.deleteReview = handlerFactory.deleteOne(Review)
2) ReviewRoute.js
let express = require('express')
let Router = express.Router({ mergeParams: true })
let reviewController = require('../controllers/reviewController')
let authController = require('../controllers/authController')
Router
.route('/')
.get(reviewController.getAllReviews)
.post(authController.protect,
authController.restrictTo('user'),
reviewController.setTourUserIds,
reviewController.createReview)
Router
.route('/:id')
.get(authController.protect, reviewController.getReview)
.patch(reviewController.updateReview)
.delete(reviewController.deleteReview)
module.exports = Router
3) handlerFactory.js
exports.createOne = Model => catchAsync(async (req, res, next) => {
let doc = await Model.createOne(req.body)
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(201).json({
status: 'success!',
data: { doc }
})
})
Please change your code from
exports.createReview = handlerFactory.createOne(Review)
exports.updateReview = handlerFactory.updateOne(Review)
exports.deleteReview = handlerFactory.deleteOne(Review)
to
exports.createReview = () => { handlerFactory.createOne(Review)}
exports.updateReview = () => { handlerFactory.updateOne(Review) }
exports.deleteReview = () => { handlerFactory.deleteOne(Review) }
You should pass a function to the router
Check following syntax:
Router
.route('/:id').post(function(){})

How do I make hbs render an array from a callback function?

Currently I have axios and cheerio return data from a webpage. I then setup express to setup a few views. I double checked my index.hbs and it include {{data}} inside the body. This should allow the page to render the text from the index render data: dealss . Am I missing anything ? The dealss obj holds 4 different objects that I can access.
getdeals(result => console.log(result.totaldeals[0].date))
This returns [ 09/04/2019/ ] in the console.
const path = require('path')
const express = require('express')
const hbs = require('hbs')
const axios = require('axios');
const cheerio = require('cheerio');
const app = express()
// Define paths for express config
const publicDirPath = path.join(__dirname, '../public')
const viewsPath = path.join(__dirname, '../templates/views')
const partialsPath = path.join(__dirname, '../templates/partials')
// Setup handlebars engine and views location
app.set('view engine', 'hbs')
app.set('views', viewsPath)
hbs.registerPartials(partialsPath)
// Setup static directory to serve
app.use(express.static(publicDirPath))
// Views
app.get('', (req, res) => {
res.render('index', {
title: 'ClearVision',
data: dealss,
name: 'Chris'
})
})
app.get('/about', (req, res) => {
res.render('about', {
title: 'ClearVision - About Us',
header: 'About Us',
name: 'Chris'
})
})
app.get('/help', (req, res) => {
res.render('help', {
title: 'ClearVision - Help',
helptext: 'Please contact x for help.',
name: 'Chris'
})
})
app.get('/weather', (req, res) => {
res.send({
forecast: 'It is sunny.',
location: 'x, Ca'
})
})
app.listen(1337, () => {
console.log('Server is currently running on port 1337.')
})
const url = 'https://abcdef.com/';
axios.defaults.withCredentials = true
// Get the deals
const getdeals = (callback) => {
axios(url, {
headers: {
Cookie: "x=xx;"
}
})
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
// Deals Page
const statsTable = $('tbody > tr');
const totaldeals = [];
// Loop Table for data in each row
statsTable.each(function () {
const nwline = "\n"
let date = $(this).find('td:nth-child(1)').text()
let bodydeals = $(this).find('td:nth-child(2)').text()
let newdeal = $(this).find('td:nth-child(3)').text()
let revdeal = $(this).find('td:nth-child(4)').text()
let monthlydealrev = $(this).find('td:nth-child(5)').text()
// Clear /n
if (date.includes(nwline)) {
date = date.toString().replace("\n", ""),
date = date.toString().replace("\n", "")
}
// Clear /n
if (bodydeals.includes(nwline)) {
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", ""),
bodydeals = bodydeals.toString().replace("\n", "")
}
// Clear /n
if (newdeal.includes(nwline)) {
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", ""),
newdeal = newdeal.toString().replace("\n", "")
}
// Clear /n
if (revdeal.includes(nwline)) {
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", ""),
revdeal = revdeal.toString().replace("\n", "")
}
// Clear /n (lookup jquery table functions)
if (monthlydealrev.includes(nwline)) {
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", ""),
monthlydealrev = monthlydealrev.toString().replace("\n", "")
}
totaldeals.push({
date,
bodydeals,
newdeal,
revdeal,
monthlydealrev
})
})
callback({
totaldeals
})
//console.log(totaldeals[1].date)
})
.catch(console.error);
}
function newFunction() {[getdeals(result => console.log(result.totaldeals))]}
I added a data: dealss under the res.render for the index. I also checked the index.hbs which has {{data}} in there. Shouldn't this just add the text to the screen?
Any ideas on how to print it to the view?
You just need to pass it as a variable to your hbs file:
app.get('', (req, res) => {
getdeals(result => {
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
});
})
Improvements
If you rewrite getdeals() to return a Promise instead of accepting a callback you can use async/await:
const getdeals = () => {
// NOTE THIS CHANGE, return axios promise:
return axios(url, {
/* ... */
})
.then(response => {
/* .. */
statsTable.each(function () {
/* .. */
})
return totaldeals; // NOTE we return the result instead
// of calling a callback. This will
// return a resolved Promise
})
// Don't catch here, your request will hang if an error occurs
}
Now with the change above (return axios and return the result) we can rewrite the route as:
app.get('', async (req, res, next) => { // must have async keyword!
try {
let result = await getdeals();
res.render('index', {
title: 'ClearVision',
data: result, // or result.totaldeals depending
name: 'Chris' // on what you really mean
})
}
catch (err) {
console.log(err);
next(err); // this will close the request socket
}
})

'id' not defined when referencing JSON object in NodeJs endpoint

I am new to Javascript and have been following along with a course on NodeJs and am stuck on an exercise.
I am creating a basic CRUD API for 'genres' in a made up video store.
I have created all of the above but when I call any of the endpoint with a specific id I get an error.
TypeError: Cannot read property 'id' of undefined
at genres.find.g (/Users/richardcurteis/Desktop/NodeJsCourse/vidly-node/index.js:60:65)
at Array.find (native)
at findGenre (/Users/richardcurteis/Desktop/NodeJsCourse/vidly-node/index.js:60:26)
The issue is (I think) related to the findGenre method which checks to see if a genre with the matching id exists.
function findGenre(req) {
const genre = genres.find(g => g.id === parseInt(req.params.id));
if(genre) return genre;
false;
}
The json object is currently hardcoded.
This is the request (sent from Postman):
GET > http://localhost:5000/api/genres/1
This returns:
[
{
"id": 1,
"name": "Action"
},
{
"id": 2,
"name": "Horror"
},
{
"id": 3,
"name": "Drama"
}
]
I have checked against the solution and it appears to be the same implementation as mine, save for my refactoring repetitive checks and error codes.
Maybe I'm just going code blind, but maybe someone can see what I'm missing?
I have included the entire class (?) but like I said the error is related to the checking of the json objects id.
const Joi = require('joi');
const express = require('express');
const app = express();
app.use(express.json());
const genres = [
{ id: 1, name: 'Action' },
{ id: 2, name: 'Horror' },
{ id: 3, name: 'Drama' },
];
app.get('/api/genres', (req, res) => {
res.send(genres);
});
app.post('/api/genres', (req, res) => {
const { error } = validateGenreName(req.body);
if(error) return sendValidationError(res, error);
const genre = {
id: genres.length + 1,
name: req.body.name
};
genres.push(genre);
res.send(genre);
});
app.put('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
const { error } = validateGenreName(req.body);
if(error) return sendValidationError(res, error);
genre.name = req.body.name;
res.send(genre);
});
app.delete('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) genreNotFoundError(res);
const index = genres.indexOf(genre);
genres.splice(index, 1);
res.send(genre);
});
app.get('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
// Functions
function findGenre(req) {
const genre = genres.find(g => g.id === parseInt(req.params.id));
if(genre) return genre;
false;
}
function genreNotFoundError(res) {
res.status(404).send('Genre not found');
}
function validateGenreName(genre) {
const schema = {
name: Joi.string().min(3).required()
};
return Joi.validate(genre, schema);
}
function sendValidationError(res, error) {
res.status(400).send(error.details[0].message);
}
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
Now I see your problem. You have flipped (req, res) for the .get handler.
app.get('/api/genres/:id', (res, req) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
Should be
app.get('/api/genres/:id', (req, res) => {
const genre = findGenre(req);
if(!genre) return genreNotFoundError(res);
res.send(genre);
});
The same goes for .delete and .put.

Categories

Resources