I'm trying to implement a Firebase function that generates a custom token for my app. But I keep getting the following error message :
Error: could not handle the request
Or it ends up in timeout.
Do you have any idea of what could be wrong with my code hereafter ? I'm trying it with a 'test' uid.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
exports.customFunction = functions.https.onRequest((data, context) => {
return admin.auth().createCustomToken('test')
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
return {
status: 'success',
customToken: customToken
};
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
return {
status: 'error'
};
});
});
Your Cloud Function is an HTTPS one. In order to terminate it you need to call res.redirect(), res.send(), or res.end() as explained in the doc.
In your code you actually return the Promises chain: this is the correct way to terminate Cloud function triggered by background events (which is not the case of an HTTPS Cloud Function which is triggered by a call to the URL it exposes).
So, the following changes should do the trick (untested):
exports.customFunction = functions.https.onRequest((req, res)(data, context) => {
admin.auth().createCustomToken('test') // No need to return
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
response.status(200).send({
status: 'success',
customToken: customToken
});
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
response.status(500).send(error);
});
});
Note that with an HTTPS Cloud Function, the objects you pass to the handler are not the Firebase data and context objects but the Express.js request and response objects.
So it is more clear to write
exports.customFunction = functions.https.onRequest((req, res) => {...});
instead of
exports.customFunction = functions.https.onRequest((data, context) => {...});
Related
Here's my frigo controller :
const fs = require('fs');
const mqtt = require('mqtt');
const transporter = require('../params/mail')
const winston = require('../params/log');
const User = require("../models/User");
const { cli } = require('winston/lib/winston/config');
exports.OpenTheCase = async (req, res) => {};
exports.AddCard = async (req, res) => {};
exports.ShowCurrentTemperature = async (req, res) => {};
exports.ShowCurrentHumidity = async (req, res) => {};
exports.SetAlarm = async (req, res) => {};
exports.AlarmIsOn = async (req, res) => {};
const options = {
clientId: 'backendserver1032',
key: fs.readFileSync('./certs/mqtt_cert/client.key'),
cert: fs.readFileSync('./certs/mqtt_cert/client.crt'),
ca: [ fs.readFileSync('./certs/mqtt_cert/ca.crt') ]
}
const client = mqtt.connect('mqtts://localhost:8883', options);
exports.OpenTheCase = async (req, res) => {
try {
client.publish('RFID', 'RFID_OPEN');
res.status(200).json({ 'case':"opened" });
}
catch(e){
res.status(200).json({ 'state':"something went wrong" });
}
}
exports.AddCard = async (req, res) => {
try {
client.publish('RFID', 'RFID_ADD');
res.status(200).json({ 'card':"will be added" });
}
catch(e){
res.status(200).json({ 'state':"something went wrong" });
}
}
exports.ShowCurrentTemperature = async (req, res) => {
try {
client.subscribe('temperature');
client.on('message', (topic, message, packet) => {
res.status(200).json({ 'temperature': message.toString('ascii') })
client.unsubscribe('temperature')
})
}
catch(e){
res.status(200).json({ 'state':"something went wrong" });
}
return
}
exports.ShowCurrentHumidity = async (req, res) => {
try {
client.subscribe('humidity');
client.on('message', (topic, message) => {
res.status(200).json({"temperature": message.toString('ascii')});
client.unsubscribe('humidity')
});
}
catch(e){
res.status(200).json({ 'state':"something went wrong" });
}
return
}
The problem is : when I try to get "ShowCurrentTemperature", it works once and after it. It says that the http header was already send.
Here's my route :
router.get("/frigo/Temperature",auth.verifyToken, frigoController.ShowCurrentTemperature)
I really thank you.
I had try several things, like adding return or trying to end the connection but none of them works.
I'm getting out of idea. If someone can help me through this.
This design is never going to work.
You are basically leaking client.on('message',...) handlers. Every call to the HTTP endpoint adds a new handler which holds a reference to it's local res object.
Calling unsubscribe() does not remove the message handler, so the instant you call subscribe() again in either of the HTTP routes the very next message to arrive will be delivered to ALL the old handlers which will ALL try to call res.send() on a already complete HTTP transaction.
You are trying to map an inherently asynchronous protocol (MQTT) into a synchronous HTTP request, which is a really bad idea.
You may be able to get it to work by swapping all the client.on('message', ...) calls to client.once('message', ....) so the handlers only fire once, but this is a truly UGLY hack. (EDIT: On reflection, this still has a race condition where you may end up handling the wrong message if both HTTP endpoints are called too close together, so I stand by my first statement that this design can never work properly)
The right thing to do is to run the whole MQTT client totally independently of the HTTP requests and just have the single background client.on('message',...) handler update some global variables with the latest temperature and humidity that the HTTP routes can just return the latest value, rather than try and grab the next published message.
In a nutshell, this error happens when you try to send more than one response using res. This could happen in your code when there is an exception during client.unsubscribe('temperature'), because then the execution will flow into the catch where you are sending another response. You can avoid that by moving the unsubscribe after the try-catch:
exports.ShowCurrentTemperature = async(req, res) => {
try {
client.subscribe('temperature');
client.on('message', (topic, message, packet) => {
res.status(200).json({
'temperature': message.toString('ascii')
})
})
} catch (e) {
res.status(200).json({
'state': "something went wrong"
});
}
client.unsubscribe('temperature')
}
Update:
Actually, the more likely explanation is that you receive more than one message before you unsubscribe, and hence the first res.. is executed multiple times. So probably best to unsubscribe before sending a response. Since more handlers could queue up in the meantime, though, you probably need to add a guard as well to make sure you never send more than one response:
exports.ShowCurrentTemperature = async(req, res) => {
let done = false;
try {
client.subscribe('temperature');
client.on('message', (topic, message, packet) => {
client.unsubscribe('temperature')
!done && res.status(200).json({
'temperature': message.toString('ascii')
})
done = true;
})
} catch (e) {
!done && res.status(200).json({
'state': "something went wrong"
});
}
}
BTW, you could also just use mqtt directly on the client, which would be more elegant, if that's fine from an authorization perspective.
I am currently writing a route which allows me to recieve information from a stored procudre I have in a database. I have written a request in AngularJS and a route in NodeJS but I am just recieving a pending request in the chrome Network developer window. I can see that the console.log in the NodeJs app has the data I require so it has retrieved it but there is nothing coming back in any of the console logs in the the AngularJS app.
Here is the code for the both the angularJS app and the Node App:
AnglaurJS:
checkForReplenishmentEmptyTrolley = async () => {
LIBRIS.extensions.openLoadingModal();
console.log('in checkForReplenishmentEmptyTrolley');
try {
const varPromise = await $http.get(`${LIBRIS.config.stockService}stockMovement/checkForUnattachedTrolley`)
.then((response) => {
console.log(response);
// Request completed successfully
}, (error) => {
// Request error
console.log(error);
});
console.log(varPromise.data);
// 1. check that there are no ghost replenish - lines 1-15
console.log('in try/catch');
console.log('promise', varPromise);
} catch (error) {
console.log(error);
}
},
NodeJS code:
app.get(`${ROUTE}/attachTrolley`, async function(req, res){
const newRequest = await DB.newRequest();
console.log('we have made it to the route');
try {
console.log('we have made it to the Try/Catch route');
newRequest.input();
const record = await newRequest.execute('dbo.usp_STK_CheckForUnattachedTrolley');
res.json(record)
console.log(record, 'record');
} catch (err){
handleError(res, err);
console.log(err);
}
});
The problem is that you are doing a .then on a awaited promises and not returning anything from that. You have two choice here
Either return response from then so when you try to access the value here console.log(varPromise.data); it works.
Or remove the .then alltogather as it is not required because you are awaiting it any ways.
Basically just do this
checkForReplenishmentEmptyTrolley = async () => {
LIBRIS.extensions.openLoadingModal();
console.log("in checkForReplenishmentEmptyTrolley");
try {
const varPromise = await $http.get(`${LIBRIS.config.stockService}stockMovement/checkForUnattachedTrolley`);
console.log(varPromise.data);
// 1. check that there are no ghost replenish - lines 1-15
console.log("in try/catch");
console.log("promise", varPromise);
} catch (error) {
console.log(error);
}
};
Hope this fixes your issue.
Solved it! I had no return statement in my route!
When sending {"identifiant": "iJXB5E0PsoKq2XrU26q6"} to the below Cloud Function, I cannot get the identifiant value in the request body and it will always return PROBLEMAS NO REQUEST.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
const identifiant = req.body.identifiant;
if(identifiant) res.status(200).json('ok').end();
res.status(500).json('PROBLEMAS NO REQUEST').end();
});
Unlike a Callable function, the body of a request is not parsed automatically and needs to be parsed before you can use it.
In addition, json(...) will call end() internally so you don't need both. Also make sure that you don't call end(), send(), json(), etc. multiple times, as this will lead to errors.
const jsonParser = require('body-parser').json();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
jsonParser(req, res, (err) => {
if (err) {
res.status(500).json({error: err.message});
return; // stop here
}
const identifiant = req.body.identifiant;
if (!identifiant) {
res.status(500).json({error: 'PROBLEMAS NO REQUEST'});
return; // stop here
}
// continue
res.status(200).json({ status: 'ok' });
})
});
I need to store my values from the request body to the cloud firestore and sent back the foruminsertdata.Name back in the response. But I am not able to do this.
const functions = require('firebase-functions');
const admin =require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.helloWorld = functions.https.onRequest((req, res) => {
if(req.method === 'POST'){
foruminsertdata = req.body;
db.collection('forum').add({
Name: foruminsertdata.Name,
Description: foruminsertdata.Description,
Heading: foruminsertdata.Heading,
PostedOn: foruminsertdata.PostedOn,
Status: foruminsertdata.Status,
})
.then(ref => {
console.log('Added document with ID: ', ref.id);
return res.status(200).json(
{
message: foruminsertdata.Name
});
})
.catch(err => {
console.log('Error getting documents', err);
});
res.json({
message: foruminsertdata.Status,
});
}
})
I don't know what is happening...Whatever I do I always get the output as
{
message: foruminsertdata.Status,
}
in which "foruminsertdata.Status" has some value that I give
but what I expect the output as
{
message: foruminsertdata.Name
}
Your function is immediately returning foruminsertdata.Status to the client without waiting for the promise from the database operations to resolve. Any function that returns a promise is asynchronous and returns immediately. Execution will continue in the callbacks you attach to it.
I'm not sure why you have two calls to res.json() in your code, but if you want to send a response only after your query completes, you'll remove the second one and just send a response after the query is done. You will probably also want to send a response in the catch callback as well to indicate an error.
I am trying to call a rest API from Firebase function which servers as a fulfillment for Actions on Google.
I tried the following approach:
const { dialogflow } = require('actions-on-google');
const functions = require('firebase-functions');
const http = require('https');
const host = 'wwws.example.com';
const app = dialogflow({debug: true});
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});
function callApi (param1) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the vehicle
let path = '/api/' + encodeURIComponent(param1);
console.log('API Request: ' + host + path);
// Make the HTTP request to get the vehicle
http.get({host: host, path: path}, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (d) => { body += d; }); // store each response chunk
res.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let output = {};
//copy required response attributes to output here
console.log(response.length.toString());
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the API: ${error}`)
reject();
});
}); //http.get
}); //promise
}
exports.myFunction = functions.https.onRequest(app);
This is almost working. API is called and I get the data back. The problem is that without async/await, the function does not wait for the "callApi" to complete, and I get an error from Actions on Google that there was no response. After the error, I can see the console.log outputs in the Firebase log, so everything is working, it is just out of sync.
I tried using async/await but got an error which I think is because Firebase uses old version of node.js which does not support async.
How can I get around this?
Your function callApi returns a promise, but you don't return a promise in your intent handler. You should make sure you add the return so that the handler knows to wait for the response.
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
return callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});