Hoodie - Update a CouchDB document (Node.js) - node.js

I'm handling charges and customers' subscriptions with Stripe, and I want to use these handlings as a Hoodie plugin.
Payments and customer's registrations and subscriptions appear normally in Stripe Dashboard, but what I want to do is update my _users database in CouchDB, to make sure customer's information are saved somewhere.
What I want to do is updating the stripeCustomerId field in org.couchdb.user:user/bill document, from my _users database which creates when logging with Hoodie. And if it is possible, to create this field if it does not exist.
In hoodie-plugin's document, the update function seems pretty ambiguous to me.
// update a document in db
db.update(type, id, changed_attrs, callback)
I assume that type is the one which is mentioned in CouchDB's document, or the one we specify when we add a document with db.add(type, attrs, callback) for example.
id seems to be the doc id in couchdb. In my case it is org.couchdb.user:user/bill. But I'm not sure that it is this id I'm supposed to pass in my update function.
I assume that changed_attrs is a Javascript object with updated or new attributes in it, but here again I have my doubts.
So I tried this in my worker.js:
function handleCustomersCreate(originDb, task) {
var customer = {
card: task.card
};
if (task.plan) {
customer.plan = task.plan;
}
stripe.customers.create(customer, function(error, response) {
var db = hoodie.database(originDb);
var o = {
id: 'bill',
stripeCustomerId: 'updatedId'
};
hoodie.database('_users').update('user', 'bill', o, function(error) {
console.log('Error when updating');
addPaymentCallback(error, originDb, task);
});
db.add('customers.create', {
id: task.id,
stripeType: 'customers.create',
response: response,
}, function(error) {
addPaymentCallback(error, originDb, task);
});
});
}
And between other messages, I got this error log:
TypeError: Converting circular structure to JSON
And my file is not updated : stripeCustomerId field stays null.
I tried to JSON.stringify my o object, but It doesn't change a thing.
I hope than some of you is better informed than I am on this db.update function.

Finally, I decided to join the Hoodie official IRC channel, and they solved my problem quickly.
Actually user.docs need an extra API, and to update them you have to use hoodie.account instead of hoodie.database(name)
The full syntax is:
hoodie.account.update('user', user.id, changedAttrs, callback)
where user.id is actually the account name set in Hoodie sign-up form, and changedAttrs an actual JS object as I thought.
Kudos to gr2m for the fix; ;)

Related

Hubspot API - list of properties needed (nodejs)

I am integrating the hubspot API to track user interaction with our site. I am creating dynamic lists and I want to filter a user into a certain contact list by which URL they visit.
"filters": [
[
{
"operator": "CONTAINS",
"property": "hs_URL",
"value": `${id}`
},
],
]
I keep getting this error for all my attempts:
{"status":"error","message":"Couldn't find a Property with the given name 'hs_URL'","correlationId":"0723dcee-534b-4f92-9104-509d6885abbe","propertiesErrorCode":"PROPERTY_NOT_FOUND"},
I cannot seem to find a master property list and have tried many string combinations. Anyone familiar with hubspot property lists would be my savior.
Thank you~!
It's been a few months, so you may not need this anymore, but since I landed here while looking for a way to get all the properties from an object type in hubspot using nodejs, this might help others looking for the solution.
The master list of properties can be retrieved with the following API call:
const response = await hubspotClient.crm.properties.coreApi.getAll(objectType, false);
The arguments for getAll() expect:
objectType: a string, i.e "contacts".
archived: a boolean, i.e false. Set this true if you want to get archived properties.
The following code was adapted based on this page from the hubspot API docs:
https://developers.hubspot.com/docs/api/crm/properties
Once you're on the page, you can click on the "Endpoints" Tab to reveal code snippets for multiple environments, including nodejs.
For this example, getProperties(), retrieves all properties for a given object type. I used contacts for the object type, which I believe is where you are storing the url property, but you could use the same function to get properties for other object types such as companies or deals.
It might be worth noting that I mapped the results to return just the property names, which sounds like all you need for your case, but more information is contained in the results if you need it. Just remove this bit to get more information on each property:
.map(prop => prop.name)
const hubspot = require('#hubspot/api-client')
const hubspotClient = new hubspot.Client({ apiKey: "YOUR_API_KEY" })
const getProperties = async (objectType) => {
try {
const response = await hubspotClient.crm.properties.coreApi.getAll(objectType, false);
return response.body.results.map(prop => prop.name);
} catch (e) {
e.message === 'HTTP request failed'
? console.error(JSON.stringify(e.response, null, 2))
: console.error(e);
}
}
Here's an example for running the function to get a list of all property names for contacts.
(async () => {
var properties = await getProperties("contacts");
console.log(JSON.stringify(properties ,null,2));
})();
It took me a bit to find this, so figured I would post here in the hopes it saves time for someone else. This is the first time I've posted a solution, and I'm pretty new to this API and Hubspot in general, so feedback and/or better solutions are welcome. Cheers.

Why is the object value not changing?

I am trying to pull a Post object from my MongoDB. This Post has an author field, which is an ObjectID referencing who authored the blog post. Once I fetch the blog post, I want to fetch the Author's username and replace the ID in the post object before sending it out to as a response to clients.
When running the code, the correct Post shows when I log it, and the correct Username is shown when it is fetched, but I can never modify the object itself.
BlogController.getPost(id, (error, result) => {
if (error) res.send({success:false, message:error})
else {
var post = result
AuthController.getUserByID(post.author, (error, resultNested) => {
if (error) res.send({success:false, message:error})
else {
post.author = resultNested.username
console.log(post)
console.log(resultNested)
res.send({success:true, message:post})
}
})
}
})
I would include the console log outputs to show you the structure of the objects and that the values I am trying to modify xist, but the new StackOverflow UI makes inputting code even more of a pain in the ass than the past. Somehow...
I expect for the post object to have it's authorfield modified, but the author field remains an ObjectID.
I would suggest storing the name of the author in the post model as a start. MongoDB is referred to as a 'Document Based' DB and I therefore like to think of each record in a collection as a standalone 'document'.
I can understand the logic of the relational model you are trying to implement, but since MongoDB stores data in JSON format - one could argue that there is some room for redundancy.
I would recommend...
post{
author : string;
.
.
.
relUser : string;
}
Where the relUser field will be the id of the user that posted the blog entry and we store the author/username in the author field. In this way you simplify the number of calls you need to make, by getting more info per single call.

In Cloud function how can i join from another collection to get data?

I am using Cloud Function to send a notification to mobile device. I have two collection in Firestore clientDetail and clientPersonalDetail. I have clientID same in both of the collection but the date is stored in clientDetail and name is stored in clientPersonal.
Take a look:
ClientDetail -- startDate
-- clientID
.......
ClientPersonalDetail -- name
-- clientID
.........
Here is My full Code:
exports.sendDailyNotifications = functions.https.onRequest( (request, response) => {
var getApplicants = getApplicantList();
console.log('getApplicants', getApplicants);
cors(request, response, () => {
admin
.firestore()
.collection("clientDetails")
//.where("clientID", "==", "wOqkjYYz3t7qQzHJ1kgu")
.get()
.then(querySnapshot => {
const promises = [];
querySnapshot.forEach(doc => {
let clientObject = {};
clientObject.clientID = doc.data().clientID;
clientObject.monthlyInstallment = doc.data().monthlyInstallment;
promises.push(clientObject);
});
return Promise.all(promises);
}) //below code for notification
.then(results => {
response.send(results);
results.forEach(user => {
//sendNotification(user);
});
return "";
})
.catch(error => {
console.log(error);
response.status(500).send(error);
});
});
}
);
Above function is showing an object like this
{clienId:xxxxxxxxx, startDate:23/1/2019}
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID.
What should do ?
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name .
Can Anybody please Help.?
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID. What should do ?
Unfortunately, there is no JOIN clause in Firestore. Queries in Firestore are shallow. This means that they only get items from the collection that the query is run against. There is no way to get documents from two top-level collection in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection.
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name.
So the most simple solution I can think of is to first query the database to get the clientID. Once you have this id, make another database call (inside the callback), so you can get the corresponding name.
Another solution would be to add the name of the user as a new property under ClientDetail so you can query the database only once. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but same rules apply to Cloud Firestore.
Also, when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
The "easier" solution would probably be the duplication of data. This is quite common in NoSQL world.
More precisely you would add in your documents in the ClientDetail collection the value of the client name.
You can use two extra functions in this occasion to have your code clear. One function that will read all the documents form the collection ClientDetail and instead of getting all the fields, will get only the ClientID. Then call the other function, that will be scanning all the documents in collection ClientPersonalDetail and retrieve only the part with the ClientID. Compare if those two match and then do any operations there if they do so.
You can refer to Get started with Cloud Firestore documentation on how to create, add and load documents from Firestore.
Your package,json should look something like this:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"firebase-admin": "^6.5.1"
}
}
I have did a little bit of coding myself and here is my example code in GitHub. By deploying this Function, will scan all the documents form one Collection and compare the ClientID from the documents in the other collection. When it will find a match it will log a message otherwise it will log a message of not matching IDs. You can use the idea of how this function operates and use it in your code.

Cannot delete json element

I have a node js function:
function func() {
USER.find({},function(err, users){
user = users[0];
console.log(user); // {"name":"mike", "age":15, "job":"engineer"}
user.name = "bob"; //{"name":"bob", "age":15, "job":"engineer"}
delete user.name;
console.log(user); // {"name":"mike", "age":15, "job":"engineer"} name still there??
});
}
Here USER is a mongoose data model and find is to query the mongodb. The callback provide an array of user if not err. The user data model looks like
{"name":"mike", "age":15, "job":"engineer"}.
So the callback is invoked and passed in users, I get the first user and trying to delete the "name" from user. The wired part is I can access the value correctly and modify the value. But if I 'delete user.name', this element is not deleted from json object user. Why is that?
As others have said, this is due to mongoose not giving you a plain object, but something enriched with things like save and modifiedPaths.
If you don't plan to save the user object later, you can also ask for lean document (plain js object, no mongoose stuff):
User.findOne({})
.lean()
.exec(function(err, user) {
delete user.name; // works
});
Alternatively, if you just want to fetch the user and pay it forward without some properties, you can also useselect, and maybe even combine it with lean:
User.findOne({})
.lean()
.select('email firstname')
.exec(function(err, user) {
console.log(user.name); // undefined
});
Not the best workaround, but... have you tried setting to undefined?
user.name = undefined;

Mongoose/node.js how to find, populate, do stuff, 'depopulate' and update

I want to make a nice and elegant call to db, but constantly suffer from lack of mongoose experience.
Hope i'm not too annoying, i'm trying to adress Stack Overflow only when my docs-google-Stack-digging skills fails.
What I want to do:
-find a doc in DB
-populate array inside this doc (this array in Schema is an array of Objectids from UserMeta Schema)
-send it throug sockets to some better places
-update found doc with another _id reffering to doc in UserMeta
-save this updates to db & as future reference to var currentroom
Problem occures in last 2 steps, as i can't 'unpopulate' doc, that i already got as response and update-save it further.
For the moment that is how i'm doing this without any population:
Room.findOne({ Roomid: roomid }, function (err, oldRoom) {
client.emit('others', oldRoom);
oldRoom.UsersMeta.push(currentUser._id);
oldRoom.save(function (err, newRoom) {
currentroom = newRoom;
});
})
I can just brute-force-ish copy needed docs through toJSON from parrent UserMeta to this Room doc and just manually maintain both of them. But if there is a way to do this automagically via handy mongoose tools, I would like to take this way. And in the sake of curiosity, of course.
It's a continuation of my previous question Saving reference to a mongoose document, after findOneAndUpdate -
just a remark, you dont really need to go there
Upd: Thing is that I need to run populate() In query with findOne, therefore in response I got oldRoom already with populated _ids
Room.findOne({ Roomid: roomid }).populate('UsersMeta').exec(function (err, oldRoom) {
if (err) {
console.log(err);
}
else if (oldRoom) {
console.log(oldRoom.UsersMeta)
client.emit('others', oldRoom.UsersMeta);
oldRoom.UsersMeta.push(currentUser._id);
oldRoom.save(function (err, newRoom) {
currentroom = newRoom;
console.log(newRoom);
});
}
else { console.log('nothing found') };
})
upd2: so i figured out, that if i push new _id in already populated oldroom and save it, in db it will automagically appear as set of just _id's as it should be. Yet I now confused if i will continue to work with this currentroom reference, as it was already pupulated, how can i safely remove something from populated array without removing populated entry from db completely.
upd3: Ah i just made a mess in my head. For some weird reason i thought that reference to doc saved in variable for each socket client will be always pointing to up-to-date doc in db, and that i will be able to work with this doc through it elluminating need to using find db tools more than once to get this reference... I need to rethink my db logic.
SO
There is a question then. If user connected to Rooms which is a doc from RoomSchema, and a user is a socket user i.e he has a personal scope in which i can store his personal session details. Can i somehow store direct link to this particular Room doc to elluminate need of searching for this room through whole db if user, for example, changes room's name. If i NEED to searh - it seems that it a better practice to save an id of room in which user is, and then just look up in db for room by this id and change it's name, am I right?
Here is the query to get UserMeta with ids only
Room.findOne({ Roomid: roomid },function (err, oldRoom) {
});

Resources