Nodejs API's are not working when cron job is executing - node.js

I have added two cron jobs to run every 15 mins and every 3 hours during this time I'm unable to access API's. I'm getting a 502 bad gateway errors.
import Agenda from 'agenda';
import { updateCinemaData } from './updateCinemaData';
import { updateStreamingData } from './updateStreamingData';
import dotenv from 'dotenv';
dotenv.config();
const uri = process.env.MONGO_URI;
const MAX_CONCURRENCY_JOBS = 4;
const agenda = new Agenda({
db: {
address: uri,
options: { useNewUrlParser: true },
collection: 'schedule-jobs'
},
processEvery: '30 seconds',
});
const graceful = async () => {
await agenda.stop();
process.exit(0);
};
process.on('SIGTERM', graceful);
process.on('SIGINT', graceful);
agenda.define('get cinema data', { priority: 1 }, async (job, done) => {
console.log('get cinema data')
await updateCinemaData();
done();
});
agenda.define('get streaming data', { priority: 2 }, async (job, done) => {
console.log('get streaming data')
await updateStreamingData();
done();
});
(async function () {
const getCinemaData = agenda.create('get cinema data', {});
const getStreamingData = agenda.create('get streaming data', {});
await agenda.start();
agenda.maxConcurrency(MAX_CONCURRENCY_JOBS);
const jobNames = [
'get cinema data',
'get streaming data'
];
for (let jobName of jobNames) {
try {
let jobs = await agenda.jobs({ name: jobName });
for (let job of jobs) {
job.remove();
}
} catch (error) {
console.log(error);
}
}
await getCinemaData.repeatEvery('*/15 * * * *').save(); //Runs every 15 mins.
await getStreamingData.repeatEvery('* */2 * * *').save(); //Runs every 3 hours.
})();
export default agenda;
To catch all the exceptions I have added the below code in my server.js
process.on('uncaughtException', function (exception) {
log.error(exception);
});
with this also I'm not able to get the errors and this issue is only on the production. Also checked pm2 logs and Nginx logs but no use. Can anyone explain to me what is the issue I'm facing? I'm a beginner in Nodejs and not able to resolve this.

Related

How to mock function using node-tap on fastify inject

I want to make 100% coverage on this function with node-tap but I can't mock the error part, it always throw
Cannot find module 'create' Require stack: - /home/mptr8/Code/Projects/me/fastify-example/fastify-postgres/test/integration.js
But I have create function on my query.js file, what do I do wrong here? Why it doesn't invoke the method?
t.mock("../query.js", {
create: () => {
throw new Error();
},
});
I also try this combination, because query.js are dependent on db.js. Now the mock error gone but still I'm not getting the error throw from my fastify.inject.
t.mock("../db.js", {
"../query.js": {
create: () => { throw new Error() },
},
});
app.post("/", async (request, reply) => {
try {
const { body } = request;
const book = create(body.title);
reply.send(book);
} catch (error) {
// this part are not covered
reply.code(500).send({ message: "internal server error" });
}
});
here are my complete code. You can see the full code on this github repository.
// server.js
const fastify = require("fastify");
const {
migration,
create,
} = require("./query");
const db = require("./db");
function build(opts = {}) {
const app = fastify(opts);
migration();
app.post("/", async (request, reply) => {
try {
const { body } = request;
const book = create(body.title);
reply.send(book);
} catch (error) {
reply.code(500).send({ message: "internal server error" });
}
});
app.addHook("onClose", (_instance, done) => {
db.close();
done();
});
}
module.exports = build;
// db.js
const { Pool } = require("pg");
const pool = new Pool({
connectionString:
"postgresql://postgres:postgres#localhost:5432/fastify_postgres?schema=public",
});
module.exports = {
query: (text, params) => pool.query(text, params),
close: () => pool.end(),
};
// query.js
const db = require("./db");
async function migration() {
await db.query(`
CREATE TABLE IF NOT EXISTS books (
id serial PRIMARY KEY,
title varchar (255) NOT NULL
)
`);
}
async function create(title) {
return await db.query("INSERT INTO books (title) VALUES ($1)", [title]);
}
module.exports = { migration, create };
// test.js
const tap = require("tap");
const fastify = require("../server");
tap.test("coba", async (t) => {
const app = await fastify();
t.test("should success create books", async (t) => {
const response = await app.inject({
method: "POST",
url: "/",
payload: {
title: "Hello,World!",
},
});
t.equal(response.statusCode, 200);
});
t.test("should throw error", async (t) => {
const app = await fastify();
// it doesn't throw the error :((
t.mock("../query.js", {
create: () => {
throw new Error();
},
});
const response = await app.inject({
method: "POST",
url: "/",
payload: {
title: "Hello,World!",
},
});
t.equal(response.statusCode, 500);
// call app close on last test child to close app and db properly
app.close();
});
});
You should use the returned value by the t.mock function:
const build = t.mock({
"../server": {
"./query.js": {
create: () => { throw new Error() },
}
}
})
const app = await build({})

Stripe Webhook fails to fire, write data to firebase

I have a webhook I'm using to write data to firebase when a stripe purchase happens, and I cant get it to work.
I've tried adding try/catch statements around everything, i cant seem to catch the error regardless.
Im using the nextJS backed debugging as found here:
https://nextjs.org/docs/advanced-features/debugging
My question is why? all my api keys are correct.
the error :
[ERROR] Failed to POST: Post "http://localhost:3000/api/webhook": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
The Webhook:
import { buffer } from "micro";
import * as admin from "firebase-admin";
//secure a connection to firebase
const serviceAccount = require("../../../permissions.json");
const app = !admin.apps.length
? admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
: admin.app();
//connect to stripe
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
const fullfillOrder = async (session) => {
try {
return app
.firestore()
.collection("users")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set({
amount: session.amount_total / 100,
amount_shipping: session.total_details.amount_shipping / 100,
images: JSON.parse(session.metadata.images),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`SUCCESS : Order ${session.id} has been added to DB.`);
});
} catch (error) {
console.error(error);
}
};
export default async (req, res) => {
try {
} catch (error) {
console.error(error);
}
if (req.method === "POST") {
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
let event;
//verify that event posted from stripe
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook error : ${err.message}`);
}
//Handle checkout session completed event
try {
if (event.type === "checkout.session.completed") {
const session = event.data.object;
return fullfillOrder(session)
.then(() => res.status(200))
.catch((err) =>
res.status(400).send(`Webhook Error :${err.message}`)
);
}
} catch (error) {
console.log("🚀 ~ file: webhook.js ~ line 56 ~ error", error);
}
}
};
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};

Agenda Cannot read property insertOne of undefined

I am trying to create and schedule jobs with agenda. So i split it into two files, one file the consumer creates jobs and the other file the producer schedules jobs.
this is the consumer file
const Agenda = require("agenda");
async function run() {
try {
const agenda = new Agenda({
db: {
address: process.env.MONGODB_URL,
collection: 'agendaJobs',
options: {
useUnifiedTopology: true
}
}
});
agenda.define('ticket creation', async (job, done) => {
const { time } = job.attrs.data;
console.log(time);
done();
});
agenda.on("ready", function () {
agenda.start();
console.log('Agenda Started');
});
//await agenda.start();
} catch (error) {
console.error(error);
process.exit(-1);
}
}
module.exports = {
run: run
};
and the producers file is
//import { default as Agenda } from 'agenda';
const Agenda = require("agenda");
const jobs = async (job) => {
try {
const agenda = new Agenda({
db: {
address: process.env.MONGODB_URL,
collection: 'agendaJobs',
options: {
useUnifiedTopology: true
}
}
});
const created = await agenda.schedule(job.time, 'ticket creation', {
time: job.time
});
return created;
} catch (error) {
console.error(error);
process.exit(-1);
}
};
module.exports = jobs;
and this is my controller where i use it
const jobs = require('./producer');
const { run } = require('./consumer');
run();
app.post('/create/job', async (req, res) => {
try {
const { time } = req.body;
await jobs({
time: time
});
return res.status(200).send({
success: true,
message: 'Job created'
});
} catch (error) {
return res.status(error.status).send(error);
}
});
is there anywhere i am wrong, i know this connects to the db
an example of the body sent to the controller
{
"time": "3 minutes"
}
i have used the node debugger to trace the error, it always fails at a line that looks like this, but i don't know what is causing it
yield schedule(this).save()
then shows an error in the terminal saying
TypeError: Cannot read property 'insertOne' of undefined
** The props property of this is always undefined
https://github.com/agenda/agenda/issues/335#issuecomment-249154993
agenda.on("ready", async () => {
const created = await agenda.schedule(job.time, 'ticket creation', {
time: job.time
});
});

How can I make my Hapi route wait for data before returning a value?

I am using Hapi.js and have a route that I want to use to fetch data and then return a result.
I have tried to use async/await, but I must be doing something wrong because while the function I am calling eventually prints a result to the console, the route is returning without waiting for that function to return a value.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
const getWalletBalance = async () => {
web3.eth.getBalance(`${walletAddress}`, async function(err, result) {
if (err) {
console.log('There was an error: ' + err);
return ({ error: 'The wallet balance call failed.' });
} else {
ethBalance = await web3.utils.fromWei(result, "ether");
console.log("This should be first: The wallet balance via API call is " + ethBalance + " ETH.");
return ethBalance; // I expect the walletbalance route to wait for this to be returned
}
});
};
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
let result = null;
try {
result = await getWalletBalance();
console.log('This should be second, after the getWalletBalance function has printed to the console.'); // this prints first, so await isn't working as expected
return ({ ethBalance: result });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Any idea where I have gone wrong here? This is the first time I have used async/await.
ETA: My console looks like this:
[nodemon] starting `node index.js`
Server running on http://localhost:3000
This should be second, after the getWalletBalance function has printed to the console.
This should be first: The wallet balance via API call is 4061.894069996147660079 ETH.
And this is the JSON I get back when I use the wallet balance route:
{}
Based on the answer I was given, I was able to get the results I wanted with this:
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
async function getWalletBalance(){
let ethBalance = await web3.eth.getBalance(`${walletAddress}`);
if (ethBalance.err) {
console.log('error in the called function');
} else {
return ethBalance;
}
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
const ethBalanceInWei = web3.utils.fromWei(result, "ether");
return ({ balance: ethBalanceInWei });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Thank you for the help! That got me going in the right direction.
Basically your getWalletBalance function is using multiple concepts. callback style functions and inside that you are using await. I have restructured your code a little bit. Hopefully that should fix the issue which you are facing.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
function getWalletBalance() {
return Promise((resolve, reject) => {
web3.eth.getBalance(`${walletAddress}`, (err, result) => {
if (err) {
console.log('There was an error: ' + err);
reject({ error: 'The wallet balance call failed.' });
} else {
resolve(result);
}
});
});
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
ethBalance = await web3.utils.fromWei(result, "ether");
return ethBalance;
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();

Jest how to test express API POST request?

I need to test if my POST request to my endpoint works properly with a Jest test. I had the idea of first getting the count of my Services table (I'm using sequelize orm), then to send a new post request and to finally get the new count and compare if the old count + 1 will equal to the new count, if true then the POST request works just fine.
test('Create a valid Service', async (done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async () => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
});
})
.catch(err => console.log(`Error ${err}`));
});
});
For me the test looks fine, but when I run it I get:
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Is something missing or is there even a better way to test a POST request? with Jest?
It is because you are not calling the done callback passed in jest callback function. It can be done like this.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
await Service.count().then(async function (count) {
await request(app)
.post('/api/services')
.send(service)
.then(async() => {
await Service.count().then(function (newcount) {
expect(newcount).toBe(count + 1);
// execute done callback here
done();
});
})
.catch(err => {
// write test for failure here
console.log(`Error ${err}`)
done()
});
});
});
You can also write this code in this way so that the readability can be improved and maximize the use of async/await.
test('Create a valid Service', async(done) => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
done()
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
done()
}
});
By default Jest also resolves the promise in async/await case. We can achieve this without the callback function also
test('Create a valid Service', async() => {
const service = {
name: "cool",
description: "description"
};
try {
const count = await Service.count();
await request(app).post('/api/services').send(service)
const newCount = await Service.count()
expect(newCount).toBe(count + 1);
} catch (err) {
// write test for failure here
console.log(`Error ${err}`)
}
});

Resources