Google Calendar API (Saving events in MongoDB, Express JS) - mongodb

I can't figure out how to save fetched events from Calendar API. I was able to print out array of events in console. I would require save multiple events at once and have verification if they already exist in database with unique id.
Here's my event.js scheme in express js.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const EventSchema = new Schema({
id: {
type: String,
required: false,
unique:true
},
start: {
type: String
},
end: {
type: String
},
status: {
type: String
},
creator: {
type: Array
},
description: {
type: String
}
});
module.exports = Event = mongoose.model('events', EventSchema);
Here's my event.js router in express js.
router.post("/google/get", async (req, res, next) => {
const {
google
} = require('googleapis')
const {
addWeeks
} = require('date-fns')
const {
OAuth2
} = google.auth
const oAuth2Client = new OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET
)
oAuth2Client.setCredentials({
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
})
const calendar = google.calendar({
version: 'v3',
auth: oAuth2Client
})
calendar.events.list({
calendarId: 'MY CALENDAR ID',
timeMin: new Date().toISOString(),
timeMax: addWeeks(new Date(), 1).toISOString(),
singleEvents: true,
orderBy: 'startTime',
},
function (err, response) {
if (err) {
console.log("The API returned an error: " + err)
return
}
var events = response.data.items
events.forEach(function (event) {
var start = event.start.dateTime || event.start.date
console.log("%s - %s", start, event.summary)
})
}
)

In Mongoose, in order to save something to a database, all you need to do is to instantiate the model that you created. Your event schema exports Event as a model that you can then treat as a regular object. So you would do something along the lines of:
let currentEvent = new Event({id, start, end, status, creator, description});
currentEvent.save();
Once that is done, it should be stored in your MongoDB. I assume that as the code for this is not visible it is already set up and working. You can just run the above inside of your for loop with some minor tweaks to grab each value correctly and it should sort your issue out!
As for your unique ID and making sure that it doesn't already exist in your database, you can use the same model to find values by checking the id against your database and seeing if it exists. As follows:
Event.findById(id, (err, event) => {
if(event == null) {
let currentEvent = new Event({id, start, end, status, creator, description});
currentEvent.save();
} else {
alert("Error, this event already exists")
}
});
I believe something like this should work, however I might have it wrong with how to check if the event exists, I can't remember if it returns null or something different, so just console log the value of event and check to see what it returns if there isn't an event that exists with that ID, and just re-run your if statement with that instead.

Related

How to populate an array of ObjectIds in mongoose?

I have a User model with a schema that I would like to validate an array of multiple friends by their id's. The portion of the schema that is supposed to do this is:
friends: {
type: [mongoose.SchemaTypes.ObjectId],
},
Then, when I try to add a friend with an id value and populate it inside the API endpoint, it adds the id to the database, but does not populate it. Here is the code:
if (method === "POST") {
const userId = getIdFromCookie(req);
try {
const newFriend = {
friends: req.body.friend
};
const updatedUser = await User.findByIdAndUpdate(userId, newFriend, {new: true})
const popUser = await User.findById(userId).populate("friends")
res.status(200).json({success: true, data: updatedUser});
} catch (error) {
res.status(400).json({success: false});
}
} else {
res.status(400).json({error: "This endpoint only supports method 'POST'"})
}
I want to know how I can add a friend's id to the database, whilst simultaneously populating it in the same endpoint.
The user schema is missing the ref field.
Example from the docs:
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
Without the ref, Mongoose doesn't know where to lookup the ObjectId.

Can't create multiple objects with MongoDB + Node

I am trying to create multiple objects with mongodb, mongoose and express (by using Insomnia). I have managed to create the first object, but when I try to create the following one it gives me the following error:
{
"success": false,
"message": {
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000
}
}
I tried to solve it by including different data in the object (although I didn't specify uniqueness in the properties), but it will throw the 11000 (duplicate key) error anyways.
The log of req.body returns { inmovilizadoInmaterial: 12, inmovilizadoMaterial: 13 } (as the data included for the registration are those values)
Here's the model
const mongoose = require("mongoose");
const { Schema } = mongoose;
const balanceSchema = new Schema(
{
inmovilizadoInmaterial: { type: Number, default: 0 },
inmovilizadoMaterial: { type: Number, default: 0 },
}
);
module.exports = mongoose.model("Balance", balanceSchema);
Here's the router that connects the model and the controller the object:
const express = require('express');
const router = express.Router();
const balanceController = require('../controllers/balance.controller');
router.post('/create', balanceController.create);
module.exports = router;
And finally here's the controller that has the function that creates the object.
const balanceController = {};
const Balance = require('../models/Balance')
balanceController.create = async(req,res)=>{
const balance = new Balance(req.body);
balance.save();
}
module.exports = balanceController;
I know it must be a very simple mistake but I'm very new to the technology.
Thank you very much in advance!
Can you try to change your controller like this:
balanceController.create = async(req,res)=>{
try {
console.log('Req body:', req.body);
const balance = await Balance.create(req.body);
res.status(200).json({success: true});
} catch(error) {
console.log('ERROR: ', error);
res.status(400).json({success: false, error});
}
}
Note that req.body will be logged so you can check whether your controller receiving correct data (maybe you are sending empty req.body from the frontend). Also error will be logged in. If this does not work, add both output of console.log(req.body) and console.log(error) to your question.

Using the $inc function across the MongoDB documents

I am currently working on a moderation system for my Discord bot and came across an unexpected issue. I've been using the $inc function to increase the values for a single document, though I have sadly not achieved to use the $inc function across multiple different documents, meaning I would like to increase ($inc) the value of the new document according to the numbers of the previous document.
Example: Cases
Current code:
async run(client, message, args, Discord) {
const targetMention = message.mentions.users.first()
const userid = args[0]
const targetId = client.users.cache.find(user => user.id === userid)
const username = targetMention.tag
if(targetMention){
args.shift()
const userId = targetMention.id
const WarnedBy = message.author.tag
const reason = args.join(' ')
if(!reason) {
message.delete()
message.reply('You must state the reason behind the warning you are attempting to apply.').then(message => {
message.delete({ timeout: 6000})
});
return;
}
const warningApplied = new Discord.MessageEmbed()
.setColor('#ffd200')
.setDescription(`A warning has been applied to ${targetMention.tag} :shield:`)
let reply = await message.reply(warningApplied)
let replyID = reply.id
message.reply(replyID)
const warning = {
UserId: userId,
WarnedBy: WarnedBy,
Timestamp: new Date().getTime(),
Reason: reason,
}
await database().then(async database => {
try{
await warnsSchema.findOneAndUpdate({
Username: username,
MessageID: replyID
}, {
$inc: {
Case: 1
},
WarnedBy: WarnedBy,
$push: {
warning: warning
}
}, {
upsert: true
})
} finally {
database.connection.close()
}
})
}
if(targetId){
args.shift()
const userId = message.member.id
const WarnedBy = message.author.tag
const reason = args.join(' ')
if(!reason) {
message.delete()
message.reply('You must state the reason behind the warning you are attempting to apply.').then(message => {
message.delete({ timeout: 6000})
});
return;
}
const warning = {
userId: userId,
WarnedBy: WarnedBy,
timestamp: new Date().getTime(),
reason: reason
}
await database().then(async database => {
try{
await warnsSchema.findOneAndUpdate({
userId,
}, {
$inc: {
Case: 1
},
WarnedBy: WarnedBy,
$push: {
warning: warning
}
}, {
upsert: true
})
} finally {
database.connection.close()
}
const warningApplied = new Discord.MessageEmbed()
.setColor('#ffd200')
.setDescription(`A warning has been applied to ${targetId.tag} :shield:`)
message.reply(warningApplied)
message.delete();
})
}
}
Schema attached to the Code:
const warnsSchema = database.Schema({
Username: {
type: String,
required: true
},
MessageID: {
type: String,
required: true
},
Case: {
type: Number,
required: true
},
warning: {
type: [Object],
required: true
}
})
module.exports = database.model('punishments', warnsSchema)
Answer to my own question. For all of those who are attempting to do exactly the same as me, there is an easier way to get this to properly work. The $inc (increase) function will not work as the main property of a document. An easier way to implement this into your database would be by creating a .json file within your Discord bot files and adding a line such as the following:
{
"Number": 0
}
After that, you'd want to "npm i fs" in order to read directories in live time.
You can proceed to add a function to either increase or decrease the value of the "Number".
You must make sure to import the variable to your current coding document by typing:
const {Number} = require('./config.json')
config.json can be named in any way, it just serves as an example.
Now you'd be able to console.log(Number) in order to make sure the number is what you expected it to be, as well as you can now increase it by typing Number+=[amount]
Hope it was helpful.

How to trigger a mongoose updatedAt

I need to update my model when the data has changed. Sadly, this seems to not work.
const mongoose = require('mongoose');
const moment = require('moment');
const Schema = mongoose.Schema;
const SomeSchema = new Schema({
query: String,
data: Object,
userId: String,
// Date.now() does work. I'm working with existing code.
createdAt: { type: Date, default: moment().format() },
updatedAt: { type: Date, default: moment().format() }
});
// Not sure why I need this 😕
// Have also used 'save' instead of 'updateOne'
SomeSchema.pre('updateOne', function(next) {
this.updated = Date.now();
// this.updatedAt = Date.now() does not work either.
return next();
});
mongoose.model('someModel', SomeSchema);
Actual usage:
const mongoose = require('mongoose');
const Model = mongoose.model('someModel');
// Ideally, I wanted something like "Model.findOrCreate" but... cant see that
const obj = {..};
// Im happy nothing will error here with this.
// Would love to use "findOrCreate" instead.
const data = await Model.updateOne({ ...obj });
// I hate this so much... by hey.
if (data.n === 0) {
// Create
Model.create({...obj}).save
}
All Im saying is, if the data is there, update it and if not, create it. But my updatedAt key is not updating at all. It stays the same as the createdAt. Based on the docs, I dont see how I'd use $set here.
The main thing is to trigger updatedAt whenever the data was found.
Script example using MongoDB Atlas Triggers:
exports = function(changeEvent) {
const { updateDescription, fullDocument, ns } = changeEvent;
const updatedFields = Object.keys(updateDescription.updatedFields);
// For debug
//console.log('changeEvent', JSON.stringify(changeEvent));
const isUpdated = updatedFields.some(field =>
field.match(/updatedAt/)
);
const updatedAt = fullDocument.updatedAt;
// Prevent update again after the update
if (!isUpdated || !updatedAt) {
const { _id } = fullDocument;
console.log(`Triggered! ${ns.db}:${ns.coll}:${_id}, isUpdated:${isUpdated ? 'true' : 'false'}, updatedAt:${updatedAt}`);
const mongodb = context.services.get(ns.db /* Cluster Name, like the DB name */);
const collection = mongodb.db(ns.db).collection(ns.coll);
collection.updateOne({
_id: _id,
}, {
$set: {
updatedAt: new Date(),
}
});
}
};
Looks like there is a typo in the Pre middleware function. Based on our Schema the key name is updatedAt, but in the function, it's mentioned as updated.

How to query nested data in mongoose model

I am attempting to build a Vue.js app with a MEVN stack backend and Vuex. I am configuring my Vuex action handler with a GET request that prompts a corresponding Express GET route to query data nested in Mongoose.
A username is passed into the handler as an argument and appended to the GET request URL as a parameter:
actions: {
loadPosts: async (context, username) => {
console.log(username)
let uri = `http://localhost:4000/posts/currentuser?username=${username}`;
const response = await axios.get(uri)
context.commit('setPosts', response.data)
}
}
The corresponding Express route queries activeUser.name, which represents the nested data in the Mongoose Model:
postRoutes.route('/currentuser').get(function (req, res) {
let params = {},
username = req.query.activeUser.name
if (username) {
params.username = username
}
Post.find(params, function(err, posts){
if(err){
res.json(err);
}
else {
res.json(posts);
}
});
});
Below is my Mongoose model, with activeUser.name representing the nested data queried by the Express route:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Post = new Schema({
title: {
type: String
},
body: {
type: String,
},
activeUser: {
name: {
type: String
}
}
},{
collection: 'posts'
});
module.exports = mongoose.model('Post', Post);
Even with this setup, the GET route does not appear to send a response back to the action handler. I thought adding username = req.query.activeUser.name in the express route would be the right method for querying the nested data in Mongoose, but apparently not. Any recommendations on how to configure the above Express route in order to query the nested data in the Mongoose model? Thanks!
name is inside activeuser so you need to construct params object variable like this:
postRoutes.route("/currentuser").get(function(req, res) {
let params = {
activeUser: {}
};
let username = req.query.activeUserName;
if (username) {
params.activeUser.name = username;
}
Post.find(params, function(err, posts) {
if (err) {
res.json(err);
} else {
res.json(posts);
}
});
});
Note that I also used activeUserName as query param like this: /currentuser?activeUserName=JS_is_awesome18