I'm trying to integrate Firebase Cloud Functions into an Android app. I'm new in Cloud Functions and Node.js.
So, I have the below simple node.js code to save data in Firestore database:
'use strict';
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
const Firestore = require('#google-cloud/firestore');
exports.saveData = functions.https.onRequest((request, response) => {
var firestoreDB = new Firestore({
projectId: 'my_project_id',
keyFilename: 'my_key_file_name'
});
const line1 = request.body.line1;
const line2 = request.body.line2;
var data = {
name: line1,
number: line2
};
return firestoreDB.collection('myData').doc('firstDoc')
.add( data )
.then(ref => {
console.log('Data saved.');
response.end();
});
});
This code is working fine, but being executed twice. It writes the data into DB twice, it logs every single log twice in Firebase console log screen.
I have tried another function to send a message using FCM from Android device to Android device, it also send the message twice.
I checked Android side to make sure it's triggered once. I'm wondering if there is anything I'm missing out to make it work. Any help or suggestions appreciated.
You are not sending response from API so browser not getting any response and it retry the API untill timeout of 60 sec.
response.status(200).send('OK');
Enter this above line as show below -
exports.saveData = functions.https.onRequest((request, response) => {
var firestoreDB = new Firestore({
projectId: 'my_project_id',
keyFilename: 'my_key_file_name'
});
const line1 = request.body.line1;
const line2 = request.body.line2;
var data = {
name: line1,
number: line2
};
return firestoreDB.collection('myData').doc('firstDoc')
.add( data )
.then(ref => {
console.log('Data saved.');
response.status(200).send('OK');
});
});
So, the real problem was in Volley on Android (Java) side. By default, Volley makes the second attempt to reach the server if it doesn't hear back in 2.5 sec. Because I have had heavy operations and sometimes a drop in Internet speed, it always took more that 2.5 sec to respond back.
If anyone is facing similar issue, just double check Volley and make sure to add the following at the end of Volley request
stringRequest.setRetryPolicy(
new DefaultRetryPolicy(
15000, // timeout timimg of 15 sec
0, // number of retries. By default the number is 1
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
// Add the request to the RequestQueue.
MySingleton.getInstance().addToRequestQueue(stringRequest);
Related
I am now using the below cloud code to only update "downloads" column on my parse server running on AWS EC2 instance. But I am getting the error code 141(invalid function)
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
query.get(request.params.objectId)
.then((watchFace) => {
downloads = watchFace.get("downloads")
watchFace.set("downloads", downloads + 1);
await watchFace.save(null, { useMasterKey: true });
return "download updated";
}, (error) => {
return "something went wrong";
});
});
I have place my code in /opt/bitnami/cloud/main.js.
I even tried adding “cloud”: “/opt/bitnami/cloud/main.js” in config.json file but then the parse server gives 503 Service Unavailable error. So I removed it.
If you don't add the cloud code main.js file to your parse server configuration, parse server will never find your function, and that's why you get the invalid function error.
If you get error when adding the file, you are either adding it in a wrong way (you need to check your parse server initialization code) or the config.json is in wrong format or the cloud code has a problem.
The best way to figure it out is by checking your logs.
At a first glance, a problem that I see (may have others) is the usage of await in a function that is not async. You are also using a combination of async and then, which is little strange.
I'd recommend you to change the code to something like:
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
const watchFace = await query.get(request.params.objectId);
const downloads = watchFace.get("downloads");
watchFace.set("downloads", downloads + 1); // You can use inc function to avoid concurrency problem
await watchFace.save(null, { useMasterKey: true });
return "download updated";
});
I followed the documentation on pub/sub notifications with the push method here
And I want to have authentication on my call with JWT. I looked at their GitHub example here
app.post('/pubsub/authenticated-push', jsonBodyParser, async (req, res) => {
// Verify that the request originates from the application.
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
// Verify that the push request originates from Cloud Pub/Sub.
try {
// Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
const bearer = req.header('Authorization');
const [, token] = bearer.match(/Bearer (.*)/);
tokens.push(token);
// Verify and decode the JWT.
// Note: For high volume push requests, it would save some network
// overhead if you verify the tokens offline by decoding them using
// Google's Public Cert; caching already seen tokens works best when
// a large volume of messages have prompted a single push server to
// handle them, in which case they would all share the same token for
// a limited time window.
const ticket = await authClient.verifyIdToken({
idToken: token,
audience: 'example.com',
});
const claim = ticket.getPayload();
claims.push(claim);
} catch (e) {
res.status(400).send('Invalid token');
return;
}
// The message is a unicode string encoded in base64.
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
messages.push(message);
res.status(200).send();
});
But I have some questions.
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it in my environment?
const [, token] = bearer?.match(/Bearer (.*)/); throws the following error
Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
Why do they push the claims and tokens in an array if they never check that array in this function for already existing tokens / claims?
I am trying to implement this with a Firebase Cloud Function and this is what I have. Is it even possible to cache the tokens / claims?
//Service account auth client
const authClient = new google.auth.JWT({
email: android_key.client_email,
key: android_key.private_key,
scopes: ["https://www.googleapis.com/auth/androidpublisher"]
});
export const handlePubSub = functions.region('europe-west1').https.onRequest(async (req, res) => {
// What is PUBSUB_VERIFICATION_TOKEN???
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
try {
const bearer = req.header('Authorization');
const [, token] = bearer?.match(/Bearer (.*)/); //Error Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
tokens.push(token); // Why do this? Can I do this in firebase cloud functions
const ticket = await authClient.verifyIdToken({
idToken: token,
});
const claim = ticket.getPayload();
claims.push(claim); // Why do this? Can I do this in firebase cloud functions
} catch (e) {
res.status(400).send('Invalid token');
return;
}
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
console.log(message);
return res.status(200).json({
statusCode: 200,
method: req.method,
message: 'Recieved successfully'
});
});
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it
in my environment?
PUBSUB_VERIFICATION_TOKEN can be any value you want. Easiest way to set an environment variable is on the command line when running node:
PUBSUB_VERIFICATION_TOKEN=whatevertoken node app.js
The req.query.token that is compared too comes from the URL query string.
GET /whatever?token=whatevertoken
Type 'RegExpMatchArray | null | undefined' must have a
'Symbol.iterator' method that returns an iterator.ts(2488)
That's a bug in their code. bearer.match can return undefined/null which can't be spread into the array [, token]. The example will only work when there is a successful regex match. This will parse in plain javascript but typescript highlights this issue at compile time.
const bearer = req.header('Authorization');
const m = /Bearer (.*)/.exec(bearer)
if (m) tokens.push(m[1])
Why do they push the claims and tokens in an array if they never check
that array in this function for already existing tokens / claims?
The example comments // List of all messages received by this instance.
So more a debug store than something functional.
I have a Node js API written in express framework.
I am sending some data over all my api does is calculate number of Packages to make For example :- 100/10 = 10 packages to make.
Loops and creates packages in sales force and firebase one by one.
Works fine from postman.
Problem:
When i try to hit the api from my app it works fine when the package count is <= 10. when > 10 ,Lets say 25 it calculates packages and run a loop of 25 and creates packages,crashes after 11th iteration and restarts the route, calculate again 25 packages to create and resulting in "Over weight error".
1- thought it was from android error may be i was hitting two request one after an other ( this was not the case ).
2- Tried sending header "Connection" "Keep-Alive" ( as Postman does ) not working.
3- tried to put up the timeout in the below code it did not work either ( tried variations of time out like 0, 50,000 ms )
else {
console.log('=====By Item=============');
const supplierFinishedGood = {
Name: parentBatchDoc['itemName'],
Supplier_Product__c: parentBatchDoc['id'],
Package_Size__c: 'a090S000001ZQ5kQAG', // Hard coded PackageSize in 'Gram' as per SALESFORCE
On_Hand_Amount__c: childBatch['batchWeight']
}
console.log('=====By Item============= 2');
const SupplierFinishedProductID = await createSupplierFinishedProduct(supplierFinishedGood, bearerToken);
const Quantity_Packaged__c = Math.floor((childBatch['batchWeight'] - childBatch['batchTestAmount']) / noOfPackage);
console.log('=====By Item============= 3');
//console.log('Quantity_Packaged__c ==== Remaining_Grams_Available_for_Packaging__c', Quantity_Packaged__c, parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c']);
for (let index = 0; index < noOfPackage; index++) {
if (parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c'] > Quantity_Packaged__c) {
let package = {
Batch__c: childId,
Product__c: SupplierFinishedProductID,
Inventory_Location__c: 'a030S000003x7M7QAI', //Hard coded InventoryLocation 'StorageFinished' as per SALESFORCE
Number_Of_Items__c: noOfItemInPackage,
Quantity_Packaged__c: Quantity_Packaged__c,
Date_Packaged__c: datePackaged,
Expiration_Date__c: expirationDate
};
console.log('Before creating apcaktge ', index);
const packageID = await createPackage(package, bearerToken);
console.log('After creating package ', index, parentBatchSalesforce['Remaining_Grams_Available_for_Packaging__c']);
package['parentBatchId'] = parentId;
package['status'] = 'Ready to checkout';
package['uid'] = packageID;
const packageFBResponse = await db.collection('packages').doc(packageID).set(package, { merge: true });
reponseBody.push(packageID);
} else {
console.log('======Over
Weight====');
}
Above code is what produces the error.
There is a If condition before this it works fine i have tested it
as it has some other scenario.
End result should not be a timeout error.
API should create all the packages and return the result.
I have found a workaround. I am Playing around batch status and my code returns me the IDs which i need and packages are created.
This is not the ultimated best solution it is a work around. i am still open to know what the actual problem is.
parentBatchDoc['childBatches'][childId]['batchStatus'] = "In Processing";
const parentBatchDocResponse = await db.collection('batches').doc(parentId).set(parentBatchDoc, { merge: true });
if(parentBatchDoc['childBatches'][childId]['batchStatus'] !== "In Processing"){
===================Rest of the case of =======================
}
First I generated a FCM token and stored in firestore. After that I wrote a cloud functions to send notifications based on FCM token. and I deployed cloud functions it says successfully sent notifications with status ok. But it doesn't displays in mobile device. My Index.js is
'use strict';
const functions = require('firebase-functions');
const Firestore = require('#google-cloud/firestore');
const admin = require('firebase-admin');
const firestore = new Firestore();
const db = admin.firestore();
admin.initializeApp(functions.config().firebase);
exports.hellouser = functions.firestore
.document('users/{token}')
.onWrite(event =>{
var document = event.data.data();
console.log("tokens",document);
var token = ['cdNN0AbYKU0:APA91bEyL0zo3zwHZD8H43Vp7bxAfYgehlVI8LrKktPO2eGuByVDdioysIGxHe5wocwq8ynxRToJPpOve_M59YY_MIRbWLnF9AIgoTwJORXZbw6VBw7']// this is my FCM token.
if(
const payload = {
notification: {
title: "Message",
body: "hi hello",
sound: "default"
}
};
return admin.messaging().sendToDevice(token, payload).then((response)=> {
console.info("Successfully sent notification")
}).catch(function(error) {
console.warn("Error sending notification " , error)
});
});
How to send notifications based on the FCMtoken.
If it's the exact code you use then check syntax near if(. This may help you.Next write some code to go through your response object. Firebase may take your tokens and payload, process them and return 200 OK response but in the response you will have errors. Response has general structure like this: { results:
[ { //stuff related to one token },{ //stuff related to one token } ],
canonicalRegistrationTokenCount: 0,
failureCount: 1,
successCount: 0,
multicastId: SOME_LONG_NUMBER }Take in mind that response.results array has status of each message sent to token in the same order as tokens in your token array. You can see all posible errors in Firebase Documentation. If response.failureCount > 0 then no messages were sent and you should get corresponding error in response.results.Also learn about options variable. options.priority must be 'high' to guarantee fast message delivery. Maybe this will help.
This is my first time using Cloud Functions for Firebase and Node.js, so
I have no idea why I can't see any logs on my Firebase console.
At least, I've successfully deployed the function below
'use strict';
const functions = require('firebase-functions');
//import admin module
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/* Listens for new messages added to events and sends a notification to
subscribed users */
exports.pushNotification =
functions.database.ref('/events/{pushId}').onWrite( event => {
console.log('Push notification event triggered');
/* Grab the current value of what was written to the Realtime Database
*/
var valueObject = event.data.val();
/* Create a notification and data payload. They contain the
notification information, and message to be sent respectively */
const payload = {
data: {
eventType: valueObject.eventType,
receiver: valueObject.receiver,
requester: valueObject.requester
}
};
/* Create an options object that contains the time to live for the
notification and the priority. */
const options = {
priority: "high",
timeToLive: 60 * 60 * 24 //24 hours
};
return admin.messaging().sendToTopic(valueObject.receiver, payload,
options).then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
However, I couldn't see any log messages on Firebase Console even if I write some data to my 'events' database.
Does it mean this function wasn't triggered at all?
I would really appreciate if someone could suggest the cause of this issue.
If you've set up your application correctly in the console, then there's no reason why logging would not happen if the function is fired.
That leaves you with the possibility that the function is not being fired up. A possible reason for that is that the data is not being written at the path provided in your function.
So, are you adding a value to /events such as 'true' or 'ABC', or are you adding a child to it such as {'ABC' = true}?
In the case of .ref(/events/{pushId}), the function will be called in the latter case and not the former.