When trying to make a call to a Cloud Function I get a "CloudFunctionsException"
The code of the exception is "INTERNAL
The message is "Response is not valid JSON object."
Describe the bug
To Reproduce
Steps to reproduce the behavior:
- My call from the application is
HttpsCallable _getName;
_getName = CloudFunctions.instance.getHttpsCallable(functionName: 'getName',);
try {
HttpsCallableResult resp = await _getName.call(<String, dynamic>{'name': name,});
Scaffold.of(context).showSnackBar(SnackBar(content: Text("${resp.data}")));
} on CloudFunctionsException catch (e) {
showErrorMessage(context, 'Cloud functions exception with code: ${e.code}, and Details: ${e.details}, with message: ${e.message} ');
} catch (e) {
showErrorMessage(context, e.toString());
}
My Cloud Function is written as so:
exports.getName = functions.https.onCall((data, context) => {
return {
"data" : "You hit the call at least!"
};
});
Expected behavior
In my response, I should get back the data: "You hit the test call". Instead, I get the error
Additional context
When I make calls to the same function but with the HTTP package and receive it on the back end with "onRequest", it works.
void _checkPersonsNameGET(String name)async{
try {
http.Response resp = await http.get(_cloudFunctionUrl,, );
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("${resp.body}", style: TextStyle(color: Colors.green))));
} catch (e) {
showErrorMessage(context, e.toString());
}
}
void _checkPersonsNamePOST(String name)async{
try {
http.Response resp = await http.post(_cloudFunctionUrl, body: { "name" : name } );
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("${resp.body}", style: TextStyle(color: Colors.green))));
} catch (e) {
showErrorMessage(context, e.toString());
}
}
exports.getName = functions.https.onRequest((request, response) => {
const name = request.query.name || request.body.name;
switch (name) {
case 'Andrew':
request.query.name ? response.send("Na he is the boi Q!") : response.send("Na he is the boi B!");
break;
case 'Brett':
request.query.name ? response.send("Na just wierd! Q") : response.send("Na just wierd! B");
break;
case 'Eddie':
request.query.name ? response.send("My brother but yeah! Q") : response.send("My brother but yeah! B");
break;
case 'James':
request.query.name ? response.send("The biggest! Q") : response.send("The biggest! B");
break;
default:
request.query.name ? response.send("Dunno who that is! Q") : response.send("Dunno who that is! B");
break;
}
});
It's a mock application and can be seen here
https://github.com/earyzhe/firebase_cloud_functions_play
In my case, the cloud function was in a file that was not exported in the index file. Make sure your function is properly exported and has been deployed properly.
Related
So I am making a simple route for my app, which basically, calls another server and returns the data back to me (since CORS is not enabled, I cant do it from the frontend atm).
const getBotState: NextApiHandler = async (req, res) => {
console.log(req.method);
switch (req.method) {
case 'GET':
try {
res.status(200).json({ someData: "hi get" });
} catch (e) {
console.log(e);
res.status(404).json({ err: e });
}
case 'POST':
try {
res.status(200).json({ someData: "hi post" }); // this is the line that breaks it
} catch (e) {
console.log('post error');
console.log(e);
res.status(404).json({ err: e });
}
default:
}
};
export default getBotState;
The problems started after I added the POST case.
If I comment out the res.status.... line, the error will go away, but I am making ONLY GET requests currently to this API endpoint, I am not making POST requests at all, and yet, the catch block in the POST case is what triggers the error.
switch has a unique attribute and its attribute is its ability to 'fall-through' and access the next case statement. This is by design, every programming language does this.
How to fix this? Add a break.
const getBotState: NextApiHandler = async (req, res) => {
console.log(req.method);
switch (req.method) {
case 'GET':
try {
res.status(200).json({ someData: "hi get" });
} catch (e) {
console.log(e);
res.status(404).json({ err: e });
}
break;
case 'POST':
try {
res.status(200).json({ someData: "hi post" }); // this is the line that breaks it
} catch (e) {
console.log('post error');
console.log(e);
res.status(404).json({ err: e });
}
break;
default:
}
};
export default getBotState;
And then you're done!
From MDN Docs:
The optional break statement associated with each case label ensures that the program breaks out of switch once the matched statement is executed and continues execution at the statement following switch. If break is omitted, the program continues execution at the next statement in the switch statement. The break statement is not required if a return statement precedes it.
Alright i'm losing my mind here,
in my flutter app, i'm using this function to perform post requests :
Future<Map> postRequest(String serviceName, Map<String, dynamic> data) async {
var responseBody = json.decode('{"data": "", "status": "NOK"}');
try {
http.Response response = await http.post(
_urlBase + '$_serverApi$serviceName',
body: jsonEncode(data),
);
if (response.statusCode == 200) {
responseBody = jsonDecode(response.body);
//
// If we receive a new token, let's save it
//
if (responseBody["status"] == "TOKEN") {
await _setMobileToken(responseBody["data"]);
// TODO: rerun the Post request
}
}
} catch (e) {
// An error was received
throw new Exception("POST ERROR");
}
return responseBody;
}
The problems are :
I get a ClientException (Not every time)
In another class, I stored the result of this function in a variable, it's supposed to return a Future<Map<dynamic, dynamic>>, when i printed it it shows :
I/flutter ( 9001): Instance of 'Future<Map<dynamic, dynamic>>'
But when i run the same post request directly (without using a function) it worked, and it shows the message that i was waiting for.
note: in both cases (function or not), in the server side it was the same thing.
this is the function where i used the post request:
void _confirm() {
if (_formKey.currentState.saveAndValidate()) {
print(_formKey.currentState.value);
var v = auth.postRequest("se_connecter", _formKey.currentState.value);
print(v);
} else {
print(_formKey.currentState.value);
print("validation failed");
}
}
Well for the second problem, i just did these changes:
void _confirm() async {
and
var v = await auth.postRequest('se_connecter', _formKey.currentState.value);
and yes it is stupid.
For the exception, it was the ssl encryption that caused it, so i removed it from my backend.
I have the following function to change the email on a Firebase user account. I want to display an ionic2 alert when complete, whether it was successful or there was an error. From my code below I do get the alert to display BUT it is blank. Most likely it is a timing issue on the Firebase promise but I don't know how to fix it.
private doChangeEmail(data): void {
var myAlert: {
title?: string,
subtitle?: string
} = {};
this.auth.ref.changeEmail({
oldEmail: data.oldemail,
newEmail: data.newemail,
password: data.password
}, function(error) {
if (error) {
switch (error.code) {
case "INVALID_PASSWORD":
myAlert.title = 'Invalid Password';
myAlert.subtitle = 'The specified user account password is incorrect.';
break;
case "INVALID_USER":
myAlert.title = 'Invalid User';
myAlert.subtitle = 'The specified user account does not exist.';
break;
default:
myAlert.title = 'Error creating user';
myAlert.subtitle = error;
}
} else {
myAlert.title = 'DONE';
myAlert.subtitle = 'User email changed successfully!';
}
});
let alert = Alert.create({
title: myAlert.title,
subTitle: myAlert.subtitle,
buttons: [{
text: 'OK',
handler: () => {
}
}]
});
this.nav.present(alert);
}
put the alert code inside the promise result....
this.auth.ref.changeEmail({
oldEmail: data.oldemail,
newEmail: data.newemail,
password: data.password
}, function(error) {
if (error){
// do error stuff..
} else {
// do success stuff..
}
// show alert here...
})
I found the following comment by Frank van Puffelen which solved my issue:
You're using this inside a callback function, where it has a different meaning. One solution is to use fat-arrow/rocket notation for the callback, which ensures this is what you expect it to be:
The correct syntax should be
this.auth.ref.changeEmail({
oldEmail: data.oldemail,
newEmail: data.newemail,
password: data.password
}, (error) => {
if (error){
// do error stuff..
} else {
// do success stuff..
}
// show alert here...
})
and the alert shows correclty as pointed out by Aaron Saunders
I'm using Meteor for first time and i'm trying to have a simple http call within a method so i can call this method from the client.
The problem is that this async call it's keep running even if i put it within a wrapper.
Client side:
Meteor.call('getToken', function(error, results) {
console.log('entered');
if(error) {
console.log(error);
} else {
console.log(results);
}
});
Server Side
Meteor.methods({
getToken: function(){
// App url
var appUrl = 'myAppUrl';
// Key credentials
var apiKey = 'mykey';
var apiSecret = 'mySecret';
function asyncCall(){
Meteor.http.call(
'POST',
appUrl,
{
data: {
key: apiKey,
secret: apiSecret
}
}, function (err, res) {
if(err){
return err;
} else {
return res;
}
}
);
}
var syncCall = Meteor.wrapAsync(asyncCall);
// now you can return the result to client.
return syncCall;
}
});
I'm always getting an undefined return.
If i log the response within the http.post call i'm geting the correct response.
If i try to log the syncCall i get nothing.
I would very appreciate any help on this.
You should use the synchronous version of HTTP.post in this case. Give something like this a try:
Meteor.methods({
getToken: function() {
var appUrl = 'myAppUrl';
var data = {apiKey: 'mykey', apiSecret: 'mySecret'};
try {
var result = HTTP.post(appUrl, {data: data});
return result;
} catch (err) {
return err;
}
}
});
Instead of returning the err I'd recommend determining what kind of error was thrown and then just throw new Meteor.Error(...) so the client can see the error as its first callback argument.
I have a method and it throws an error so I can catch it in my event and display it to the user, like this:
Meteor.methods({
addPlayer: function(nickname) {
if (nickname == "") {
throw new Meteor.Error('empty-nickname', 'You must choose a nickname');
} else {
Player.insert({
nickname: nickname,
});
}
},
})
and in my event
'submit form': function (e) {
e.preventDefault();
var nickname = $('input').val();
Meteor.call('addPlayer', nickname, function(error, result) {
if (error) {
console.log(typeof error);
console.log(error);
}
});
}
However, meteor still throws an Exception while simulating the effect of invoking 'addPlayer', and the error variable is not an error object, but a string with the same message as the console log, so I get two errors in my console instead of an error object.
Wrapping method.call in a try/catch does not work.
What am I missing here?
-- Edit
Here is an print screen of the result:
Image link for full resolution: http://i.stack.imgur.com/zABar.png
Throw the error only on the server. Wrap it inside if(!this.isSimulation) {}
Meteor.methods({
addPlayer: function(nickname) {
if (nickname == "") {
if(!this.isSimulation) {
throw new Meteor.Error('empty-nickname', 'You must choose a nickname');
}
} else {
Player.insert({
nickname: nickname,
});
}
},
})