findandupdate return null with promise all - mongoose - javascript

I am trying to create a nodejs API separated by controllers and routes. I am trying to findandupdate in multiple collections and then put them in multiple promises to return a single response but i get just a null what am i doing wrong below ?
controller.js
var x = (req, res, next, userID, product) => {
let query = {
uid: userID
}
let update = {
$push: {
product: product,
}
}
let options = {
safe: true,
new: true,
upsert: true
}
Model.findOneAndUpdate(query, update, options).exec()
.then(result => {
return true
})
.catch(err => {
console.log(err);
res.status(500).json({ error: err });
})
};
module.exports = x;
Route.js
const controller = require('./user-product')
router.post('/api', function (req, res, next) {
var p1 = controller(req, res, next, userID, product)
var allDone = Promise.all([p1])
allDone
.then(function (e) {
res.send(e) //this is null
})
.catch(function (e) {
console.log(e);
})
});

You are not returning promise from the controller function. You are just returning the result and the error
So, Instead you should return the promise inside the controller function
Route.js
const controller = require('./user-product')
router.post('/api', function (req, res, next) {
var p1 = controller(req, res, next, userID, product)
var allDone = Promise.all([p1])
allDone.then(function (e) {
res.send(e)
})
.catch(function (e) {
console.log(e);
})
})
controller.js
var x = (req, res, next, userID, product) => {
let query = { uid: userID }
let update = { $push: { product: product }}
let options = {
safe: true,
new: true,
upsert: true
}
return Model.findOneAndUpdate(query, update, options).exec()
}
module.exports = x;
And probably easier with the async await syntax
Route.js
const controller = require('./user-product')
router.post('/api', async(req, res, next) => {
try {
const p1 = await controller(req, res, next, userID, product)
console.log(p1)
} catch (err) {
console.log(err)
}
})
controller.js
var x = async(req, res, next, userID, product) => {
let query = { uid: userID }
let update = { $push: { product: product }}
let options = {
safe: true,
new: true,
upsert: true
}
return Model.findOneAndUpdate(query, update, options).exec()
}
module.exports = x;

Related

SINON - an issue mocking a middleware

I have the following middleware in a Node.js REST API
const Authorized = (req, res, next) => {
if (!req.headers["authorization"]) {
res.status(401).send("Unauthorized");
} else {
jwt.verify(req.headers["authorization"], PRIVATE_KEY, (err, decoded) => {
if (err) {
res.status(403).send("Forbidden");
} else {
req.businessId = decoded.businessId;
req.roleId = decoded.roleId;
next();
}
});
}
};
As you can see I'm adding to variables to the request object
In the mockup of my tests I'm trying to do this:
sandbox = sinon.createSandbox();
sandbox.stub(Authorized, "Authorized")
.callsFake(async (req, res, next) => {
req.businessId = businessAux.id;
return next();
});
But this doesn't work and my actual function to be tested needs this variable:
listPermissionAndRolesByPk() {
this.app.get(`${config.API_PATH}/permissionrolebypk`, Authorized.Authorized, async (req, res) => {
const id = req.query.id;
const businessId = req.businessId;
if (!id) return res.status(400).send(messages[23].message.replace("${object}", "Permission Id"));
try {
const permission = await PermissionDAO.getPermissionAndRolesByPk(id, businessId ? businessId : 0);
if (permission) {
return res.status(200).json(permission);
} else {
return res.status(404).send(messages[8].message.replace("${object}", "Permission"));
}
} catch (error) {
return res.status(error.httpCode).send(error.message);
}
});
}
Any help will be appreciated.

why ID is not defined?

When i send DELETE request i got message error
ReferenceError: id is not defined
at Object.removeOne (...\services\user.js:16:38
I have no idea what id about in \services\user.js, and why it is not defined...
./generalRepository.js
function Repository() {}
Repository.prototype.findAndRemoveById = findAndRemoveById;
function findAndRemoveById(id, callback) {
var model = this.model;
var query = model.deleteOne({
_id: id
});
query.exec(callback);
}
module.exports = Repository;
.routers/user.js
const router = require("express").Router();
const userService = require("../../services/user");
router.delete("/:id", (req, res, next) => {
userService.removeOne(String(req.params.id), (err, data) => {
if (!err) {
res.send('success delete query');
} else {
console.log("wrong delete query");
res.status(400);
res.end();
}
});
});
module.exports = router;
.services/user.js
const UserRepository = require("../repositories/UserRepository");
module.exports = {
removeOne: () => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};
You need to update removeOne function to following by expecting arguments (id and callback) to be passed when it is being called.
const UserRepository = require("../repositories/UserRepository");
module.exports = {
removeOne: (id, callback) => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};
Maybe you should be like this:
module.exports = {
removeOne: (id) => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};

getModel() is not defined - but it was defined

I am creating file upload functionality on Cloud Storage. Here's the backend code:
const Storage = require('#google-cloud/storage')
const storage = Storage({
projectId: 'a-1485'
})
const bucket = storage.bucket('a-1485.appspot.com')
function getPublicUrl (filename) {
return 'https://storage.googleapis.com/a-1485.appspot.com/${filename}'
}
function sendUploadToGCS (req, res, next) {
if (!req.file) {
return next()
}
const gcsname = Date.now() + req.file.originalname
console.log(gcsname)
const file = bucket.file(gcsname)
const stream = file.createWriteStream({
metadata: {
contentType: req.file.mimetype
}
})
stream.on('error', (err) => {
req.file.cloudStorageError = err
next(err)
})
stream.on('finish', () => {
req.file.cloudStorageObject = gcsname
req.file.cloudStoragePublicUrl = getPublicUrl(gcsname)
next()
})
stream.end(req.file.buffer);
}
module.exports = {
getPublicUrl,
sendUploadToGCS
}
In my app.js file:
app.post('/upload-image', multer.single('image'), images.sendUploadToGCS, (req, res, next) => {
let data = req.body
console.log(data)
if (req.file && req.file.cloudStoragePublicUrl) {
data.imageUrl = req.file.cloudStoragePublicUrl
}
getModel().create(data, (err, savedData) => {
if (err) {
next(err)
return
}
res.redirect(`${req.baseUrl}/${savedData.id}`);
})
}
)
However, when I upload an image I get an error thrown saying 'getModel()' is not defined. But as you can see above it is defined. What is the problem?

Sequelize, Deleting multiple rows with React

I'm using React with a Postgres DB with Sequelize.
within my project, I have a promise that is "suppose" to delete all songs relating to the album, using the the Album.id in my state.
** Instead of deleting the rows of songs relating to the Album, after the delete request in the database, it removes the value of the AlbumId of the song. **
Is there an update I am missing
When I console.log outside of the service and in the promise this.state.Album.id remains the same.
It hit's the server with the appropriate number.
This is the function within the React Component
DeleteAlbum (e) {
e.preventDefault()
axios.delete(`${domain}/albums/${this.state.Album.id}`)
.then((res) => {
axios.delete(`${domain}/songs/ByAlbumId/${this.state.Album.id}`)
.then((res) => {
window.location.href = '/#/'
})
.catch((error) => {
console.log('axios error', error)
})
})
.catch((error) => {
console.log('axios error', error)
})
}
This is the Database to the Songs Route
const express = require('express')
const router = express.Router()
const bodyParser = require('body-parser')
const db = require('./../models')
const Song = db.Song
router.use(bodyParser.json({ extended: false }))
const exists = (req) => {
if (typeof parseInt(req.params.id) === 'number') {
Album.findOne({
where: {
id: req.params.id
}
})
.then((album) => {
if (album) {
return true
};
return false
})
.catch((err) => {
return false
})
} else {
return false
}
}
router.delete('/ByAlbumId/:id', function (req, res) {
Song.destroy({
where: {
AlbumId: req.params.id
}
})
.then(function (data) {
return res.json(data)
})
.catch(function (err) {
return res.json({ error: err})
})
})
router.delete('/:id', function (req, res) {
if (exists) {
Song.destroy({
where: {
id: req.params.id
}
})
.then(function (data) {
return res.json(data)
})
.catch(function (err) {
return res.json({ error: err})
})
} else {
res.json({success: false})
}
})
This is the Album Route
const express = require('express')
const router = express.Router()
const bodyParser = require('body-parser')
const db = require('./../models')
const Album = db.Album
router.use(bodyParser.json({ extended: false }))
const exists = (req) => {
if (typeof parseInt(req.params.id) === 'number') {
Album.findOne({
where: {
id: req.params.id
}
})
.then((album) => {
if (album) {
return true
};
return false
})
.catch((err) => {
return false
})
} else {
return false
}
}
router.delete('/:id', function (req, res) {
if (exists) {
Album.destroy({
where: {
id: req.params.id
}
})
.then(function (data) {
return res.json(data)
})
.catch(function (err) {
return res.json({ error: err})
})
} else {
res.json({success: false})
}
})
If I place console logs all over the place, the output is what I expect it to be. There's is just something going wrong with Deleting two songs from my app. I can delete multiple songs if I hit the server directly with postman
Any idea?
You are actually destroying the album, before you destroy the songs.
In this case, since they probably have onDelete: 'SET NULL' option added, you will just de-associate the songs with that album.
Your fix will be to just replace the order of your calls :
// First we delete the songs and then the album
axios.delete(`${domain}/songs/ByAlbumId/${this.state.Album.id}`)
.then((res) => {
axios.delete(`${domain}/albums/${this.state.Album.id}`)
.then((res) => {

How to wait on sequelize executing a findOne

I've got a route using Sequelize.js
app.get('/api/users/:username', (req, res) => {
const foundUser = getUserByUsername(req.params.username);
console.log(`foundUser = ${foundUser}`);
return res.send(foundUser);
});
the getUserByUsername function is as follows
const getUserByUsername = username => {
Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return response.dataValues;
});
};
I hoped on getting the object in my const foundUser in my route, but it seems I need to wait until the findOne has been executed, because in my console I can see that the log of foundUser (which is undefined then) is executed before the function getUserByUsername
foundUser = undefined
Executing (default): SELECT `id`, `username`, `instakluiten`, `role`, `createdAt`, `updatedAt` FROM `viewers` AS `viewer` WHERE `viewer`.`username` = 'instak' LIMIT 1;
{ id: 19,
username: 'instak',
instakluiten: 18550,
role: 'moderators',
createdAt: 2016-10-02T16:27:44.000Z,
updatedAt: 2016-10-09T10:17:40.000Z }
How can I make sure that my foundUser will be updated with the data áfter the user has been found?
You have to return the promise that Sequelize creates and then wait for it to resolve. So the getUserByUsername becomes:
const getUserByUsername = username => {
return Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return response.dataValues;
});
};
and in the route:
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username).then(foundUser => {
res.send(foundUser);
});
});
This is because you need to keep the chain of promises. If you forget to return it, the function returns undefined end even if the promise is finallly resolved, the value it resolves to never gets up back in the chain.
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username, function(err, result){
const foundUser = result;
console.log(`foundUser = ${foundUser}`);
res.send(foundUser);
});
});
const getUserByUsername = function(username, callback) {
Viewer.findOne({
where: {username}
}).then(response => {
console.log(response.dataValues);//the object with the data I need
return callback(null, response.dataValues);
});
};
You can avoid it with promise or with callback
app.get('/api/users/:username', (req, res) => {
getUserByUsername(req.params.username, function(err, foundUser) {
if (!err) {
console.log(`foundUser = ${foundUser}`);
return res.send(foundUser);
} else {
res.send(err)
}
});
});
const getUserByUsername = (username, callback) => {
Viewer.findOne({
where: {
username
}
}).then(response => {
console.log(response.dataValues); //the object with the data I need
return callback(null, response.dataValues);
});
};

Categories

Resources