I have a function that creates a new user, using name, email, password and role. It validates name and email, but it does not validate the passoword.
This is the function that creates a new user:
const createNewUser = async () => {
try {
const response = await axios.post("/user/create", {
name: newUser.newName,
email: newUser.newEmail,
password: newUser.newPassword,
role: newUser.newRole,
});
if (response.status === 200) {
fetchAllUsers(token).then((res) => {
dispatch(dispatchGetAllUsers(res));
});
setNewUser({
newName: "",
newEmail: "",
newPassword: "",
newRole: 0,
});
}
else {
setErrorNew(response.msg);
}
} catch (error) {
setErrorNew(error.response.data.msg);
}
};
And this is the function that returns error message if input is empty:
const handleChangeNew = (ev) => {
const { id, value } = ev.target;
setErrorNew("");
setNewUser({ ...newUser, [id]: value });
};
I would like to return a error messaje if password input length is less than 6 characters
Just do something like this:
const handleChangeNew = (ev) => {
const { id, value } = ev.target;
if(value.length < 6 && id === 'newPassword') {
setErrorNew("Your password should contain minimum of 6 characters");
} else {
setErrorNew("");
}
setNewUser({ ...newUser, [id]: value });
};
Related
I'm trying to see if the userlookUp in the User.prototype.userExists function is true based on the UserChema.findOne() but for some unknown reason, the block is not being executed if its true. In this case, return this.errors.push('User already exists'), is not being executed.
I have some other error checks in another function, and they work great as they are supposed to (being shown in the browser console) except this one.
Looking for some help.
I appreciate it.
userController.js
const User = require('../models/User');
exports.login = function () {};
exports.logout = function () {};
exports.register = function (req, res) {
let user = new User(req.body);
user.register();
if (user.errors.length) {
res.send(user.errors);
} else {
res.send(user);
res.send('Congrats, there are no errors.');
}
};
exports.home = function (req, res) {
res.send('API up and running!');
};
User.js
const validator = require('validator');
const UserSchema = require('./UserSchema');
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
let User = function (data) {
this.data = data;
this.errors = [];
};
User.prototype.cleanUp = function () {
if (typeof this.data.username != 'string') {
this.data.username = '';
}
if (typeof this.data.email != 'string') {
this.data.email = '';
}
if (typeof this.data.password != 'string') {
this.data.password = '';
}
// get rid of any bogus properties
this.data = {
username: this.data.username.trim().toLowerCase(),
email: this.data.email.trim().toLowerCase(),
password: this.data.password,
};
};
User.prototype.validate = function () {
if (this.data.username == '') {
this.errors.push('You must provide a username.');
}
if (
this.data.username != '' &&
!validator.isAlphanumeric(this.data.username)
) {
this.errors.push('Username can only contain letters and numbers.');
}
if (!validator.isEmail(this.data.email)) {
this.errors.push('You must provide a valid email.');
}
if (this.data.password == '') {
this.errors.push('You must provide a password longer than 6 characters.');
}
if (this.data.password.length > 0 && this.data.password.length < 6) {
this.errors.push('The password must be longer than 6 characters.');
}
if (this.data.password.length > 50) {
this.errors.push('The password cannot exceed 50 characters.');
}
if (this.data.username.length < 3 && this.data.username.length > 15) {
this.errors.push('The username must be at least 3 characters.');
}
};
User.prototype.userExists = async function () {
try {
let userLookUp = await UserSchema.findOne({
email: this.data.email,
});
if (userLookUp) {
return this.errors.push('User already exists');
} else {
const avatar = gravatar.url(this.data.email, {
s: '200',
r: 'pg',
d: 'mm',
});
userLookUp = new UserSchema({
username: this.data.username,
email: this.data.email,
password: this.data.password,
avatar: avatar,
});
const salt = await bcrypt.genSalt(10);
userLookUp.password = await bcrypt.hash(this.data.password, salt);
await userLookUp.save();
}
} catch (e) {
console.log('there is a server problem');
}
};
User.prototype.register = function () {
// Step #1: Validate user data
this.cleanUp();
this.validate();
this.userExists();
// Step #2: See if user exists
// Step #3: Get users gravatar
// Step #4: Encrypt the password
// Step #5: Return jsonwebtoken
// Step #6: Only if there are no validation errors
// then save the user data into a database
};
module.exports = User;
In the User.register function you run some functions that are promises (async functions) which are not fulfilled before the User.register function returns.
You can do something like this:
User.prototype.register = async function () {
this.cleanUp();
this.validate();
await this.userExists();
};
...
exports.register = async function (req, res) {
let user = new User(req.body);
await user.register();
if (user.errors.length) {
res.send(user.errors);
} else {
res.send(user);
res.send('Congrats, there are no errors.');
}
};
Good Evening,
I am using the MERN stack to create an application. I am using the Stripe API to gather invoices and save some information about the invoice in MongoDB. Right now I have a function that updates my DB with any object that does not exist and I have a delete function that deletes any documents in my DB that have the paid: true. when an invoice is paid Stripe marks it paid, when its paid I want to delete it from my DB. What happens now is that I delete it and then update function runs and just puts it right back.
The data in my DB and from stripe are an array of objects:
DB Sample:
[
{
Id: String,
created: Number,
customer_email: String,
customer_name: String,
due_date: Number,
hosted_invoice_url: String,
paid: Boolean
}
]
Codebase functions:
// Update and store function
async function saveInvoice(stripeInvoice) {
const condition = {id: stripeInvoice.id};
const query = {
id: stripeInvoice.id,
created: stripeInvoice.created,
customer_email: stripeInvoice.customer_email,
customer_name: stripeInvoice.customer_name,
due_date: stripeInvoice.due_date,
hosted_invoice_url: stripeInvoice.hosted_invoice_url,
paid: stripeInvoice.paid,
};
const options = { upsert: true };
const update = await InvoiceModel.updateMany(condition, query, options).catch((e) => {console.log(e)});
if(update.nModified > 0 ) {
console.log('Number of Invoices added or updated to DB:', update.nModified)
}
}
// Find common ID
function findCommoneID(stripeInvoice, DBInvoices) {
var isSame = true;
if(DBInvoices.length <= 0 || stripeInvoice.length <= 0) {
console.log('An Error has occured with Database or Stripe Data')
return isSame = false;
} else {
stripeInvoice.forEach((StripeElement) => {
DBInvoices.forEach((DBElement) => {
if(StripeElement.id !== DBElement.id) {
saveInvoice(StripeElement, DBElement);
isSame = false;
}
})
});
}
console.log('IDS match')
return isSame;
}
// Delete Function
async function deletePaidInvoicesFromDB(dbInvoices) {
for(let i = 0; i < dbInvoices.length; i++) {
if(dbInvoices[i].paid === true ) {
console.log(dbInvoices[i].id);
const deletedInvoices = await InvoiceModel.deleteMany({paid: true});
console.log(deletedInvoices.deletedCount)
break;
}
}
}
The save function takes in the data gathered from the findCommonId() function and if the id doesn't exist it adds the stripe object to the DB.
// Save Function
async function saveInvoice(stripeInvoice) {
if(stripeInvoice.paid === true) {
return;
} else {
const condition = {id: stripeInvoice.id};
const query = {
id: stripeInvoice.id,
amount_due: stripeInvoice.amount_due,
created: stripeInvoice.created,
customer_email: stripeInvoice.customer_email,
customer_name: stripeInvoice.customer_name,
due_date: stripeInvoice.due_date,
hosted_invoice_url: stripeInvoice.hosted_invoice_url,
paid: stripeInvoice.paid,
};
const options = { upsert: true };
const update = await InvoiceModel.updateMany(condition, query, options).catch((e) => {console.log(e)});
if(update.nModified > 0 ) {
console.log('Number of Invoices added or updated to DB:', update.nModified)
}
}
}
// findCommoneId
function findCommoneID(stripeInvoice, DBInvoices) {
if(DBInvoices.length <= 0 || stripeInvoice.length <= 0) {
InvoiceModel.create(stripeInvoice, () => {console.log('Invoices saved to database')});
console.log('An Error has occured with Database or Stripe Data')
stripeInvoice.forEach((StripeElement) => {
if(StripeElement.paid === true) {
return;
}
})
stripeInvoice.forEach((StripeElement) => {
DBInvoices.forEach((DBElement) => {
if(StripeElement.id !== DBElement.id) {
saveInvoice(StripeElement);
}
})
});
}
console.log('IDS match')
}
i am trying to validate an address:
line1: Yup.string()
.test(
"Address Line 1 Validation Test",
"Please enter valid Line 1 address",
async (line1: string) => {
let delayTimer = null;
let isValid = false;
const doSearch = () => {
clearTimeout(delayTimer);
delayTimer = setTimeout(async () => {
const { data } = await axios.get<{ status: string }>(
"https://maps.googleapis.com/maps/api/geocode/json?components=country:USA",
{
params: {
address: line1,
key: GEOCODE_API_KEY,
},
}
);
console.log("line1: ", line1);
console.log("data: ", data);
isValid = data.status === "OK" ? true : false;
}, 1000); // Will do the ajax stuff after 1000 ms, or 1 s
};
doSearch();
return isValid;
}
)
.required("Line 1 is required"),
i want to integrate delay search like this so i don't flood my api everytime user type like this: AJAX: Delay for search on typing in form field
but it's doing api request everytime user type. how do i implement?
The problem is that you are actually never clearing the timeout.
Each time your handler runs, new delayTimer, isValid and doSearch variables are created and initialized. Those variables have to be placed in an outer scope. Something like this:
let delayTimer = null;
let isValid = false;
Yup.string()
.test(
'Address Line 1 Validation Test',
'Please enter valid Line 1 address',
async (line1: string) => {
clearTimeout(delayTimer);
delayTimer = setTimeout(async () => {
const {data} =
(await axios.get) <
{status: string} >
('https://maps.googleapis.com/maps/api/geocode/json?components=country:USA',
{
params: {
address: line1,
key: GEOCODE_API_KEY
}
});
console.log('line1: ', line1);
console.log('data: ', data);
isValid = data.status === 'OK';
}, 1000); // Will do the ajax stuff after 1000 ms, or 1 s
return isValid;
}
)
.required('Line 1 is required');
Now, even if that fixes your initial problem, there is another issue to address. Your function will always return a promise with the wrong value of isValid.
What you have to do depends on what you want, but I'll give you the following insight:
let delayTimer = null;
let isValid = false;
let resolveRef = null;
Yup.string()
.test(
'Address Line 1 Validation Test',
'Please enter valid Line 1 address',
async (line1: string) => {
clearTimeout(delayTimer);
if (resolveRef) {
resolveRef(isValid);
resolveRef = null;
}
return await new Promise((resolve) => {
resolveRef = resolve;
delayTimer = setTimeout(async () => {
const {data} =
(await axios.get) <
{status: string} >
('https://maps.googleapis.com/maps/api/geocode/json?components=country:USA',
{
params: {
address: line1,
key: GEOCODE_API_KEY
}
});
isValid = data.status === 'OK';
resolve(isValid);
resolveRef = null;
}, 1000);
});
}
)
.required('Line 1 is required');
Hope it works. Please let me know.
Im not sure why my state wont update using errorOption = key + 'Error' and then setting state with [errorOption]: true. Because my formErrors state uses the same initial key as formDetails, but with 'Error' at the end so i just concatenated it. I've tried using template literals as-well, but concatenation looks easier to read here.
I want it to validate form to check for empty and change error state to true
const defaultData = {
username: '',
password: '',
}
const defaultErrorState = {
usernameError: false,
passwordError: false,
}
const [formDetails, setFormDetails] = useState(defaultData);
const [formErrors, setFormError] = useState(defaultErrorState);
const {
username,
password
} = formDetails
const {
usernameError,
passwordError
} = formErrors
const validateForm = (inputs) => {
const checkThreshold = (currentValue) => currentValue.length > 0;
let errorOption
Object.entries(inputs).forEach(([key, value]) => {
if (!checkThreshold(value)) {
errorOption = key + 'Error'
setFormError({
...formErrors,
[errorOption]: true
})
}
})
Object.entries(inputs).every(checkThreshold)
}
const handleSubmit = (event) => {
const basicInfoValues = {
username: username,
password: password,
}
if (validateForm(basicInfoValues)){
// login stuff
} else {alert("Fill in all fields")}
}
I am having an issue with Vue Test Utils. When I run a unit test, I am always confronted with:
TypeError{line: 73983, sourceURL: 'http://localhost:9876/base/index.js?045b00affe888fcd6b346c4fe50eadd13d471383', stack: 'mounted#http://localhost:9876/base/index.js?045b00affe888fcd6b346c4fe50eadd13d471383:73983:30.....
This only happens when I have the mounted() function in the Vue component
Settings.vue
mounted() {
this.$refs.address.update(this.profile.address)
},
Settings.spec.js
it('calls updateUserInformation before mount', () => {
const spy = sinon.spy(Settings.methods, 'updateUserInformation')
shallow(Settings, { propsData })
Vue.nextTick().then(() => {
spy.should.have.calledOnce()
})
})
I am using Mocha & Chai with vue-test-utils. Does anyone know why this is happening?
Thank you in advance!
UPDATE
Settings.vue component HTML
<vue-google-autocomplete
ref="address"
id="map"
classname="input"
placeholder="Address"
v-on:placechanged="getAddressPlaceChanged"
v-on:inputChange="getAddressInputChange"
:country="['sg']"
>
</vue-google-autocomplete>
Settings.vue component Javascript
export default {
components: {
GoogleMaps,
AutoComplete,
VueGoogleAutocomplete,
Partner,
},
watch: {
// whenever school changes, this function will run
school() {
// Check if school value is an empty string or character is lesser than FIX_STR_LENGTH
if (this.school === '' || this.school.length < this.FIX_STR_LENGTH) {
this.removeMarker('school')
}
this.fetchSchools()
},
},
methods: {
async onSubmit() {
// Check if input fields are empty
if (this.address !== undefined && this.phoneNumber !== null && this.school !== '') {
const { placeResultData = {}, addressData = {} } = this.address
let isSuccessful = false
let tempLat = null
let tempLong = null
let tempAddress = null
// Check if address is an empty object
if (_.isEmpty(this.address)) {
const { latlong = {}, address = '' } = this.profile
const [lat, long] = latlong.coordinates
tempLat = lat
tempLong = long
tempAddress = address
} else {
// User changed address location
tempLat = addressData.latitude
tempLong = addressData.longitude
tempAddress = placeResultData.formatted_address
}
// Validate school address array
let tempSchoolAddress = []
if (this.selectedSchool !== null) {
tempSchoolAddress.push(this.selectedSchool.postal_code)
} else {
tempSchoolAddress = this.profile.schoolAddress
}
// Construct user object for registration/update
const user = new User(
this.profile.name,
tempAddress,
tempLat,
tempLong,
tempSchoolAddress,
)
// If user does not exist in database, perform a POST API registration request
if (this.userExist === false) {
// Add user properties for user registration
user.phoneNumber = this.phoneNumber
await UserSession.register(user, localStorage.getItem('id_token')).then((response) => {
const { data = {} } = response
const profile = data.user
this.updateUserInformation(profile)
isSuccessful = true
this.profile = profile
}).catch((error) => {
console.log(error.response)
})
}
// Perform a PUT API update request
await UserSession.update(user, localStorage.getItem('id_token')).then((response) => {
const { data = {} } = response
const profile = data.user
this.updateUserInformation(profile)
isSuccessful = true
this.profile = profile
}).catch((error) => {
console.log(error.response)
})
if (isSuccessful) {
this.profileChanged()
this.hasChanged = true
}
}
},
profileChanged() {
this.$emit('profileChanged', this.profile)
},
addMarker(name, params) {
if (params === null || params === '') {
return
}
gMapSession.default(params).then((response) => {
const { location = {} } = response.data.results[0].geometry
// Remove existing marker before replacing it
this.removeMarker(name)
this.markers.push({
position: location,
name,
})
this.zoom = 11
}).catch((error) => {
console.log(error.response)
})
},
removeMarker(name) {
let index = 0
let exist = false
for (let i = 0; i < this.markers.length; i++) {
if (this.markers[i].name === name) {
index = i
exist = true
break
}
}
if (exist) {
this.markers.splice(index, 1)
}
},
// Function called when user selects an option from the school autocomplete dropdown
getSelectedSchoolData(event) {
this.selectedSchool = event
// Check if selected school is defined
if (this.selectedSchool !== undefined && this.selectedSchool !== null) {
this.addMarker('school', this.selectedSchool.postal_code)
} else {
this.removeMarker('school')
}
},
// Function called when user types in the address autocomplete input field
getAddressInputChange(data) {
const { newVal = {} } = data
if (newVal === '' || newVal.length < this.FIX_STR_LENGTH) {
this.removeMarker('user')
}
},
// Function called when user selects an option from the address autocomplete dropdown
getAddressPlaceChanged(addressData, placeResultData) {
this.address = {
placeResultData,
addressData,
}
if (addressData !== undefined && addressData !== null) {
this.addMarker('user', addressData.postal_code)
} else {
this.removeMarker('user')
}
},
async updateUserInformation(profile) {
this.phoneNumber = profile.phoneNumber
this.addMarker('user', profile.address)
// TODO: schoolAddress is an array and UI must cater to multiple schools
await SchoolSession.default(profile.schoolAddress[0]).then(async (res) => {
const { result = {} } = res.data
const { records = [] } = result
// Assume that a single postal code contains only 1 school
this.school = records[0].school_name
this.addMarker('school', records[0].postal_code)
})
},
// Fetch school information base on school search query
fetchSchools: _.debounce(function getSchools() {
if (this.school.trim() === '') {
this.schools = []
return
}
const vm = this
SchoolSession.default(this.school).then((response) => {
// JSON responses are automatically parsed.
const { records = {} } = response.data.result
vm.schools = records
}).catch((error) => {
console.log(error.response)
})
}, 500),
},
data() {
return {
FIX_STR_LENGTH: 5,
school: '',
address: '',
schools: [],
markers: [],
phoneNumber: null,
selectedSchool: null,
userExist: false,
hasChanged: false,
center: { lat: 1.3521, lng: 103.8198 },
zoom: 7,
}
},
async created() {
this.profile = this.$attrs
// Check if user was a registered member by phone number
if (this.profile.phoneNumber === undefined) {
return
}
// User exist in the database
this.userExist = !this.userExist
// Update form information
this.updateUserInformation(this.profile)
},
mounted() {
this.$refs.address.update(this.profile.address)
},
}