Google Cloud Function request node module 404 - javascript

When i checking my "img" entry on firebase with google cloud function and the request module i get an error but only when i deployed it on local emulator it works.
Error i get on my cloud logs:
removeExpiredDocuments
Exception from a finished function: TypeError: Cannot read properties of undefined (reading 'statusCode')
Example URL for 404:
https://ch.cat-ret.assets.lidl/catalog5media/ch/article/5104560/third/lg/5104560_01.jpg
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const requesth = require('request');
admin.initializeApp();
exports.removeExpiredDocuments = functions.region('europe-west1').runWith({ memory: "256MB" }).pubsub.schedule("every 1 hours").onRun(async (context) => {
const db = admin.firestore();
const snaps = await db.collection("products").get();
let promises = [];
snaps.forEach((snap) => {
requesth(snap.data().img, function (error, response) {
functions.logger.info('[img] error: ' + error, { structuredData: true });
if ((response) && (response.statusCode) == 404) {
promises.push(snap.ref.delete());
functions.logger.info('[img] not found ' + snap.data().name, { structuredData: true });
}
});
});
return Promise.all(promises);
});
I wanna use google cloud function for checking if the "img" entry is statusCode 404 and if its statusCode 404 delete the document.
#edit new random error without changed anything
#2 Image of random change errors

Related

Analytics.js data to PubSub GCP

I am using analytics.js to make a custom tracking over my website. As I wish to send the hit to PubSub, I used this documentation (Node.JS tab) to connect my TypeScript code to PubSub (not perfect I know. I am trying to make it work before cleaning).
ga(() => {
ga("set", "cookieExpires", 0);
const tracker = ga.getByName(trackerName);
tracker.set("sendHitTask", (model: any) => {
var refusedParam = ["_gid", "tid"];
let hit = model.get("hitPayload").split("&").filter((paramValue: string) => {
let param = paramValue.split("=")[0];
return (refusedParam.indexOf(param) == -1);
}).join("&");
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
const topicNameOrId = 'tracking-test';
const data = JSON.stringify(hit);
// Creates a client; cache this for further use
const pubSubClient = new PubSub();
console.log("DATA IS " + data);
async function publishMessage() {
// Publishes the message as a string, e.g. "Hello, world!" or JSON.stringify(someObject)
const dataBuffer = Buffer.from(data);
try {
const messageId = await pubSubClient
.topic(topicNameOrId)
.publishMessage({data: dataBuffer});
console.log(`Message ${messageId} published.`);
} catch (error) {
console.error(`Received error while publishing: ${error.message}`);
process.exitCode = 1;
}
}
publishMessage();
});
});
I don't have any error when building and running this code. But, when I locally connect to my website, I have the following error inside the JS console Uncaught TypeError: a.grpc is undefined.
I tried to put grpc inside my package.json, but no success at removing the error and having a correct behavior.
Did I miss something ? How can I use analytics.js, and send data directly to PubSub ?

Getting error: Test webhook error: 400 when trying to send a test event to a webhook endpoint

I am attempting to send a test webhook as instructed in this tutorial.
But when I go to do it I get the error seen in the first link, and below:
Test webhook error: 400
Here is my index.ts code & functions I have deployed to firebase functions.
import * as functions from 'firebase-functions';
​
​
// const functions = require('firebase-functions');
const stripe = require('stripe')(functions.config().keys.webhooks);
const admin = require('firebase-admin');
​
admin.initializeApp();
const endpointSecret = functions.config().keys.signing;
​
exports.events = functions.https.onRequest((request, response) => {
​
let sig = request.headers["stripe-signature"];
​
try {
let event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret); // Validate the request
return admin.database().ref('/events').push(event) // Add the event to the database
.then((snapshot: { ref: { toString: () => any; }; }) => {
// Return a successful response to acknowledge the event was processed successfully
return response.json({ received: true, ref: snapshot.ref.toString() });
})
.catch((err: any) => {
console.error(err) // Catch any errors saving to the database
return response.status(500).end();
});
}
catch (err) {
return response.status(400).end(); // Signing signature failure, return an error 400
}
});
​
exports.exampleDatabaseTrigger = functions.database.ref('/events/{eventId}').onCreate((snapshot, context) => {
return console.log({
eventId: context.params.eventId,
data: snapshot.val()
});
});
How do I fix this and successfully run the test?
My current thinking is that the problem may have something to do with:
How I wrote this line: snapshot: { ref: { toString: () => any; };
Update:
From my testing, this does not appear to be the case.
I don't believe that the 'test webhook' properly signs them; you should use Stripe CLI for this instead.

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object

I am using the source code from a security rules tutorial to attempt to do integration testing with Jest for my Javascript async function async_create_post, used for my firebase HTTP function create_post The files involved has a directory structure of the following:
Testing file: root/tests/handlers/posts.test.js
File to be tested: root/functions/handlers/posts.js
Helper code from the tutorial: root/tests/rules/helpers.js
And here is the source code that is involved:
posts.test.js
const { setup, teardown} = require("../rules/helpers");
const {
async_get_all_undeleted_posts,
async_get_post,
async_delete_post,
async_create_post
} = require("../../functions/handlers/posts");
describe("Post Creation", () => {
afterEach(async () => {
await teardown();
});
test("should create a post", async () => {
const db = await setup();
const malloryUID = "non-existent uid";
const firstPost = {
body: "First post from Mallory",
author_id: malloryUID,
images: ["url1", "url2"]
}
const before_post_snapshot = await db.collection("posts").get();
expect(before_post_snapshot.docs.length).toBe(0);
await async_create_post(firstPost); //fails at this point, expected to create a new post, but instead threw an error
const after_post_snapshot = await db.collection("posts").get();
expect(after_post_snapshot.docs.length).toBe(1);
});
});
posts.js
const {admin, db } = require('../util/admin');
//admin.initializeApp(config); //my credentials
//const db = admin.firestore();
const { uuid } = require("uuidv4");
const {
success_response,
error_response
} = require("../util/validators");
exports.async_create_post = async (data, context) => {
try {
const images = [];
data.images.forEach((url) => {
images.push({
uid: uuid(),
url: url
});
})
const postRecord = {
body: data.body,
images: images,
last_updated: admin.firestore.FieldValue.serverTimestamp(),
like_count: 0,
comment_count: 0,
deleted: false,
author_id: data.author_id
};
const generatedToken = uuid();
await db
.collection("posts")
.doc(generatedToken)
.set(postRecord);
// return success_response();
return success_response(generatedToken);
} catch (error) {
console.log("Error in creation of post", error);
return error_response(error);
}
}
When I run the test in Webstorm IDE, with 1 terminal running Firebase emulators:start , I get the following error message.
console.log
Error in creation of post TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object
at validateString (internal/validators.js:120:11)
at Object.basename (path.js:1156:5)
at GrpcClient.loadProto (/Users/isaac/Desktop/project/functions/node_modules/google-gax/src/grpc.ts:166:23)
at new FirestoreClient (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/v1/firestore_client.js:118:38)
at ClientPool.clientFactory (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/index.js:330:26)
at ClientPool.acquire (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/pool.js:87:35)
at ClientPool.run (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/pool.js:164:29)
at Firestore.request (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/index.js:961:33)
at WriteBatch.commit_ (/Users/isaac/Desktop/project/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:485:48)
at exports.async_create_post (/Users/isaac/Desktop/project/functions/handlers/posts.js:36:5) {
code: 'ERR_INVALID_ARG_TYPE'
}
at exports.async_create_post (/Users/isaac/Desktop/project/functions/handlers/posts.js:44:13)
Error: expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
<Click to see difference>
at Object.<anonymous> (/Users/isaac/Desktop/project/tests/handlers/posts.test.js:59:45)
Error in creation of post comes from the console.log("Error in creation of post", error); in posts.js, so the error is shown in the title of this post.
I want to know why calling the async_create_post from posts.test.js will cause this error and does not populate my database with an additional record as expected behaviour. Do inform me if more information is required to solve the problem.
Here are some code snippets that may give more context.
helpers.js [Copied from the repository]
const firebase = require("#firebase/testing");
const fs = require("fs");
module.exports.setup = async (auth, data) => {
const projectId = `rules-spec-${Date.now()}`;
const app = firebase.initializeTestApp({
projectId,
auth
});
const db = app.firestore();
// Apply the test rules so we can write documents
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore-test.rules", "utf8")
});
// write mock documents if any
if (data) {
for (const key in data) {
const ref = db.doc(key); // This means the key should point directly to a document
await ref.set(data[key]);
}
}
// Apply the actual rules for the project
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync("firestore.rules", "utf8")
});
return db;
// return firebase;
};
module.exports.teardown = async () => {
// Delete all apps currently running in the firebase simulated environment
Promise.all(firebase.apps().map(app => app.delete()));
};
// Add extensions onto the expect method
expect.extend({
async toAllow(testPromise) {
let pass = false;
try {
await firebase.assertSucceeds(testPromise);
pass = true;
} catch (error) {
// log error to see which rules caused the test to fail
console.log(error);
}
return {
pass,
message: () =>
"Expected Firebase operation to be allowed, but it was denied"
};
}
});
expect.extend({
async toDeny(testPromise) {
let pass = false;
try {
await firebase.assertFails(testPromise);
pass = true;
} catch (error) {
// log error to see which rules caused the test to fail
console.log(error);
}
return {
pass,
message: () =>
"Expected Firebase operation to be denied, but it was allowed"
};
}
});
index.js
const functions = require('firebase-functions');
const {
async_get_all_undeleted_posts,
async_get_post,
async_delete_post,
async_create_post
} = require('./handlers/posts');
exports.create_post = functions.https.onCall(async_create_post);
The error message means that a method of the path module (like path.join) expects one of its arguments to be a string but got something else.
I found the offending line by binary search commenting the program until the error was gone.
Maybe one of your modules uses path and you supply the wrong arguments.

How to fix firebase database initialised multiple times due to React SSR initialised database and cloud function firebase initialised database?

I have updated the question as found the root cause of the issue.
As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?
ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'?
So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why?
If I look at the console I get results till console.log('deviceToken', deviceToken);
so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
.onCreate(async(snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const adminApp = admin.initializeApp(appOptions, 'app');
const { message, senderId, receiverUid } = snap.val();
console.log(message, senderId, receiverUid);
console.log('------------------------');
const deleteApp = () => adminApp.delete().catch(() => null);
try {
const db = adminApp.database();
const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
console.log('reciverUserRef', reciverUserRef);
const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
const deviceToken = await deviceTokenSnapshot.val();
console.log('deviceToken', deviceToken);
const payload = {
notification: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
}
};
const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
console.log('Successfully sent message: ', JSON.stringify(messageDone));
return deleteApp().then(() => res);
} catch (err) {
console.log('error', err);
return deleteApp().then(() => Promise.reject(err));
}
});
Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK?
So switched to
const payload = {
data: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
},
token: deviceToken
};
const messageDone = await admin.messaging().send(payload);
Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.
EDIT: Finally got the function working.
My index.js is exporting to functions, follwoing
exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);
exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance.
When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?
You can initialise db outside export function.
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code
Update:
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
try {
const defaultApp = adminApp.name
if(defaultApp) {
const adminApp1 = admin.initializeApp(apps);
}else {
const adminApp1 = admin.initializeApp(options, apps);
}
}catch(err) {
console.error(err);
}
}
Modify this snippet as per your need and try it out
It abstracts initialize of app in another function. Just call this function at appropriate place in your code.

How to fix Cloud Function error admin.database.ref is not a function at exports

I'm currently trying to modify my Cloud Functions and move in under https.onRequest so that i can call use it to schedule a cron job. How it i'm getting the following error in the logs.
TypeError: admin.database.ref is not a function
at exports.scheduleSendNotificationMessageJob.functions.https.onRequest (/user_code/index.js:30:20)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9)
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
admin.database.ref('/notifications/{studentId}/notifications/{notificationCode}')
.onCreate((dataSnapshot, context) => {
const dbPath = '/notifications/' + context.params.pHumanId + '/fcmCode';
const promise = admin.database().ref(dbPath).once('value').then(function(tokenSnapshot) {
const theToken = tokenSnapshot.val();
res.status(200).send(theToken);
const notificationCode = context.params.pNotificationCode;
const messageData = {notificationCode: notificationCode};
const theMessage = { data: messageData,
notification: { title: 'You have a new job reminder' }
};
const options = { contentAvailable: true,
collapseKey: notificationCode };
const notificationPath = '/notifications/' + context.params.pHumanId + '/notifications/' + notificationCode;
admin.database().ref(notificationPath).remove();
return admin.messaging().sendToDevice(theToken, theMessage, options);
});
return null;
});
});
You cannot use the definition of an onCreate() Realtime Database trigger within the definition of an HTTP Cloud Function.
If you switch to an HTTP Cloud Function "so that (you) can call use it to schedule a cron job" it means the trigger will be the call to the HTTP Cloud Function. In other words you will not be anymore able to trigger an action (or the Cloud Function) when new data is created in the Realtime Database.
What you can very well do is to read the data of the Realtime Database, as follows, for example (simplified scenario of sending a notification):
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
//get the desired values from the request
const studentId = req.body.studentId;
const notificationCode = req.body.notificationCode;
//Read data with the once() method
admin.database.ref('/notifications/' + studentId + '/notifications/' + notificationCode)
.once('value')
.then(snapshot => {
//Here just an example on how you would get the desired values
//for your notification
const theToken = snapshot.val();
const theMessage = ....
//......
// return the promise returned by the sendToDevice() asynchronous task
return admin.messaging().sendToDevice(theToken, theMessage, options)
})
.then(() => {
//And then send back the result (see video referred to below)
res.send("{ result : 'message sent'}") ;
})
.catch(err => {
//........
});
});
You may watch the following official Firebase video about HTTP Cloud Functions: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3. It shows how to read data from Firestore but the concept of reading and sending back the response (or an error) is the same for the Realtime Database. Together with the 2 other videos of the series (https://firebase.google.com/docs/functions/video-series/?authuser=0), it also explains how it is important to correctly chain promises and to indicate to the platform that the work of the Cloud Function is finished.
For me, this error happened when writing admin.database instead of admin.database().

Categories

Resources