Using Firebase Cloud Functions to fetch data from an external API (Timeout) - node.js

This is my code:
const functions = require('firebase-functions');
const axios = require('axios');
const cors = require('cors')({ origin: true });
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
cors(req, res, () => {
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
// ! res.end();
});
I'm already using the Blaze Plan but I always get this when I invoke it >> Function execution took 60002 ms, finished with status: 'timeout'.
If I try to terminate the function like the documentation says with res.send() or res.end() I get this error: Error: Can't set headers after they are sent.
It works fine when I try to fetch other APIs.

You need a return keyword before the cors() function call.
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
return cors(req, res, () => { // <------ RETURN NEEDED HERE
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
});
Your axios code is being executed, and I suspect it's receiving a response, but because you aren't returning the result from CORS, the "parent" cloud function never knows the task has finished.
This example shows a sample from the official Firebase GitHub where they're using Cors.

Related

Promise not returning resolve from express app.get Nodejs

I'm trying to return a resolve or reject depending on if the promise was successful or not. I can't seem to figure out why this isn't returning the response. All I get from my promise is [object Object]. This is what I get in response.
Here's the code:
app.get('/', (req,res) => {
return new Promise((resolve,reject) => {
var sql = "INSERT INTO usersinfo (firstname,lastname,email,number,latitude,longitude) VALUES(?,?,?,?,?,?)";
conn.query(sql,[fname,lname,email,num,req.query.latitude,req.query.longitude], (err,result) => {
if (err) {
res.send('error')
console.log(err,'there has been an error')
reject('There was an error')
return
}else{
console.log('inserted')
resolve({ success:'true' })
}
res.end()
})
})
})
Where I'm fetching the url:
const res = await fetch(`http://MYIPADDRESS?latitude=${latitude}&longitude=${longitude}`)
console.log(res)
I don't seem to get what's wrong. And if I attach the then((response) => console.log(response)) method I get an error in the expo app saying There was an error sending log messages to your development environment PrettyFormatPluginError:value.hasOwnProperty is not a function. (In 'value.hasOwnProperty('tag')','value.hasOwnProperty is undefined)
You don't need to create a promise. The send method can be called asynchronously -- when the response is ready:
app.get('/', (req,res) => {
var sql = "INSERT INTO usersinfo (firstname,lastname,email,number,latitude,longitude) VALUES(?,?,?,?,?,?)";
conn.query(sql,[fname,lname,email,num,req.query.latitude,req.query.longitude], (err,result) => {
if (err) {
res.send('error');
console.log(err,'there has been an error');
} else {
res.send({ success:'true' }); // <---
console.log('inserted');
}
});
});
NB: also, there is no need to call res.end() as res.send() already implies that.
On the client side you'll have to await the JSON content to be generated:
const response = await fetch(`http://MYIPADDRESS?latitude=${latitude}&longitude=${longitude}`);
const result = await response.json(); // <---
console.log(result);

Axios "catch" is not executing on 403 response

Returning a 403 response from a node/express app. The browser network inspector works as expected, but axios "catch" does not execute.
Axios "then" is executing and with a undefined response.
If I return 200 instead of 403, the response object doesn't get undefined value.
Thoughts ?
Middleware:
function authorize(permissions) {
return async function (req, res, next) {
if (await req.user.can(permissions)){
return next();
}
return res.status(403).json({message: 'Not enough permissions', permissionsRequired: permissions});
};
}
Route:
router.get('/', authorize('read_user'), async (req, res) => {
return await simplePaginateModelJson(req, res);
});
Client side:
const getData = (searchFields = {}, page = 1) => {
simplePaginateList(props.modelName, searchFields,{page: page, limit: props.itemsCountPerPage}).then(r => {
console.log('res', r);
}).catch(err => {
console.log('err', err);
});
}
Network:
Console:

How to check the response from global fetch in JEST test case

So, i am using jest for testing my node function which is calling fetch() APi to get the data, now when I am writing the test cases for the same i am getting an error like :
expect(received).resolves.toEqual()
Matcher error: received value must be a promise
Received has type: function
Received has value: [Function mockConstructor]
my function :
export function dataHandler (req, res, next) {
const url= "someURL"
if (url ) {
return fetch(url )
.then((response) => response.json())
.then((response) => {
if (response.data) {
console.log(response);
res.redirect(somewhere`);
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
next(error);
});
}
}
testcase :
it('check if fetch returning the response', async () => {
// Setup
const req = jest.fn(),
res = { redirect: jest.fn() },
next = jest.fn();
global.fetch = jest.fn().mockImplementation(() => {
return new Promise((resolve) =>
resolve({
json: () => {
return { data: "hello"};
}
})
);
});
await middlewares.dataHandler(req, res, next);
// Assert
expect(global.fetch).resolves.toEqual({ data: "hello" });
});
Please be advised I am not using any mocking API, and would prefer not to.
Can anyone help me with what's going wrong?
.resolves can only be used with a Promise.
global.fetch is a function so Jest throws an error.
If you are trying to assert that the Promise returned by calling global.fetch resolves to an object with a json function that returns { data: 'hello' } then you can do this:
expect((await global.fetch()).json()).toEqual({ data: 'hello' }); // Success!
...but I suspect that you are really trying to verify that response.data existed and that res.redirect was called with 'somewhere' in which case your assertion should just be this:
expect(res.redirect).toHaveBeenCalledWith('somewhere'); // Success!

Res.Redirect is not getting called in Jest Test

i am using node-express and am trying to check the redirect when the call from API is successful. I am getting an error that - Expected mock function to have been called, but it was not called.
here is my function:
export function globalAuthHandler (req, res, next) {
const global_signin_url = config.get('url');
if (global_signin_url) {
console.log(global_signin_url);
fetch(global_signin_url)
.then((response) => response.json())
.then((response) => {
console.log('Response', response);
if (response.data) {
console.log('Success!!!');
res.redirect('/signIn');
} else {
console.log('going here 1' + response);
res.redirect('/session-expired');
throw Error(response.statusText);
}
})
.catch((error) => {
console.log('going global here 2 ' + error);
next(error);
});
} else {
console.log('going here 3');
res.redirect('/session-expired');
}
}
here is the test :
it('should throw the error and redirect if the API fails with 404.', async () => {
// Setup
Config.get = jest.fn();
Config.get.mockReturnValue(true);
Config.initialize = jest.fn(() => Promise.resolve({ data: {} }));
const req = jest.fn(),
res = { redirect: jest.fn() },
next = jest.fn();
//global.fetch = jest.fn(() => new Promise((resolve) => resolve({ response: { ok: false, status: 404 } })));
global.fetch = jest.fn(
() =>
new Promise((resolve) =>
resolve({
json: () => {
return { };
}
/* data: { ok: true } */
})
)
);
// Act
await middlewares.globalAuthHandler(req, res, next);
// Assert
expect(res.redirect).toHaveBeenCalled();
expect(res.redirect).toHaveBeenCalledWith('/signIn');
});
I am not able to figure out that - even after going to the success!!! log, redirect is not getting triggered.
Calling await on middlewares.globalAuthHandler doesn't wait for it to complete since it isn't returning the Promise.
Return the Promise created by fetch:
export function globalAuthHandler (req, res, next) {
...
return fetch(global_signin_url) // <= return the Promise
...
}
...and the test will wait for the Promise to resolve before continuing to the expect statements.
That will give res.redirect a chance to be called before it is tested.

Promise only resolves on the first request

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

Resources