Object defined outside fetch but not inside - javascript

I'm using tmi.js to get the data of a chat message on www.twitch.tv.
This is my code:
const tmi = require('tmi.js');
require('dotenv').config();
const options = {
options: {
debug: true
},
identity: {
username: process.env.OAUTH_USERNAME,
password: process.env.OAUTH_PASSWORD
},
connection: {
reconnect: true
},
channels: [`instak`]
};
const client = new tmi.client(options);
client.on(`chat`, (channel, userstate, message/*, self */) => {
console.log(userstate.username); //This logs my userstate, I can see the username which is passed on correctly.
switch (message) {
case `!kluiten`:
fetch(`http://localhost:8000/api/users/${userstate.username}`) // fetch from Express.js server
.then(response => response.json())
.then(result => {
console.log(`USERSTATE IS`, userstate);
client.action(channel, `${userstate[`display-name`]}, you've got ${result.instakluiten} instakluiten.`);
});
break;
default:
break;
}
});
// Connect the client to the server..
client.connect();
As I said in the commented part after the console.log(userstate), I get all the correct information I was expecting, so outside of the switch case and the fetch. However, when I log my userstate.username inside the fetch, userstate is undefined... I don't see why that happens, because when I did it in the front-end I had no problems... Now I'm doing it in node and it's undefined...
This looks like a common "you need to wait for your response"-problem, but I don't see a logical explanation for that being the problem because in the client.on(chat) userstate is defined... I'm troubled.

Related

how to catch error in redux action and display the error message in UI

I have a redux action called academyRedirect.js which is invoked whenever an user is redirected to the path /user/academy when he pushes the 'academy' button in my main page.
export const getAcademyAutoLogin = () => {
axios.get(`/user/academy`)
.then((response) => {
window.location.replace(response.data); // this is replaced by an URL coming from backend
})
.catch((error) => {
reject(error.response);
});
return null;
};
Whenever the user is not allowed to access the academy (for not having credentials) or whenever i get an error 500 or 404, i need to display a modal or something to inform the user that an error occurred while trying to log into the academy. Right now im not being able to do it, the page just stays blank and the console output is the error.response.
Any help is appreciated
Redux Store
export const messageSlice = createSlice({
name: 'message',
initialState: { isDisplayed: false, errorMessage: ''},
reducers: {
displayError(state, action) {
state.isDisplayed = true
state.errorMessage = action.message
},
resetErrorState() {
state.isDisplayed = false
state.errorMessage = ''
},
}
})
export const messageActions = messageSlice.actions;
Inside the component:-
const Login = () => {
const errorState = useSelector(globalState => globalState.message)
const onClickHandler = async => {
axios.get(`/user/academy`)
.then((response) => { window.location.replace(response.data) })
.catch((error) => {
dispatch(messageActions.displayError(error))
});
}
return (
{errorState.isDisplayed && <div>{errorState.errorMessage}</div>}
{!errorState.isDisplayed &&<button onClick={onClickHandler}>Fetch Data </button>}
)
}
Maybe this is of help to you
You can try to add interceptor to your axios.
Find a place where you create your axios instance and apply an interceptor to it like this
const instance = axios.create({
baseURL: *YOUR API URL*,
headers: { "Content-Type": "application/json" },
});
instance.interceptors.request.use(requestResolveInterceptor, requestRejectInterceptor);
And then, in your requestRejectInterceptor you can configure default behavior for the case when you get an error from your request. You can show user an error using toast for example, or call an action to add your error to redux.
For second case, when you want to put your error to redux, its better to use some tools that created to make async calls to api and work with redux, for example it can be redux-saga, redux-thunk, redux-axios-middleware etc.
With their docs you would be able to configure your app and handle all cases easily.

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 do I call the function with a parameter I set up in firebase functions

I've deployed this code to my firebase functions project:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()
export const getEmail = functions.https.onRequest((request, response) => {
var from = request.body.sender;
admin.auth().getUserByEmail(from)
.then(snapshot => {
const data = snapshot.toJSON()
response.send(data)
})
.catch(error => {
//Handle error
console.log(error)
response.status(500).send(error)
})
})
Which takes in a email parameter that it gets from the user's input on my app. My app's code looks like this:
Functions.functions().httpsCallable("https://us-central1-projectname.cloudfunctions.net/getEmail").call(email) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
//email isnt taken
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(code, message, details)
}
// ...
}
if let text = (result?.data as? [String: Any])?["text"] as? String {
// email taken
}
}
When I run the app and when that function is called, it seems to do nothing, no error message is shown and no data has been sent back. What am I missing?
Update: I went to the logs and nothing has happened in there as if the function was never called.
You are actually mixing up HTTP Cloud Functions and Callable Cloud Functions:
You Cloud Function code corresponds to an HTTP one but the code in your front-end seems to call a Callable one.
You should adapt one or the other, most probably adapt your Cloud Function to a Callable one, along the following lines:
exports.getEmail = functions.https.onCall((data, context) => {
const from = data.sender;
return admin.auth().getUserByEmail(from)
.then(userRecord => {
const userData = userRecord.toJSON();
return { userData: userData };
})
});
Have a look at the doc for more details, in particular how to handle errors. The doc is quite detailed and very clear.

Firebase Cloud Function Query Not working

I deployed a function with the following query:
admin.firestore().collection("fcm").where("devices",'array-contains', mobile).get().then((snapshots)=> {...});
This returns the following error from the Cloud Function Log:
msgTrigger: Function execution started
msgTrigger: Function returned undefined, expected Promise or value
msgTrigger: Function execution took 8429 ms, finished with status: 'ok'
msgTrigger: Unhandled rejection
msgTrigger: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined at admin.firestore.collection.where.get.then (/user_code/index.js:23:65) at process._tickDomainCallback (internal/process/next_tick.js:135:7)
Anyone please?
Fighting for days with the editor here. decided to post my function code in chunks:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var msgData;
var mobile;
Second part:
exports.msgTrigger = functions.firestore
.document('Messages/{MessageID}')
.onCreate((snapshot, context) => {
msgData = snapshot.data();
mobile = msgData.mobile;
admin.firestore().collection("fcm").where("devices", 'array-contains', mobile).get().then((snapshots) => {
Third part:
var tokens = [];
if (snapshots.empty) {
console.log('No devices');
} else {
for (var token of snapshot.docs) {
tokens.push(token.data().token);
}
var payLoad = {
"notification": {
"title": "de " + msgData.name,
"body": "Alerta de Emergência!",
"sound": "default",
"icon": msgData.icon
},
"data": {
"remetente": +msgData.name,
"mensagem": "Alerta de Emergência!"
}
}
Fourth part:
return admin.messaging().sendToDevice(tokens, payLoad).then((response) => {
console.log("mensagens enviadas");
}).catch((err) => {
console.log("erro: " + err);
});
}
});
});
Firestore 0.8 is quite an old version, see https://cloud.google.com/nodejs/docs/reference/firestore/0.8.x/. It is only from version 0.16 that you can use the array-contains query operator , see https://github.com/googleapis/nodejs-firestore/releases/tag/v0.16.0. So you should update to the latest version.
I've also adapted your function code by first (and very important, see below) returning the promise returned by the first asynchronous task, then re-organising your promises chaining in the if/then/else.
Does it execute correctly now??
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(); // Here changed, see https://firebase.google.com/docs/functions/beta-v1-diff#new_initialization_syntax_for_firebase-admin
exports.msgTrigger = functions.firestore
.document('Messages/{MessageID}')
.onCreate((snapshot, context) => {
const msgData = snapshot.data(); //No need to declare this outside of the Cloud Function, see https://www.youtube.com/watch?v=2mjfI0FYP7Y
const mobile = msgData.mobile;
return admin. // <- HERE return
.firestore()
.collection('fcm')
.where('devices', 'array-contains', mobile)
.get()
.then(snapshots => {
let tokens = [];
if (snapshots.empty) {
console.log('No devices');
return null;
} else {
for (var token of snapshot.docs) {
tokens.push(token.data().token);
}
var payLoad = {
notification: {
title: 'de ' + msgData.name,
body: 'Alerta de Emergência!',
sound: 'default',
icon: msgData.icon
},
data: {
remetente: +msgData.name,
mensagem: 'Alerta de Emergência!'
}
};
return admin.messaging().sendToDevice(tokens, payLoad);
}
})
.catch(err => {
console.log('erro: ' + err);
return null;
});
});
Why is it important to return the promises returned by asynchronous tasks? Watch the 3 videos about "JavaScript Promises" from the official Firebase video series (https://firebase.google.com/docs/functions/video-series/) for the answer!!
Removing ".where('devices', 'array-contains', mobile)" gives the same error. I added msgData.name and msgData.mobile to console.log and they get printed, so the first part is fine:
1: 31: 05.140 AM
msgTrigger
Function execution started
1: 31: 11.289 AM
msgTrigger
Nome: Alan
1: 31: 11.291 AM
msgTrigger
mobile: 91983372845
1: 31: 11.291 AM
msgTrigger
erro: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
1: 31: 11.297 AM
msgTrigger
Function execution took 6158 ms, finished with status: 'ok'
Ok, for what it's worth I finally found the culprit:
for (var token of snapshot.docs) {
snapshot should be snapshots. Yes it's emabarrasing and it took nothing less than the excellent Firebase Support Team to point it out to me. Wished Android Studio could pick up this kind of silly typos in js code.
Will still mark Renaud's answer, since he helped optimize my code and gave me some usefull tips along the way.

Categories

Resources