How to compare old and new value with Cloud Functions for Firebase with .onWrite or onchange? - javascript

Lets take the following data structure:
Now I want to refresh the accessTokenFacebook with a Firebase Function.
I tested two option:
the onWrite, and the:
the onChanged
The onWrite looks the best to me, but with the following function:
exports.getFacebookAccessTokenOnchange = functions.database.ref('/users/{uid}/userAccountInfo/lastLogin').onWrite(event => {
const lastLogin = event.data;
let dateObject = new Date();
let currentDate = dateObject.toUTCString();
return lastLogin.ref.parent.parent.child('services').child('facebook').update({'accessTokenFacebook': currentDate});
});
Something happens I don'understand/can solve: when I delete a whole userUID-record (for a cleanup), the userUID-record automatically create, then only with the following path {uid}/services/facebood/accesTokenFacebook...
It seems that a deletion also triggers a onWrite.
I also tried the .onchange, but that one only triggers when there is still no accessTokenFacebook. When the change make this one, the change never triggered again.
So the next thing I want to do is a comparison between the old and new value. Do you have an example? Or is there a better solution?

UPDATE:
Cloud Functions recently introduced changes to the API as noted here.
Now (>= v1.0.0)
exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
});
Before (<= v0.9.1)
exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
const beforeData = event.data.previous.val(); // data before the write
const afterData = event.data.val(); // data after the write
});

Now that these functions are deprecated and this is the number one search result for this subject, here is the new updated version that Cloud Functions now use.
exports.yourFunction = functions.database.ref('/path/{randomPath}/goes/here').onWrite((change, context) => {
// Your value after the write
const newVal = change.after.val();
// Your value before the write
const oldVal = change.before.val();
});

Related

Flutter Firebase Functions Error: Database not defined

I've created a delete oldFiles function for my Database that deletes nodes from my chat messages. I've used the example function provided by Firebase and updated it to fit my use. My database structure is databaseName/messages/{pushId} and I've added const functions = require('firebase-functions') and const admin = require('firebase-admin') and admin.initializeApp(). Here is what I have...
exports.deleteOldItems = functions.database.ref('messages/{pushId}').onWrite(async (change) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = (DateTime.now().millisecondsSinceEpoch - CUT_OFF_TIME);
const oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
When I review my Function logs, I'm getting the following errors...
ReferenceError: DateTime is not defined
at exports.deleteOldItems.functions.database.ref.onWrite (/srv/index.js:17:18)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
And my Functions are finishing with status: error. Any ideas to what may be going on?
DateTime isn't a valid JavaScript object or identifier. If you want to work with dates and times, you will need to work with Date, as you are in the line just above where you have DateTime. You should probably review the JavaScript documentation for Date to learn how it works.

Is there a way to trigger validation manually in monaco editor?

I'm using the default TypeScript service and the models are initialized asynchronously with one model depending on the other. There's a case where the two models cannot detect each other so it shows a semantic error. If I make some edits in the dependent model, which causes the model to be re-validated, the errors disappear.
I have tried to setModel manually, which solves the problems. However, it destroys the undo history.
Is there a way to re-validate the model manually?
That's my solution, which is extracted from monaco-typescript:
async function revalidateModel(model) {
if (!model || model.isDisposed()) return;
const getWorker = await monaco.languages.typescript.getTypeScriptWorker();
const worker = await getWorker(model.uri);
const diagnostics = (await Promise.all([
worker.getSyntacticDiagnostics(model.uri.toString()),
worker.getSemanticDiagnostics(model.uri.toString())
])).reduce((a, it) => a.concat(it));
const markers = diagnostics.map(d => {
const start = model.getPositionAt(d.start);
const end = model.getPositionAt(d.start + d.length);
return {
severity: monaco.MarkerSeverity.Error,
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end.lineNumber,
endColumn: end.column,
message: flattenDiagnosticMessageText(d.messageText, "\n")
};
});
const owner = model.getLanguageIdentifier().language;
monaco.editor.setModelMarkers(model, owner, markers);
}
Call the function above when model is created asynchronizedly.
This is what I did to fix it:
setInterval(() => {
const range = new monaco.Range(1,1,1,1);
const addEmptySpace = {forceMoveMarkers: true, range, text: ' '};
for (const m of monaco.editor.getModels()) {
const toInvert = m.applyEdits([addEmptySpace]);
m.applyEdits(toInvert);
}
}, 50*1000)
Every fifty seconds you insert and immediately remove a space. I don't like it, but it works.

Get the ChildrenCount of a child in Firebase using JavaScript

I have been doing this for an hour. I simply want to get the number of children in the child "Success" in the database below. The answers in similar stackoverflow questions are not working. I am new in Javascript Programming.
So far I have tried this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
and also this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
Where might I be going wrong.
As explained in the doc, you have to use the numChildren() method, as follows:
var ref = firebase.database().ref("Success");
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.numChildren());
});
If you want to use this method in a Cloud Function, you can do as follows:
exports.children = functions.database
.ref('/Success')
.onWrite((change, context) => {
console.log(change.after.numChildren());
return null;
});
Note that:
The new syntax for Cloud Functions version > 1.0 is used, see https://firebase.google.com/docs/functions/beta-v1-diff?authuser=0
You should not forget to return a promise or a value to indicate to the platform that the Cloud Function execution is completed (for more details on this point, you may watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/).
const db = getDatabase(app)
const questionsRef = ref(db, 'questions')
const mathematicalLiteracy = child(questionsRef, 'mathematicalLiteracy')
onValue(mathematicalLiteracy, (snapshot) => {
const data = snapshot.val()
const lenML = data.length - 1
console.log(lenML)
})
This method worked for me. I wanted to get the children's count of the mathematicalLiteracy node in my database tree. If I get its value using .val() it returns an array that contains that node's children and an extra empty item. So, I subtracted that one empty item's count. Finally, I get my needed children's count.

Increment Realtime Database count on new user with Firebase Cloud Function

* UPDATED: THIS WORKS. SEE ANSWER BELOW *
I'm trying to write a Firebase Cloud Function that increments a Realtime Database /userCount value whenever a new user is created.
I've tried the following, but am getting "TypeError: userCountRef.transaction is not a function" in incrementCountOnNewUser.
Transactions are working for my other function incrementCountOnOpen when the value of garage is set to true, but the ref is derived from the after event object.
Any suggestions on how to do this?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// const userCountRef = functions.database.ref("/userCount"); // does NOT work
const userCountRef = admin.database().ref('/userCount'); // THIS WORKS!
exports.incrementCountOnNewUser = functions.auth.user().onCreate((user) => {
return userCountRef.transaction(count => count + 1);
});
exports.incrementCountOnOpen = functions.database.ref("/garage").onUpdate(({after}) => {
const countRef = after.ref.parent.child('count');
const newValue = after.val();
return newValue
? countRef.transaction(count => count + 1)
: null;
});
It turns out that the code above works! I had switched from the commented out code (which does NOT work). I guess it didn't wait long enough for it propagate after I published, because I see it working now!
Sorry for the confusion.

Cloud Functions for Firebase: how to reference a key in a list that has been just pushed

My question is basically what to do in your cloud function, if you want to reference keys that have been generated when the client called push().
/providerApps/{UID}/ is my path to a list of appointment nodes, so each appointment node is at /providerApps/{UID}/someKey.
I need the "new item in the list", the one that was added with push(), so I thought I could order the keys and simply get the last one, but that does not work:
// (Try to) Listen for new appointments at /providerApps/{pUID}
// and store the appointment at at /clientApps/{cUID}
// cUID is in the new appointment node
exports.storeNewAppForClient = functions.database.ref("/providerApps/{UID}").onWrite(event => {
// Exit when the data is deleted.
if (!event.data.exists()) {
console.log("deletion -> exiting");
return;
}
const pUID = event.params.UID;
const params = event.params;
console.log("params: ", params);
const firstAppVal = event.data.ref.orderByKey().limitToLast(1).val();
// TypeError: event.data.ref.orderByKey(...).limitToLast(...).val is not a function
const date = firstAppVal["dateStr"];
const cUID = firstAppVal["clientUID"];
return event.data.ref.root.child("clientApps").child(cUID).child(date).set(pUID);
});
I guess I could do it on the client side with push().getKey() and allow providers to write into clientApps node, but that seems to be less elegant.
Any ideas how to do this with cloud functions?
As an illustration, my data structure looks like this:
there are provider and their clients who make appointments
Cheers
Change your trigger location to be the newly created appointment instead of the list of appointments. Then you can access the appointment data directly:
exports.storeNewAppForClient = functions.database.ref("/providerApps/{UID}/{pushId}").onWrite(event => {
// Exit when the data is deleted.
if (!event.data.exists()) {
console.log("deletion -> exiting");
return;
}
const pUID = event.params.UID;
const params = event.params;
console.log("params: ", params);
const date = event.data.child('dateStr').val();
const cUID = event.data.child('clientUID').val();
return admin.database().ref('clientApps').child(cUID).child(date).set(pUID);
});
(updated for Frank's comment)

Categories

Resources