When editing an existing document in React, Meteor Simple Schema validation on client side always returns 'true', despite invalid form data - mongodb

I have a form component in React that can be used to either save a new address, or edit an existing address object in MongoDB. I am using SimpleSchema for client-side schema validation (as well as server-side schema validation).
The document is being passed as a prop to the form with shippingAddress:
<ShippingAddressForm
onChange={this.handleAddressChange.bind(this)}
shippingAddress={shippingAddress}
editModeType={editModeType}
/>
In case of New Address mode type, shippingAddress is passed as a skeleton object:
shippingAddress = {
firstName: "",
address: "",
city: "",
zipCode: "",
company: "",
lastName: "",
address2: "",
state: "",
addressTypeSelected: "",
country: "US",
phoneNumber: ""
}
In case of Edit Address, shippingAddress is used to pass corresponding values from existing MongoDB document:
shippingAddress = {
address: "Arcadia Bay Ct"
city: "Delray Beach"
company: "AT&T"
country: "US"
createdAt: Tue Aug 23 2016 11:59:13 GMT+0530 (IST)
favorite: false
firstName: "Max"
label: "Arcadia Bay Ct"
lastName: "Pricefield"
phoneNumber: "987654321"
state: "FL"
userId: "WEwmG3iYgShasmzGT"
zipCode: "33446"
}
Then the ShippingAddressForm inputs are populated using these values.
Before submitting the form, I validate the formData using a React handler. (This is being done via a submit button in a higher-level component, to which any state changes are regularly propagated.)
onSubmitHandler() {
// get address from form
// check it's complete and send error messages
// if successful save and return to address book
const { userId, saveShippingAddress } = this.props;
let formData = this.state.shippingAddress
let invalidKeys,
validityContext = AddressSchema.namedContext("shippingAddress");
formData.userId = userId;
formData.label = formData.address;
AddressSchema.clean(formData);
if(!validityContext.validate(formData)) {
invalidKeys = validityContext.invalidKeys();
invalidKeys = _.map(invalidKeys, function (o) {
return _.extend({message: validityContext.keyErrorMessage(o.name)}, o);
});
return toastr.error(invalidKeys[0].message, 'Error');
}
return saveShippingAddress(formData);
}
The validation in the above function works perfectly in case of a New Address. If any required key is missing, validityContext.validate(formData) returns false as it should.
However, in case of Edit Address, validityContext.validate(formData) always returns true, even if the data is invalid and required keys are missing etc.
I have checked the formData object being passed into the .validate() function, it is fine. It is not passing the old database object. If my form is missing a 'Last Name' field entry, then formData object is also missing a lastName key. But the SimpleSchema validator still returns true in edit mode.
I have also tried checking just one key using .validateOne(). Same issue occurs there.
The same invalid data is then being caught in server-side validation check and error is returned. However, I need the client-side validation to work in all cases as well.
I am not using MongoDB modifiers.
Has anyone else faced this issue?

Related

Mongoose subdocuments return different ID every time

I have a model Franchise that has another Schema Employee as its subdocuments for a field. The structure is as follows.
Franchise.js
const Franchise = new mongoose.Schema(
{
franchiseName: String,
address: String,
managers: [Employee]
});
export default mongoose.model(
"Franchise",
Franchise
);
Employee.js
const Employee = new mongoose.Schema(
{
name: String,
email: String,
phoneNo: Number,
password: String,
});
export default Employee;
The issue I am facing is with every query to Franchise, it returns a new _id for the objects in managers field. Is there any way to make it constant ?
For instance, I am running a simple findById on Franchise and store it in franchise variable.
and then I console.log(franchise.managers).
It prints different IDs each time the query is run.
[
{
_id: new ObjectId("61925d2697852574eb0ba9ab"),
name: 'Franchise Manager 1',
email: 'franchise1#sfc.com',
phoneNo: 1234567890,
}
]
Second time the query is run:
[
{
_id: new ObjectId("61925ba8130aca93a7dd3dbc"),
name: 'Franchise Manager 1',
email: 'franchise1#sfc.com',
phoneNo: 1234567890
}
]
As you can see, the employee is the same, however it has different Id for each call. Kindly help.
Thanks.
Alright, I figured it out. The issue is that there was no _id stored in the database for existing data of managers. The Employee schema was added later on, so as the _id was not present in the database only, a new one was being generated each time.

Unable to insert array data into mongodb using mongoose

I am trying to insert data into my mongodb database. I am using mern stack.
This is my user model for the city data that is in array containing city_name & city_code
city: [{
city_name: {type: String},
city_code: {type: String},
}],
I am passing the values to the register function in this manner
city: [{city_name: this.state.c_name, city_code: this.state.c_code}],
This is the function defined to register into the database
city: [{ city_name: req.body.c_name, city_code: req.body.c_code}],
No error's message is being returned in the console. I am using a message where if the user is registered successfully it returns User Registered or else the error message. But I am not getting anything in the console.
Constructor defined in the front end side to get the values
city: [{city_name: '', city_code: ''}],
UPDATED
This is the function I am using to post the data
export const register = newUser => {
return axios
.post('users/sign-up', {
username: newUser.username,
email: newUser.email,
phone: newUser.phone,
dob: newUser.dob,
city: [{city_name: newUser.c_name, city_code: newUser.c_code}],
password: newUser.password
})
.then(response => {
console.log('Registered')
})
}
SOLUTION
So whenever you are accessing the data stored in array we need to give the index value.
<td>{this.props.obj.city[0].cityname}</td>
<td>{this.props.obj.city[0].citycode}</td>

Cannot set the reference to the user document in courses mongoose

I am currently working on a RESTful API, and I am trying to reference the users schema in the courses document such that, when a POST request gets sent to the route of the course, a course is created in the DB and has as one of its fields a reference to the user that created it. However, for the life of me, I cannot figure out why the "user" field is not appearing when I post. There seem to be quite a few of these questions here on Stack so I may just be adding to the pile, but I tried their solutions and they did not work for me
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
emailAddress: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
var CourseSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User'}, //FOR some reason this is not showing up on any courses created using the
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
estimatedTime: {
type: String
},
materialsNeeded: {
type: String
}
});
var User = mongoose.model('User', userSchema);
var Course = mongoose.model('Course', CourseSchema);
module.exports = {Course, User};
Do you see anything in here that would preclude the user field from appearing when a new course is created?
I have attached some screenshots to further explain.
This first image is a screen of the currently authenticated user credentials (fake data obviously). This is the user that is sending the POST request for the new course. I would expect his information to be attached to the course (see screenshot 3)
This image shows the body of the request that is sent. You can see that the key-value pairs match what is in the CourseSchema. I would expect that the "user" field would be created once the POST request is sent.
This last image is some dummy data that is the expected result.
Thanks all for taking a look at this!
User field will not be automatically added to the course document. You have to manually set the user field in the request body itself or while creating a course.
Example of the course body to be sent:-
{
user: "userId",
title: "test",
description: "test",
estimatedTime: "test",
materialsNeeded: 1
}
Also, the result of this will not include the whole user document as you have mentioned in the expected result. It will only return the userId. However, while accessing the course you can populate the user field to get the whole user document. Example for the same
Course.find({...query}).populate("user")

Correctly inserting and/or updating many datasets to MongoDB (using mongoose)?

So from time to time I get new exports of a cities database of POIs and info about them and I want to have all that data in my MongoDB with a Loopback-API on it. Therefore I reduce the data to my desired structure and try to import it.
For the first time I receive such an export, I can simply insert the data with insertMany().
When I get a new export, it means that it includes updated POIs which I actually want my existing POIs to be replaced with that new data. So I thought I'd use updateMany() but I could'nt figure out how I'd do that in my case.
Here's what I have so far:
const fs = require('fs');
const mongoose = require('mongoose');
const data = JSON.parse(fs.readFileSync('data.json', 'utf8'));
// Connect to database
mongoose.connect('mongodb://localhost/test', {
useMongoClient: true
}, (err) => {
if (err) console.log('Error', err);
});
// Define schema
let poiSchema = new mongoose.Schema({
_id: Number,
name: String,
geo: String,
street: String,
housenumber: String,
phone: String,
website: String,
email: String,
category: String
});
// Set model
let poi = mongoose.model('poi', poiSchema);
// Generate specified data from given export
let reducedData = data['ogr:FeatureCollection']['gml:featureMember'].reduce((endData, iteratedItem) => {
endData = endData.length > 0 ? endData : [];
endData.push({
_id: iteratedItem['service']['fieldX'],
name: iteratedItem['service']['fieldX'],
geo: iteratedItem['service']['fieldX']['fieldY']['fieldZ'],
street: iteratedItem['service']['fieldX'],
housenumber: iteratedItem['service']['fieldX'],
phone: iteratedItem['service']['fieldX'],
website: iteratedItem['service']['fieldX'],
email: iteratedItem['service']['fieldX'],
category: iteratedItem['service']['fieldX']
});
return endData;
}, []);
//
// HERE: ?!?!? Insert/update reduced data in MongoDB collection ?!?!?
//
mongoose.disconnect();
So I just want to update everything that has changed.
Of course if I leave it to insertMany() it fails due to dup key.
For the second time, use mongo's update command with upsert set to true.
db.collection.update(query, update, options)
In the query pass the _id ,in update pass the object and in option set upsert to true. This will update the document if it exists creates a new document if that doesn't exist.

mongoose - how to validate specific fields only?

I have following mongoose model and routing file.
user.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId,
var userSchema = new Schema({
nick_name: {
type: String,
unique: true
},
email: {
type: String,
unique: true
},
first_name: String,
last_name: String,
birth_date: {
type: Date
},
password: {
type: String,
select: true
},
user_type: {
type: Number,
},
is_active: {
type: Number,
default: -1
}
}, { collection: 'user' });
/*
*Validations
*/
userSchema.path('nick_name').required(true, 'nick name is required!');
userSchema.path('email').required(true, 'email is required!');
userSchema.path('password').required(true, 'password is required!');
userSchema.path('user_type').required(true, 'user type is required!');
userSchema.path('is_active').required(true, 'is active is required!');
userSchema.path('is_close').required(true, 'is close is required!');
userSchema.path('first_name').required(true, 'first name is required!');
userSchema.path('last_name').required(true, 'last name is required!');
userSchema.path('birth_date').required(true, 'birth date is required!');
var User = module.exports = mongoose.model("User", userSchema);
router.js
var express = require('express');
var router = express.Router();
var User = require('../models/user');
router
.route('/api/user/register')
.post(
function(req, res, next) {
var user_ = new User(req.body);
/*
*here all validations are required
*/
user_.validate(function(err) {
if (err) {
res.json({ "status": 0, "error": err });
} else {
user_.save(function(err) {
if (err) {
res.json({ "status": 0, "error": { "other": "Oops! something went wrong, please try again later." } });
} else {
res.json({ error: 1, message: 'User registered' });
}
});
}
}
});
}
});
In above routing file I can validate all fields by using validate() method but, I have need validation as following conditions
->When user register, following fields are required
nick_name
email
password
user_type
is_active
->When user edit his profile (after register), all fields are required.
Can anybody help me to solve this issue ?
I just found myself in this situation, want to update a comment model and want a specific field validation for field 'content'.
Im thinking about a hack, pull off that full comment document from the database, then create a new schema object with the same properties from the comment document that i just pulled off from the database and validate this document model copy as if i were to create a new document, but i wont, i wont use the save() method. If there is an error with the 'content' field, which is the only one i care, i would know after validation, if there is no errors then i forget about that new object schema copy that i created by pulling off the comment document from the database, ill forget about it since i already know my 'content' field is valid since no errors where shown, so ill proceed with my flow.
Perhaps instead of pulling off that document from the database i can just create a new object with some fake but valid fields... Then pass the real value i want to test which in my case is 'content', i wouldnt fake that value since i already have it.
NOTE: my comment model has property 'createdAt' so i would replace that for the current date, cause i could have errors at validation saying new comment must be from current date and no from past dates, but since i wont be saving that new date to the database i can add the current date, recall that i will forget about that new object, i wont save it to the database, all i care is the 'content' field validation and see if there is any errors.