KeystoneJS show error for unqiue index failure - keystonejs

I have a model for tracking redirects like so:
var Redirect = new keystone.List('Redirect', {
});
Redirect.add({
from: { type: String, required: true, initial: true, unique: true },
to: { type: String, required: true, initial: true },
status: { type: Types.Select, options: [{value: 302, label: 'Temporary'},{value: 301, label: 'Permanent'}], default: 302, numeric: true, initial: true }
});
The unique constraint works, I can create new models as long as it doesn't fail to have a unique 'from', but when I do create one with a non-unique 'from' I get an error in the server console but the frontend does nothing.
Error saving changes to Redirect 5664d5860d7c730f09880de1: {
[MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key
error index: keystone.redirects.$from_1 dup key: { : "/herp" }]
name: 'MongoError', code: 11000, err: 'insertDocument :: caused by
:: 11000 E11000 duplicate key error index: keystone.redirects.$from_1
dup key: { : "/herp" }' }
Is there another setting I need to do to get the error to show on the frontend so the user knows what went wrong?

You will have to catch that error in your server logic and present a friendlier flash message to your visitors. At least the database error message is descriptive of the problem duplicate key error ...
exports.flashMessages = function(req, res, next) {
var flashMessages = {
error: req.flash('error')
};
res.locals.messages = _.any(flashMessages,
function(msgs) { return msgs.length }) ? flashMessages
: false;
next();
};
See Common Route Middleware
and to customize the message ensure the passed object contains the appropriate fields for the template
you can provide flash messages in your server logic by adding req.flash
req.flash(
'success',
'Your job has been submitted. Allow 30 days for approval.'
);
See https://github.com/r3dm/shpesfba.org/blob/b83d9e6a3859a83f4b4738e7a13ead08ce611bd3/routes/views/jobForm.js#L44
To display a flash error in the adminUI create an error object and pass that back into the next callback like so
Redirect.schema.pre('save', function(next) {
if (saveFailure) {
var err = new Error('MongoDB is barfing on this');
next(err);
}
}
The difficult part will be to craft your logic with mongoose pre and post lifecycle hooks to catch the mongodb error. Please do some experimenting.
See https://github.com/r3dm/shpesfba.org/blob/b83d9e6a3859a83f4b4738e7a13ead08ce611bd3/models/Event.js#L76

Related

Mongoose update gets no errors, but does not update

Cant figure out what I'm missing, and I havent had this issue before on any of my other updates. I expanded a collection and want to be able to update certain fields depending on where in the app the user is interacting. I've had no issue working with subdocs using separate calls, but with this particular nested field I'm getting no errors, and getting the correct document returned without the update. (I have another nested field that is updating fine - "personalInfo" while the "medical" field is the one giving me trouble)
The model looks like this:
const clientSchema = new Schema({
fullName: String,
firstName: String,
lastName: String,
enrollment: {
enrolled: Boolean,
enrollDates: [
{
begin: Date,
end: Date
}
]
},
personalInfo: {
dateOfBirth: Date,
phone: String,
email: String,
address: {
addressLineOne: String,
addressLineTwo: String,
city: String,
state: String,
zip: String
},
regionalCenter: String,
serviceCoordinator: String,
serviceCoordinatorPhone: String,
rcId: String,
emergencyContact: String,
livingSituation: String,
ihss: {
provider: String,
hours: Number,
services: String
}
},
medical: {
primaryIns: String,
primaryInsId: String,
secondaryIns: String,
secondaryInsId: String,
hasMediCal: Boolean,
mediCalId: String,
mediCalEnroll: Date,
hasMedicare: Boolean,
medicareId: String,
medicareEnroll: Date,
logs: {type: [logSchema], default: []},
},
contracts: {type: [contractSchema], default: []},
visits: [{ type: Schema.Types.ObjectId, ref:'Visit' }],
users: [{ type: Schema.Types.ObjectId, ref: 'User' }],
servicePlans: [{ type: Schema.Types.ObjectId, ref: 'ServicePlan'}],
currentPlan: String,
income: {type: [incomeSchema], default: []},
contacts: {type: [contactSchema], default: []}
}
The route:
router.route("/clients/:clientId").patch(updateClient)
And the controller... since I want to keep the controller as restful as possible, but conditionally set the fields depending on the api call, I conditionally set the different aspects and then pass in the body an additional field to tell the controller which aspect to update (so the personalInfo section has a field "personalInfo": "personalInfo" and the medicalInfo field has its own. The personalInfo object updates fine (I commented out the initial line since it was stated in another post that these calls work better doing a findOneAndUpdate- but that hasnt yielded any progress, and the personalInfo update worked without issue).
exports.updateClient = async (req, res) => {
try {
//const client = await Client.findOne({ _id: req.params.clientId })
if (req.body.firstName) {
client.firstName = req.body.firstName
}
if (req.body.lastName) {
client.lastName = req.body.lastName
}
if (req.body.personalInfo === 'personalInfo') {
client.updateOne({$set: {personalInfo: req.body}}, {new: true}, function(err, updatedDoc){
if(err){
console.log("error updating personal info: ", err)
}
})
}
if (req.body.enrollment === 'enrollment') {
client.updateOne({$set: {enrollment: req.body}}, {new: true}, function(err, updatedDoc){
if(err){
console.log("error updating personal info: ", err)
}
})
}
if(req.body.medicalInfo === 'medicalInfo'){
console.log("medInfo: ", req.body)
let clientId = req.params.clientId
// const client = await Client.findById(clientId)
// console.log("Client ", client)
// client.medical.set(req.body)
Client.findById(clientId)
.then((client) => {
client.medical.set(req.body
// hasMediCal: req.body.hasMediCal,
// hasMedicare: req.body.hasMedicare,
// mediCalId: req.body.mediCalId,
// medicareId: req.body.medicareId,
// mediCalEnroll: req.body.mediCalEnroll,
// medicareEnroll: req.body.medicareEnroll,
// primaryIns: req.body.primaryIns,
// primaryInsId: req.body.primaryInsId,
// secondaryIns: req.body.secondaryIns,
// secondaryInsId: req.body.secondaryInsId
);
client.save();
res.send(client)
})
// Client.findOneAndUpdate(
// { _id: req.params.clientId},
// {$set: {medical: req.body}},
// {new: true},
// function(err, updatedDoc){
// if(err){
// console.log("error updating personal info: ", err)
// }
// })
// client.markModified('medical');
}
// await client.save()
// res.send(client)
} catch (error) {
res.status(404)
res.send({ error: "Client not updated: ", error})
}
}
Finally, the body being sent:
{
"hasMediCal": false,
"hasMedicare": false,
"mediCalEnroll": "2005-04-22T08:00:00",
"mediCalId": "91234567A",
"medicalInfo": "medicalInfo",
"medicareEnroll": "2005-04-03T08:00:00",
"medicareId": "9FHS-ASU-95F8",
"primaryIns": "Molina",
"primaryInsId": "91234567A",
"secondaryIns": "SilverScript - Rx",
"secondaryInsId": "08dfA8d8"
}
Whether I've tried findOneAndUpdate, or findOne and then setting the field on the result, or setting each subfield in the object specifically, I keep getting the correct document returned, just not updated, and with no errors. I thought possibly it was because I was attempting to set the update within the conditionals, so I created a separate update controller but that got the same results as well. Really lost as how else to pursue this.
Please let me know if you see anything missing or where I'm going wrong. Much appreciated.
So after running around on this for hours, I came to a working solution, which essentially is no different, other than setting the query as a variable rather than writing it out. If anyone has any guess as to why this works when the multiple other methods didnt, I'd be grateful for your thoughts.
if(req.body.medicalInfo === 'medicalInfo'){
console.log("medInfo: ", req.body)
let clientId = req.params.clientId
let query = {_id: clientId};
Client.findOneAndUpdate(query, {$set: {medical: req.body}}, {new: true, upsert: true}, function(err, doc){
if(err) return res.status(500).send( {error:err});
return res.send(doc)
})
}

UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key error collection: db.footballers index: data_1 dup key: { data: null }

I have never had this issue before. I was creating instances easily all the time. But with this project it throwing me error about index or something. I don't even know what that is. Thank you in advance.
Here is my schema
const mongoose = require("mongoose");
const footballersSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
}
})
module.exports = mongoose.model('footballers', footballersSchema);
and is just simple controller which triggers creating a new instance
exports.checkUserForReg = async function(req, res, next){
await footballersModel.create(req.body);
res.(201).json({status: "Ok"})
}
but it gives me error UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key error collection: db.footballers index: data_1 dup key: { data: null }
Yes, every instance should have unique name field, but even when I try different it give me this error. I see error says index: data_1 dup key: { data: null }, but I don't have index key in my Schema at all! What is that ? Thank you!

I have an sql error when i type in a command

So I'm trying to make dynamic command handling for my bot, but it just doesn't work. Everything works without dynamic command handling.
So this is there error i get when i type in command !work:
UniqueConstraintError [SequelizeUniqueConstraintError]: Validation error
at Query.formatError (C:\Users\mmede\Desktop\Discord Bot\node_modules\sequelize\lib\dialects\sqlite\query.js:409:16)
at Query._handleQueryResponse (C:\Users\mmede\Desktop\Discord Bot\node_modules\sequelize\lib\dialects\sqlite\query.js:72:18)
at Statement.afterExecute (C:\Users\mmede\Desktop\Discord Bot\node_modules\sequelize\lib\dialects\sqlite\query.js:246:27)
at Statement.callbackTrampoline (internal/async_hooks.js:129:14) {
errors: [
ValidationErrorItem {
message: 'user_id must be unique',
type: 'unique violation',
path: 'user_id',
value: '485384232131100693',
origin: 'DB',
instance: [users],
validatorKey: 'not_unique',
validatorName: null,
validatorArgs: []
}
],
fields: [ 'user_id' ],
parent: [Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: users.user_id] {
errno: 19,
code: 'SQLITE_CONSTRAINT',
sql: 'INSERT INTO `users` (`user_id`,`balance`) VALUES ($1,$2);'
},
original: [Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: users.user_id] {
errno: 19,
code: 'SQLITE_CONSTRAINT',
sql: 'INSERT INTO `users` (`user_id`,`balance`) VALUES ($1,$2);'
},
sql: 'INSERT INTO `users` (`user_id`,`balance`) VALUES ($1,$2);'
}
and this is my work script:
const Discord = require('discord.js');
const { Users, CurrencyShop } = require('../dbObjects');
const currency = new Discord.Collection();
Reflect.defineProperty(currency, 'add', {
//eslint-disable-next-line func-name-matching
value: async function add(id, amount) {
const user = currency.get(id);
if (user) {
user.balance += Number(amount);
return user.save();
} else {
try {
const newUser = await Users.create({ user_id: id, balance: amount });
currency.set(id, newUser);
return newUser;
} catch (err) {
// print the error details
console.log(err);
}
}
},
});
module.exports = {
name: 'work',
description: 'balancas',
execute(message, async) {
message.channel.send('You hacked someones computer and you gained 1million vbucks')
currency.add(message.author.id, 1000000);
}
}
If anyone would like to help me I would really appreciate it. I'm new to sql so that would be great if you explained where I made the mistake.
The problem is that you're trying to insert an entry (user) into the users table with a user_id that already exists.
Since the users table has a UNIQUE constraint, that column is a primary key for the table and there can be no duplicate user_ids i.e. no two users can have the same user_id.
If you want to edit a particular user, try the update command.

document.invalidate gives empty json as response

I wanted to check if the body in a document is unique. If not unique give appropriate error.
NewsSchema.pre("save", true, function (next: any, done: any) {
var self = this;
News.findOne({body: self.body}, "body", function (err: any, body: string) {
if(err)
done(err);
else if(body) {
self.invalidate("body", "the news body already exists", self.body);
done(new Error("the news body already exists"));
}
else {
next();
}
});
});
When intentionally giving a duplicate body. It returns with an empty json. Whereas I was expecting it to be:
{
message: 'Validation failed',
name: 'ValidationError',
errors:
{
size:
{
message: 'the news body already exists',
name: 'ValidatorError',
path: 'body',
type: 'String',
value: 'some string'
}
}
}
you need to use validate() to check if it is valid or not.
var self = this; //type document
...
self.invalidate("body", "the news body already exists", self.body);
//use validate() to check the validity
self.validate(function(err){
console.log(err);//this will give you your expected results.
});

How do I operate the m.withAttr tutorials code?

A contrived example of bi-directional data binding
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
m.render("body", [
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
]);
}
};
https://lhorie.github.io/mithril/mithril.withAttr.html
I tried the above code does not work nothing.
It was the first to try to append the following.
m.mount(document.body, user);
Uncaught SyntaxError: Unexpected token n
Then I tried to append the following.
var users = m.prop([]);
var error = m.prop("");
m.request({method: "GET", url: "/users/index.php"})
.then(users, error);
▼/users/index.php
<?php
echo '[{name: "John"}, {name: "Mary"}]';
Uncaught SyntaxError: Unexpected token n
How do I operate the m.withAttr tutorials code?
Try returning m('body', [...]) from your controller.
view: function (ctrl) {
return m("body", [
...
]);
}
render should not be used inside of Mithril components (render is only used to mount Mithril components on existing DOM nodes).
The example is difficult to operate because it's contrived, it's not meant to be working out-of-the-box. Here's a slightly modified, working version:
http://jsfiddle.net/ciscoheat/8dwenn02/2/
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
return [
m("input", {
oninput: m.withAttr("value", controller.user.name),
value: controller.user.name()
}),
m("h1", controller.user.name())
];
}
};
m.mount(document.body, user);
Changes made:
m.mount injects html inside the element specified as first parameter, so rendering a body element in view will make a body inside a body.
Changed the input field event to oninput for instant feedback, and added a h1 to display the model, so you can see it changing when the input field changes.
Using m.request
Another example how to make an ajax request that displays the retrieved data, as per your modifications:
http://jsfiddle.net/ciscoheat/3senfh9c/
var userList = {
controller: function() {
var users = m.prop([]);
var error = m.prop("");
m.request({
method: "GET",
url: "http://jsonplaceholder.typicode.com/users",
}).then(users, error);
return { users: users, error: error };
},
view: function(controller) {
return [
controller.users().map(function(u) {
return m("div", u.name)
}),
controller.error() ? m(".error", {style: "color:red"}, "Error: " + controller.error()) : null
];
}
};
m.mount(document.body, userList);
The Unexpected token n error can happen if the requested url doesn't return valid JSON, so you need to fix the JSON data in /users/index.php to make it work with your own code. There are no quotes around the name field.