Functions are being are executed twice by node in my slackbot application - node.js

When I start up my application, it sends "App Started" twice, and if I write in Slack (Which is where the app "lives" (sorry for being unable to state this accurately, but you get the point), #saltbot dadjoke everything works but it happens twice. I am using the slackbots library.
This one only gets executed twice (the console log only logs once), but the handleMessage(data) then executes the dadjoke function, which also only has one console.log output. Regardless, the result is that it sends two messages (four total because of 2 per dadjoke()).
bot.on('message', (data) => {
if(data.type !== 'message') {
return;
}
console.log("Message received")
handleMessage(data);
})
Function that runs twice, but only consoles logs once
const dadJokes = (userId) => {
console.log("Userid: ", userId)
axios({
"method":"GET",
"url":"https://dad-jokes.p.rapidapi.com/random/jokes",
"headers":{
"content-type":"application/octet-stream",
"x-rapidapi-host":"dad-jokes.p.rapidapi.com",
"x-rapidapi-key":"ab6f131638mshd9ccda499375f86p1a3471jsnb7698b37e834",
"useQueryString":true
}
})
.then((response)=>{
bot.postMessage(userId, response.data.setup)
setTimeout(() => {
bot.postMessage(userId, response.data.punchline)
}, 7000)
})
.catch((error)=>{
console.log(error)
})
}
Here is the full code: https://github.com/hagenek/saltbot

So.. Not sure if I should delete question or not, but the solution: closed everything and restarted my computer. Loaded up my zsh terminal and ran the app. Now it works.

Related

Abandoned http requests after server.close()?

I have a vanilla nodejs server like this:
let someVar // to be set to a Promise
const getData = url => {
return new Promise((resolve, reject) => {
https.get(
url,
{ headers: { ...COMMON_REQUEST_HEADERS, 'X-Request-Time': '' + Date.now() } },
res => {
if (res.statusCode === 401) return reject(new RequestError(INVALID_KEY, res))
if (res.statusCode !== 200) return reject(new RequestError(BAD_REQUEST, res))
let json = ''
res.on('data', chunk => json += chunk)
res.on('end', () => {
try {
resolve(JSON.parse(json).data)
} catch (error) {
return reject(new RequestError(INVALID_RESPONSE, res, json))
}
})
}
).on('error', error => reject(new RequestError(FAILED, error)))
})
}
const aCallback = () =>
console.log('making api call')
someVar = getData('someApiEndpoint')
.then(data => { ... })
}
const main = () => {
const server = http.createServer(handleRequest)
anInterval = setInterval(aCallback, SOME_LENGTH_OF_TIME)
const exit = () => {
server.close(() => process.exit())
log('Server is closed')
}
process.on('SIGINT', exit)
process.on('SIGTERM', exit)
process.on('uncaughtException', (err, origin) => {
log(`Process caught unhandled exception ${err} ${origin}`, 'ERROR')
})
}
main()
I was running into a situation where I would ctrl-c and would see the Server is closed log, followed by my command prompt, but then I would see more logs printed indicting that more api calls are being made.
Calling clearInterval(anInterval) inside exit() (before server.close()) seems to have solved the issue of the interval continuing even when the server is closed, so that's good. BUT:
From these node docs:
Closes all connections connected to this server which are not sending a request or waiting for a response.
I.e., I assume server.close() will not automatically kill the http request.
What happens to the http response information when my computer / node are no longer keeping track of the variable someVar?
What are the consequences of not specifically killing the thread that made the http request (and is waiting for the response)?
Is there a best practice for cancelling the request?
What does that consist of (i.e. would I ultimately tell the API's servers 'never mind please don't send anything', or would I just instruct node to not receive any new information)?
There are a couple things you should be aware of. First off, handling SIGINT is a complicated thing in software. Next, you should never need to call process.exit() as node will always exit when it's ready. If your process doesn't exit correctly, that means there is "work being done" that you need to stop. As soon as there is no more work to be done, node will safely exit on its own. This is best explained by example. Let's start with this simple program:
const interval = setInterval(() => console.log('Hello'), 5000);
If you run this program and then press Ctrl + C (which sends the SIGINT signal), node will automatically clear the interval for you and exit (well... it's more of a "fatal" exit, but that's beyond the scope of this answer). This auto-exit behavior changes as soon as you listen for the SIGINT event:
const interval = setInterval(() => console.log('Hello'), 5000);
process.on('SIGINT', () => {
console.log('SIGINT received');
});
Now if you run this program and press Ctrl + C, you will see the "SIGINT received" message, but the process will never exit. When you listen for SIGINT, you are telling node "hey, I have some things I need to cleanup before you exit". Node will then wait for any "ongoing work" to finish before it exits. If node doesn't eventually exit on it's own, it's telling you "hey, I can see that there are some things still running - you need to stop them before I'll exit".
Let's see what happens if we clear the interval:
const interval = setInterval(() => console.log('Hello'), 5000);
process.on('SIGINT', () => {
console.log('SIGINT received');
clearInterval(interval);
});
Now if you run this program and press Ctrl + C, you will see the "SIGINT received" message and the process will exit nicely. As soon as we clear the interval, node is smart enough to see that nothing is happening, and it exits. The important lesson here is that if you listen for SIGINT, it's on you to wait for any tasks to finish, and you should never need to call process.exit().
As far as how this relates to your code, you have 3 things going on:
http server listening for requests
an interval
outgoing https.get request
When your program exits, it's on you to clean up the above items. In the most simple of circumstances, you should do the following:
close the server: server.close();
clear the interval: clearInterval(anInterval);
destroy any outgoing request: request.destroy()
You may decide to wait for any incoming requests to finish before closing your server, or you may want to listen for the 'close' event on your outgoing request in order to detect any lost connection. That's on you. You should read about the methods and events which are available in the node http docs. Hopefully by now you are starting to see how SIGINT is a complicated matter in software. Good luck.

Getting "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" while using Axios

I am trying to use different Axios calls to get some data from a remote server. One by one the calls are working but as soons as I call them directly after each other its throwing the error message about the headers. I did some research already and I guess it has sth to do that there the headers of the first call gets in the way of the second call. That is probably a very simplematic description of the problem but I am new to node js and the way those axios calls are working.
This is an example of one of my Api calls:
app.get('/api/ssh/feedback', function(req, res){
conn.on('ready', function(){
try {
let allData = {}
var command = 'docker ps --filter status=running --format "{{.Names}}"'
conn.exec(command, function(err, stream){
if (err) throw console.log(err)
stream.on('data', function(data){
allData = data.toString('utf8').split('\n').filter(e=>e)
return res.json({status: true, info: allData})
})
stream.on('close', function(code){
console.log('Process closed with: ' + code)
conn.end()
})
stream.on('error', function(err){
console.log('Error: ' + err)
conn.end()
})
})
} catch (err) {
console.error('failed with: ' + err)
}
}).connect(connSet)
})
I am using express js as a middleware and the shh2 package to get the connection with the remote server. How I mentioned before the call is working but crashes if it is not the first call. I am able to use the api again after I restart the express server.
This is how I am calling the api through axios in my node js frontend:
getNetworkStatus(e){
e.preventDefault()
axios.get('/api/ssh/network').then(res =>{
if(res.data.status){
this.setState({network_info: 'Running'})
this.setState({network: res.data.info})
} else {
this.setState({network_info: 'No Network Running'})
this.setState({network: 'No Network detected'})
}
}).catch(err => {
alert(err)
})
}
I would be really grateful for any help or advice how to solve this problem. Thanks to everyone who spends some time to help me out.
There are two issues in the code you've provided:
You are making assumptions about 'data' events. In general, never assume the size of the chunks you receive in 'data' events. You might get one byte or you might get 1000 bytes. The event can be called multiple times as chunks are received and this is most likely what is causing the error. Side note: if the command is only outputting text, then you are better off using stream.setEncoding('utf8') (instead of manually calling data.toString('utf8')) as it will take care of multi-byte characters that may be split across chunks.
You are reusing the same connection object. This is a problem because you will continue to add more and more event handlers every time that HTTP endpoint is reached. Move your const conn = ... inside the endpoint handler instead. This could also be causing the error you're getting.

How to check states after sending request in server?

I am trying to check states after sending requests to the server by using axios. I designed the server that if you submitted the form with an empty input, you will get an error. If you can see in the code, I have tried to check the states in finally block but it is not working properly. Like when I submitted the form initially with no inputs, the console log displays no errors and when I try to submit the form with the inputs, it doesn't display anything in the console. I just want to check if there is an error with the request because I want to run a function between them.
The server I used is live and running and you can get the data/submitted form by changing the URL into /getUser
Code here: https://codesandbox.io/s/quizzical-danny-dv1l7?file=/src/App.js
It doesnt works like that.
const [error, setError] = useState("");
error is the initial value (empty string). In dont realy knows how useState is working but, error is a string so it s a value, not a reference. There is no way this variable get updated it the finaly block.
The simple answer is you are setting your state inside the function and then trying to read it as your "current state". Try this instead...
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post("https://testing-name-app.herokuapp.com/create", {
first,
last
});
} catch (error) {
console.log("error found");
return setError(error.response.data.errorMessage);
}
// be CAREFUL with this pattern! This just means the request came back
// with no errors, but there may be a message from your call that
// contained an error from your db/server etc
return console.log("no errors");
};
And here's a way to quickly see what's going on in your call...
const handleSubmit = async (e) => {
e.preventDefault();
let res
try {
res = await axios.post("https://testing-name-app.herokuapp.com/create", {
first,
last
});
} catch (error) {
res = error
console.log("error found");
setError(error.response.data.errorMessage);
}
finally {console.log('res: ', res)}
};

Where to Put subscription.pull when want to get the publish data Google Cloud pub/sub NODE JS

So i've tried to develop some pub/sub system based on node js
i am using express post request to publish the data that i wanted. it's send the data well, but my question is where should i put my code for the subscription
Right now i put the code at the root of my file like this
pubSub.subscribe()
.then(({results, subscription}) => {
results[0].data.forEach((item) => {
let key = ['UserId', fakeId(1, 100), 'FeedId', fakeId(100, 200), 'plugin', fakeId(1, 100)]
upsert(key, item, () => {
console.log('Sync Success')
console.log(item)
}, error => console.error(error))
})
subscription.ack(results.map((result) => result.ackId));
})
.catch(error => console.error(error))
i have some helper to subscribe like this
function subscribe () {
const subscription = pubSub.subscription('plugin_subscription')
return new Promise ((resolve, reject) => {
return subscription.pull((error, results) => {
console.log('ini ke trigger')
if (error) reject(error)
resolve({results, subscription})
})
})
}
well it's kind of only work once. if i publish message i dont' have any response log from the subscriber, but if i restart the node js server my log is show that i successfully receive the data and could proceed to the next step.
Am i doing something wrong here?
thanks a lot man
A couple ideas:
You're using promises to handle received messages. A promise on its
own can only be triggered once - to trigger it multiple times, you'd
need some sort of loop or recursive call.
Try using event
handlers (see this example) instead of promises - those should trigger every time an
event occurs without any additional looping or recursion. Note that for this example, you'll need to remove the code that removes the messageHandler listener.
Hopefully this helps!

Nodejs app hangs when there is no interval running

So I have been building a cli/gui app using electron for work. I have a progress bar that needs an interval to be run every so often to refresh the progress bar. Recently we wanted to run this in jenkins so having progress bars refresh ever 80 ms would be super anoying. I added some code to clear the interval if a certain environment variable was set and then for some reason the app started to hang after sending http requests. The server would never receive the requests and the app would just sit there. After a lot of debugging i have found that putting a setInterval(() => { }, 80); (one that does anything or nothing for any amount of time) anywhere in the code fixes the problem. Has anyone seen this before? i feel like I'm going crazy!
edit:
setting a timeout on the request will make the request fail after the timeout.
edit:
so I can't show you the exact code but here is the jist of one of the request calls.
return new Promise((resolve, reject) => {
request.put({
url: this.buildUrl(armada, '/some-path'),
body: vars,
json: true
}, (err, resp, body) => {
logger.comment('err "%s"', JSON.stringify(err));
logger.comment('resp "%s"', JSON.stringify(resp));
logger.comment('body "%s"', JSON.stringify(body));
let e = this.handleError(err, resp, body, 'Error getting stuff');
if (e) { return reject(e); }
logger.comment('got back body "%s"', body);
resolve(resp);
});
});
and then if I have an interval somewhere it doesn't hang. and if I don't it does.
i can paste this code anywhere in my code and everything starts working
setInterval(() => { }, 80);
Now it's not a specific request made as the app makes a lot of different requests. any of the requests it makes can hang. And they don't always hang. about 1 in 10 times everything will work just fine for an individual request.

Resources