I am developing an API to create a warehouse structure. Because we are using a microservice architecture I need to make a request via rabbitmq to another microservice to generate the address for the new warehouse.
Therefore I use the ampq consume function wrapped in a function which returns a promise. When I hit the endpoint the first time the promise gets resolved and I can continue with my data. But in the second request, the promise will not get resolved.
Maybe it's for an obvious reason but at the moment I don't get it.
So here is my code:
routes.js
router.post('/', (req, res) => {
...
const validate = ajv.compile(warehoseSchema);
const valid = validate(req.body);
if (valid) {
sendToQueue('addressMgmt', req.body.address);
consume()
.then((result) => {
const {
id_address: idAddress,
license_plate: licensePlate,
town,
} = result.body;
createWarehouseHandler(
customernumber, req.body, idAddress, licensePlate, town,
)
.then((insertId) => {
res.json({
id: 'warehouses02',
message: `Warehouse with id ${insertId} successfully created`,
});
})
.catch((err) => {
res.status(err.status).json({
id: err.id,
message: err.message || err.sqlMessage,
});
});
}).catch((err) => {
res.status(err.status).json({
id: err.id,
message: err.message || err.sqlMessage,
});
});
} else {
res.status(417).json({
id: 'warehouses01',
message: `Invalid JSON: ${ajv.errorsText(validate.errors)}`,
});
}
});
const consume = () => new Promise((resolve, reject) => {
const q = 'warehouseMgmt';
amqpCh.consume(q, (msg) => {
const message = JSON.parse(msg.content.toString());
if (Object.keys(message).includes('body')) {
resolve(message);
} else {
const err = new Error();
err.status = 500;
err.id = 'rabbit01';
err.message = 'No message was cosumed';
reject(err);
}
}, { noAck: true });
});
On the first request consume().then() gets called but on the second and following requests, it doesn't.
Thanks for your help
Related
I'm refactoring my code to remove a "callback hell" using Promises, but encountered an error that I cannot pass. My code receives list of IDs and processes them making few database calls, that is why I had this "callback hell".
Everything worked fine until Promises. The res is equal 0 when I had to respond back to the client.
function processVMDelete(returnedVMIDs){
return new Promise((resolve, reject) => {
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlUpdateDELETE_STATE_ByVMID, [
'DELETE',
returnedVMIDs
], function(err, rows) {
if (err){
reject(err);
}
console.log('finish update');
// dont' need to return anything here
resolve(0);
});
mariasqlClient.end();
});
}
function getListExpVM(){
return new Promise((resolve, reject) => {
var vmList = [];
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlSearch_ByUSERNAMEAndSTATE, [
requesterUsername,
'ACTIVE'
], function(err, rows) {
if (err){
reject(err);
}
vmList = filterExpiredVMs(rows);
var response = {
status : 200,
success : 'Successfull',
data : vmList,
requester: requesterUsername
};
resolve(response);
});
mariasqlClient.end();
});
}
router.post('/processVMs', function(req, res) {
var returnedVMIDs = JSON.parse(req.body.data);
processVMDelete(returnedVMIDs)
.then(res => {
console.log('done');
// check if there is more available for the user:
getListExpVM()
.then(response => {
console.log('sending back list of VMs');
//===>>> ERROR HERE: res.end is not a function
res.end(JSON.stringify(response));
})
.catch(err => {
console.log('error', err.message);
logger.error("Error getting expired VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
})
.catch(err => {
console.log('error', err.message);
logger.error("Error processing VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
});
You've redefined res with this:
processVMDelete(returnedVMIDs)
.then(res => {...})
This will hide the higher scoped res associated with the overall request (the one you need to use for res.end()). Change the name of this one to something else like result and then change the corresponding references that use this result.
What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch
I try the response model after create data but it doesn't work. It shows data on "console.log" and it didn't respond when I use "resolve({})".
In the routers.js:
const register = require('./functions/register');
module.exports = router => {
router.get('/', (req, res) => res.end('Welcome to Idol Fan With your Idol !'));
//======REGISTER & LOGIN WITH SOCIAL =====
router.post('/socialuser', (req, res) => {
const social_id = req.body.social_id;
const token = req.body.token;
const name = req.body.name;
const email = req.body.email;
const photoprofile = req.body.photoprofile;
const tokenfirebase = req.body.tokenfirebase;
if (!social_id) {
res.status(400).json({message: 'Invalid Request !'});
} else {
register.registerUser(social_id, name, email, photoprofile, token, tokenfirebase)
.then(result => {
res.status(result.status).json({status: result.status, message: result.message, user: result.user})
})
.catch(err => res.status(err.status).json({message: err.message}));
}
});
}
Function Register.js:
const userfan = require('../models/user');
exports.registerUser = (social_id, name, email, photoprofile, token,
tokenfirebase) =>
new Promise((resolve, reject) => {
const d = new Date();
const timeStamp = d.getTime();
userfan.find({social_id: social_id})
.then(users => {
if (users.length == 0) {
let newUser = new userfan({
social_id: social_id,
name: name,
email: email,
photoprofile: photoprofile,
token: token,
tokenfirebase: tokenfirebase,
created_at: timeStamp
});
newUser.save()
.then(doc => {
console.log("run... " + doc);
resolve({
status: 200,
message: 'User Register Sucessfully !',
user: doc
});
})
.catch(err => {
console.error(err)
if (err.code == 11000) {
reject({status: 409, message: 'User Already Registered !'});
} else {
reject({status: 500, message: 'Internal Server Error !'});
}
});
} else {
return users[0];
}
})
.then(usertemp => resolve({status: 200, message: "Login Successfully !", user: usertemp}))
.catch(err => {
console.log(err.message);
reject({status: 500, message: err.message});
});
});
This is my result after run on server:
As a result and code above. I have a question Why "user: doc" no response?. Thank you so much!
userfan.find.then.then (synchronous since nothing requires waiting) is called before newUser.save.then (asynchronous since under the hood it waits for the DB to answer).
So both resolve are called, but only the first call is considered, and the first one to be called is the one using usertemp. And this one receives undefined as argument, because of the implicit return undefined at the end of userfan.find.then.
Your flow should be:
userfan.find().then(users => {
if (!users.length) {
let newUser = ...
// explicitly return the promise, which will give the created user
return newUser.save().then(doc => {
// format and return the value you want
return {user: doc, message: 'registered'};
});
} else {
// another explicitly returned value, but you already have this
return {user: users[0], message: 'login'};
}
// it's not possible here because we returned before it, but your code reached this point
// and, implicitly, any function ending with no return does:
return undefined;
// this .then receives *either* the promise from newUser.save *or* the object from the else block
}).then(structure => {
structure.status = 200;
return structure; // here structure is {status: 200, message: 'registered'|'login', user: document}
});
Also, note that using the syntax shortcut (without curly braces around the function body) for arrow functions implies the one-line body is returned:
singleArgument => doSomething();
// is actually *strictly* equivalent to
(singleArgument) => { return doSomething(); }
With these ways of writing, it's easy to lose the reflex of writing return when it's needed.
Been hunting the internet trying to find an answer to why the following doesn't work.
I am trying to pass in the req object when I add the job so that I have access to it when the job is processed.
But the process is never executed when the whole req object is passed to job.data. Yet I can pass parts of the req object.
What I'm trying to do maybe anti-pattern and a big no no. But, I am trying to understand why it won't work. It seems strange that it just continues without any error.
Below is an example, hopefully it is clear.
My kue is abstracted into a separate file, and initialised onto app.locals.Q as follows:
// Q.js
class Q {
constructor(options) {
this.q = kue.createQueue(options)
}
addJob = (name, data) => {
return Queue.create({
queue_job: name,
queue_route: data.route,
queue_user: data.user,
queue_added: new Date(),
})
.then(response => {
this.q.create(name, {
id: response.get('queue_id'),
route: data.route,
request: data.request
})
.save();
return Promise.resolve(response);
})
.catch(error => {
return Promise.reject(error);
});
processJob = (name, work, options = {}) => {
const {concurrency} = options;
this.q.process(name, concurrency || 1, (job, done) => {
const {data: {id, route, request}} = job;
Queue.update({
queue_running: true
}, {
where: {
queue_id: id
}
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`running job ${id} from ${route}`);
}
return new Promise((resolve, reject) => {
return work(resolve, reject, request);
});
})
.then(results => {
return Queue.update({
queue_running: false,
queue_completed: new Date(),
queue_results_path: results || null
}, {
where: {
queue_id: job.data.id
}
});
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`completed job ${id} from ${route}`);
}
done();
})
.catch((error) => {
if (process.env.NODE_ENV !== 'production') {
console.log(`failed job ${id} from ${route}`);
console.log(error);
}
Queue.update({
queue_running: false,
queue_error: `${error}`
}, {
where: {
queue_id: id
}
})
.then(() => {
done(error);
})
.catch(err => {
console.error(err);
done(err);
});
});
});
};
};
// example route
queue = (req, res) => {
const {locals: {Q}} = req.app;
Q.addJob('foo', {
route: req.path,
user: req.user.get('username'),
request: req
})
.then(queue_id => {
Q.processJob('foo', (resolve, reject, request) => {
console.log(request)
resolve('complete')
})
res.json({sucess: true})
})
}
redis can't serialize the req object.
kue simply silently fails.
I send data from my input fields to my api:
$.ajax({
url: '/api/login',
type: 'GET',
dataType: 'json',
ContentType: 'application/json',
data: {formData},
success: (data) => {
console.log('SUCCESS')
console.log(data)
this.setState({
isInProcess: false
})
},
error: (jqXHR) => {
console.log(jqXHR)
console.log('ERROR')
this.setState({isInProcess: false})
}
})
on my server-side I have a function to see if I have required user in db:
async function findUser(data) {
try {
const user = await User.findOne({email: data.email,
password: data.password})
console.log('User was found')
return { user }
} catch (err) {
console.log('error', err)
throw err
}
}
which will be executed here:
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
return res.json(foundUserData)
} catch (err) {
return res.status(400).json(err)
}
})
It works fine, but if a user wasn't found in db i sends success anyway. Why?
await findUser(data.formData) won't throw error, return either null or user object. You may check something following
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
if(foundUserData && foundUserData.user) {
return res.json(foundUserData)
} else {
return res.status(400).json({message: 'User not found'});
}
} catch (err) {
return res.status(500).json(err)
}
})
It sends success because none of your queries error'ed, just because it didn't find anything does not mean that the query failed because it obviously succeeded in finding out if what ever you're looking for exists or not.
To send an error in case of not found you need to check if response is empty in which case you want to send error
When no user is find you get a null value. You may try to put more logic on your success parameter with that for example:
success: function (data) {
if(!!data && data != null) {
alert('Success');
} else {
alert('No data');
}
}