Weird bug with Accounts.onCreatedUser - javascript

Here is a weird bug I am having. First here is my code
User = {};
User._defaults = {
emails : [],
enabled : true
}
User.defaults = function(){
var defaults = _.clone(User._defaults);
return _.extend(defaults,{
createdAt : new Date()
});
}
Accounts.onCreateUser(function(options, user){
console.log(User.defaults());
// returns {emails: [ { address: 'francis#domain.com', verified: true } ], enabled: true}
_.defaults(user, User.defaults());
user.emails.push(user.services.linkedin.emailAddress);
return user;
});
As you can see, when I call User.defaults() it returns an object with the emails array filled with the email address of the previous new use.
But what is even weirder, is that when I do this :
Accounts.onCreateUser(function(options, user){
console.log(User._defaults, User.defaults());
user.enabled = true;
user.emails = [];
user.createdAt = new Date();
// _.defaults(user, User.defaults());
user.emails.push(user.services.linkedin.emailAddress);
return user;
})
The logged User.defaults() actually return an object with the emails array empty.
Anyone has an idea what can cause this ??
Thanks !!

This would have to do with the Underscore clone function. If you read the docs.
Create a shallow-copied clone of the provided plain object. Any nested
objects or arrays will be copied by reference, not duplicated.
The important part is the shallow copy. Your array is not an actual copy, it's just a reference. So you are always referring to the same array every time.
I would do the following.
User = {};
User.defaults = function(){
return {
emails : [],
enabled : true,
createdAt : new Date()
}
}

Related

'$set' is empty even though it's not

I have a problem in updating a document in MongoDB...
in the params.sendingMethodPushTime and SmsTime i receive a new Date();
tried setting the $ set inside the update function itself and tried to use hardcoded values
userScheme.statics.updateAlertSendingTimes = function (params, cb) {
var query = {uId: params.uIds};
var set = {};
if (params.alertType) {
set['alertSendingTimes'] = {};
set['alertSendingTimes'][params.alertType] = {};
set['alertSendingTimes'][params.alertType]['push'] = params.sendingMethodPushTime;
set['alertSendingTimes'][params.alertType]['sms'] = params.sendingMethodSmsTime;
var update = {
'$set': set
}
this.update(query, update, {upsert: true}, cb);
} else {
cb(null)
}
};
$set should pass and MongoDB document is updated but instead, I get $set is empty etc
EDIT: my mongoose version is 4.13.4 and my node version is 6.16 and params.alertType is a string received dynamically (in my case it's '​1' or '2')
The issue was with the scheme... it was alerts and not alert

Javascript Comparing Booleans

Currently, I have an mongo database that keeps track of answers users submit. When a user makes an input for an answer they already did, that is yes or no I want to check to see if there answer has changed. The issue is this only working half of the time.
Below I have a sails service called Questions Service which is called from the Questions Controller when a user send an answer to the node.js server.
QuestionServer.js
var ObjectID = require('mongodb').ObjectID;
module.exports = {
submitAnswer: function(tokenKey, questionId, answer, done) {
User.findOne({tokenKey: tokenKey}).exec(function (err, findOneRecord){
if(findOneRecord) {
var qId = ObjectID(questionId);
QuestionsAnswered.findOrCreate({question: qId, user: findOneRecord.id}).exec(function createFindCB(error, createdOrFoundRecord){
if(error) {
done(error, findOneRecord, null);
}else if(createdOrFoundRecord){
var oldAnswerChanged = QuestionService.submitAnswerCheck(createdOrFoundRecord.answer, answer);
console.log("Old Changed Answer = " + oldAnswerChanged);
createdOrFoundRecord.answer = answer;
createdOrFoundRecord.save(function(saveErr) {
QuestionService.submitCount(questionId, answer, oldAnswerChanged, done);
});
}
});
}
});
},
submitAnswerCheck: function(recordAnswer, answer) {
console.log("recordAnswer = " + recordAnswer);
console.log("answer = " + answer);
recordAnswer = Boolean(recordAnswer);
answer = Boolean(answer);
if(recordAnswer != answer){
return true;
}
return false;
}
}
As you can see from my code above in my submitAnswerCheck function I want to make sure that the booleans being passed to this function are always primitives and never boolean objects.
Below you will find the output of all the console logs going to the server.
Additional note about the output below, changing != to !== does not change the out from the console. Answers below were still the same.
Output to console:
recordAnswer = true
answer = true
Old Changed Answer = false
recordAnswer = true
answer = false
Old Changed Answer = false
recordAnswer = false
answer = false
Old Changed Answer = true
recordAnswer = false
answer = true
Old Changed Answer = true
recordAnswer = true
answer = true
Old Changed Answer = false
What I would like to see is example output 2 and 4 both return true for the "Old Changed Answer", but what seems to be happening is that whenever the recordAnswer is false the submitAnswerCheck is always returning true.
Lastly here is the model that is saving the boolean to mongodb:
QuestionsAnswered.js
module.exports = {
attributes: {
question: {
model: "question",
required: true
},
user: {
model: "user",
required: true
},
answer: {
type: "boolean",
boolean: true
}
}
};
I am not sure if I am miss understanding the difference between boolean primitives and boolean objects or if there is something else I am missing when setting up a boolean in my model. Have other people been having this problem when comparing boolean in javascript/sails.js?
Found the problem
1st off as this article explains using Boolean function is very dangerous because it turns out JavaScript is pretty aggressive about type coercion. Which means you can't relay on the Boolean function to convert your variables to primitives.
From this I found out that Postman was sending my answer booleans as string example:
debug: answer = 'true'
The solution was as simple as checking if answer equals 'true' as shown below:
Correct way (QuestionController.js):
module.exports = {
answer: function(req, res) {
var tokenKey = req.param("tokenKey");
var questionId = req.param("questionId");
var answer = (req.param("answer") == 'true');
QuestionService.submitAnswer(tokenKey, questionId, answer, function(err, yes, no){
return res.json({
countYes: yes,
countNo: no,
err: err
});
});
}
};
Incorrect way:
module.exports = {
answer: function(req, res) {
var tokenKey = req.param("tokenKey");
var questionId = req.param("questionId");
var answer = req.param("answer");
QuestionService.submitAnswer(tokenKey, questionId, answer, function(err, yes, no){
return res.json({
countYes: yes,
countNo: no,
err: err
});
});
}
};

How to clone document to another collection? [duplicate]

My approach would be to get the document instance, and create a new one from the instance fields. I am sure there is a better way to do it.
You need to reset d1.isNew = true; as in:
Model.findById(yourid).exec(
function(err, doc) {
doc._id = mongoose.Types.ObjectId();
doc.isNew = true; //<--------------------IMPORTANT
doc.save(callback);
}
);
Can you clarify what you mean by "copy/clone"? Are you going trying to create a duplicate document in the database? Or are you just trying to have two vars in your program that have duplicate data?
If you just do:
Model.findById(yourid).exec(
function(err, doc) {
var x = doc;
Model.findById(yourid).exec(
function(err, doc2) {
var y = doc2;
// right now, x.name and y.name are the same
x.name = "name_x";
y.name = "name_y";
console.log(x.name); // prints "name_x"
console.log(y.name); // prints "name_y"
});
});
In this case, x and y will be two "copies" of the same document within your program.
Alternatively, if you wanted to insert a new copy of the doc into the database (though with a different _id I assume), that would look like this:
Model.findById(yourid).exec(
function(err, doc) {
var d1 = doc;
d1._id = /* set a new _id here */;
d1.isNew = true;
d1.save(callback);
}
);
Or if you're doing this from the outset, aka you created some document d1, you can just call save twice without setting the _id:
var d1 = new Model({ name: "John Doe", age: 54 });
d1.save(callback);
d1.save(callback);
There will now be two documents with differing _id's and all other fields identical in your database.
Does this clarify things a bit?
My two cents:
const doc = await DocModel.findById(id);
let obj = doc.toObject();
delete obj._id;
const docClone = new DocModel(obj);
await docClone.save();
So, a lot of these answers will work well for simple docs, but there could be an error case when you're trying to make a deep clone of complex documents.
If you have arrays of subdocs, for example, you can end up with duplicate _ids in your copied document, which can cause subtle bugs later.
To do a deep clone of a mongoose doc, I suggest trying something like:
//recursively remove _id fields
function cleanId(obj) {
if (Array.isArray(obj))
obj.forEach(cleanId);
else {
delete obj['_id'];
for (let key in obj)
if (typeof obj[key] == 'object')
cleanId(obj[key]);
}
}
let some_doc = await SomeModel.findOne({_id: some_id});
let new_doc_object = cleanId(some_doc.toObject());
let new_doc = new SomeModel(new_doc_object);
await new_doc.save();
This is going to be a pretty safe approach, and will ensure that every part of your object is cloned properly with newly generated _id fields on save.
The following code to clone documents:
Model.findById(yourid).exec(
function(err, doc) {
var newdoc = new Model(doc);
newdoc._id = mongoose.Types.ObjectId();
newdoc.save(callback);
}
);
For simply clone use this :
Context.findOne({
_id: context._id
})
.then(function(c) {
c._id = undefined;
c.name = context.name;
c.address = context.address;
c.created = Date.now();
return Context.create(c.toObject());
}).then(function(c) {
return res.json({
success: true,
context: context
});
}).catch(function(err) {
next(err, req, res);
});
const cloneDoc = (doc, model)=>{
const copyDoc = new Model({
...doc.toObject(),
_id: undefined,
});
copyDoc.isNew = true;
return copyDoc;
}
To copy a document into the same collection or different collection first get (query) the data and make a copy of the data. Afterwards remove the _id from the new list as you can't from the current data. This will allow you to insert a new record with new _id assigned from mongodb
change searchBy to what you are trying to find the document by.
change collectionA and collectionB to the name of the collection to create you copy to. Currently we are searching in collectionA and copying the data in collectionB
collectionA.find(searchBy).exec(function (err, doc) {
// create a new copy
let newDoc = { ...doc[0] }._doc;
// delete property from new copy (remove _id).
delete newDoc._id;
// insert copy into db
var newdoc = new collectionB(newDoc);
newdoc.save();
});
You can basically use .clone() to get a copy.
const cars = Cars.find();
const carsCopy = cars.clone();
await cars;
await carsCopy;
https://mongoosejs.com/docs/api.html#schema_Schema-clone

Javascript loop on a object

I want to loop on a object to find what's the object name who have the socket id, and then when it's found console.log it
Code:
getUsernameBySocketID: function(socketid) {
for(var User in Users.Obj) {
var u = Users.Obj[User];
if(u.socketID == socketid) {
return u.username;
}
}
},
EDIT:
User.Obj :
I add elements to user.obj with a function to create a new user:
Users.Obj[id] = new Users.User({username: username, socketID: socketID});
and Users.User contains :
User: function(data) {
this.username = data.username;
this.socketID = data.socketID;
},
but when I call the function and console.log it, it return nothing.
How can I solve that ? Thanks
Finally I've found my problem using #Watte's help
Users.Obj were just empty

JSON.parse returns children objects with null value, children values not being parsed

I have a JavaScript object that I am stringifying with JSON.stringify that returns a JSON string with parent and children data.
When I try to parse this string back to an object, the children objects are now null.
function cacheForm(agency) {
var agency = ko.toJS(this); //easy way to get a clean copy
delete agency.contacts; //remove an extra property
for (i in agency.offices) {
for (val in agency.offices[i]) {
//delete agency.offices[i].agency;
//delete agency.offices[i].agencyID;
}
}
for (i in agency.offices) {
for (ii in agency.offices[i].contacts) {
for (val in agency.offices[i].contacts[ii]) {
//delete agency.offices[i].contacts[ii].office;
//delete agency.offices[i].contacts[ii].agencyID;
//delete agency.offices[i].contacts[ii].officeID;
}
}
}
var value = agency;
var cache = [];
parsed = JSON.stringify(value, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
var data = JSON.parse(parsed);
}
Edit
Agency part of my view model that I am passing into my cacheForm function and I am using
var agency = ko.toJS(this);
to have my data available in an object which can be parsed to JSON string. I may of deleted this code in my post because my original code had many annotations.
Your question initially showed a screen shot where data.offices = [null] was highlighted.
It's not a parsing error, but an error in stringify. Your paste already has data.offices = [null].
MDN states regarding replacer:
Note: You cannot use the replacer function to remove values from an array. If you return undefined or a function then null is used instead.
And furthermore regarding stringify:
If undefined, a function, or an XML value is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array).
I don't have access to your original object, and hence cannot tell which of the two you are hitting...
Implementing toJSON (or just explicitly constructing another object from the source object) instead of a replacer to filter arrays would be the way to go, if the problem is within your current replacer implementation.
there are various js libraries predefined for parsing json and to get children values . What i usually do to parse json is use http://developer.yahoo.com/yui/json/ YUI library.
So I eventually solved my problem and this is how I did it.
function cacheForm(agency) {
// GET my object from agency vm
var agency = ko.toJS(agency);
var s = YUI().use("json-stringify", function (Y) {
var jsonStrAgency = Y.JSON.stringify(agency, ["activities", "agencyName", "agencyID", "campaignBillings", "category", "declaredBillings", "immediateParent", "numberOfEmployees", "ultimateParent", "uRL"]); // Use an array of acceptable object key names as a whitelist.
var jsonStrOffices, jsonStrContacts;
for (i in agency.offices) {
jsonStrOffices = Y.JSON.stringify(agency.offices, ["address1", "address2", "address3", "address4", "address5", "agencyID", "faxNumber", "officeID", "postCode", "telephoneNumber"]);
for (ii in agency.offices[i].contacts) {
jsonStrContacts = Y.JSON.stringify(agency.offices[i].contacts, ["agencyID", "emailAddress", "firstName", "jobName", "officeID", "personID", "surName", "title"]);
}
}
localStorage.setItem('Agency', jsonStrAgency);
localStorage.setItem('Offices', jsonStrOffices);
localStorage.setItem('Contacts', jsonStrContacts);
});
}
Firstly I am passing in my ko.observableArray to the function cacheForm. This parameter is called agency and it is part of my viewmodel.
I want to parse my observableArray and convert it into a standard javascript object. By using ko.toJS I can do this. There will be no ko constructors after using toJS.
Then I have to get my JSON strings. Since my object has children and grandchildren I have to parse these parts separately. Stringify doesn't like arrays within an object, they will be changed to null and your children data will be lost.
Because of circular recursion, I have to use this:
var s = YUI().use("json-stringify", function (Y) {
This is part of the Yahoo API. This is the script reference:
<script src="http://yui.yahooapis.com/3.11.0/build/yui/yui-min.js"></script>
Y.JSON.stringify takes an object as one parameter and an option paremter which is an array. The purpose of this array is to contain the property names of the object you want to stringify. From other forums I found out this is known as whitelisting.
With all my JSON strings I can store them in HTML5 local storage.
When the page loads I then check to see if my local storage contains data. If true I retrieve my data and serialize from JSON string to a javascript object.
define(['services/datacontext'], function (dataContext) {
var initialized = false;
var agency;
if (localStorage.Agency && localStorage.Offices && localStorage.Contacts) {
var objAgency = new Object(ko.mapping.fromJSON(localStorage.getItem('Agency')));
var objOffices = new Object(ko.mapping.fromJSON(localStorage.getItem('Offices')));
var objContacts = new Object(ko.mapping.fromJSON(localStorage.getItem('Contacts')));
objAgency.offices = objOffices;
objAgency.offices._latestValue[0].contacts = objContacts;
agency = ko.observableArray([ko.mapping.fromJS(objAgency)]);
ko.applyBindings(agency);
initialized = true;
}
else {
agency = ko.observableArray([]);
}
Finally I reconstruct my object to how it was before stringify and map it back to an observableArray and finally bind it.
Hopefully this helps other people using a combination of knockoutJS and complicated objects.
See below for my full code:
define(['services/datacontext'], function (dataContext) {
var initialized = false;
var agency;
if (localStorage.Agency && localStorage.Offices && localStorage.Contacts) {
var objAgency = new Object(ko.mapping.fromJSON(localStorage.getItem('Agency')));
var objOffices = new Object(ko.mapping.fromJSON(localStorage.getItem('Offices')));
var objContacts = new Object(ko.mapping.fromJSON(localStorage.getItem('Contacts')));
objAgency.offices = objOffices;
objAgency.offices._latestValue[0].contacts = objContacts;
agency = ko.observableArray([ko.mapping.fromJS(objAgency)]);
ko.applyBindings(agency);
initialized = true;
}
else {
agency = ko.observableArray([]);
}
var save = function (agency, myStoredValue) {
// Clear Cache because user submitted the form. We don't have to hold onto data anymore.
//amplify.store("Agency", null);
return dataContext.saveChanges(agency);
};
var vm = { // This is my view model, my functions are bound to it.
//These are wired up to my agency view
activate: activate,
agency: agency,
title: 'agency',
refresh: refresh, // call refresh function which calls get Agencies
save: save,
cacheForm: cacheForm
};
return vm;
function activate() {
vm.agency;
if (initialized) {
return;
}
initialized = false;
return refresh();
}
function refresh() {
return dataContext.getAgency(agency);
}
function cacheForm(agency) {
// GET my object from agency vm
var agency = ko.toJS(agency);
var s = YUI().use("json-stringify", function (Y) {
var jsonStrAgency = Y.JSON.stringify(agency, ["activities", "agencyName", "agencyID", "campaignBillings", "category", "declaredBillings", "immediateParent", "numberOfEmployees", "ultimateParent", "uRL"]); // Use an array of acceptable object key names as a whitelist.
var jsonStrOffices, jsonStrContacts;
for (i in agency.offices) {
jsonStrOffices = Y.JSON.stringify(agency.offices, ["address1", "address2", "address3", "address4", "address5", "agencyID", "faxNumber", "officeID", "postCode", "telephoneNumber"]);
for (ii in agency.offices[i].contacts) {
jsonStrContacts = Y.JSON.stringify(agency.offices[i].contacts, ["agencyID", "emailAddress", "firstName", "jobName", "officeID", "personID", "surName", "title"]);
}
}
localStorage.setItem('Agency', jsonStrAgency);
localStorage.setItem('Offices', jsonStrOffices);
localStorage.setItem('Contacts', jsonStrContacts);
});
}
});

Categories

Resources