Firebase Cloud Function - Reference.update failed - node.js

I'm getting the following error when invoking a GCF:
Error: Reference.update failed: First argument path specified exceeds the maximum depth that can be written (32) or object contains a cycle in property
Done a bit of digging online but can't find the same issue. Without adding way too much info, I'm trying to add dish objects into a category object in Realtime Database. The odd thing is, the function works fine for the first 6 dishes, and when I try to add a 7th, this error pops up and the update method fails.
The full error log, with my unique property data is:
Error: Reference.update failed: First argument path specified exceeds the maximum depth that can be written (32) or object contains a cycle in property
'users.sIRf7m1uWfa9j0iF6UuuzvdD5TG2.dishcategories.default01.dishes.u3o1p278vriqo3odeslle.categories.0.dishes.irdrl2q7blsi1y7ih3jhh.categories.0.dishes.v8pl7r9llhfp7sqmz7uikk.categories.0.dishes.2ee3ajy6d5vymneewgflze.categories.0.dishes.btdib119nz5cnm6zk5uu4t.categories.0.dishes.4wyu5yqyn2z0bgvcejix9.categories.0.dishes.w1cfcpktym7nkp76p521n.categories.0.createdDate'
at ValidationPath.checkValid_ (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:1035:19)
at ValidationPath.push (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:1015:14)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:1478:18
at Object.forEach (/srv/node_modules/#firebase/util/dist/index.node.cjs.js:837:13)
at validateFirebaseData (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:1462:14)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:1479:13
at Object.forEach (/srv/node_modules/#firebase/util/dist/index.node.cjs.js:837:13)
at validateFirebaseData (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:1462:14)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:1479:13
at Object.forEach (/srv/node_modules/#firebase/util/dist/index.node.cjs.js:837:13)
Here's the cloud function code from index.ts:
// Now we need to check if this dish's categories already exist as user categories.
// If they do, we can add this newly created dish into them.
// If they don't, we can create them with this newly added dish in them.
dish.categories.forEach( async (dishCategory) => {
const index = objectInArrayByID(dishCategory, userCategories)
if ( index !== -1 ) {
return userCategoriesRef.child(`${dishCategory.id}/dishes/${id}`).update(dish) // *** This is the update method producing the error in this case ***
}
else {
await userCategoriesRef.child(`${dishCategory.id}`).update(dishCategory)
return userCategoriesRef.child(`${dishCategory.id}/dishes/${id}`).update(dish)
}
})
Anyone know what this error means, and perhaps what I'm doing wrong here? Thanks!

The error message is quite explicit: you're trying to write data that is more than 32 levels deep, or which contains a cyclic object.
Given the code you shared, the latter seems most likely: you're looping over each category of a dish, and then write that dish to each category again. The simplest fix I can think of is to write the dish-without-categories to each category:
dish_without_categories = dish;
delete dish_without_categories.categories;
dish.categories.forEach( async (dishCategory) => {
const index = objectInArrayByID(dishCategory, userCategories)
if ( index !== -1 ) {
return userCategoriesRef.child(`${dishCategory.id}/dishes/${id}`).update(dish_without_categories)
}
else {
await userCategoriesRef.child(`${dishCategory.id}`).update(dishCategory)
return userCategoriesRef.child(`${dishCategory.id}/dishes/${id}`).update(dish_without_categories)
}
})

Related

Jest Expected and receive are identical

Is there a reason why Jest would see this as a not identical when both seem exactly the same?
So here is the code I am using to do the test, it's basically just a function that calls for an event emiter, in the event emiter if the date is invalid, I let it as is :
const datepickerComponent: Datepicker = new Datepicker();
const mockEvent = {
target: {
classList: {
remove: jest.fn(),
add: jest.fn(),
},
value: '01-01-197',
},
} as unknown as InputEvent;
datepickerComponent.onInput(mockEvent);
const emitMock: jest.Mock = jest.fn();
datepickerComponent.dsdDatepickerInputChange = { emit: emitMock } as unknown as EventEmitter<dsdDatepickerInputChangeEvent>;
// when
datepickerComponent.onInput(mockEvent);
const dateValue = new Date('197-01-01T00:00:00');
// then
expect(emitMock).toHaveBeenCalledWith({ value: '197-01-01', valueAsDate: dateValue });
The reason why you are observing this error is because whilst Date { NaN } values look the same, they actually refer to different object instances and cannot be traversed for equality any further, hence the actual error should be the following:
Expected: {"value": "197-01-01", "valueAsDate": Date { NaN }}
Received: serializes to the same string
(To reproduce this error - create two new dates using new Date('197-01-01T00:00:00') and pass them into .equals())
To get past this error, all you need to do is to simply refactor your .toHaveBeenCalledWith test into the following:
const calledWithArg = emitMock.mock.calls[0][0];
expect(JSON.stringify(calledWithArg)).toEqual(JSON.stringify({ value: '197-01-01', valueAsDate: dateValue }));
The reason why .toHaveBeenCalledWith does not work is because it does not allow us to reshape the argument object before a comparison (in our case we need to stringify it), hence we can alternatively extract the argument that the mock was called with via .mock.calls[0][0], stringify it and then compare it to the stringified version of the expected object.
Reference of valueAsDate is different. But Jest doc affirm it uses .toEqual for comparison. That means it try to deeply compare two new Date. This is a bad idea, you don't control the Date class, and there are probably some moving parts inside.
To loose match a value, you can check this issue: https://stackoverflow.com/a/55569458/7696155

console.log is omitting some key pairs? [duplicate]

This question already has answers here:
mongoose .find() method returns object with unwanted properties
(5 answers)
Closed 5 years ago.
Working with a strange problem here. This is an array of objects which is pulled from mongodb and passed into the following function.
I tried the following 3 logs sequentially within the forEach on the array pulled from the database:
e (the object element within the array) which returns correctly. as you can see all the properties (keys) exist:
{ paid: false,
hotelWebsite: 'www.testing.com',
_id:5951848a24bb261eed09d638,
hotelAddress: '123 easy street',
...etc }
console.log(Object.keys(e)) is returning things that are not the keys...
[ '__parentArray',
'__parent',
'__index',
'$__',
'isNew',
'errors',
'_doc',
'$init' ]
and finally:
for(key in e){
console.log(key);
}
which returns an absolute mess of data, part of which DOES contain the actual keys of the object:
__parentArray
__parent
__index
$__
isNew
errors
_doc
$init
id
_id
hotelWebsite
hotelAddress
hotelNumber
hotelName
courseCost
courseDate
courseState
courseCity
courseName
paid
studentComments
studentEmail
studentPhone
studentCountry
studentZip
studentState
studentCity
studentAddress
studentCompany
studentName
schema
constructor
$__original_remove
remove
_pres
_posts
$__original_validate
validate
toBSON
markModified
populate
save
update
inspect
invalidate
$markValid
$isValid
ownerDocument
$__fullPath
parent
parentArray
on
once
emit
listeners
removeListener
setMaxListeners
removeAllListeners
addListener
$__buildDoc
init
$hook
$pre
$post
removePre
removePost
_lazySetupHooks
set
$__shouldModify
$__set
getValue
setValue
get
$__path
unmarkModified
$ignore
modifiedPaths
isModified
$isDefault
isDirectModified
isInit
isSelected
isDirectSelected
$__validate
validateSync
$__reset
$__dirty
$__setSchema
$__getArrayPathsToValidate
$__getAllSubdocs
$__handleReject
$toObject
toObject
toJSON
toString
equals
execPopulate
populated
depopulate
And a relevant sample of the code if needed:
studentsArray.forEach( (e, i) => {
if(task === 'nameTag'){
console.log(e);
console.log(Object.keys(e));
for(k in e){
console.log(k);
}
}
....
I need access to the properties (keys) for further processing within the forEach function. I am very confused on what is causing this and have never run into this sort of issue before. For the record the objects exist, using a console.log(typeof e) it IS an object (not a data "string"). I can access the properties using the dot or bracket notation but NOT using Object.keys() or for (keys in obj).
Can anyone help me sort this out please?
for ... in iterates all enumerable properties, both own and inherited. This is not "a strange bug," this is in fact the intended behavior.
As for the Object.keys(), unless it was overwritten by a non-compliant implementation, those are in fact enumerable keys of the object itself, so you are most likely mistaken. The e object has a .toJSON() method in its prototype that is implicitly called when you do console.log(e), so that is probably the output you are seeing there, and is not likely going to reflect exactly the same property keys as the original object. Try calling console.log(e.toJSON()) and I'm guessing it will be the same output as in the first one.
If you want only the object's own properties, use Object.getOwnPropertyNames(e).
If you want the keys printed in the first output, then use Object.keys(e.toJSON()).

NodeJS (gettting error notes.push is not a function)

When I run this code I get push is not a function. I have gone over the code so many times and can't figure out where I went wrong. i have also read many of post and I still can't figure it out. I am new to programming and could use the help.
const fs= require('fs')
const getNotes = function() {
    return 'This just returns get notes'
        
enter code here
};
const addNote  = function (title, body) {
    const notes = loadNotes()
    
    notes.push({
        title: title,
        boby: body
    })
    saveNotes(notes)
    
};
const saveNotes = function (notes) {
    const dataJSON = JSON.stringify(notes)
    fs.writeFileSync('notes.json',dataJSON)
}
// Code below loads the notes. Above, addNote adds the note.
const loadNotes = function () {
    try {
        const dataBuffer = fs.readFileSync('notes.json')
        const dataJSON= dataBuffer.toString()
        return JSON.parse(dataJSON)
    } catch (error) {
        return('Note such file')
    }
    
    
}
module.exports ={
    getNotes: getNotes,
    addNote: addNote
}
So, you have this:
const notes = loadNotes()
notes.push({
title: title,
boby: body
});
If you're getting an error that notes.push is not a function, then that is because loadNotes() is not return an array. That could be for a couple reasons:
JSON.parse(dataJson) successfully parses your json, but its top level object is not an array.
JSON.parse(dataJson) throws and you end up returning a string instead of an array.
You can fairly easily diagnose this by adding a console.log() statement like this:
const notes = loadNotes();
console.log(notes); // see what this shows
notes.push({
title: title,
boby: body
});
FYI, returning a string fromloadNotes()as an error really doesn't make much sense unless you're going to check for a string after calling that function. IMO, it would make more sense to either return null for an error or just let it throw. Both would be simpler and easier to check after calling loadNotes().
And, in either case, you must check for an error return value after calling loadNotes() unless you want loadNotes() to throw upon error like it is.

Mongoose idGetter error: Cannot set property '_id' of null

I am getting the following error from the Mongoose idGetter function. Can anyone shed some light on what may be causing it?
return this.$__._id = null == this._id
^
TypeError: Cannot set property '_id' of null
at model.idGetter (e:\Users\Christine\test\mvt\node_modules\mongoose\lib\schema.js:98:23)
The error situation involves multiple client requests performing document removes (on different documents in the same collection), on a database being accessed over internet (i.e. slow response time). The failure happens on accessing the id of one of the removed documents. There appears to be a small timing window in which it occurs - everything I've tried to debug it causes the problem to 'go away'.
Outline of the application code:
Details.findOneAndRemove({ _id : remove_id}, afterRemove);
function afterRemove(err, removedDoc){
if (!err && removedDoc){
OtherDetails.find({ _id: some_id}, afterFind);
}
function afterFind(err, foundDoc){
if (!err && foundDoc){
// next line causes exception
if (foundDoc.details_id === removedDoc.id) { // do stuff };
}
}
}
Do you have an id property defined on the document that you have removed?
If not, you should probably want to compare foundDoc.details_id with removedDoc._id
Below is the source code for the idGetter function in the mongoose code base:
function idGetter () {
if (this.$__._id) {
return this.$__._id;
}
return this.$__._id = null == this._id
? null
: String(this._id);
}
As I understand, I think that the idGetter is specifically aimed at ObjectId types, hence that exception.
You could also try to add an id property on the document if it does not yet exist to see if it continues to throw the same exception.
fwiw: it is possible that the problem was related to using 32-bit Node on 64-bit Windows (8.1). After installing 64-bit Node, I could not reproduce the problem.
Unsatisfying level of certainty, but I thought I would share in case anyone else gets a similar problem or can shed light on whether this might really have been the cause.

DynamicMethod code unverifiable in .Net 4.0 (found ref 'this' pointer... expected ref '<>f__AnonymousType1`)

Was using this solution to convert anonymous types to dictionaries using reflection.emit. Was working fine until I changed to .Net 4.0 from 3.5.
Now, I'm getting the "System.Security.VerificationException: Operation could destabilize the runtime." error.
Converted the anonymously loaded dynamic method to one hosted in a dynamic assembly, saved it, then ran peverify.exe on it to find out what was wrong.
Got: [IL]: Error: [DynamicAssemblyExample.dll : MyDynamicType::MyMethod][offs
et 0x0000000D][found ref ('this' ptr) 'MyDynamicType'][expected ref '<>f__AnonymousType1`3[System.String,System.Int32,System.Byte]'] Unexpected type on the stac
k.
[IL]: Error: [DynamicAssemblyExample.dll : MyDynamicType::MyMethod][offs
et 0x0000000D] Method is not visible.
2 Error(s) Verifying DynamicAssemblyExample.dll
The code:
foreach (PropertyInfo property in itemType.GetProperties(attributes).Where(info => info.CanRead))
{
// load Dictionary (prepare for call later)
methIL.Emit(OpCodes.Ldloc_0);
// load key, i.e. name of the property
methIL.Emit(OpCodes.Ldstr, property.Name);
// load value of property to stack
methIL.Emit(OpCodes.Ldarg_0);
methIL.EmitCall(OpCodes.Callvirt, property.GetGetMethod(), null);
// perform boxing if necessary
if (property.PropertyType.IsValueType)
{
methIL.Emit(OpCodes.Box, property.PropertyType);
}
// stack at this point
// 1. string or null (value)
// 2. string (key)
// 3. dictionary
// ready to call dict.Add(key, value)
methIL.EmitCall(OpCodes.Callvirt, addMethod, null);
}
Is there a way to derefence the pointer to the actual property? Or do I have to cast it somehow? Any pointers?
Regards!
Sorry guys, made a mistake, since the actual dynamic method creates a delegate type that acts on the instance of the anonymous (or non-anonymous) type, the Ldarg_0 code is looking for a something that is not there in this debug implementation.
So I, changed it to OpCodes.Ldnull.
var attributes = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo property in itemType.GetProperties(attributes).Where(info => info.CanRead))
{
// load Dictionary (prepare for call later)
methIL.Emit(OpCodes.Ldloc_0);
// load key, i.e. name of the property
methIL.Emit(OpCodes.Ldstr, property.Name);
// load value of property to stack
methIL.Emit(OpCodes.Ldnull);
//methIL.Emit(OpCodes.Castclass, itemType);
methIL.EmitCall(OpCodes.Callvirt, property.GetGetMethod(), null);
// perform boxing if necessary
if (property.PropertyType.IsValueType)
{
methIL.Emit(OpCodes.Box, property.PropertyType);
}
// stack at this point
// 1. string or null (value)
// 2. string (key)
// 3. dictionary
// ready to call dict.Add(key, value)
methIL.EmitCall(OpCodes.Callvirt, addMethod, null);
}
But I still get a method not visible error after peverifying it. Is it that get methods for properties of anonymous types are not visible via reflection?
Just a suggestion, have you tried to rewrite the code that emits IL to actually write to the dictionary - i.e. no Reflection.Emit ? My bet is that the generated IL is not proper in some way, not the code that accesses the anonymous type.

Resources