I'm trying to create a new object (Group) containing selected objectId's (User). I break my head since days now but I don't find the right way to do it.
The group model looks like this:
import mongoose from 'mongoose';
const groupSchema = new mongoose.Schema(
{
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
title: { type: String },
members: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
],
},
{
timestamps: true,
}
);
const Group =
mongoose.models.Group || mongoose.model('Group', groupSchema);
export default Group;
and here is the user model:
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema(
{
name: { type: String },
},
{
timestamps: true,
}
);
const User =
mongoose.models.User || mongoose.model('User', userSchema);
export default User;
So I want users give the possibility to create groups, select existing users from a list and add them into members array.
I understand how to create a group and assign users after by findAndUpdate and query them with populate, but I realy don't get it how to add users ObjectId's into array before creating the group. Would someone give me an advice how to do the post operation for this case?
Here is my current post operation, which of course doesn't work.
import nc from 'next-connect';
import db from '../../../utils/db';
const handler = nc();
//create group
handler.post(async (req, res) => {
await db.connect();
const user = await User.findById(req.params.id);
const newGroup = new Group({
creator: req.user._id,
title: req.body.title,
members: [{
item: user,
}],
});
const group = await newGroup.save();
await db.disconnect();
res.send({ message: 'Group created', group });
});
export default handler;
Thanks for helping me out!!
Your models are looking good and as you defined the
members: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
],
you can just save the objectIds as an array and at the time of query you can assign the populate method which will automatically populate users according to the objectId.
So in order to save the group members try updating your save method like this
handler.post(async (req, res) => {
await db.connect();
// you can remove this query
// const user = await User.findById(req.params.id);
const newGroup = new Group({
creator: req.user._id,
title: req.body.title,
// array of objectIds (user ids)
members: [req.params.id],
});
const group = await newGroup.save();
await db.disconnect();
res.send({ message: 'Group created', group });
});
And in order to get the group members you can add the populate method at the time of querying groups
Group.find({}).populate('members') // the ref field name in Group model
So I have been trying to figure out how to check if the mentioned user has the muted role before attempting to add it, and if they do, say they are already muted, and I can't figure it out. Here is my code for my mute command, any help is appreciated.
const { Command } = require('discord.js-commando');
const { MessageEmbed } = require('discord.js');
module.exports = class MuteCommand extends Command {
constructor(client) {
super(client, {
name: 'mute',
aliases: ['mute-user'],
memberName: 'mute',
group: 'guild',
description:
'Mutes a tagged user (if you have already created a Muted role)',
guildOnly: true,
userPermissions: ['MANAGE_ROLES'],
clientPermissions: ['MANAGE_ROLES'],
args: [
{
key: 'userToMute',
prompt: 'Please mention the member that you want to mute them.',
type: 'member'
},
{
key: 'reason',
prompt: 'Why do you want to mute this user?',
type: 'string',
default: message =>
`${message.author.tag} Requested, no reason was given`
}
]
});
}
run(message, { userToMute, reason }) {
const mutedRole = message.guild.roles.cache.find(
role => role.name === 'Muted'
);
if (!mutedRole)
return message.channel.send(
':x: No "Muted" role found, create one and try again.'
);
const user = userToMute;
if (!user)
return message.channel.send(':x: Please try again with a valid user.');
user.roles
.add(mutedRole)
.then(() => {
const muteEmbed = new MessageEmbed()
.addField('Muted:', user)
.addField('Reason', reason)
.setColor('#420626');
message.channel.send(muteEmbed);
})
.catch(err => {
message.reply(
':x: Something went wrong when trying to mute this user.'
);
return console.error(err);
});
}
};
To see if a mentioned user has a role you can do:
member = message.mentions.first();
if (member._roles.includes('<role ID>') {
//code
}
and obviously, replace <role ID> with the role id of the muted role.
This works because members objects have a _roles array that contains all the IDs of the roles they have.
I want to change permissions in a channel for everyone. This is my current code:
module.exports = {
name: "lockdown", // this would be changed to "remove"
run: async (client, message, args) => {
message.guild.channels.cache.forEach(r => {
message.guild.members.cache.forEach(d => {
r.overwritePermissions([
{
id: d.user.id,
deny: ['SEND_MESSAGES']
}]);
})
})
}
}
This does not work, it changes the permissions for no one and does not do anything. Why?
Help would be appreciated!
You can do that without using forEach statement on the guild members. All you have to do is the following:
module.exports = {
name: "lockdown",
run: (client, message, args) => {
message.guild.channels.cache.forEach(r => {
r.overwritePermissions([
{
id: message.guild.roles.everyone.id,
deny: ['SEND_MESSAGES']
}]);
})
}
}
I am writing this on mobile, so it is very hard but that should work! Read more about the #everyone role here: https://discord.js.org/#/docs/main/stable/class/RoleManager?scrollTo=everyone
You can simply declare the permissions for the #everyone role (which obviously every user has), by setting the id of the role/user you want to update as a Guild object's id property:
module.exports = {
name: "lockdown",
run: async (client, message, args) => {
message.guild.channels.cache.forEach(r => {
r.overwritePermissions([
{
id: message.guild.id,
deny: ['SEND_MESSAGES']
}]);
})
})
}
}
I am new to Mongodb and nodejs. My questions is with respect to performing an post operation on collection in mongodb that stores ObjectId references to another collection.
Exact Scenario being:
I have set of registered users in the Users document. Similarly, I have set of roles available in the Roles document.
Now, what I am trying to do is post the user with the roles from the Roles document. A user could have multiple roles and hence I want to store all the ObjectId references to the Roles in Users document.
I have the following definition for the Users and Roles document.
var roleSchema = new mongoose.Schema({
roleId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Roles'
}
});
const userSchema = new mongoose.Schema({
Username: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
FirstName: {
type: String
},
LastName: {
type: String
},
Email: {
type: String,
required: true,
minlength: 5,
maxlength: 255
},
Password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
Roles: [roleSchema],
Active: {type: Boolean, default: true},
SuperUser: {type: Boolean, default: false}
},{
timestamps: true
});
const rolesSchema = new mongoose.Schema({
RoleName: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
Description: {
type: String
},
Active: {type: Boolean, default: true}
}, {
timestamps: true
});
router.post('/', [auth, admin], async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ Username: req.body.Username });
if (user) return res.status(400).send('User with username: ', req.body.Username, 'already exists. Please try with any other username');
// Begin: Code added to populate Roles in Users document. Look up the roles information in req.body payload
let rolesArray = {};
if(req.body.Roles !== null) {
req.body.Roles.forEach(element => {
objectId = mongoose.Types.ObjectId(element);
const roleInfo = Roles.findById(objectId);
if (!roleInfo) return res.status(400).send('Invalid Role in Input.');
});
}
console.log('Roles : ', rolesArray);
user = new User(_.pick(req.body, ['Username', 'FirstName', 'LastName' ,'Email', 'Password', 'Active','SuperUser', 'Roles']));
rolesArray.forEach(role => {
console.log('Role in user : ', role);
user.Roles.push = role;
});
// End: Code added to populate Roles in Users document. Look up the roles information in req.body payload
const salt = await bcrypt.genSalt(10);
user.Password = await bcrypt.hash(user.Password, salt);
await user.save();
const token = user.generateAuthToken();
res.header('x-auth-token', token).send(_.pick(user, ['_id', 'FirstName', 'LastName' ,'Email', 'Roles']));
});
I have attached the code for the POST operation but even that is not working.
I can work on the PUT operation if the POST operation is successful.
Despite all my trials, I am not able to understand how to loop through the req.body.Roles to set the ObjectIds in the Users document.
Also, while performing the GET operation, I would like to have the Role Name as well as the RoleID retrieved in response when I query the Users document.
Thanks.
I have a table that is fetching 2 items on the initial page load. This correctly returns the 2 rows. When I check the Request Payload I see the following information:
{"query":"query CampaignQuery {
viewer {
id,
...F0
}
}
fragment F0 on User {
_campaigns3iwcB5:campaigns(first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{}}
I then have a button that triggers a function and sets a variable to return additional items via this.props.relay.setVariables.
I then get a 3 retries error and the following error:
[{message: "Cannot query field "node" on type "Query".", locations: [{line: 2, column: 3}]}]
when I check the Request Payload I notice that it is querying "node" instead of "viewer" like it did previously.
{"query":"query Listdata_ViewerRelayQL($id_0:ID!) {
node(id:$id_0) {
...F0
}
}
fragment F0 on User {
_campaignsgsWJ4:campaigns(after:\"CkkKFwoEbmFtZRIPGg1DYW1wYWlnbiBGb3VyEipqEXN+cmVhc29uaW5nLXVzLTAxchULEghDYW1wYWlnbhiAgICA3pCBCgwYACAA\",first:2) {
edges {
node {
id,
account_id,
start_time
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}","variables":{"id_0":"VXNlcjo="}}
This is my schema.js file
/* #flow */
/* eslint-disable no-unused-consts, no-use-before-define */
import {
GraphQLBoolean,
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
// GraphQLEnumType,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
// const types = require('graphql').type;
// const GraphQLEnumType = types.GraphQLEnumType;
import {
connectionArgs,
connectionDefinitions,
connectionFromArray,
fromGlobalId,
globalIdField,
mutationWithClientMutationId,
nodeDefinitions
} from 'graphql-relay';
import {
User,
Feature,
getUser,
getFeature,
getFeatures,
getEventStream,
Campaign,
getCampaigns,
resolveCampaigns,
campaignById,
} from './database';
// Import loader DataLoader
import Loader from './loader';
/**
* We get the node interface and field from the Relay library.
*
* The first method defines the way we resolve an ID to its object.
* The second defines the way we resolve an object to its GraphQL type.
*/
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
} else if (type === 'Feature') {
return getFeature(id);
} else if (type === 'Campaign') {
return campaignById(id);
} else {
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
} else if (obj instanceof Feature) {
return featureType;
} else if (obj instanceof Campaign) {
return campaignType;
} else {
return null;
}
}
);
/**
* Define your own types here
*/
const campaignType = new GraphQLObjectType({
name: 'Campaign',
description: 'A campaign',
fields: () => ({
id: globalIdField('Campaign'),
account_id: {
type: GraphQLString,
description: 'ID of the ad account that owns this campaign',
},
adlabels: {
type: GraphQLString,
description: 'Ad Labels associated with this campaign',
},
buying_type: {
type: GraphQLString,
description: 'Buying type, possible values are: AUCTION: default, RESERVED: for reach and frequency ads',
},
can_use_spend_cap: {
type: GraphQLBoolean,
description: 'Whether the campaign can set the spend cap',
},
configured_status: {
type: GraphQLString, // GraphQLEnumType,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED}. If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. Prefer using \'status\' instead of this.',
},
created_time: {
type: GraphQLID, // this should be a datetime
description: 'Created Time',
},
effective_status: {
type: GraphQLString, // GraphQLEnumType,
description: 'The effective status of this campaign. {ACTIVE, PAUSED, DELETED, PENDING_REVIEW, DISAPPROVED, PREAPPROVED, PENDING_BILLING_INFO, CAMPAIGN_PAUSED, ARCHIVED, ADSET_PAUSED}',
},
name: {
type: GraphQLString,
description: 'Campaign\'s name',
},
objective: {
type: GraphQLString,
description: 'Campaign\'s objective',
},
recommendations: {
type: GraphQLString, // GraphQLList,
description: 'If there are recommendations for this campaign, this field includes them. Otherwise, this field will be null.',
},
spend_cap: {
type: GraphQLFloat,
description: 'A spend cap for the campaign, such that it will not spend more than this cap. Expressed as integer value of the subunit in your currency.',
},
start_time: {
type: GraphQLID, // this should be a datetime
description: 'Start Time',
},
status: {
type: GraphQLString,
description: '{ACTIVE, PAUSED, DELETED, ARCHIVED} If this status is PAUSED, all its active ad sets and ads will be paused and have an effective status CAMPAIGN_PAUSED. The field returns the same value as \'configured_status\', and is the suggested one to use.',
},
stop_time: {
type: GraphQLID, // this should be a datetime
description: 'Stop Time',
},
updated_time: {
type: GraphQLID, // this should be a datetime
description: 'Updated Time',
},
}),
interfaces: [nodeInterface],
});
const userType = new GraphQLObjectType({
name: 'User',
description: 'A person who uses our app',
fields: () => ({
id: globalIdField('User'),
// advertising campaigns
campaigns: {
type: campaignConnection,
description: 'list of campaigns',
args: connectionArgs,
resolve: (viewer, args, source, info) => {
return resolveCampaigns(viewer, args, source, info);
},
},
features: {
type: featureConnection,
description: 'Features available to the logged in user',
args: connectionArgs,
resolve: (_, args) => connectionFromArray(getFeatures(), args)
},
username: {
type: GraphQLString,
description: 'Users\'s username'
},
website: {
type: GraphQLString,
description: 'User\'s website'
}
}),
interfaces: [nodeInterface]
});
const featureType = new GraphQLObjectType({
name: 'Feature',
description: 'Feature integrated in our starter kit',
fields: () => ({
id: globalIdField('Feature'),
name: {
type: GraphQLString,
description: 'Name of the feature'
},
description: {
type: GraphQLString,
description: 'Description of the feature'
},
url: {
type: GraphQLString,
description: 'Url of the feature'
}
}),
interfaces: [nodeInterface]
});
/**
* Define your own connection types here
*/
const {
connectionType: featureConnection
} = connectionDefinitions({
name: 'Feature',
nodeType: featureType
});
// Campaign list ConnectionType
const {
connectionType: campaignConnection,
} = connectionDefinitions({
name: 'Campaign',
nodeType: campaignType
});
/**
* This is the type that will be the root of our query,
* and the entry point into our schema.
*/
// Setup GraphQL RootQuery
const RootQuery = new GraphQLObjectType({
name: 'Query',
description: 'Realize Root Query',
fields: () => ({
viewer: {
type: userType,
resolve: () => '1'
},
})
});
/**
* This is the type that will be the root of our mutations,
* and the entry point into performing writes in our schema.
*/
const mutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
// Add your own mutations here
})
});
/**
* Finally, we construct our schema (whose starting query type is the query
* type we defined above) and export it.
*/
export default new GraphQLSchema({
query: RootQuery
// Uncomment the following after adding some mutation fields:
// mutation: mutationType
});
I came across someone else having a similar issue and although they did not mention how they fixed it they did say this:
The problem was in my nodeDefinitions. I wasn't loading the user id correctly or identifying the node object. Once I got those working everything worked properly
Any help would be greatly appreciated.
TL;DR;
Your root query does not have a node field. That's why fetching more items fail. Add the node field like this:
const RootQuery = new GraphQLObjectType({
...
fields: () => ({
viewer: {
...
},
node: nodeField,
})
});
when I check the Request Payload I notice that it is querying "node" instead of "viewer" like it did previously.
The first time Relay fetches an object, it uses the regular query.
viewer {
id,
...F0
}
Now Relay knows the global ID of viewer. Later when more data of viewer need to be fetched, Relay uses node root field to query that object directly.
node(id:$id_0) {
...F0
}
See an excellent answer by steveluscher to how node definitions work in Relay.