how to update info in firebase version 9 - javascript

I would like besides add new record , also change 'bill'.
But I don't know what input in this string
const newPostKey = (child(ref(db), )).key;
This action in vuex, that I use for create request to firebase:
async updateInfo({ dispatch, commit, getters }, toUpdate) {
try {
const uid = await dispatch('getUid')
const db = getDatabase();
const updateData = { ...getters.info, ...toUpdate }
const postListRef = ref(db, `/users/${uid}/info`);
const newPostKey = (child(ref(db), ????)).key;
const updates = {};
updates[newPostKey] = updateData;
update(postListRef, updates)
commit('setInfo', updateData)
} catch (e) {
commit('setError', e)
throw e
}
},
Code work up to step const newPostKey..
If I input in this string 'info':
const newPostKey = (child(ref(db), 'info')).key;
Record will be added, but 'info' will be update incorrect:

If you just want to update the value of bill then you can try the following:
const userRef = ref(db, `/users/${uid}/info`);
update(userRef, { bill: 100 }).then(() => { // <-- replace 100 with your bill amount
console.log("Bill Amount Updated")
}).catch(e => console.log(e))
If you want to increment the bill by a certain amount, you can do so using increment() function.
update(userRef, { bill: increment(100) }) // increment by 100

Related

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;
}
});

Getting total Number of User Items in Firebase Realtime database in javascript

I have been trying to get amount of users that have same "referralId" in my firebase Realtime database,
users {
AY35bkc5cbkufik8gfe3vjbmgr {
email: james #gmail.com
verify: none
referralid: AY35ja35
}
B16fty9jDchfNgdx7Fxnjc5nxf5v {
email: mobilewisdom #gmail.com
verify: none
referralid: AY35ja35
}
}
How can I use JavaScript to count the amount of account with referralid:AY35ja35
I tried:
const query = ref.orderByChild('referralid').equalTo('AY35ja35');
query.get().then((results) => {
console.log(Object.keys(results.val()).length);
results.forEach((snapshot) => {
console.log(snapshot.key, snapshot.val());
});
});
But am getting this error in my console:
Uncaught TypeError: query.get is not a function
In v8/namespaced syntax that'd be:
const ref = firebase.database().ref('users');
const query = ref.orderByChild('referralid').equalTo('AY35ja35');
const results = await query.get();
console.log(Object.keys(results.val()).length);
results.forEach((snapshot) => {
console.log(snapshot.key, snapshot.val());
});
Or if you're in an environment where await doesn't work:
const ref = firebase.database().ref('users');
const query = ref.orderByChild('referralid').equalTo('AY35ja35');
query.get().then((results) => {
console.log(Object.keys(results.val()).length);
results.forEach((snapshot) => {
console.log(snapshot.key, snapshot.val());
});
});
In v9/modular syntax the equivalent would be:
const ref = ref(getDatabase(), 'users');
const query = query(ref, orderByChild('referralid'), equalTo('AY35ja35'));
const results = await get(query);
console.log(Object.keys(results.val()).length);
results.forEach((snapshot) => {
console.log(snapshot.key, snapshot.val());
});
Or if you're in an environment where await doesn't work:
const ref = ref(getDatabase(), 'users');
const query = query(ref, orderByChild('referralid'), equalTo('AY35ja35'));
get(query).then((results) => {
console.log(Object.keys(results.val()).length);
results.forEach((snapshot) => {
console.log(snapshot.key, snapshot.val());
});
});
Firstly you will have to run a query to get the data from firebase using either firebase package, axios or fetch
Your data is fetched in form of Object containing different objects. You can either user for .. in .. loop to iterate through the object.
var count = 0
for (const user in users) {
if (user.referralid==="AY35ja35") {
count++
}
}
Or use Object.values(object) function to get array of users. Now you can use a JavaScript array function such as map or filter.
by using map. you can do it like this:
var count=0
user.map(user=>{if(user.referralid==="AY35ja35"){count++})
or by using filter
const matchedUsers = user.filter(user=>(user.referralid==="AY35ja35"))
const count = matchedUsers.length
Hope this helps :)

"too much recursion" while updating params(setData) into Firebase RTDB through setInterval and getData

I'm new to react native. I have a problem while updating data(latitude, longitude, timestamp) for each user(member) to Firebase database realtime, I get endless loops
The error I have "too much recursion"
firebase.config.ts
const firebaseConfig = {
...
};
export const getCurrentLocation = (phoneNumber: number, setData: (locationParams: any) => void) => {
const db = getDatabase();
const reference = ref(db, 'members/' + phoneNumber);
onValue(reference, (snapshot) => {
const data = snapshot.val();
setData(data);
})
};
export const setCurrentLocation = (phoneNumber: number, latitude: number, longitude: number, timestamp: number) => {
const db = getDatabase();
const reference = ref(db, 'members/' + phoneNumber);
set(reference, {
latitude: latitude,
longitude: longitude,
timestamp: timestamp,
}).then(() => console.log('setCurrentLocation to mainUser/firebase.config'));
};
const app = initializeApp(firebaseConfig);
memberList.tsx
const [userPhoneNumber, setUserPhoneNumber] = useState('0');
const members = useSelector((state: any) => state.members);
//get user's phoneNumber
const auth = getAuth();
useEffect( ()=> {
onAuthStateChanged(auth, (user) => {
if (user) {
setUserPhoneNumber(user.email.replace(/\D/gi, '') || '');
} else {
console.log('logged out')
}
});
}, [])
useEffect(() => {
const timer = setInterval( () => {
members.map(function (el) {
getCurrentLocation(el.phoneNumber, (locationParams: any) => {
let timestamp = new Date().getTime()/1000;
setCurrentLocation(el.phoneNumber, locationParams.latitude, locationParams.longitude, timestamp)
})
})
}, 10000);
return () => clearInterval(timer);
}, []);
This looks wrong:
const timer = setInterval( () => {
members.map(function (el) {
getCurrentLocation(el.phoneNumber, (locationParams: any) => {
let timestamp = new Date().getTime()/1000;
setCurrentLocation(el.phoneNumber, locationParams.latitude, locationParams.longitude, timestamp)
})
})
}, 10000);
Since your getCurrentLocation attaches a listener with onValue, just doing that once for each user will execute your setData call right away for the current value in the database, and again whenever that user's data is updated. Combining onValue with an interval timer is an antipattern in Firebase, the database already calls back to your code when the data changes. Right now you attach a new listener for each user each second, so if there's an update to a user's data after more than a few seconds, your callback (and setData) will be called way more often than necessary.
In Firebase, attach your listeners once (typically when the page loads) and let them continue to work until the page is unloaded (or other changes are needed in the listeners).
So if you remove the setInterval from the code, you should be getting better results.

Trouble understanding Cloud Function Error

I am new to cloud funcations node.js and type script. I am running the below code and getting the error below and can't make sense of it after watch a ton of videos about promises and searching other questions.
any help would be appreciated.
Function returned undefined, expected Promise or value
exports.compReqUpdated = functions.firestore
.document('/compRequests/{id}')
.onUpdate((change, contex)=>{
const newData = change.after.data();
//const oldData = change.before.data();
const dbConst = admin.firestore();
const reqStatus:string = newData.requestStatus;
const compId:string = newData.compID;
const reqActive:boolean = newData.requestActive;
if (reqStatus == "CANCELED" && reqActive){
const query = dbConst.collection('compRequests').where('compID', '==', compId);
const batch = dbConst.batch();
query.get().then(querySnapshot => {
const docs = querySnapshot.docs;
for (const doc of docs) {
console.log(`Document found at path: ${doc.ref.path}`);
console.log(doc.id);
const docRef = dbConst.collection('compID').doc(doc.id);
batch.update(docRef, {requestStatus: 'CANCELED',requestActive: false});
};
return batch.commit()
})
.catch(result => {console.log(result)});
}else{
return
}
});
The firebase docs state that the callback passed to the onUpdate function should return PromiseLike or any value, but you aren't returning anything right now. If you change your code to something as follows I reckon it should work as expected:
exports.compReqUpdated = functions.firestore
.document('/compRequests/{id}')
.onUpdate((change, contex) => {
const newData = change.after.data();
//const oldData = change.before.data();
const dbConst = admin.firestore();
const reqStatus: string = newData.requestStatus;
const compId: string = newData.compID;
const reqActive: boolean = newData.requestActive;
if (reqStatus == "CANCELED" && reqActive) {
const query = dbConst.collection('compRequests').where('compID', '==', compId);
const batch = dbConst.batch();
return query.get().then(querySnapshot => {
const docs = querySnapshot.docs;
for (const doc of docs) {
console.log(`Document found at path: ${doc.ref.path}`);
console.log(doc.id);
const docRef = dbConst.collection('compID').doc(doc.id);
batch.update(docRef, { requestStatus: 'CANCELED', requestActive: false });
};
return batch.commit()
}).catch(result => { console.log(result) });
} else {
return false;
}
});

Cypress: Cypress custom command's return value actually returns null in the test file

I created a Cypress custom command which displays the value using console.log (so I know it works). However, when I call the custom command in my Cypress test file, it returns blank / null.
support/commands.js:
Cypress.Commands.add('scanAWSDb', (siteId) => {
let siteName = null //default it to null
... some AWS SDK function which scans the DB and check for corresponding Name of the ID: 12345 ...
siteName = <new value>
console.log("Value returned is: " + siteName //This displays the value in the web console for the corresponding ID: 12345, let's say name is Taylor
return siteName //Expected to return "Taylor" in the test file
})
integration/test1.spec.js:
describe('Display value from the Custom command that scanned the AWS DB', ()=> {
it('Display value from the Custom command that scanned the AWS DB', () => {
const siteId = "12345"
cy.scanAWSDb(siteId)
.then((returned_value) => {
cy.log(returned_value) //This displays a null value so it is not picking up the return value from the custom command which is supposedly Taylor
})
})
})
===
Update:
This worked but when trying to do an assertion, it does not work as I am unable to convert an Object Promise to a string.
export const scanTable = async (tableName, recordId) => {
const params = {
TableName: tableName,
FilterExpression: '#Id = :RecordId',
ExpressionAttributeNames: {
'#Id': 'Id',
},
ExpressionAttributeValues: {
':RecordId': recordId
}
};
let scanResults = [];
let items
let index = 0
do{
items = await docClient.scan(params).promise()
items.Items.forEach((item) => scanResults.push(item))
params.ExclusiveStartKey = items.LastEvaluatedKey
let scannedRecordId = JSON.stringify(items.Items[index].Id)
cy.log('Record successfully found in table: ' + scannedRecordId )
index += 1
}while(typeof items.LastEvaluatedKey != "undefined")
return scannedRecordId;
};
Cypress.Commands.add('scanDB', (tableName, recordId, cb) => {
const record = scanTable(tableName, recordId)
cb(record) // Callback function
})
Test file:
const tableName = 'table1';
let recordId = '';
cy.scanDB(tableName, recordId, $returnValue => {
cy.log($returnValue) //<-- THIS DISPLAYS THE OBJECT BUT I NEED TO CONVERT IT TO STRING SO I CAN DO ASSERTION like this:
//expect($returnValue).to.eq(recordId)
})
===
Update#2: This displays the returned value but not picked up for assertion
aws.js file:
const AWS = require('aws-sdk')
const region = Cypress.env('aws_region')
const accessKeyId = Cypress.env('aws_access_key_id')
const secretAccessKey = Cypress.env('aws_secret_access_key')
const sessionToken = Cypress.env('aws_session_token')
let scannedRecordId = ''
AWS.config.update({region: region})
AWS.config.credentials = new AWS.Credentials(accessKeyId, secretAccessKey, sessionToken)
const docClient = new AWS.DynamoDB.DocumentClient();
export const scanTable = async (tableName, recordId) => {
const params = {
TableName: tableName,
FilterExpression: '#Id = :RecordId',
ExpressionAttributeNames: {
'#Id': 'RecordId',
},
ExpressionAttributeValues: {
':RecordId': recordId // Check if Id is stored in DB
}
};
let scanResults = [];
let items
let index = 0
do{
items = await docClient.scan(params).promise()
items.Items.forEach((item) => scanResults.push(item))
params.ExclusiveStartKey = items.LastEvaluatedKey
scannedRecordId = JSON.stringify(items.Items[index].Id)
cy.log('Record successfully found in table: ' + scannedRecordId)
index += 1 // This may not be required as the assumption is that only a unique record is found
}while(typeof items.LastEvaluatedKey != "undefined")
return scannedRecordId;
};
Cypress.Commands.add('scanDB', (tableName, recordId, cb) => {
const record = scanTable(tableName, recordId)
cb(record) // Callback function
// const record = scanTable(tableName, recordId).then(record => { cb(record) }) //This does not work and returns a console error
})
awsTest.js file:
const tableName = 'random-dynamodb-table'
let myId = '12345'
it('Verify if ID is stored in AWS DynamoDB', () => {
cy.scanDB(tableName, myId, $returnValue => {
cy.log($returnValue)
cy.log(`Record ID: ${myId} is found in table: ` + $returnValue)
expect($returnValue).to.deep.eq(myId) //This asserts that the Id is found
})
})
If this AWS function is async you should handle it like promise so instead:
let siteName = null //default it to null
... some AWS SDK function which scans the DB and check for corresponding Name of the ID: 12345 ...
siteName = <new value>
AWSFunction().then((newValue) => {
siteName = newValue
console.log("Value returned is: " + siteName)
return siteName
});
Also if in test you need to only read this value you can use callback instead of promise: e.g.
Cypress.Commands.add('scanAWSDb', (siteId, cb) => {
AWSFunction().then((newValue) => {
cb(newValue);
})
})
// test
cy.scanAWSDb(siteId, (returned_value) => {
cy.log(returned_value)
});
Updated:
To assert string from object you can use wrap and invoke cypress methods: docs
scan function is async, so you have to call it like this:
Cypress.Commands.add('scanDB', (tableName, recordId, cb) => {
const record = scanTable(tableName, recordId).then(record => { cb(record) };
})

Categories

Resources