Map becomes empty after sending it through express params - node.js

I am using express and Discord.js.
website.js
const express = require("express");
const app = express();
//...
ap.get("/testpage/:guildID", checkAuth, (req, res) => {
const guild = client.guilds.get(req.params.guildID); //get the guild object which holds further object maps
//console.log(guild) output is expected with this console log
//I made the guildRoles since I thought maybe I had to send the roles map instead of the guild object, but it does not work either
const guildRoles = guild.roles;
//console.log(guildRoles) has expected output
renderTemplate(res, req, "configure.ejs", {guild, guildRoles});
};
configure.ejs
//...
<script>
//...
function testFunction() {
let guildRolesVar = <%- guildRoles %>;
console.log(guildRolesVar);
//this outputs an empty object, {}, when it should return {...} ... with data in it
//same with <%- guild %>, however, it does return data, however, object maps are empty (same as above), but things that are not maps
};
</script>
I expected that the object map data would pass over, but it doesn't, and I have no idea why.
A guild structure is:
Guild {
roles:
Collection [Map] {
'1111111111111110' => Role {
guild: [Circular],
id: '1111111111111110',
name: '#testRole' },
'1837219387129378' => Role {
guild: [Circular],
id: '1837219387129378',
name: '#anotherRole' } },
name: 'the guild name',
id: '12103981203980',
}
As you can see, the guild object has further object maps. When I console.log the guild object in my app.get(...), it outputs the full data with nothing missing. However, when I pass the object through, the roles object is empty ({}).
Why is this happening and how can I get the full data?

This is most likely happening because express will JSON stringify() them and because discord.js objects have circular references the will be {}.
A possible solution would be to use d.js master/v12, such allows JSON.stringify() on its objects.

Related

Redis-om search cannot find key after updating

I have some strange behaviour happening with my Redis-OM application, I understand that this is still a very BETA version of the software but I just wanted to make sure I wasn't doing something dumb (which I might be)
So I am setting up an application to keep a playlist of video ID's within a Room that I store temporarily in a Redis Cloud Database.
I have a function that creates a Room, one that fetches the Room details (everything currently in the room) and one that adds a new video to the Playlist within that room. (see below) - NOTE: the data variable within createRoom(data) is just a string of the Room ID
class Room extends Entity {}
let schema = new Schema(
Room,
{
code: { type: 'string' },
playlist: {
type: 'array',
videos: {
type: 'object',
},
},
},
{
dataStructure: 'JSON',
}
);
export async function createRoom(data) {
await connect();
const repository = new Repository(schema, client);
const room = repository.createEntity(data);
const id = await repository.save(room);
await client.execute(['EXPIRE', `Room:${id}`, 43200]);
return id;
}
export async function getRoom(code) {
await connect();
const repository = new Repository(schema, client);
const room = await repository
.search()
.where('code')
.equals(code)
.returnFirst();
return room;
}
export async function addVideoToRoom(code, videoDetails) {
const room = await getRoom(code);
await room.playlist.push(videoDetails);
await connect();
const repository = new Repository(schema, client);
const id = await repository.save(room);
return id;
}
The primary issue that I'm having is adding a second video to the playlist. What happens is
Create Room - adds a new room to the DB
Search for video
Click to add video to DB (video is successfully added)
Click to add second video to DB (fails because getRoom(code)fails - returns null)
This was working yesterday, however I'm not sure why it no longer works.
If anyone has any idea's why that might be please let me know, I have a feeling it may be how I'm handling clients or indexes with Redis so I have popped my functions for those below too.
const client = new Client();
async function connect() {
if (!client.isOpen()) {
await client.open(process.env.REDIS_URL);
}
}
export async function createIndex() {
await connect();
const repository = new Repository(schema, client);
await repository.dropIndex();
await repository.createIndex();
}
Thanks very much programmers of Stack - If I'm being super dumb I do apologise.
Redis OM for Node.js supports neither nested objects nor a type of 'object' within the Schema. Valid types are 'string', 'number', 'boolean', and 'array'. Arrays are only arrays of strings. The rest are self-explanatory.
If you want to have a Room that has multiple Videos, you need to define a Room entity, perhaps with a playlist that is defined as an array not of objects, but of Video ids.
Details on this can be found in the README.

Possible to enrich data in a collection?

As the title suggests, I'm wondering if it's possible to enrich data in a collection or if there is another recommended best practice to achieve the same result.
Consider the following example:
const user = await client.user(userId).get();
const object = await client.collections.add({
'Message',
messageId,
message: {
contents: 'Hello world!',
user // this fails with the error "Converting circular structure to JSON"
}
});
await feed.addActivity({
actor,
verb,
object
});
// I want the objects enriched in this request to also contain the enriched user object
await feed.get({ enrich: true });

mongoose .find() scope: How can I call found data out of the query?

I'm trying to call the the founded variable outside of the array but it return an empty array instead. Can someone explain why the console.log inside the function work but not outside of the function.
// Video Schema
let mongoose = require("mongoose");
let Schema = mongoose.Schema;
var videoSchema = new Schema ({
title: String,
videoURL: String,
author: String,
time: String,
viewcount: Number,
categories: [{
type: Schema.Types.ObjectId,
ref: "Category"
}],
description: String,
})
let Video = mongoose.model("Video", videoSchema);
module.exports = {
videoSchema: videoSchema,
Video: Video
}
app.js
let Video = require(__dirname + "/dbs/Video.js").Video;
app.get("/", function(req,res) {
let videos = []
Video.find(function(err, foundVideo) {
if (!err) {
videos = foundVideo.slice(0)
console.log(videos) // this return me with an object array [{obj1}, {obj2}]
} else {
return err
}
})
console.log(videos) // This return an empty array []
}
How can I store the foundVideos array in the videos variable so that can call the variable global?
When you do this operation:
Video.find(function(err, data) {
// something
console.log("one")
})
// nothing
console.log("two")
The function between parentheses is the callback of the find() operation. This means it will be called back when the find execution ends, and it may make use of err or data parameters inside its scope. It will execute the console.log("one").
This way to "wait" for results is due to the asynchronous nature of js.
Instead, the code outside the callback method will be triggered just after the find is called, and it will not wait for the find operation to be finished. Therefore, in this example, two will be printed before one.
In your example, the variable videos you try to print outside the callback method console.log(videos) is empty as is printed before the videos are actually there.
You should write all your callback code in the !err case:
if (!err) {
videos = foundVideo.slice(0)
console.log(videos) // this return me with an object array [{obj1}, {obj2}]
}
Update
As you noticed, a coder is forced to implement the code in the callback method. Then, other methods or requests depending on the data from first request tend to make it complex to structure the code.
const processVideos = function(data) {
if(!data) {
// do something when data is empty
}
// process videos here
}
const notifyError = function(err) {
if(!err)
return
// do something with the error here
}
Video.find(function(err, data) {
processVideos(data)
notifyError(err)
})
Use always your "genius" and programming patterns to avoid complex code, large methods and unreadable sections.

Sending a DM through a command, to a specific person from a database, discord.js-commando

So, I having an issue of sending a DM to a specific person without an author tag, and without a mention of the person. I tried duplicating the mention array:
/*jshint esversion: 6*/
const commando = require('discord.js-commando');
class Msgowner extends commando.Command {
constructor(client) {
super(client, {
name: 'msgowner',
group: 'info',
memberName: 'msgowner',
description: 'Gives rules on mock or legit duels.',
examples: ['ladderrules type'],
});
}
async run(message) {
var user = {
id: '12345',
username: 'Bloodmorphed',
discriminator: '12345',
avatar: 'd510ca3d384a25b55d5ce7f4c259b2d0',
bot: false,
lastMessageID: null,
lastMessage: null,
};
user.send('Test');
}
}
module.exports = Msgowner;
There is a reason why I need to send DMs this way, but I can't seem to figure out how. (The error it gives now is a unknown function). Also replacing id and discriminator with generic numbers, but they are correct in my code.
Try something like this - get the member you're looking for using message.channel.members.find() method:
async run(message) {
// get Collection of members in channel
let members = message.channel.members;
// find specific member in collection - enter user's id in place of '<id number>'
let guildMember = members.find('id', '<id number>');
// send Direct Message to member
guildMember.send('test message');
}
Edit: It looks like it's also possible to find users outside the current channel by doing something like this:
async run(message) {
// get client from message's channel
let client = message.channel.client;
// fetch user via given user id
let user = client.fetchUser('<id number>')
.then(user => {
// once promise returns with user, send user a DM
user.send('Test message');
});
}
Okay, found my answer:
async run(message) {
var user = {
id: '12345,
username: 'Bloodmorphed',
discriminator: '12345',
avatar: 'd510ca3d384a25b55d5ce7f4c259b2d0',
bot: false,
lastMessageID: null,
lastMessage: null,
};
console.log(user);
message.member.user.send('Test');
}
}
module.exports = Msgowner;
EDIT: This was NOT the answer, still looking for one.

Sails/Waterline: How to retrieve relations inside a relation?

I need to retrieve an object and also get the relations and nested relations.
So, I have the three models below:
User model:
module.exports = {
attributes: {
name: {
type: 'string'
},
pets: {
collection: 'pet',
via: 'owner',
}
}
Pet model:
module.exports = {
attributes: {
name: {
type: 'string'
},
owner: {
model: 'user'
},
vaccines: {
collection: 'vaccine',
via: 'pet',
}
}
Vaccine model:
module.exports = {
attributes: {
name: {
type: 'string'
},
pet: {
model: 'pet'
}
}
Calling User.findOne(name: 'everton').populate('pets').exec(....) I get the user and associated Pets. How can I also get the associated vaccines with each pet? I didn't find references about this in the official documentation.
I've ran into this issue as well, and as far as I know, nested association queries are not built into sails yet (as of this post).
You can use promises to handle the nested population for you, but this can get rather hairy if you are populating many levels.
Something like:
User.findOne(name: 'everton')
.populate('pets')
.then(function(user) {
user.pets.forEach(function (pet) {
//load pet's vaccines
});
});
This has been a widely discussed topic on sails.js and there's actually an open pull request that adds the majority of this feature. Check out https://github.com/balderdashy/waterline/pull/1052
While the answer of Kevin Le is correct it can get a little messy, because you're executing async functions inside a loop. Of course it works, but let's say you want to return the user with all pets and vaccines once it's finished - how do you do that?
There are several ways to solve this problem. One is to use the async library which offers a bunch of util functions to work with async code. The library is already included in sails and you can use it globally by default.
User.findOneByName('TestUser')
.populate('pets')
.then(function (user) {
var pets = user.pets;
// async.each() will perform a for each loop and execute
// a fallback after the last iteration is finished
async.each(pets, function (pet, cb) {
Vaccine.find({pet: pet.id})
.then(function(vaccines){
// I didn't find a way to reuse the attribute name
pet.connectedVaccines = vaccines;
cb();
})
}, function(){
// this callback will be executed once all vaccines are received
return res.json(user);
});
});
There is an alternative approach solving this issue with bluebird promises, which are also part of sails. It's probably more performant than the previous one, because it fetches all vaccines with just one database request. On the other hand it's harder to read...
User.findOneByName('TestUser')
.populate('pets')
.then(function (user) {
var pets = user.pets,
petsIds = [];
// to avoid looping over the async function
// all pet ids get collected...
pets.forEach(function(pet){
petsIds.push(pet.id);
});
// ... to get all vaccines with one db call
var vaccines = Vaccine.find({pet: petsIds})
.then(function(vaccines){
return vaccines;
});
// with bluebird this array...
return [user, vaccines];
})
//... will be passed here as soon as the vaccines are finished loading
.spread(function(user, vaccines){
// for the same output as before the vaccines get attached to
// the according pet object
user.pets.forEach(function(pet){
// as seen above the attribute name can't get used
// to store the data
pet.connectedVaccines = vaccines.filter(function(vaccine){
return vaccine.pet == pet.id;
});
});
// then the user with all nested data can get returned
return res.json(user);
});

Resources