Firebase function to timestamp last change runs multiple times - firebase

I have a firebase function to timestamp an item when it is updated. I have seen other examples where code gets stuck in an infinite loop because the timestamp for the latest update causes it to run again, but I thought I had that part figured out in my code below. I believe this was working fine at one point, now it seems to get stuck in an update loop. Am I missing something?
exports.itemUpdate = functions.database.ref('/items/{id}').onUpdate((event) => {
//dont mark updated timestamp if item is deleted
if (!event.data.exists()) {
return null;
}
//dont update if we are simply updating timestamp
if (event.data.child('lastUpdated').changed()) {
return null;
}else{
return admin.database().ref('/items/' + event.params.id + '/lastUpdated').set(admin.database.ServerValue.TIMESTAMP);
}
});
Please note, this is for pre v1.0 beta version of firebase functions.

I believe your code is not getting into the if block, as there's no method changed in the dataSnapshot.
You could try to check if lastUpdated changed with something like this:
const oldValue = event.data.previous.val();
const newValue = event.data.val();
if(oldValue.lastUpdated !== newValue.lastUpdated) {
return null;
} else {
return admin.database().ref('/items/' + event.params.id + '/lastUpdated').set(admin.database.ServerValue.TIMESTAMP);
}

Related

Blazor WebAssembly **Microsoft.JSInterop.JSException** Error: The value 'sessionStorage.length' is not a function

From a basic standpoint what I am trying to do is get a list of keys (key names) from session storage.
The way I am trying to do this is by calling the JsRuntime.InvokeAsync method to:
Get the number of keys in session storage, and
loop thought the number of items in session storage and get the key name.
public async Task<List<string>> GetKeysAsync()
{
var dataToReturn = new List<string>();
var storageLength = await JsRuntime.InvokeAsync<string>("sessionStorage.length");
if (int.TryParse(storageLength, out var slength))
{
for (var i = 1; i <= slength; i++)
{
dataToReturn.Add(await JsRuntime.InvokeAsync<string>($"sessionStorage.key({i})"));
}
}
return dataToReturn;
}
When calling the JsRuntime.InvokeAsync($"sessionStorage.length")) or JsRuntime.InvokeAsync($"sessionStorage.key(0)")) I am getting an error "The value 'sessionStorage.length' is not a function." or The value 'sessionStorage.key(0)' is not a function.
I am able to get a single items using the key name from session storage without issue like in the following example.
public async Task<string> GetStringAsync(string key)
{
return await JsRuntime.InvokeAsync<string>("sessionStorage.getItem", key);
}
When I use the .length or .key(0) in the Chrome console they work as expected, but not when using the JsRuntime.
I was able to get this to work without using the sessionStorage.length property. I am not 100% happy with the solution, but it does work as needed.
Please see below code. The main thing on the .key was to use the count as a separate variable in the InvokeAsync method.
I think the reason for this is the JsRuntime.InvokeAsync method adds the () automatically to the end of the request, so sessionStorage.length is becoming sessionStorage.length() thus will not work. sessionStorage.key(0) was becoming sessionStorage.key(0)(). etc. Just that is just a guess.
public async Task<List<string>> GetKeysAsync()
{
var dataToReturn = new List<string>();
var dataPoint = "1";
while (!string.IsNullOrEmpty(dataPoint) )
{
dataPoint = await JsRuntime.InvokeAsync<string>($"sessionStorage.key", $"{dataToReturn.Count}");
if (!string.IsNullOrEmpty(dataPoint))
dataToReturn.Add(dataPoint);
}
return dataToReturn;
}

sqlite_reset() on an INSERT with RETURNING statement rollback/cancel it

I want to INSERT data in a SQLite table and do this :
sqlite3_stmt *pStmt;
sqlite3_prepare(db,"INSERT INTO table(col2,col3) VALUES (?,?) RETURNING col1;",-1,&pStmt,NULL);
for (int i = 0; i < dataset_length; i++) {
sqlite3_bind_int(pStmt,1,dataset[i].value1);
sqlite3_bind_int(pStmt,2,dataset[i].value2);
switch (sqlite3_step(pStmt)) {
case SQLITE_ROW: {
// Nice! A row has been inserted.
dataset[i].id = sqlite3_column_int(pStmt,0);
} break;
case SQLITE_DONE: {
// No results. What? Return an error.
} return false;
default: {
// Return an error
} return false;
}
// ↓ Problem below ↓
sqlite3_reset(pStmt);
}
//sqlite3_cleanup(pStmt); <- Don't worry about cleanups
return true;
sqlite3_step() always returns SQLITE_ROW and the RETURNING expression works.
If I do a SELECT before the sqlite3_reset(), it returns the freshly inserted row. If I prepare and run the same query after the sqlite3_reset(), my table is empty, the row is vanished.
I tried without the sqlite3_reset() and that works, but I don't understand why and think it's due to the auto-reset feature I OMIT in the Windows build.
Where I am wrong in my SQLite usage?
I finally find out where I was wrong. SQLite mandate to call sqlite_reset() only after receiving an error or SQLITE_DONE.
In my code I only generate a SQLITE_ROW, but I sqlite_reset() before getting a SQLITE_DONE and it cause SQLite to somewhat "reset" the statement and rolling back changes from my point of view.
The correct way is to after a SQLITE_ROW, to call sqlite_step() again that generate a SQLITE_DONE and then sqlite_reset(). That means :
// The way to SQLite with a RETURNING INSERT
for (...) {
// sqlite3_bind...()
sqlite3_step(); // Returns SQLITE_ROW
// sqlite3_column...()
sqlite3_step(); // Returns SQLITE_DONE
sqlite3_reset(); // Returns SQLITE_OK
}
Here is below my fixed code from my question :
sqlite3_stmt *pStmt;
sqlite3_prepare(db,"INSERT INTO table(col2,col3) VALUES (?,?) RETURNING col1;",-1,&pStmt,NULL);
for (int i = 0; i < dataset_length; i++) {
sqlite3_bind_int(pStmt,1,dataset[i].value1);
sqlite3_bind_int(pStmt,2,dataset[i].value2);
switch (sqlite3_step(pStmt)) {
case SQLITE_ROW: {
// Nice! A row has been inserted.
dataset[i].id = sqlite3_column_int(pStmt,0);
// Generate a SQLITE_DONE
if (sqlite3_step(pStmt) != SQLITE_DONE)
// Something went wrong, return an error
return false;
} break;
case SQLITE_DONE: {
// No results. What? Return an error.
} return false;
default: {
// Return an error
} return false;
}
sqlite3_reset(pStmt);
}
//sqlite3_cleanup(pStmt); <- Don't worry about cleanups
return true;
Of course my code imply that there is only 1 row returned by SQLite, adapt your code if SQLite returns more. The rule is that a sqlite_step() must returns a SQLITE_DONE before doing a sqlite_reset().

How do I query for the existance of a property without knowing its path

Is there a way to query cosmos DB for the existence of a property within a document without knowing where exactly this property is located as it might appear in different locations?
Basically
SELECT *
FROM SomeCollection
WHERE IS_DEFINED("here should be just the prop name w/o any path")
Edit:
I initially missed to state that I was looking for a solution on the query level instead of writing a user defined stored procedure
you can create UDF which will recursively navigate though all object properties and return true if found on any level. In your case udf body can look similar to this
function findRecursive(theObject, searchingProperty){
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
if (findRecursive(theObject[i]){
return true;
}
}
}
else
{
for(var prop in theObject) {
if(prop == searchingProperty) {
return true;
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array){
if (findRecursive(theObject[prop])){
return true;
}
}
}
}
return false;
}
UDF should be register same as stored procedures. And can be called from queries.
So your select will look like next
SELECT *
FROM root
WHERE udf.findRecursive(root, "here should be just the prop name w/o any path")
P.S. I didn't test the code, but you should get the basic idea

SQLite read from SQLITE database in plugin

I need to read values from an SQLite database in my plugin. For that I found the sqlite.jsm module. My problem is I want to make a row as a global variable, but the code used in SQLite (promises and tasks) is asynchronous. Is there a way I can collect information from my database into a global variable?
let iDs = [];
Task.spawn(function* () {
let db = yield Sqlite.openConnection({ path:
permissionFilePath});
try {
let row = yield db.execute(
"SELECT id FROM 'moz_hosts'");
for ( i=0; i < row.length; i++) {
console.log("row["+ i +"] :" +
row[i].getResultByIndex(0));
yield iDs.push(row[i].getResultByIndex(0));
}
}
finally {
yield db.close();
}
});
// Part of the code that doesn't work, because IDs are not yet assigned any values!
console.log("debug");
for (i=0; i<iDs.length; i++) {
yield console.log("iDs ["+i+"] = "+ iDs[i]);
}
First of all, for a Task you only have to yield things that return promises and therefore run asynchronously. There is no need to yield iDs.push(row[i].getResultByIndex(0));, because the push operation will synchronously return the new length of the array. This shouldn't be much of an issue for the code itself though.
Do you really need the ids to be global? Maybe you can refactor your code so that you don't need to save them globally.
If this is not an option, you will have to block all operations that are going to access the ids until the SQL call has completed. You can do this by relying on the fact that Task.spawn() itself will also return a promise. This also has the nice side effect that you don't need the extra global array:
let idsPromise = Task.spawn(function*() {
let ids = [];
let db = yield Sqlite.openConnection({ path: permissionFilePath});
try {
let row = yield db.execute("SELECT id FROM 'moz_hosts'");
for (let i = 0, len = row.length; i < len; i++) {
ids.push(row[i].getResultByIndex(0));
}
// Instead of the loop you can also use:
// ids = row.map(row => row.getResultByIndex(0));
} finally {
yield db.close();
}
return ids;
});
Then, in other parts of your code, when you want the ids you can use:
idsPromise.then(function(ids) {
// Do something with ids
});
Alternatively, you can also get them in a Task:
Task.spawn(function*() {
let ids = yield idsPromise;
});
You can do this multiple times. Once a promise is resolved the then() part will be executed as soon as possible.

XPages Dojo DataStore Get and Cycle Through Return Values

My goal is to grab all the rows in the DataGrid that I display, cycle through each row, grab certain items and values, and update fields in the UI. Based on what I have read, it is best to grab the datastore and go through that.
The user is editing the Dojo datagrid (editable columns) and clicking a Save button to save the changes (RESTService.save()). During the Save button process, I would like to cycle through the datastore.
I am using examples from this:
http://xcellerant.net/2013/06/13/dojo-data-grid-21-locking-columns/
This is my REST Service:
<xp:this.resources>
<xp:dojoModule name="dojo.data.ItemFileWriteStore"></xp:dojoModule></xp:this.resources><xe:restService id="rsVictims" pathInfo="rsVictimsData">
<xe:this.service>
<xe:viewJsonService defaultColumns="true"
viewName="InvoiceMPRVictims">
<xe:this.databaseName><![CDATA[#{javascript:applicationScope.get("appConfig").keywords.appDataStore.join("!!");}]]></xe:this.databaseName>
<xe:this.keys><![CDATA[#{javascript:viewScope.get("mprKeysValue");}]]></xe:this.keys>
</xe:viewJsonService>
</xe:this.service>
</xe:restService>
This is the Save button:
<xp:button value="Save Changes" id="victimsSaveButton">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[// Save the changes...
try{
rsVictims.save();
var jsonStore;
// Get the datastore...
jsonStore = new dojo.data.ItemFileWriteStore({url: "invoices_page1_doc.xsp/rsVictimsData"});
// Cycle through the jsonStore...
}
catch (e) {
alert(e);
}
]]></xp:this.script>
</xp:eventHandler>
</xp:button>
I was hoping to get a few examples on how to cycle through the datastore return. I see ideas in this reference, but not sure which one to use:
http://dojotoolkit.org/reference-guide/1.9/dojo/data/ItemFileWriteStore.html
Also, does saving the REST Service (rsVictims.save();) before grabbing the datastore give me the current values?
Thanks!
-------------- EDIT 1/6/2014 ----------------------
I have updated the Save button to use this code:
try {
rsVictims.save();
var item;
var itemStore;
var itemName;
var itemValue;
// Get the datastore...
itemStore = new dojo.data.ItemFileReadStore( {
url : "invoices_page1_doc.xsp/rsVictimsData"
});
itemName = "month_11";
function failed(error) {
// Do something with the provided error.
alert(error);
}
function gotItem(item) {
if (itemStore.isItem(item)) {
itemValue = itemStore.getValue(item, itemName);
alert(itemValue);
} else {
// This should never occur.
throw new Error("Unable to locate the item with identity [sv]");
}
}
// Invoke the lookup. This is an async call as it may have to call back to a
// server to get data.
itemStore.fetchItemByIdentity( {
identity : itemName,
onItem : gotItem,
onError : failed
});
} catch (e) {
alert(e);
}
However, I do not get any return values in the gotItem function and I receive no errors.
I may have missed something -- any help would be great!
Thanks!

Resources