mongoose code only works if I make 2 http requests - express

Main objective:
On call to route I either get a new or update existing CartItem object.
The Object amount and total are passed in the middleware so the object total gets recalculated.
The Schema seems to properly apply its middleware properly on the first request (based on the console logs).
However I only get an object with the updated total if I make another http request.
This is beyond my understanding and I would appreciate some assistance.
Schema:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const Product = require('./Product');
const Cart = require('./Cart');
const refIsValid = require('../middleware/refIsValid');
const cartItemSchema = mongoose.Schema({
name: { type: String },
productRef: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
cartRef: { type: mongoose.Schema.Types.ObjectId, ref: 'Cart', required: true },
price: { type: Number, default: 0 },
imgUrl: { type: String },
amount: { type: Number, required: true },
total: { type: Number, default: 0 },
active: { type: Boolean, default: true },
uniqueName: { type: String, unique: true },
});
cartItemSchema.path('productRef').validate((value, respond) => {
return refIsValid(value, respond, Product);
}, 'Invalid product ref.');
cartItemSchema.path('cartRef').validate((value, respond) => {
return refIsValid(value, respond, Cart);
}, 'Invalid cart ref.');
cartItemSchema.path('price').get(function(num) {
return num.toFixed(2);
});
cartItemSchema.pre('save', async function(next) {
const refCart = await Cart.findById(this.cartRef).lean().exec();
const refProduct = await Product.findById(this.productRef).lean().exec();
const uniqueName = `${refProduct._id}_${refCart._id}`;
this.name = refProduct.name;
this.price = refProduct.price;
this.imgUrl = refProduct.imgUrl;
this.total = (this.price * this.amount).toFixed(2);
this.uniqueName = uniqueName;
next();
});
cartItemSchema.post('findOneAndUpdate', async function(result) {
console.log('TCL: result', result);
await result.save(function(err) {
if (err) {
console.error('ERROR!');
}
});
console.log('TCL: docToUpdate', result);
});
cartItemSchema.plugin(uniqueValidator);
module.exports = mongoose.model('cartItem', cartItemSchema);
controller:
static async updateOrCreate(req, res, next) {
try {
let { cartRef, productRef, amount } = req.body;
let options = { upsert: true, new: true, setDefaultsOnInsert: true };
// const uniqueName = `${productRef._id}_${cartRef._id}`;
const updateOrCreate = await CartItem.findOneAndUpdate(
{ cartRef: cartRef, productRef: productRef },
{ amount: amount },
options,
);
if (updateOrCreate) {
const result = await CartItem.findById(updateOrCreate._id);
console.log('TCL: CartItemController -> updateOrCreate -> result', result);
res.status(200).json({
isNew: false,
message: 'item updated',
productItem: result,
});
return;
}
} catch (error) {
error.statusCode = 500;
next(error);
}
}

Related

Freecodecamp execrise tracker unable to fetch date from data base

Unaable to fetch data from get request : /api/users/:_id/logs
url passed to this request : https://boilerplate-project-exercisetracker.sushilgupta4.repl.co/api/users/63a42aa0beed29d45418f2d5/logs?from=2022-10-10&to=2022-12-30&limit=2
link to my project : https://replit.com/#SushilGupta4/boilerplate-project-exercisetracker
As when I save the data for exercise for particular user the date is saved in the format of 2022-11-05T00:00:00.000+00:00.
But When I recevice date from the user request then convert date to new Date(date) and date is converted in the format of 2022-10-10T00:00:00.000Z
I think if able to change format of either one my code will work. Please look into to code and please tell me how can solve this problem.
This is the project from freecodecamp which i need complete in order to get back-end certificate.
https://www.freecodecamp.org/learn/back-end-development-and-apis/back-end-development-and-apis-projects/exercise-tracker
const express = require('express')
const app = express()
const cors = require('cors')
const mongoose = require('mongoose')
require('dotenv').config()
try {
mongoose.connect(process.env.MANGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
console.log('db connected')
} catch (err) {
console.log(err)
}
const personSchema = new mongoose.Schema({
username: {
type: String,
require: [true, 'username must be provided'],
unique: true
},
})
const exerciseSchema = new mongoose.Schema({
userId: {
type: String,
require: [true, 'userId must be provided']
},
username: {
type: String,
require: [true, 'username must be provided']
},
description: {
type: String,
require: [true, 'desc must be provided']
},
duration: {
type: Number,
require: [true, 'Duration must be provided']
},
date: {
type: String,
require: [true, 'date must be provided']
},
})
const Person = mongoose.model("Person", personSchema)
const Exercise = mongoose.model("Exercise", exerciseSchema)
app.use(cors())
app.use(express.static('public'))
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {
res.sendFile(__dirname + '/views/index.html')
});
app.post('/api/users', function(req, res) {
const username = req.body.username
const data = new Person({ username: username })
data.save((err, data) => {
if (err) {
res.json("username already Taken")
} else {
res.json({ username: data.username, _id: data._id })
}
})
})
app.get('/api/users', (req, res) => {
Person.find({}, (err, data) => {
if (!data) {
res.send("No user")
} else {
res.json(data)
}
})
})
app.post('/api/users/:_id/exercises', (req, res) => {
const _id = req.body[':_id']
const { description, duration, date } = req.body
console.log(new Date(date).toISOString())
let formatDate = new Date(date).toISOString()
console.log("format date" + formatDate)
Person.findById(_id, (err, data) => {
if (!data) {
res.send("User Not found")
} else {
const username = data.username
const addExercise = new Exercise({ userId: _id, username, description, duration, date: formatDate })
addExercise.save((err, user) => {
if (err) console.log(err)
res.json({ username: username, description: user.description, duration: user.duration, date: user.date.toDateString, _id })
})
}
})
})
app.get('/api/users/:_id/logs', (req, res) => {
const userId = req.params._id
const _id = userId
let { from, to, limit } = req.query
let filter = { userId }
let dateFilter = {}
if (from) {
from = new Date(from)
from = from.slice(23)
dateFilter['&gte'] = from
console.log(from)
}
if (to) {
dateFilter['&lte'] = new Date(to)
}
if (from || to) {
filter.date = dateFilter
}
console.log(filter)
Person.findById({ _id }, (err, data) => {
if (!data) {
res.send("userId not found")
} else {
console.log("found the id user")
const username = data.username
//{date: {$gte: new Date(from), $lte:new Date (to)}}
Exercise.find(filter).limit(limit).exec((err, data) => {
console.log("found user exericse")
if (err) console.log(err)
//console.log(data)
let customdata = data.map(items => {
let formattedDate = new Date(items.date).toDateString()
return { description: items.description, duration: items.duration, date: formattedDate }
})
if (!data) {
res.json({
"_id": userId,
"username": username,
"count": 0,
"log": []
})
} else {
console.log("inside")
res.json({
"_id": userId,
"username": username,
"count": data.length,
"log": customdata
})
}
})
}
})
})
const listener = app.listen(process.env.PORT || 3000, () => {
console.log('Your app is listening on port ' + listener.address().port)
})
I was expecting how can i fetch the data from that get request or how can i change the date format.

findOne is not a function

I am trying to create a model using Sequelize and mysql db.I am trying to post to '/students/register' it keeps giving me an error saying findOne is not a function. I tried requiring my sql but it's not working ..I also tried a different function like findAll and still not working.what seems to be the problem
const Sequelize = require('sequelize');
module.exports = function (sequelize, Sequelize) {
const Stundet = sequelize.define(
'student', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
created: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
}, {
timestamps: false
});
module.exports = Stundet;
}
routes
const Student_Info = require("../models/students")
student.post('/register', (req, res) => {
const dataToday = new Date()
const studentData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
created: dataToday
}
Student_Info.findOne({
where: {
email: req.body.email
}
})
.then(student => {
if (!student) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
studentData.password = hash
Student_Info.create(studentData)
.then(student => {
res.json({
status: student.email + 'registered'
})
})
.catch(err => {
res.send('error' + err)
})
})
} else {
res.json({
error: 'Student already registered'
})
}
})
.catch(err => {
res.send('error' + err)
})
})
module.exports = student;
When you use module.exports, you should return Stundet. You already export the whole function. And I think you should pass DataTypes instead of Sequelize.
Something like this:
module.exports = function (sequelize, DataTypes) {
const Stundet = sequelize.define(
//...
return Stundet;
}
So in your route in order to use your model:
const Sequelize = require('sequelize');
const DataTypes = sequelize.DataTypes;
let sequelize = new Sequelize(...);
const Student = require('../models/students')(sequelize, DataTypes);
I suspect that your Student_Info is null. Does you application successfully connect to the database? It helps to log... e.g.
sequelizeDB
.authenticate()
.then(() => {
console.log('Yes! DB Connection);
...
})
.catch(err => {
console.error('No! Unable to connect to DB', err);
});
... and IMHO the code reads better when you name the DB instance something other than "sequelize".

Is it possible create a moleculer service with many Validator instances?

I would like to have more than one Validator instance on my service to handle different languages. Is there any way to implement that?
Something like that:
{
en: new Validator({ messages: { ... }}),
de: new Validator({ messages: { ... }})
// ...
}
It is not available. You should create a custom multi-validators. Here is a quick example:
"use strict";
const _ = require("lodash");
const { ServiceBroker } = require("moleculer");
const BaseValidator = require("moleculer").Validators.Base;
const Validator = require("fastest-validator");
const DefaultMessages = require("fastest-validator/lib/messages");
const { ValidationError } = require("moleculer").Errors;
// --- I18N VALIDATOR CLASS ---
class I18NValidator extends BaseValidator {
constructor(messages) {
super();
0;
this.validators = {};
Object.keys(messages).forEach(lang => {
this.validators[lang] = new Validator();
this.validators[lang].messages = Object.assign({}, DefaultMessages, messages[lang]);
});
}
compile(schema) {
this.checks = {};
Object.keys(this.validators).forEach(lang => {
this.checks[lang] = this.validators[lang].compile(schema);
});
return this.checks;
}
middleware() {
return function I18NValidator(handler, action) {
// Wrap a param validator
if (action.params && typeof action.params === "object") {
const checks = this.compile(action.params);
return function validateContextParams(ctx) {
const check = checks[ctx.meta.lang] || checks["en"];
const res = check(ctx.params);
if (res === true)
return handler(ctx);
else
return Promise.reject(new ValidationError("Parameters validation error!", null, res));
};
}
return handler;
}.bind(this);
}
}
let broker = new ServiceBroker({
logger: true,
validation: true,
validator: new I18NValidator({
"en": {
"string": "The '{field}' field must be a string!"
},
"hu": {
"string": "A '{field}' mezőnek szövegnek kell lennie!"
}
})
});
// --- TEST BROKER ---
broker.createService({
name: "greeter",
actions: {
hello: {
params: {
name: { type: "string", min: 4 }
},
handler(ctx) {
return `Hello ${ctx.params.name}`;
}
}
}
});
broker.start()
// No meta lang
.then(() => broker.call("greeter.hello", { name: 100 }).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data))
// "hu" lang
.then(() => broker.call("greeter.hello", { name: 100 }, { meta: { lang: "hu" }}).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data))
// "en" lang
.then(() => broker.call("greeter.hello", { name: 100 }, { meta: { lang: "en" }}).then(res => broker.logger.info(res)))
.catch(err => broker.logger.error(err.message, err.data));
It reads the lang from the ctx.meta.lang but you can change it for your case.

Hapi Lab The following leaks were detected:lr

Here Hapi Lab why Test failed when all the tests are passed why the test is failed.
In this new question why I get The following leaks were detected:lr even if there is no global var in the code.
Running this simple test
var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var server = require('../../');
lab.experiment('Users', function () {
lab.test('create joi required', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
lab.test('create', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload:{
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var token = response.result.token;
var payload = options.payload;
Code.expect(response.statusCode).to.equal(201);
done();
});
});
});
2 tests complete
Test duration: 363 ms
The following leaks were detected:lr
but I don't see any lr var !
and the strange is if I run this
payload.passdword
instead of
payload.password
var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var server = require('../../');
lab.experiment('Users', function () {
lab.test('create joi required', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
lab.test('create', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload:{
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
passdword: 'mysecret'
}
};
server.inject(options, function(response) {
var token = response.result.token;
var payload = options.payload;
Code.expect(response.statusCode).to.equal(201);
done();
});
});
});
I've got
1 of 2 tests failed
Test duration: 73 ms
No global variable leaks detected
with no warning about lr var.
So I don't know which way to turn :(
Can help me, please ?
UPDATE
controller
'use strict';
/**
* Module dependencies.
*/
var BcryptUtil = require('../utils/bcrypt');
var JwtUtil = require('../utils/jwt');
var Models = require('../models');
var ReplyUtil = require('../utils/reply');
var User = Models.users;
exports.create = function create(request, reply) {
var params = request.payload;
params.password = BcryptUtil.generateHash(params.password);
params.roles =JSON.stringify(['user']);
User
.create(params)
.then(function(user) {
var token = JwtUtil.getUserToken(user);
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.set('user_'+user.userId, token);
return reply(ReplyUtil.ok(token)).created('/api/users/' + user.userId);
})
.catch(function(err){
if(err instanceof Models.Sequelize.ValidationError){
return reply(ReplyUtil.badData(err,params));
}
return reply(ReplyUtil.badImplementation(err));
});
};
exports.findAll = function (request, reply) {
User
.findAll({
order: [['createdAt','DESC']],
attributes: ['userId', 'firstname', 'lastname', 'username', 'email']
})
.then(function(users) {
return reply(ReplyUtil.ok(users));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.findById = function (request, reply) {
var userId = request.params.userId;
User
.findById(
userId,
{
attributes: ['userId', 'firstname', 'lastname', 'username', 'email']
})
.then(function(user) {
if(!user){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(user));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.update = function (request, reply) {
var userId = request.params.userId;
var params =request.payload;
User
.update(params,{
where: {
userId: userId
}
})
.then(function(rows) {
var affectedRows = rows.pop();
if(!affectedRows){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(affectedRows));
})
.catch(function(err){
if(err instanceof Models.Sequelize.ValidationError){
return reply(ReplyUtil.badData(err,params));
}
return reply(ReplyUtil.badImplementation(err));
});
};
exports.destroy = function (request, reply) {
var userId = request.params.userId;
User
.destroy({
where: {
userId: userId
}
})
.then(function(rows) {
if(!rows){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(rows));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.signIn = function (request, reply) {
var params = request.payload;
User
.findOne({
where: {
email: params.email
}
})
.then(function(user) {
if(!user){
return reply(ReplyUtil.invalidPassword());
}
if(BcryptUtil.authenticate(params.password, user.password)){
var token = JwtUtil.getUserToken(user);
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.set('user_'+user.userId, token);
return reply(ReplyUtil.ok(token));
}
return reply(ReplyUtil.invalidPassword());
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.logOut = function (request, reply) {
var userId = request.auth.credentials.jti;
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.del('user_'+userId);
return reply();
};
exports.methodNotAllowed = function (request, reply) {
return reply( ReplyUtil.methodNotAllowed() );
};
route
'use strict';
/**
* Module dependencies.
*/
var User = require('../controllers/users');
var Validator = require('../validations/users');
/**
* Resource configuration.
*/
var internals = {};
internals.resourcePath = '/users';
module.exports = function() {
return [
{
method: 'POST',
path: internals.resourcePath,
config : {
handler: User.create,
validate: Validator.create
}
},
{
method: 'GET',
path: internals.resourcePath,
config : {
handler : User.findAll,
auth: {
strategy: 'token',
scope: ['admin']
}
}
},
{
method: 'GET',
path: internals.resourcePath + '/{userId}',
config : {
handler : User.findById,
validate: Validator.findById,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'PUT',
path: internals.resourcePath + '/{userId}',
config : {
handler: User.update,
validate: Validator.update,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'DELETE',
path: internals.resourcePath + '/{userId}',
config : {
handler: User.destroy,
validate: Validator.destroy,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'POST',
path: internals.resourcePath + '/signin',
config : {
handler: User.signIn,
validate: Validator.signIn
}
},
{
method: 'GET',
path: internals.resourcePath + '/logout',
config : {
handler : User.logOut,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: '*',
path: internals.resourcePath + '/{somethingss*}',
config : {
handler: User.methodNotAllowed
}
}
];
}();
I no I am a little late, but just in case anyone else has this problem. It's a problem with bcrypt. I had a similar problem where whenever I used bcrypt-nodejs it would give me The following leaks were detected:lr, password, but when I changed to reqular bycrypt it worked with no leaks. Try updating your bycrypt version.
Just add all leakimg elements to ignore list
"test": "lab -c -L -I 'Reflect,core,_babelPolyfill,regeneratorRuntime,__core-js_shared__ css'",

How to get socket.io to recognize when model has been updated

I'm relatively new to MEAN/Mongoose/socket.io and am missing something that seems like it'd be basic, but regardless, it's keeping me from moving forward.
I'm keeping track of a group's meetings and have the following Schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ScheduleSchema = new Schema({
description: String,
meetingPlace: String,
office: String,
start: { type: Date, default: Date.now },
end: { type: Date, default: Date.now },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
type: Schema.Types.ObjectId,
ref: 'User'
},
timestamp: { type: Date, default: Date.now },
active: Boolean
});
ScheduleSchema.statics = {
load: function(cb) {
this.find({})
.populate({path:'participants'})
.exec(cb);
}
};
module.exports = mongoose.model('Schedule', ScheduleSchema);
When I create a new item in the Schedule model, this code sets it in motion (from a client-side controller):
$http.post('/api/schedules', { description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
And because of this code:
'use strict';
var express = require('express');
var controller = require('./schedule.controller');
var router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports = router;
I think the request is being routed to controller.create, which is the following:
'use strict';
var _ = require('lodash');
var Schedule = require('./schedule.model');
// Get list of schedules
exports.index = function(req, res) {
Schedule.load(function (err, schedules) { /*.find*/
if(err) { return handleError(res, err); }
return res.json(200, schedules);
});
};
// Get a single schedule
exports.show = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
return res.json(schedule);
});
};
// Creates a new schedule in the DB.
exports.create = function(req, res) {
var promise = Schedule.create(req.body, function(err, schedule) {
if(err) { return handleError(res, err); }
return res.json(201, schedule);
});
};
// Updates an existing schedule in the DB.
exports.update = function(req, res){
var updatedMeeting = req.body;
var id = updatedMeeting._id;
delete updatedMeeting._id;
Schedule.update({_id : id}, updatedMeeting, { }, function (err, numberAffected, raw) {
if (err) return res.json(500, err);
updatedMeeting._id = id;
return res.json(201, updatedMeeting);
});
};
// Deletes a schedule from the DB.
exports.destroy = function(req, res) {
Schedule.findById(req.params.id, function (err, schedule) {
if(err) { return handleError(res, err); }
if(!schedule) { return res.send(404); }
schedule.remove(function(err) {
if(err) { return handleError(res, err); }
return res.send(204);
});
});
};
function handleError(res, err) {
return res.send(500, err);
}
After exports.create is run, and I don't know how, but something sends it over to here:
'use strict';
var Schedule = require('./schedule.model');
exports.register = function(socket) {
Schedule.schema.post('save', function (doc) {
onSave(socket, doc);
});
Schedule.schema.post('remove', function (doc) {
onRemove(socket, doc);
});
Schedule.schema.post('update', function (doc) {
onUpdate(socket, doc);
});
}
function onSave(socket, doc, cb) {
console.log('**********onSave**********');
Schedule
.findOne({ _id : doc._id })
.populate('participants')
.exec(function (err, event) {
if (err) return handleError(err);
socket.emit('schedule:save', event);
});
}
function onRemove(socket, doc, cb) {
socket.emit('schedule:remove', doc);
}
function onUpdate(socket, doc, cb) {
console.log('**********onUpdate**********');
socket.emit('schedule:update', doc);
}
and the line socket.emit('schedule:save', event); is called.
So that's all great, but when I update the Schedule model, I can't get socket to emit 'schedule:update' because it never gets to the "onUpdate" function in the code snippet just above this.
Starting from the client-side call:
$http.patch('/api/schedules/' + info._id, { _id: info._id, description: info.description, participants: attendees, meetingPlace: info.meetingPlace, office: info.office, start: info.start, end: info.end, timestamp: new Date(), active: true });
and the router sends that to exports.update, the model is updated, but socket doesn't emit anything, so all the clients connected see the old schedule information.
How and where can I relay to socket.io that the model has been updated? I think what I'm missing is how Schedule.schema.post('some action... gets called within the exports.register function, but I've been looking at this for a while and could use a hand - thanks.