Chaining promises when using ES6 classes - google-app-engine

I've the following code to upload a file to Google cloud and afterwards get the public URL. However, it quickly becomes messy when chaining the promises.
Could anyone help with a specific example of chaining the promises in a cleaner way in my example?
let routes = function(imageUploader) {
router.post('/upload',
imageUploader.getMulter().single('image'),
(req, res) => {
imageUploader.uploadFilePromise(req.file.path)
.then((filename, error) => {
if(error) throw new Error(error);
imageUploader.getExternalUrl(filename)
.then((publicUrl, error) => {
if(error) throw new Error(error);
console.log(publicUrl);
})
})
});
return router;
};
I included my ImageUploader class is here as well just as an FYI. Any other best practices suggestions are also much welcome.
const Multer = require('multer'), gcloudStorage = require('./vendors/gcloud');
class ImageUploader {
constructor() {
this.bucket = gcloudStorage;
}
uploadFile(req, res, next) {
if(!req.file) {
next();
}
this.bucket.upload(req.file.path, (err, file) => {
if(err) throw new Error(err);
req.file.publicUrl = this.getExternalUrl(req.file.name)
})
}
uploadFilePromise(path) {
return new Promise((resolve, reject) => {
this.bucket.upload(path, (err, file) => {
if(err) reject(err);
resolve(file.name);
})
})
}
getExternalUrl(filename) {
return new Promise((resolve, reject) => {
this.bucket.file(filename).getSignedUrl({
action: 'read',
expires: '03-17-2025'
}, (err, url) => {
if (err) reject(err);
resolve(url);
});
});
}
storage() {
return Multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname)
}
});
}
getMulter() {
return require('multer')({ storage: this.storage() });
}
}
module.exports = ImageUploader;

A callback passed to then() receives one and only one argument, not two.
And it can (should) return a value, or another promise, in order to allow further chaining.
If one of the callbacks throws, or returns a rejected promise, you can handle the error with a catch at the end of the chain:
imageUploader.uploadFilePromise(req.file.path)
.then(filename => imageUploader.getExternalUrl(filename))
.then(publicUrl => console.log(publicUrl))
.catch(error -> console.log(error));

Simply regarding chaining promises with .then, I find it cleaner to not add an indent.
ex.
promise
.then(do something)
.then(do something else);

Related

Axios promise will never resolve

For the life of me, I can never get my Axios.post promise to resolve.
I know that my front end and backend are perfectly connected.
Try/catch blocks to return the resolved promise haven't worked either.
No matter what I do, I can never get inside of my promise.then() function. What am I doing incorrectly in my backend file?
CODE THAT HASN'T WORKED TO RESOLVE THE PROMISE
async handleDateSubmit() {
let resolvedPromise = await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
resolvedPromise
.then(response => {
//I can never get to here.
console.log("Made it inside");
})
.catch(err => console.log(err));
}
//---attempt two----//
async getResolvedPromise() {
try {
return await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
} catch (error) {
console.log(error);
}
}
async handleDateSubmit() {
let resolvedPromise = this.getResolvedPromise();
//work with resolvedPromsie
}
CURRENT CODE
//------------send_info.js front end file----------//
handleDateSubmit() {
Axios.post('http://localhost:3001/get_number_of_dates_from_email', {
email: this.state.user_email_m
})
.then((response) => {
//I can never get to here.
console.log("Made it inside");
})
.catch(err => console.log(err));
}
//---------------server.js backend file---------------//
router.route('/get_number_of_dates_from_email').post(function (req, res) {
//"user_email" is correct in my schema model and "req.body.email" is always what it should be
User.findOne({ user_email: req.body.email }, (err, foundUser) => {
console.log("Inside of findOne()");
if (err) {
return res.send(err);
}
else {
let numDates = foundUser.dates_list.length;
//I always get here and numDates is always correct
console.log("Number of dates: ", numDates);
return res.json({ "numDates": numDates }); //Should I be using res.send()?
}
});
});
It seems like you're confusing promises and resolved promises at times in your code
// Attempt one
async handleDateSubmit() {
try {
let resolvedPromise = await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
// Here resolvedPromise as stated by its name is not a promise anymore, thus you can't use .then()
// You can directly work with resolvedPromise as it contains the response.
} catch (e) {
console.error(e)
}
}
// Attempt two
async getResolvedPromise() {
try {
// Here you're returning the resolved promise, but the async await syntax turn your function into an AsyncFunction object
// This type of function will wrap the return value in a promise if it's not one
return await Axios.post(
"http://localhost:3001/get_number_of_dates_from_email",
{
email: this.state.user_email_m
}
);
} catch (error) {
console.log(error);
}
}
async handleDateSubmit() {
// Thus you need to await the result of your function
let resolvedPromise = await this.getResolvedPromise();
}

Firestore document not deleted

I'm trying to delete a doc from Firestore, I don't get an error but the document is still in the database: simply as that, it keeps on being in the collection it belongs to.
The cloud function is:
exports.countdowns_crud = functions.https.onRequest((request, response) => {
var req = request;
var res = response;
if (request.method === 'DELETE') {
const countdownId = request.path.split('/')[1];
const deleteOperation = db.DeleteCountdown(countdownId);
if (!deleteOperation) {
console.log('Delete operation result: ', deleteOperation);
cors(req, res, () => {
res.status(204).send("DELETED");
});
}
else {
console.error(addOperation);
cors(req, res, () => {
res.status(500).send("INTERNAL SERVER ERROR");
});
};
return;
}
cors(req, res, () => {
res.status(405).send("NOT ALLOWED");
return;
});
})
The DeleteCountdown function is in another module:
module.exports = {
DeleteCountdown: (countdownId) => {
const countdownsCollection = app.firestore.collection('countdowns');
countdownsCollection.doc(countdownId).delete()
.then((res) => {
console.log('Result: ', res);
return null;
})
.catch((e) => {
console.error(`unable to delete the countdown ${countdowmnId}: ${e}`);
return e;
});
}
}
This is the logic in a google cloud function, which it's correctly invoked by my react app upon deletion. The passed id is correct, no error is returned, but the doc keeps on living in the collection.
I had the same problem, no error and null returned (exactly the same when it works) because I forgot to set the rules to allow writes (or specifically deletions) directly in the Firebase console or by the firestore.rules files

Express API DELETE

I am trying to add DELETE to my api, but am getting a 404: Not Found for everything I try. All of the GET and POST methods work. This is using the "Points" model that contains points, each with a unique id. I am trying to add a call to delete a point by its id.
Action
export function deletePointById(identifier) {
return dispatch => {
return axios.delete('/api/points/' + identifier)
}
}
Route for DELETE (doesn't work)
router.delete('/:identifier', (req, res) => {
Points.remove({
id: req.params.identifier
}), function (err, user) {
if (err) {
return res.send(err);
}
res.json({ message: 'Deleted' });
};
});
Here is an existing GET that works fine
Action
export function getPointsBySession(session){
return dispatch => {
return axios.get('/api/points/session/' + session)
}
}
Route for GET
router.get('/session/:session', (req, res) => {
Points.query({
select: ['id', 'number', 'quadrant', 'level', 'title', 'category'],
where: {sessionId: req.params.session}
}).fetchAll().then(point => {
res.json({ point });
})
});
It looks like your issue might be that theres an extra brace and semicolon ( };) in your code:
router.delete('/:identifier', (req, res) => {
Points.remove({
id: req.params.identifier
}), function (err, user) {
if (err) {
return res.send(err);
}
res.json({ message: 'Deleted' });
}; // <-- HERE
});
You also need to remove the semicolon on the line above the one I added the comment to.
It will look like this:
router.delete('/:identifier', (req, res) => {
Points.remove({
id: req.params.identifier
}), function (err, user) {
if (err) {
return res.send(err);
}
res.json({ message: 'Deleted' })
});

NodeJS MSSQL driver Passing data to a calling function

Working with an SQL Server in NodeJS, and I have confirmed that it's pulling the data, but I'm trying to pass the data back to a calling function.
Here's my call to the function that queries the database:
const dbq = require('./dbquery.js');
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
var set = dbq.getPersonById(req.params.id);
console.log(set);
});
and here is the function inside dbquery.js:
qry.getPersonById = (id) => {
mssql.connect(sqlConfig).then(() => {
new mssql.Request().query(`select * from FNVPeople where IndivID=${id}`).then((record)=>{
console.log(record);
return record;
}).catch((err)=>{
console.log(err);
});
});
}
Should my call to the function look like this?
var dataset = await(dbq.getPersonById(req.params.id));
Because of Async nature.
Try for the following:
const dbq = require('./dbquery.js');
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
dbq.getPersonById(req.params.id, function(err, res){
console.log(res);
});
});
qry.getPersonById = (id, callback) => {
mssql.connect(sqlConfig).then(() => {
new mssql.Request().query(`select * from FNVPeople where IndivID=${id}`).then((record)=>{
console.log(record);
callback(null, record);
}).catch((err)=>{
console.log(err);
});
});
}
You should return the promise to the client to deal with (note two additional returns):
qry.getPersonById = (id) => {
return mssql.connect(sqlConfig).then(() => {
return new mssql.Request().query(`...`)
.then((record)=>{
console.log(record);
return record;
})
.catch((err)=>{
console.log(err);
});
});
}
Then, the client deals with the promise
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
dbq.getPersonById(req.params.id).then( set =>
console.log(set);
);
});
By rewriting promises to async/await you could even have
qry.getPersonById = async (id) => {
try {
await mssql.connect(sqlConfig)
var record = await new mssql.Request().query(`...`);
console.log(record);
return record;
}
catch (err) {
console.log(err);
}
}
and
app.get('/:id', async (req, res) => {
reqlog(`/${req.params.id}`);
var set = await dbq.getPersonById(req.params.id);
console.log(set);
});

How to write test cases for catch block of actions using sinon, mocha , chai in reactsjs?

My action is like this:-
export function requestModel(param) {
return (dispatch) => {
dispatch({
type: 'REQUEST',
loadModel: true,
});
dispatch(getModel(param)).then(response => response.json())
.then(model=>
dispatch(receiveModel(model)))
.catch(() => {
dispatch({
type: MODEL_FAILURE,
loadModel:false,
});
});
};
}
I wrote the test case to cover catch block as :-
it('requestModel() handle sxception...............', (done) => {
const response = { json: () => data };
// call the actual function with stubbed function
try {
dispatchStub.withArgs(getDeviceApiStub).returns(Promise.reject(response));
dispatchStub.withArgs(getDeviceActionStub).returns(Promise.resolve(response));
const returnFunction = modelAction.requestModel(actionParam);
returnFunction(dispatchStub);
done();
sinon.assert.calledWithMatch(dispatchStub, {
type: MODEL_FAILURE,
loadModel:false,
});
done();
} catch (err) {
done(err);
}
});
but the issue is that before catch block of method it is calling the sinon.assert as i wrote above. How to deal with this, i used async await also but same issue is coming, Is there any why so that I can write test cases for the catch block of my action in reactjs?

Resources