I am building a node.js API to manage IOT devices. The IOT device have a state which can be true or false, depending on user input. The server subscribes the user to the device topic, once the user adds the device to their account. I am able to toggle the state easily, with the following code :
exports.toggleState = async (req, res, next) => {
let deviceID = req.params.id
let userId = req.userId
try {
let userdata = await User.findById(userId)
let deviceList = userdata.deviceList
deviceList = deviceList.map(el => {
return el.toString()
})
if (deviceList.indexOf(deviceID) !== -1) {
Device.findById(mongoose.Types.ObjectId(deviceID)).then(result => {
result.state = !result.state
return result.save()
})
.then(result => {
req.mqttClient.publish(`${result.topic}`, `${result.state}`, { qos: 0, retain: true }, (error) => {
if (error) {
throw error
}
})
res.status(201).json({ message: 'Device State Changed', data: result })
})
.catch(err => {
console.log(err)
throw err
})
}
else {
res.status(500).json({ message: 'Device not linked to your account!' })
}
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
I have added mqtt to make it easier for device side operation (Arduino based).
The challenge I am facing is that if the state changes from the device side (via hardware input), how do I keep the device state updated on the database through the Mqtt server? Some thing like :
Device.findById(deviceID).then(result => {
req.mqttClient.on('message', (`${result.topic}`, payload) => {
console.log('Received Message:', `${result.topic}` , payload.toString())
})
result.state = payload.toString()
return result.save()
}.then(()=>{
console.log('State successfully updated')
}
Problem is I don't know where do I plug this code so that it runs continuously, monitoring incoming messages and updating device state. Would greatly appreciate any help in this.
Related
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
Hello I want to connect my watson assisstant with an alexa device, for this I need Amazon development skill kit and AWS lambda. But i can't connect watson because i got problem with my promises and i can't see the logs of my code in the amazon developer console. And my assistant work on nodeJs application.
There is some codes that i tried :
const MyNameIsIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'SearchIntent';
},
async handle(handlerInput) {
assistant.createSession({
assistant_id: assistant_id
})
.then(res => {
session_id = res.session_id;
})
.catch(err => {
console.log(err);
});
assistant.message({
assistant_id: assistant_id,
session_id: session_id,
input: {
'message_type': 'text',
'text': "hello"
}
})
.then(res => {
console.log(JSON.stringify(res, null, 2));
speechText = res.output.generic.response.text;
})
.catch(err => {
speechText = err;
});
}, function(err){
speechText = "Problem with Api call";
});
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
},
};
And other way with promise:
try{
let res = await assistant.createSession({
assistant_id: assistant_id
});
session_id = res.session_id;
let message = await assistant.message({
assistant_id: assistant_id,
session_id: session_id,
input: {
'message_type': 'text',
'text': "hello"
}
});
speechText = message.output.generic.response.text;
}catch(err){
speechText = err;
}
The results of speechText should give me "Good day to you" it's a response that comes from Watson.but now Alexa says "Sorry, I can't understand the command. Please say again."
Do you have an others ways to try this with other way to do a promise? thanks you!
Some thoughts for consideration ...
Looking at the first attempt, what you have in handle() is two independent promise chains, efectively like this:
async handle(handlerInput) {
doSomething_1().then(...).catch(...);
doSomething_2().then(...).catch(...);
},
What you appear to need is to perform the two async operations in series:
handle(handlerInput) {
return doSomething_1().then(doSomething_2)....;
},
In full (with some guesswork/inventiveness):
handle(handlerInput) {
return assistant.createSession({ assistant_id })
.then(res => {
return assistant.message({
'assistant_id': assistant_id, // ??
'session_id': res.session_id,
'input': {
'message_type': 'text',
'text': 'hello'
}
})
.catch(err => {
err.message = 'Problem with assistant.message() call';
throw err; // send error with custom message down the down the error path.
}
}, err => {
err.message = 'Problem with assistant.createSession() call';
throw err; // send error with custom message down the down the error path.
}
.then(res => res.output.generic.response.text) // send res.output.generic.response.text down the success path.
.catch(err => err.message) // catch error from anywhere above and send err.message down the success path to appear as `speechText` below.
.then(speechText => { // `speechText` is the text delivered by the immediately preceeding .then() or .catch()
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
})
.catch(err => { // in case handlerInput.responseBuilder.speak().getResponse() throws
console.log(err);
});
}
Notes:
text is passed down the promise chain's success path and appears as speechText in the final .then(); no need for an outer variable.
custom error messages (if required) are injected with intermediate catches, which modify and (importantly) rethrow their errors.
a third intermediate catch intercepts the error path and sends err.message down the success path.
consumption of speechText is necessarily dependent on the sequence assistant.message().then(assistant.message) having completed.
code could be written with async/await but would difffer only in semantics; both versions would exploit promises.
The code is not necessarily a working solution, but should help in the understanding of the kind of data/error flow required by this application.
Good luck with it.
I need to send a high priority push notification on Android devices. I have implemented FCM. When I uncomment the priority section then FCM gives 'Undefined' in response.
FCM(deviceToken, message, badge, metadata={},time_to_live=false){
return new Promise((resolve, reject) => {
let fcm = new FCMlib(this._fcm_server_key);
/*Send notification to Android Device*/
var pushParam = {
to: deviceToken,
data : {
title: message, //'Title of your push notification',
body: message,
metadata: metadata
},
// android:{
// priority:high
// },
time_to_live: 10 ,
//The time in seconds
};
if(time_to_live === true){
delete pushParam.time_to_live;
}
fcm.send(pushParam, (err, response) => {
if(err){
console.log('err---1 ',err);
reject(err);
}
if(response) {
/*update user badge*/
console.log('response--- ',response);
this.mysql.query("UPDATE `user_device_token` SET badge = ? WHERE `
device_token` = ?", [badge, deviceToken])
.then(result => {
/*badge would be update*/
});
resolve(response);
}
});
});
}
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
I need to handle events "user is now online" and "user is now offline" on GraphQL Apollo Node.js server. What's the best way to do it?
My investigation: I pretty sure that I don't need to implement any heartbeat logic, because subscriptions are working on WebSockets. But I didn't find any info in their docs how to handle WebSockets events like "connecting" and "disconnecting" from the subscription... Actually I can handle those events from the outside of actual subscription:
SubscriptionServer.create({
execute,
subscribe,
schema,
onConnect = (...args) => {
console.log('User connected')
},
onDisconnect = (...args) => {
console.log('User disconnected')
}
}, {
server: ws,
path: '/subscriptions'
})
But can't determine which user is connected via this socket.
My implementation: for now I made it work like that:
We have express middleware for all the calls, it is pushing user object from jsonwebtoken to req object. Here I can trigger "user is now online" logic.
I've created separate subscription, client subscribes on it on login and unsubscribes on logout. Since there is no unsubscribe handler, I manage to determine that filter function gets called on user disconnect without payload, so I did this approach:
userOnlineSubscription: {
subscribe: withFilter(
() => pubSub.asyncIterator('userOnlineSubscription'),
async (payload, variables) => {
if (!payload) {
// set user offline
}
return false
}
)
}
As for me, the solution above is ugly. Can someone recommend the better approach?
I used this approach
onConnect (connectionParams, webSocket) {
const userPromise = new Promise((resolve, reject) => {
if (connectionParams.jwt) {
jsonwebtoken.verify(
connectionParams.jwt,
JWT_SECRET,
(err, decoded) => {
if (err) {
reject(new Error('Invalid Token'))
}
resolve(
User.findOne({
where: { id: decoded.id }
})
)
}
)
} else {
reject(new Error('No Token'))
}
})
return userPromise.then(user => {
if (user) {
return { user: Promise.resolve(user) }
}
return Promise.reject(new Error('No User'))
})
}