Following the discord.js guide for creating a currency system. I'm getting this error: WHERE parameter "user_id" has invalid "undefined" value
I can run my !balance command fine and I'm getting currency per message. This is for the !inventory command. My code:
index.js
client.on('messageCreate', async message => {
if (message.author.bot) return;
if (!message.content.startsWith(prefix)) return;
const commandBody = message.content.slice(prefix.length);
const args = commandBody.split(' ');
const command = args.shift().toLowerCase();
if (command === 'balance') {
const target = message.author;
return message.reply(`${target.tag} has ${currency.getBalance(target.id)}🍰!`);
}
if (command == "inventory") {
const target = message.author;
const user = await Users.findOne({where: {user_id: target.id}});
const items = await user.getItems();
if (!items.length) return message.reply(`${target.tag} has nothing!`);
return message.reply(`${target.tag} currently has ${items.map(i => `${i.amount} ${i.item.name}`).join(' , ')}`);
}
});
Users.js
module.exports = (sequelize, DataTypes) => {
return sequelize.define('users', {
user_id: {
type: DataTypes.STRING,
primaryKey: true,
},
balance: {
type: DataTypes.INTEGER,
defaultValue: 0,
allowNull: false,
},
}, {
timestamps: false,
});
};
I made sure to run the dbInit.js file.
I'm a bit lost on what to try, honestly. This is my first time working with Discord.js; I expected to see the user inventory, but it seems like the user_id is undefined and I don't know how to fix that.
The problem is not the WHERE-clause in the main code, but the WHERE-clause within the code of getItems().
I managed to find a solution here:
https://github.com/discordjs/guide/discussions/1085
It relates to the final code published on github by the creators of the guide. I don't know why it's different than in the guide, but it is:
https://github.com/discordjs/guide/blob/main/code-samples/sequelize/currency/14/dbObjects.js
Related
const user = new mongoose.Schema(
{
nano_id: {
type: String,
required: true,
default: () => nanoid(7),
index: { unique: true },
},
...
}
How to run again nanoid(7) if is not unique? (run automatically and not get any error in console)
There are two ways to do this:
Prevent the error from happening in the first place by searching for a document with a similar Nano ID, if a document exists, regenerate a new Nano ID using a recursive function.
const { customAlphabet } = require('nanoid');
const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
const nanoid = customAlphabet(alphabet, 8);
// userRegistration controller or route...
async function uniqueNanoId(query): Promise<string> {
const nanoId = nanoid();
const sameNanoId = await User.findOne({ nano_id:nanoId });
if (sameNanoId) {
return uniqueNanoId(query);
}
return nanoId;
}
const nanoId = await uniqueNanoId();
const user = User.create({...userBody,nanoId});
//...
Catch the error - as #cachius hinted - and regenerate the unique Nano ID accordingly (not tested). Catching a duplicate key has been discussed here
Bonus: Ask yourself the question, do I really need both Default Mongoose IDs and Nano IDs? If not, then this is a simple solution.
// ...
_id: {
type: String,
default: () => nanoid(),
},
// ...
The database will throw an error you have to catch and react to by generating the same record again with a newly generated nanoid.
Other than the 3 attributes hf.EnrollmentId, hf.type and hf.Affiliation, I've created a custom attribute named email and added it as attrs:[{name: 'email', value: rahul18#gmail.com, ecert: true}] and it was successfully added to the attribute list.
In my chaincode, i'm able to get the enrollmentId by using the following command : cid.GetAttributeValue(ctx.GetStub(), "hf.EnrollmentID") but i'm not able to get the email using the same method cid.GetAttributeValue(ctx.GetStub(), "email")
Any help would be appreciated regarding why the first one is working and the second isn't
Does getAttributeValue not support custom made attributes?
Here is an example that may be helpful. A previous stackoverflow contribution helped me with a similar situation. I don't have the link for it right now, but thanks anyway.
First of all, you state that you have added attributes successfully. Here is some code as an example which I had placed in the code file for registering users.
//create user attr array
let registerAttrs = [];
let registerAttribute = {
name: "recycler",
value: config.recycler,
ecert: true,
};
registerAttrs.push(registerAttribute);
const secret = await ca.register({
affiliation: config.affiliation,
enrollmentID: config.recycler,
role: "client",
attrs: registerAttrs,
},
adminUser
);
The contract code is able to find the value of "recycler" using the following code. Of particular importance is the getCurrentUserId() function.
async getCurrentUserId(ctx) {
let id = [];
id.push(ctx.clientIdentity.getID());
var begin = id[0].indexOf("/CN=");
var end = id[0].lastIndexOf("::/C=");
let userid = id[0].substring(begin + 4, end);
return userid;}
async getCurrentUserType(ctx) {
let userid = await this.getCurrentUserId(ctx);
// check user id; if admin, return type = admin;
// else return value set for attribute "type" in certificate;
if (userid == "admin") {
return userid;
}
return ctx.clientIdentity.getAttributeValue(userid);}
The user type returned from the getCurrentUserType function is subsequently examined further up in the contract code, as shown in the following example.
async readTheAsset(ctx, id) {
let userType = await this.getCurrentUserType(ctx);
const buffer = await ctx.stub.getState(id);
const asset = JSON.parse(buffer.toString());
asset.userType = userType;
asset.userID = ctx.clientIdentity.getID();
if (asset.userType === "recycler") {
throw new Error(`The record cannot be read by ${asset.userType} `);
}
return asset;}
I feel sure that this code should solve your issue, as there is a lot of similarity.
const updateObj = {
enrollmentID : userName,
type:'client',
affiliation:'' ,
attrs: [{name: 'email', value: email, ecert: true}, {name: 'orgId', value: orgId, ecert: true}, {name: 'userId', value: userName, ecert: true}] ,
}
const response = await identityService.update(userName, updateObj ,adminUser)
const clientUser = await provider.getUserContext(userIdentity, userName);
const reenrollment = await caClient.reenroll(clientUser,
[{
name: 'email',
optional: false
},
{
name: 'orgId',
optional: false
},
{
name: 'userId',
optional: false
}
]);
I have been having a lot of trouble recently with a leader board command showing users in the wrong order.
I am using the DiscordJS master branch and Sequelize v6
Here is an image of what I am describing:
Leaderboard command file:
const { MessageEmbed } = require("discord.js");
exports.run = async (client, message, args) => {
const { Users } = require("../functions/database");
let users = await Users.findAll({ limit: 10, order: [['count', 'DESC']] });
for (const user of users) {
await client.users.fetch(user.userid);
}
let embed = new MessageEmbed()
.setAuthor("Middleman Leaderboard", `${client.user.displayAvatarURL({ dynamic: true })}`)
.setDescription(users.map((user, position) => `**${position + 1}**. ${(client.users.cache.get(user.userid).username)}: ${user.count} Points`))
.setThumbnail('https://i.ibb.co/ByRSBmB/hp.png')
.setColor("#d70069");
await message.reply(embed)
}
Sequelize database model:
const Users = sequelize.define('users', {
userid: {
type: Sequelize.INTEGER,
},
count: Sequelize.INTEGER,
});
exports.Users = Users;
Please note this is greatly a proof of concept and not for production.
Any help is appreciated!
This is a solution from discordjs.guide:
users.sort((a, b) => b.balance - a.balance)
.filter(user => client.users.cache.has(user.user_id))
.first(10)
.map((user, position) => `(${position + 1}) ${(client.users.cache.get(user.user_id).tag)}: ${user.balance}💰`)
.join('\n'),
I've got a problem with the sequelize hooks. The following code does its job very well when i create a new instance. But when it comes to updating an existent database entry the values of rec.working_time and rec.break_duration are not wirtten back to the database
I can see that the hook is called by adding some console logs and the rec values change as well. But somhow there is no update on theese fields.
If i do the same with an 'beforeUpdate' everything works fine with the same code.
TimeRecording.hook('afterValidate', rec => {
var moment = require('moment');
var models = require('../models');
let workingTime = moment(rec.time_to).diff(rec.time_from, 'minutes', null);
let workingHours = workingTime / 60;
return models.BreakControl.max('break_duration',
{
where: { working_time: { lt: workingHours } }
})
.then(breakControl => {
breakControl = breakControl ? breakControl : 0;
rec.working_time = workingTime - breakControl;
rec.break_duration = breakControl;
})
.catch(err => console.error(err));
});
Here also a quck look on the important attributes in the model:
time_from: {
type: DataTypes.DATE,
allowNull: false,
},
time_to: {
type: DataTypes.DATE,
validate: {
isAfter: function (value, next) {
let self = this;
if (self.time_from > value) {
return next('time_to must be after time_from!');
}
return next();
}
}
},
required_time: {
type: DataTypes.DECIMAL(4, 0)
},
working_time: {
type: DataTypes.DECIMAL(4, 0),
validate: {
min: 0
}
},
break_duration: {
type: DataTypes.DECIMAL(4, 0)
}
So am i missing something, or did i fail to understand some critical concept here?
Thanks for any information on this.
I am building a REST api with nodejs, using mongoose and mochajs to run some tests. I have the following scheme:
var subscriptionTypeSchema = new mongoose.Schema({
typeId : { type: Number, required: true, unique: true },
name : { type: String, required: true},
active : { type: Boolean, required: true }
});
Express route:
app.post('/1.0/subscriptiontype', subscriptiontype.create);
Controller:
exports.create = function(req, res) {
validation.subscriptionTypeValidator(req);
var errors = req.validationErrors();
if (errors) {
res.status(400).json({
errors: errors
});
} else {
var subscriptionType = new SubscriptionType();
subscriptionType.typeId = parseInt(req.body.typeId);
subscriptionType.name = req.body.name;
subscriptionType.active = req.body.active;
subscriptionType.save(function(err) {
if (err) {
var parsedError = mongooseutility.parseMongooseError(err);
res.status(400).json({
errors: [parsedError]
});
} else {
res.json({identifier: subscriptionType._id});
}
});
}
};
The mongoose utility maps the error codes to a more API friendly output (error codes 11001 and 11000 are mapped to a 'duplicate' error, as can be seen in the test).
Mocha before method:
before(function(done) {
db.connection.on('open', function() {
db.connection.db.dropDatabase(function(err) {
done();
});
});
});
I've verified that the database is dropped successfully.
The test itself makes a request using supertest. Before this test, I have a test that creates a subscription type with typeId 4 successfully, so this one should fail:
it('Should not create subscription with taken type id', function (done) {
request(app.privateapi)
.post('/1.0/subscriptiontype')
.set('Authorization', authorizationHeader)
.send({
typeId: 4,
name: 'New package',
active: 1
})
.expect(function (res) {
if (res.status !== 400) {
throw new Error('Status code was not 400');
}
var expectedResponse = { errors: [ { param: 'typeId', msg: 'duplicate' } ] };
if (JSON.stringify(res.body) !== JSON.stringify(expectedResponse)) {
throw new Error('Output was not was as expected');
}
})
.end(done);
});
Tests are invoked using grunt-simple-mocha.
This test works the first time, however when I run it a 2nd time it fails on the unique validation. A third time it works again. I've done some searching and found that it probably has something to do with a race condition while recreating indexes, so I've tried restarting mongodb before running the tests again, but that doesn't work. I've found a solution here: http://grokbase.com/t/gg/mongoose-orm/138qe75dvr/mongoose-unique-index-test-fail but I am not sure how to implement this. Any ideas?
Edit: for now I fixed it by dropping the database in an 'after' method (instead of 'before'). All the tests run fine, but it would be nice to keep the test data after the tests are done, for inspection etc...
You are not testing the creation of your tables so you can just empty your collections instead of creating the db.
Something along those lines (not tested):
beforeEach(function(done){
var models = Object.keys(mongoose.models);
var expects = models.length;
if(expects == 0) return done();
var removeCount = 1;
//maybe use async or something else but whatever
models.forEach(function(model){
model.remove({}, function(){
if(removeCount == expects){
done();
}
removeCount++;
})
});
});