Keeps calling itself when using the onClick event handler - javascript

what I expect to happen:
when the user clicks addProject button the event listener will run calling
formSubmit
and we will check for the date if it's valid or not then if it's valid it will call fetchingCompanyNameAndUserData
it will fetch the required data update the state and it will call checkUniqueName which again will fetch some data making sure there is no duplication and then it's supposed to call this.insert()
which will finally insert the data into our firestore-NoSQL- DB.
The issue:
these function specially the checkUniqueName keeps calling it self over and over and I don't know what is wrong there.
the code:
formSubmit = event => {
event.preventDefault();
const isLoading = this.state;
var sdate = this.state.projectData.sdate;
var edate = this.state.projectData.edate;
if (sdate > edate) {
NotificationManager.error(`Please entre a valid dates`);
return;
} else {
// isLoading = true;
this.fetchingCompanyNameAndUserData();
}
};
fetchingCompanyNameAndUserData = async () => {
const userRef = fireStore.collection('users');
const userData = await userRef.where("Email", "==", auth.currentUser.email).get();
userData.forEach(doc => {
console.log('this one must match', doc.data().CompanyName)
const cashedFirstName = doc.data().FirstName;
const cashedLastName = doc.data().LastName;
const fullName = cashedFirstName + ' ' + cashedLastName;
return this.setState({
companyName: doc.data().CompanyName,
userName: fullName,
}, () => {
console.log('done fetching');
this.checkUniqueName();
});
})
};
checkUniqueName = async () => {
const projectName = this.state.projectData.title;
const companyName = this.state.companyName;
const projectRef = fireStore.collection('PROJECT')
const projectData = await projectRef.where("ProjectName", "==", projectName).get();
projectData.forEach(doc => {
if (doc.data().CompanyName !== companyName) {
console.log('checking unique nameing');
this.insert();
} else {
NotificationManager.error('this project already exists');
}
})
}
async insert() {
//async function foo() {
console.log('insreting proooo');
var ptitle = this.state.projectData.title;
var pdesc = this.state.projectData.desc;
var sdate = this.state.projectData.sdate;
var edate = this.state.projectData.edate;
var status = this.state.projectData.status;
var companyName = this.state.companyName;
try {
let response = await fireStore.collection("PROJECT").add(
{
ProjectName: ptitle,
CompanyName: companyName,
ProjectDescription: pdesc,
startDate: sdate,
EndDate: edate,
Status: status,
CreatedBy: auth.currentUser.email,
CreatedDate: toUTCDateString(new Date()),
LastModifiedBy: auth.currentUser.email,
LastModifiedDate: toUTCDateString(new Date()),
UserDocId: auth.currentUser.uid
});
let doc = await fireStore.collection("PROJECT").doc(response.id).get()
this.handleClose();
//alert(doc.id)
var d1 = doc.id;
this.props.history.push('/app/dashboard/addproject/' + d1);
//this.handleClose;
NotificationManager.success('Project Created Successfully!');
}
catch (error) {
//console.log('error: ', error);
console.log(error)
}
}
Hope I made it as clear as possible here

Your checkUniqueName() function can be rewritten as:
checkUniqueName = async () => {
const projectName = this.state.projectData.title;
const companyName = this.state.companyName;
const projectRef = fireStore.collection('PROJECT')
const qsMatchingProjects = await projectRef.where("ProjectName", "==", projectName).where("CompanyName", "==", companyName).get();
if (qsMatchingProjects.empty) {
this.insert();
} else {
NotificationManager.error('this project already exists');
}
}

Related

Code works fine when value is hardcoded, but fails when value is dynamic

I'm working on a chrome extension that grabs data from Rate My Professor's GraphQL API and then puts that rating on my universities courses portal. Here's my code:
background.js
const {GraphQLClient, gql} = require('graphql-request');
console.log("background.js loaded");
const searchTeacherQuery = gql`
query NewSearchTeachersQuery($text: String!, $schoolID: ID!)
{
newSearch {
teachers(query: {text: $text, schoolID: $schoolID}) {
edges {
cursor
node {
id
firstName
lastName
school {
name
id
}
}
}
}
}
}
`;
const getTeacherQuery = gql`
query TeacherRatingsPageQuery(
$id: ID!
) {
node(id: $id) {
... on Teacher {
id
firstName
lastName
school {
name
id
city
state
}
avgDifficulty
avgRating
department
numRatings
legacyId
wouldTakeAgainPercent
}
id
}
}
`;
const AUTH_TOKEN = 'dGVzdDp0ZXN0';
const client = new GraphQLClient('https://www.ratemyprofessors.com/graphql', {
headers: {
authorization: `Basic ${AUTH_TOKEN}`
}
});
const searchTeacher = async (professorName, schoolID) => {
console.log("searchTeacher called");
console.log(professorName);
console.log(typeof professorName);
console.log(schoolID);
const response = await client.request(searchTeacherQuery, {
text: professorName,
schoolID
});
if (response.newSearch.teachers === null) {
return [];
}
return response.newSearch.teachers.edges.map((edge) => edge.node);
};
const getTeacher = async (id) => {
const response = await client.request(getTeacherQuery, {id});
return response.node;
};
async function getAvgRating(professorName) {
console.log('1: ', professorName);
const teachers = await searchTeacher(professorName, 'U2Nob29sLTE0OTU=');
console.log(teachers);
const teacherID = teachers[0].id;
const teacher = await getTeacher(teacherID);
const avgRating = teacher.avgRating;
console.log(teacher);
console.log(avgRating);
return avgRating;
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('received message from content script:', request);
console.log('test:', request.professorName);
getAvgRating(request.professorName).then(response => {
sendResponse(response);
});
return true;
});
and here's content.js:
const professorLinks = document.querySelectorAll('td[width="15%"] a');
professorLinks.forEach(link => {
const professorName = link.textContent;
console.log(professorName);
chrome.runtime.sendMessage({ professorName }, (response) => {
console.log(response);
if (response.error) {
link.insertAdjacentHTML('afterend', `<div>Error: ${response.error}</div>`);
} else {
link.insertAdjacentHTML('afterend', `<div>${response}/5</div>`);
}
});
});
Now, if I set professorName to a fixed value, like this:
async function getAvgRating(professorName) {
const teachers = await searchTeacher('Hossein Kassiri', 'U2Nob29sLTE0OTU=');
console.log(teachers);
const teacherID = teachers[0].id;
const teacher = await getTeacher(teacherID);
const avgRating = teacher.avgRating;
console.log(teacher);
console.log(avgRating);
return avgRating;
}
the code works as intended, with the expected output:
but if searchTeacher is called with professorName instead of a fixed value like this:
async function getAvgRating(professorName) {
const teachers = await searchTeacher(professorName, 'U2Nob29sLTE0OTU=');
console.log(teachers);
const teacherID = teachers[0].id;
const teacher = await getTeacher(teacherID);
const avgRating = teacher.avgRating;
console.log(teacher);
console.log(avgRating);
return avgRating;
}
it returns an empty object:
dynamic graphql request vs hardcoded graphql request
i'm not sure if i'm missing something trivial, as the values being passed to searchTeacher appear to be exactly the same, but it only works when hardcoded. please let me know if i'm missing something, thank you.
After much troubleshooting, I found the issue. #Damzaky was correct, using the localeCompare method I was able to determine that the string from the HTML and my hardcoded string were not the same, was localeCompare would return -1. I cleansed the string from the HTML using this line of code:
const normalizedName = name.normalize('NFKD');
and passed that to searchTeacher, and now the behaviour is as expected. Thanks to everyone that helped.

Firebase Cloud Functions Async

I am making a function for firebase cloud functions, I want a function to be called every time a new document is created in "posts". I want this function to perform the tasks that I put inside the "onCeatePost" function.
The problem I have is that I'm not sure if this is the correct way to structure such a function.
In several firebase examples I have seen that it is always called return _; or return null; at the end of a task, but I don't know how to structure the function so that all the tasks are carried out, could someone help me to restructure my function or tell me what is wrong please.
There are several if statements in the function, if the created publication does not comply with them, I would like it to skip them but continue with the other tasks that I put inside the function.
I don't know if it's too much to ask, but I'm new to this language and I haven't been able to find the answer I'm looking for. Thank you!
exports.onPostCreate = functions.firestore.document("/posts/{postId}").onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db.collection("users").doc(uid).update({"stats.posts": admin.firestore.FieldValue.increment(1)});
if (topic) {
await db.collection("topics").doc(topic.id).collection("user-authors").doc(uid).set({"date": snap.createTime});
}
if (contentForFeed == true) {
const userPath = db.collection("users").doc(uid);
await userPath.update({"stats.lastUpdate": snap.createTime});
}
if (previous) {
const previousId = previous.id;
const previousUid = previous.uid;
const refPrev = db.collection("posts").doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(priority.seconds + 120, priority.nanoseconds);
await db.collection("posts").doc(previousId).update({"newDate": newDate});
});
if (previousUid != uid) {
const path = db.collection("users").doc(uid).collection("user-posts");
const dataToSet = {"timestamp": snap.createTime, "uid": uid, "postId": onReplyToPostId};
await path(dataToSet);
}
}
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});
You'll find below the adapted code (untested) with 4 corrections.
Here are explanations for the two most important ones:
(Correction 2) In a transaction you need to use the transaction's update() method and not the "standard one"
(Correction 4) When all the asynchronous work is complete you need to return a value or a Promise. See this documntation page for more details.
exports.onPostCreate = functions.firestore
.document('/posts/{postId}')
.onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db
.collection('users')
.doc(uid)
.update({
'stats.posts': admin.firestore.FieldValue.increment(1),
});
if (topic) {
await db
.collection('topics')
.doc(topic.id)
.collection('user-authors')
.doc(uid)
.set({ date: snap.createTime });
}
if (contentForFeed == true) {
const userPath = db.collection('users').doc(uid);
await userPath.update({ 'stats.lastUpdate': snap.createTime });
}
let previousUid; // <= Correction 1
if (previous) {
const previousId = previous.id;
previousUid = previous.uid; // <= Correction 1
const refPrev = db.collection('posts').doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(
priority.seconds + 120,
priority.nanoseconds
);
t.update(refPrev, { newDate: newDate }); // <= Correction 2
});
if (previousUid != uid) {
const path = db
.collection('users')
.doc(uid)
.collection('user-posts');
const dataToSet = {
timestamp: snap.createTime,
uid: uid,
postId: onReplyToPostId,
};
await path.add(dataToSet); // <= Correction 3
}
}
return null; // <= Correction 4
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});

Firestore : why using serverTimestamp gives different results?

I am having a hard time understanding serverTimestamp in firestore.
When I save a document in database in a firebase function using Fieldvalue.serverTimestamp() or in a javascript client code using serverTimestamp() it sometimes doesn't save the same thing in the database.
See screenshots below :
Sometime I get an object with {nanoseconds: xxx, seconds: xxx} and sometimes I get a timestamp formatted date...
The problem is when I try to query my orders using query(collectionRefOrders, orderBy('createdAt', 'desc'), limit(10)).
The orders with the object appears before the others ones even if they are created after...
Any clue why this happens ? What am I doing wrong ?
Thanks a lot.
EDIT :
Here is the code I use to add documents in the my firebase function (it is a request function I call in a website) :
const { getFirestore, FieldValue } = require('firebase-admin/firestore');
const firebaseDB = getFirestore();
exports.createOrderFromTunnel = functions.region('europe-west3')
.runWith({
timeoutSeconds: 10,
memory: "4GB",
})
.https
.onRequest(async (req, res) => {
cors(req, res, async () => {
try {
const { apiKey } = req.body;
const project = await getProjectFromApiKey(apiKey);
if (!project) {
return res.json({
success: false,
error: 'Unauthorized: invalid or missing api key'
});
}
const contactData = {
address: {},
createdAt: FieldValue.serverTimestamp()
};
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: FieldValue.serverTimestamp(),
status: 'NEW',
};
const refProject = firebaseDB
.collection('projects')
.doc(project.id);
const colOrder = firebaseDB.collection(`projects/${project.id}/orders`)
const refOrder = colOrder.doc();
const colContact = firebaseDB.collection(`projects/${project.id}/contacts`)
const refContact = colContact.doc();
await firebaseDB.runTransaction(async transaction => {
const snapProject = await transaction.get(refProject);
const dataProject = snapProject.data();
const sequenceContact = dataProject.sequenceContact;
const sequenceOrder = dataProject.sequenceOrder;
contactData.sequence = sequenceContact;
orderData.sequenceNumber = sequenceOrder;
await transaction.set(refContact, contactData);
orderData.customer.id = refContact.id;
orderData.customer.sequence = sequenceContact;
await transaction.set(refOrder, orderData);
await transaction.update(refProject, {
sequenceContact: sequenceContact + 1,
sequenceOrder: sequenceOrder + 1,
totalContacts: dataProject.totalContacts + 1,
totalOrders: dataProject.totalOrders + 1,
});
return refOrder.id;
});
return res.json({
success: true
});
} catch (err) {
functions.logger.error(err);
return res.json({
success: false,
err
});
}
});
});
Here is the code I use to add documents in my client code (it is a web app in javascript) :
const createOrder = async (projectId) => {
try {
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: serverTimestamp(),
status: 'NEW',
surface: 0,
};
const refProject = doc(firebaseDB, 'projects', projectId);
const colOrder = collection(firebaseDB, `projects/${projectId}/orders`)
const refOrder = doc(colOrder);
return await runTransaction(firebaseDB, async (transaction) => {
const snapProject = await transaction.get(refProject);
if (!snapProject.exists()) {
throw "Document does not exist!";
}
const dataProject = snapProject.data();
const sequence = dataProject.sequenceOrder;
orderData.sequenceNumber = sequence;
transaction.set(refOrder, orderData);
transaction.update(refProject, { sequenceOrder: sequence + 1, totalOrders: dataProject.totalOrders + 1 });
return refOrder.id;
});
} catch (e) {
console.error(e);
return null;
}
};

How to create user specific data when user logs in for the first time in realtime firebase database?

I want the code to behave such that it creates specific data when user is signed in but doesn't create it if already present in the firebase real-time database.
I have used the following code through which i check if the child is already present or not and if not then creates the child in firebase database, but somehow the code isn't behaving as it should.
Whenev the user logins again the complete data part is rewritten.
Snippet I need help in
if (!(checkdata(user.uid))) {
writeUserData(user.uid,user.displayName,user.email,user.photoURL)
}
var database = firebase.database();
function checkdata(userid){
var ref = firebase.database().ref("users");
ref.once("value")
.then(function(snapshot) {
var datapresent = snapshot.hasChild(userid); // true
return datapresent
});
}
function writeUserData(userId, name, email, imageUrl) {
firebase.database().ref('users/' + userId).set({
username: name,
email: email,
profile_picture : imageUrl,
cropdata : []
});
}
Complete JS file
const signInBtn = document.getElementById('signinbtn');
const signOutBtn = document.getElementById('signoutbtn');
const userDetails = document.getElementById('username');
const auth = firebase.auth();
const provider = new firebase.auth.GoogleAuthProvider();
signInBtn.onclick = () => auth.signInWithPopup(provider);
signOutBtn.onclick = () => auth.signOut();
function toggle(className, displayState){
var elements = document.getElementsByClassName(className)
for (var i = 0; i < elements.length; i++){
elements[i].style.display = displayState;
}
}
auth.onAuthStateChanged(function(user) {
if (user) {
// signed in
toggle('userishere', 'block');
toggle('usernothere', 'none');
//userDetails.innerHTML = `<h3>Hello ${user.displayName}!</h3> <p>User ID: ${user.uid}</p>`;
userDetails.innerHTML = `Hello ${user.displayName}!`
console.log(user)
if (!(checkdata(user.uid))) {
writeUserData(user.uid,user.displayName,user.email,user.photoURL)
}
} else {
// not signed in
toggle('userishere', 'none');
toggle('usernothere', 'block');
userDetails.innerHTML = '';
}
});
var database = firebase.database();
function checkdata(userid){
var ref = firebase.database().ref("users");
ref.once("value")
.then(function(snapshot) {
var datapresent = snapshot.hasChild(userid); // true
return datapresent
});
}
function writeUserData(userId, name, email, imageUrl) {
firebase.database().ref('users/' + userId).set({
username: name,
email: email,
profile_picture : imageUrl,
cropdata : []
});
}
I just found the solution, the asynchronous code wasn't waiting for my firebase response and just checeked if datapresent was true or not, so with a async definition before function and await before ref.once(value) does the trick and my problem is solve. Working code below :
const signInBtn = document.getElementById('signinbtn');
const signOutBtn = document.getElementById('signoutbtn');
const userDetails = document.getElementById('username');
var database = firebase.database();
const auth = firebase.auth();
const provider = new firebase.auth.GoogleAuthProvider();
signInBtn.onclick = () => auth.signInWithPopup(provider);
signOutBtn.onclick = () => auth.signOut();
async function checkdata(user){
let ref = firebase.database().ref("users");
let snapshot = await ref.once('value');
if (!snapshot.hasChild(user.uid)){
console.log(user)
writeUserData(user.uid,user.displayName,user.email,user.photoURL)
console.log("write done")
}
else{
console.log("did not write")
}
}
function writeUserData(userId, name, email, imageUrl) {
firebase.database().ref('users/' + userId).set({
username: name,
email: email,
profile_picture: imageUrl,
cropdata: []
});
}
function toggle(className, displayState) {
var elements = document.getElementsByClassName(className)
for (var i = 0; i < elements.length; i++) {
elements[i].style.display = displayState;
}
}
auth.onAuthStateChanged(function (user) {
if (user) {
// signed in
toggle('userishere', 'block');
toggle('usernothere', 'none');
//userDetails.innerHTML = `<h3>Hello ${user.displayName}!</h3> <p>User ID: ${user.uid}</p>`;
userDetails.innerHTML = `Hello ${user.displayName}!`
console.log(user)
checkdata(user)
}
else {
toggle('userishere', 'none');
toggle('usernothere', 'block');
userDetails.innerHTML = '';
}
})

How do i get this firebase data to use it outside the loop?

I have this script that runs through all the child items in my realtime database from firebase:
methods: {
submit: function() {
const gebruikersref = firebase.database().ref('Gebruikers/')
var self = this
gebruikersref.once('value', function(snapshot) {
const lid = self.lidnummer;
const voornaam = self.voornaam;
const achternaam = self.achternaam;
const email = self.email;
snapshot.forEach(function(childSnapshot) {
const data = childSnapshot.val()
});
if(lid == data.Lidnummer) {
console.log('err')
} else {
gebruikersref.push({
Voornaam: voornaam,
Achternaam: achternaam,
Email: email,
Lidnummer: lid
});
}
});
}
}
but how do i get const data = childSnapshot.val() outside the foreach loop so i can use it here:
if(lid == data.Lidnummer) {
console.log('err')
} else {
gebruikersref.push({
Voornaam: voornaam,
Achternaam: achternaam,
Email: email,
Lidnummer: lid
});
}
Otherwise the else method runs x times the number of children and will push my data (that only may be pushed once) x times the children
If I correctly understand your question, since the asynchronous push() method returns a ThenableReference, you can use Promise.all() as follows:
submit: function() {
const gebruikersref = firebase.database().ref('Gebruikers/')
var self = this
gebruikersref.once('value', function(snapshot) {
const lid = self.lidnummer;
const voornaam = self.voornaam;
const achternaam = self.achternaam;
const email = self.email;
const promises = [];
snapshot.forEach(function(childSnapshot) {
const data = childSnapshot.val()
if (lid == data.Lidnummer) {
console.log('err')
} else {
promises.push(gebruikersref.push({
Voornaam: voornaam,
Achternaam: achternaam,
Email: email,
Lidnummer: lid
})
);
}
});
Promise.all(promises);
});
}

Categories

Resources