I tried to get a collection of documents from firestore via firebase function.firebase function console displays the json data of documents and returned that data to android app but firebase function callable gets null value.can you help me how to receive the documents data in android app.
I even followed this Question for answer but still i receive null value in my app.
export const getproducts = functions.https.onCall((data, context)=>{
let productarray = [];
const productref = admin.firestore().collection("Products")
.orderBy("product_id").limit(2);
productref.get()
.then((DataSnapshot) => {
productarray=DataSnapshot.docs.map((doc) => {
return doc.data();
});
console.log("products returned.", JSON.stringify(productarray));
return JSON.stringify(productarray);
}).catch((error)=> {
throw new functions.https.HttpsError("unknown", error.message, error);
});
});
my code for retreiving the data from android app
mFunctions = FirebaseFunctions.getInstance();
return mFunctions
.getHttpsCallable("getproducts")
.call()
.addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() {
#Override
public void onSuccess(HttpsCallableResult httpsCallableResult) {
try {
Gson g = new Gson();
String json = g.toJson(httpsCallableResult.getData());
ProductModel productModel = g.fromJson(json,ProductModel.class);
Log.e("getproducts",productModel.getProduct_id()); //i get null value here.
} catch (Exception e) {
Log.d("Error", e.toString());
}
}
});
document that displayed in console:
10:49:59.240 AM
getproducts
products returned. [{"cutted_price":100,"dress_color":"blue","product_id":"000001"},{"cutted_price":500,"dress_color":"gray","product_id":"000002"}]
Your Cloud Function is missing a return statement.
// ...
productref.get()
.then((DataSnapshot) => {
// ...
should be
// ...
return productref.get() // <----
.then((DataSnapshot) => {
// ...
Related
I'm learning Dart (from a Java developer history), I'm working in a simple message application on Flutter.
The unique problem that I have is when I try to include a new Message in the chat's history. I am updating de array of messages in the app (Dart) and sending the entire object (Complex object with the messages array inside) to update via FirebaseFirestore to Firebase.
But I'm getting this error
I/flutter (16604): Invalid argument: Instance of 'Message'
This is my code
Adding a new message to the actual array
Message newMessage = Message('Test text',DateTime.now(), 'From me', 'For U' conversation.id);
messagesQueue.add(newMessage);//add the message to the actual array
conversation.messages = messagesQueue;
updateConversation(conversation);
Conversation object makes this transformation to send to Firebase
class Conversation {
String id;
String owner;
String destinationName;
String destination;
List<Message> messages;
String lastMessageDate;
....
//Transformacion para Firebase
Map<String, dynamic> toMap(Conversation conversation) {
return <String, dynamic>{
//'id': conversation.id,
'owner': conversation.owner,
'destinationName': conversation.destinationName,
'destination': conversation.destination,
//'messages': conversation.messages,
'messages' : List<dynamic>.from(conversation.messages.map((x) => x.toMap())),
'lastMessageDate': conversation.lastMessageDate
};
if I delete this line 'messages' : List<dynamic>.from(conversation.messages.map((x) => x.toMap())), the update works fine (no update messages obvious)
Message.dart code
class Message {
String text;
DateTime sendedTime;
String from;
String to;
String conversationId;
Message(
this.text,
this.sendedTime,
this.from,
this.to,
this.conversationId,
);
Map<String, dynamic> toMap() => {
"text": text,
"from": from,
"to": to,
"sendedTime": sendedTime.toIso8601String(),
"conversationId": conversationId,
};
}
The update method
Future<bool> updateConversation(Conversation conversation) async {
try {
await db.collection('conversations').doc(conversation.id).update(toMap(conversation));
return true;
} catch (e) {
print(e);
return false;
}
}
What is wrong with the List messages transformation?
UPDATE
I've added this line (var jsonMessages = conversation.messages.map((e) => e.toMap()).toList();) and the update works, but now I'm getting this error
_Error type 'InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Message'
Future<bool> updateConversation(String id, Conversation conversation) async {
try {
var jsonMessages = conversation.messages.map((e) => e.toMap()).toList();
//await db.collection('conversations').doc(id).update(toMap(conversation));
await db
.collection('conversations')
.doc(id)
.update({'messages': jsonMessages});
return true;
} catch (e) {
print(e);
return false;
}
}
Solved
Transformation from Firestore
Conversation.fromFirestore(DocumentSnapshot doc)
: id = doc.id,
owner = doc.get("owner"),
destinationName = doc.get("destinationName"),
destination = doc.get("destination"),
messages = List<Message>.from(
doc.get("messages").map((e) => Message.fromMap(e))),// this is the trick!
lastMessageDate = doc.get("lastMessageDate");
I am trying to call a Google cloud function from an Android app that does not work (First call just after deployment works 90 % of the times but subsequent calls fails, nothing is displayed on firebase log console either).
public Task<String> myCloudFunction() {
return FirebaseFunctions.getInstance()
.getHttpsCallable("createUser")
.call(data)
.continueWith(task -> {
String result = (String) task.getResult().getData();
return result;
});
}
Endpoint in Functions Dashboard
https://us-central1-xyz:555.cloudfunctions.net/createUser
This is how I call it.
public void callCloudFunction() {
createFirebaseUserAccount.myCloudFunction()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
} else {
Timber.d(task.getResult());
}
}
}
});
}
Here is the cloud function:
$GOOGLE_APPLICATION_CREDENTIALS is pointing to service_key.json file which contains the private key.
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://XYZ.firebaseio.com"
});
exports.createUser = functions.https.onCall((data, context) => {
const callerEmail = data.email;
const callerPassword = data.password;
const callerDisplayName = data.displayName;
return admin.auth().createUser({
email: callerEmail,
emailVerified: false,
password: callerPassword,
displayName: callerDisplayName,
disabled: false
}).then(userRecord => {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully created new user:", userRecord.uid);
return userRecord.uid;
}).catch(error => {
console.log("Error creating new user ", error);
return error;
});
});
Thanks for reading! :)
You're not returning a promise from the the function that contains the data to send to the client. Actually, you're not passing anything at all. You should instead return the promise chain from your async work:
return admin.auth().createUser({
email: callerEmail,
emailVerified: false,
password: callerPassword,
displayName: callerDisplayName,
disabled: false
}).then(userRecord => {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully created new user:", userRecord.uid);
return userRecord.uid;
}).catch(error => {
console.log("Error creating new user ", error);
return error;
});
Note the new return before the whole thing. You should definitely take some time to learn about how JavaScript promises work in order to make effective use of Cloud Functions, and they will not work correctly without observing their rules and conventions.
I made a function in my Cloud Functions to verify a purchase signature. It must return a boolean and a string value. I read on Firebase Cloud Functions documentation that I should return a JSON structure that contains all the values. I made it like this.
EDIT
This is my entire Cloud Functions:
const BASE_64_ENCODED_PUBLIC_KEY = "MY_PUBLIC_KEY_HERE"
const KEY_FACTORY_ALGORITHM = "RSA-SHA1";
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
const crypto = require('crypto');
exports.verifySignature = functions.https.onCall((data, context) => {
const purchaseJSON = data.signedData;
const signature = data.signature;
console.log("start verification");
if (purchaseJSON === null || signature === null) {
console.log("Purchase verification failed: missing data.");
return {
message: "missing data",
verified: false
}
}
const verifier = crypto.createVerify(KEY_FACTORY_ALGORITHM);
verifier.update(purchaseJSON);
if (verifier.verify(publicKey, signature, "base64")){
console.log("signature verification success!");
return {
message: "verification success",
verified: true
}
} else {
console.log("signature verification failed!");
return {
message: "verification failed",
verified: false
};
}
});
And this is my code on the client:
private Task<String> verifyValidSignature(String signedData, String signature) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("signedData", signedData);
data.put("signature", signature);
return mFunctions.getHttpsCallable("verifySignature")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
HttpsCallableResult result = task.getResult();
if (result != null) {
return result.getData().toString();
}
return null;
}
});
}
How can I get the message value and convert it to a string and the verified value and convert it to a boolean in Android/Java?
result.getData() is returning a Map type object, because you returned an object from the function. JavaScript objects become Java Maps. You just use it like you would any other Map.
Map<String, Object> data = (Map<String, Object>) result.getData();
String message = (String) data.get("message");
boolean verified = (Boolean) data.get("verified");
What you are doing is ok, the only thing missing is to promosify it, this would do the trick
return Promise.resolve({ message: “verification success”, verified: true })
Similar for the error case but instead of using Promise.resolve you will use Promise.reject
I am using firebase cloud functions as serverside for Paypal payment. Documentations are not obvious to understand.
when I am trying to send an object from android app to firebase cloud functions, nothing has happened. I think I added it wrong. so how can I pass an object from android app to the function??
public void payout(String PayerID,String paymentId) {
// Create the arguments to the callable function.
JSONObject postData = new JSONObject();
try {
postData.put("PayerID", PayerID);
postData.put("paymentId",paymentId);
} catch (JSONException e) {
e.printStackTrace();
}
mFunctions
.getHttpsCallable("payout")
.call(postData)
.continueWith(new Continuation<HttpsCallableResult, Object>() {
#Override
public Object then(#NonNull Task<HttpsCallableResult> task)
throws Exception {
return null;
}
});
}
///////////////////////////////////////////
exports.payout=functions.https.onRequest((req,res)=>{
const sender_batch_id = Math.random().toString(36).substring(9);
const payReq=JSON.stringify({
sender_batch_header: {
sender_batch_id: sender_batch_id,
email_subject: "You have a nice payment"
},
items: [
{
recipient_type: "EMAIL",
amount: {
value: 0.90,
currency: "USD"
},
receiver: "amrmahmoudM#app.com",
note: "Thank you very much.",
sender_item_id: "item_3"
}
]
});
paypal.payout.create(payReq,(error, payout)=>{
if (error) {
console.warn(error.res);
res.status('500').end();
throw error;
}else{
console.info("payout created");
console.info(payout);
res.status('200').end();
}
});
});
exports.process = functions.https.onRequest((req, res) => {
const paymentId = req.body.paymentId;
var payerId = {
payer_id: req.body.PayerID
};
return paypal.payout.execute(paymentId, payerId, (error, payout) => {
if (error) {
console.error(error);
} else {
if (payout.state === 'approved') {
console.info('payment completed successfully, description: ',
payout.transactions[0].description);
const ref=admin.firestore().collection("Users").doc(payerId);
ref.set({'paid': true});
} else {
console.warn('payment.state: not approved ?');
}
}
}).then(r =>
console.info('promise: ', r));
});
The problem comes from the fact that in your Android app you call an HTTPS Callable Function (via mFunctions.getHttpsCallable("payout")) but your Cloud Function is not an HTTPS Callable Function but a "simple" HTTPS Function.
HTTPS Callable Functions are written like:
exports.payout = functions.https.onCall((data, context) => {
// ...
});
while HTTPS Functions are written like:
exports.payout = functions.https.onRequest((req,res)=> {
// ...
})
So you should adapt the code of your Cloud Function according to the documentation: https://firebase.google.com/docs/functions/callable
Note that another option could be to write to the database (Real Time database or Firestore) and trigger the Cloud Function with an onWrite or onCreate trigger. The advantage of this approach is that you directly save the information of the payment in the database.
I’m trying to call an async Firebase function from android app and getting “INTERNAL” exception when the function returns.
Android:
private Task<String> fetchData() {
// Create the arguments to the callable function, which is just one string
Map<String, Object> data = new HashMap<>();
data.put(“id”, “abc”);
return FirebaseFunctions.getInstance()
.getHttpsCallable(“calculate”)
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
Map<String, Object> result = (Map<String, Object>) task.getResult().getData();
return (String)result.get(“data”);
}
});
}
Firebase Function:
exports.calculate = functions.https.onCall((data, context) => {
const text = data.id;
return calc.calculate( (err, response) => {
if(err) {
// handle error
} else {
const data = response.dataValue;
}
}).then(() => {
return {“data”: data};
});
});
Exception:
com.google.firebase.functions.FirebaseFunctionsException: INTERNAL
The documentation for handling errors in callable functions indicates that an instance of functions.https.HttpsError must be returned:
To ensure the client gets useful error details, return errors from a
callable by throwing (or returning a Promise rejected with) an
instance of functions.https.HttpsError... If an error other than
HttpsError is thrown from your functions, your client instead receives
an error with the message INTERNAL and the code internal.
It seems likely that your calc.calculate() call is returning an error that is not being handled correctly, resulting in a returned error status of INTERNAL.
Following the example in the document linked above, your code should be something like:
if(err) {
// handle error
throw new functions.https.HttpsError('calc-error', 'some error message');
} else {
const data = response.dataValue;
}
When you call httpCallable ,you will get an exception called FirebaseFunctionsExceptions. You have to handled this exceptions. Wrap your code with try and catch .
Example:-
try {
final result = await FirebaseFunctions.instance
.httpsCallable('deleteUser')
.call({});
} on FirebaseFunctionsException catch (error) {
print(error.message);
}
For more info follow this link.