How to get objectId of user in mongoose - node.js

I am currently working on a user registration setup. In the user model, I have defined apiKey as a property of the user and I want to set it equal to the user object Id. How can I do this ? Here is my code for the model :
const userScheama = new mongoose.Schema(
{
email: {
type: String,
trim: true,
required: true,
unique: true,
lowercase: true
},
name: {
type: String,
trim: true,
required: true
},
hashed_password: {
type: String,
required: true
},
apiKey:{
type: String,
required: false,
},
plan:{
type: String,
required: false
},
salt: String,
role: {
type: String,
default: 'subscriber'
},
resetPasswordLink: {
data: String,
default: ''
}
},
{
timestamps: true
}
);

You can use mongoose hooks here. First you need to update your apiKey to apiKey: {type:Schema.Types.ObjectId required: false }, and then add the below code to your model file
userScheama.pre('save', async function (next) {
this.apiKey = this._id;
next();
});

Related

How to allow unique fields in subdocuments when using mongoose?

I'm using mongoose to to define 2 schemas.
employee.js
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const Role = require("./role");
const employeeSchema = mongoose.Schema({
code: { type: String, required: true, unique: true, index: true },
names: { type: String, required: true },
last_names: { type: String, required: true },
role: Role.schema,
dui: { type: String, required: true, unique: true, index: true },
nit: { type: String, required: false, unique: true, index: true },
sex: { type: String, required: false },
civil_status: { type: String, required: false },
birthday: { type: Date, required: false },
telephone: { type: String, required: true },
city: { type: String, required: true },
address: { type: String, required: true },
active: { type: Boolean, required: true },
});
employeeSchema.plugin(uniqueValidator);
module.exports = mongoose.model("Employee", employeeSchema);
and role.js
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const roleSchema = mongoose.Schema({
code: { type: String, required: true, unique: true, index: true },
description: { type: String, required: true }
});
roleSchema.plugin(uniqueValidator);
module.exports = mongoose.model("Role", roleSchema);
The problem I have is that, whenever I insert a document with a role code that's repeated I get an error because of the unique validation. I have tried deleting
roleSchema.plugin(uniqueValidator);
from role.js and I have also tried using .set to alter the field in role.
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const Role = require("./role");
const subRole = Role.schema.clone().set('code', {unique: false}).set('code', {index : false});
const employeeSchema = mongoose.Schema({
code: { type: String, required: true, unique: true, index: true },
names: { type: String, required: true },
last_names: { type: String, required: true },
role: subRole,
dui: { type: String, required: true, unique: true, index: true },
nit: { type: String, required: false, unique: true, index: true },
sex: { type: String, required: false },
civil_status: { type: String, required: false },
birthday: { type: Date, required: false },
telephone: { type: String, required: true },
city: { type: String, required: true },
address: { type: String, required: true },
active: { type: Boolean, required: true },
});
employeeSchema.plugin(uniqueValidator);
module.exports = mongoose.model("Employee", employeeSchema);
I always get an error saying that role code needs to be unique. Am I using .set incorrectly? or what am I missing? Thank you very much in advance.
Solved it changing:
const subRole = Role.schema.clone().set('code', {unique: false}).set('code', `{index : false});`
to
const subRole = Role.schema.clone().set('excludeIndexes', true);
Deleted the employees's collection so it would recreate it with the right indexes and that did the trick.
It was mentioned in one of mongoose's issues in github:
https://github.com/Automattic/mongoose/issues/11547
Supposedly they implemented something about it but I didn't get it. The above worked for me though.

MongoDB schema validation with nested array or object in strict mode

i have schema which is nested
so need to validate whole schema, like if it has an extra attributes then throw error
i have tried with strict: 'throw' but that work only for main attributes, not for nested attributes.
const { Schema } = mongoose;
const DatasourceSchema = new Schema({
id: {
type: String,
unique: true,
required: true,
},
display_name: {
type: String,
required: true,
maxlength: 125
},
contact_info: {
type: Schema({
website: {
type: String,
required: false
},
registrar_phone: { type: String },
registrar_email: { type: String },
addresses: [{
type: Schema({
addr1: { type: String, required: false },
addr2: { type: String, required: false },
country: {
name: { type: String, required: true },
a2_code: { type: String, required: true },
}
}),
required: false
}]
}),
required: true,
}
},
{
strict: 'throw',
useNestedStrict: true
});
what i need is if i add any extra KEY (attributes) in object or array at any level that will throw errors

How to Access a Field in Array of Objects With Joi Fork Method?

I am trying to write a reusable validation schema and I can change the rules of fields if I need it. I do this using a method called a fork. However, this time I couldn't imagine how can I access and change the rule of an object inside of an array. In some cases, some fields must be required. So I call the changed schema the default schema with the validation method. I am using this solution for a few models and generally, it works perfectly. Can you help me to imagine how can I solve this problem?
In this model, I have a subdocument field. This field is an array of objects field. That's why I have 2 different schemas. When the create method calls, I just need the title, description, and category object fields. If a user wants to add a question to the quiz record, I need a question array.
I was thinking that I can write a validator for only the question schema but if I don't add a field to the quiz validation schema then joi throws an error with the message "ABC field not allowed". I'm stuck because of this situation and I can't continue.
Quiz Model and Validation Schema and Method
const Joi = require('joi');
const mongoose = require('mongoose');
const slugCreator = require('mongoose-slug-updater');
mongoose.plugin(slugCreator);
const QuestionSchema = mongoose.Schema({
questionText: {
type: String,
trim: true,
minLength: 10
},
firstChoiceText: {
type: String,
trim: true,
minLength: 1
},
firstChoiceIsTrue: {
type: Boolean
},
secondChoiceText: {
type: String,
trim: true,
minLength: 1
},
secondChoiceIsTrue: {
type: Boolean
},
thirdChoiceText: {
type: String,
trim: true,
minLength: 1
},
thirdChoiceIsTrue: {
type: Boolean
},
fourthChoiceText: {
type: String,
trim: true,
minLength: 1
},
fourthChoiceIsTrue: {
type: Boolean
}
}, { timestamps: true });
const QuizSchema = mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
minLength: 15,
maxLength: 250
},
description: {
type: String,
requried: true,
trim: true,
minLength: 50
},
coverImage: {
type: String,
trim: true
},
slug: {
type: String,
unique: true,
trim: true,
slug: ['title'],
slugPaddingSize: 3
},
category: {
title: {
type: String,
trim: true,
required: true
},
categoryId: {
type: mongoose.Types.ObjectId,
trim: true,
required: true
},
slug: {
type: String,
trim: true,
required: true
}
},
questions: [QuestionSchema],
createdBy: {
userId: {
type: mongoose.Types.ObjectId,
required: true
},
fullName: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
trim: true
}
},
updatedBy: {
userId: {
type: mongoose.Types.ObjectId,
required: true
},
fullName: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
trim: true
}
}
}, { collection: 'quizzes', timestamps: true });
const validationSchema = {
title: Joi.string().trim().min(15).max(250),
description: Joi.string().trim().min(50),
coverImage: Joi.string().trim(),
slug: Joi.string().trim(),
category: {
title: Joi.string().trim(),
categoryId: Joi.string().trim(),
slug: Joi.string().trim()
},
questions: Joi.array().items(
Joi.object({
questionText: Joi.string().trim().min(10),
firstChoiceText: Joi.string().trim().min(1),
firstChoiceIsTrue: Joi.boolean(),
secondChoiceText: Joi.string().trim().min(1),
secondChoiceIsTrue: Joi.boolean(),
thirdChoiceText: Joi.string().trim().min(1),
thirdChoiceIsTrue: Joi.boolean(),
fourthChoiceText: Joi.string().trim().min(1),
fourthChoiceIsTrue: Joi.boolean(),
})
),
createdBy: {
userId: Joi.string().trim(),
fullName: Joi.string().trim(),
email: Joi.string().email().trim()
},
updatedBy: {
userId: Joi.string().trim(),
fullName: Joi.string().trim(),
email: Joi.string().email().trim()
}
};
QuizSchema.statics.joiValidationForQuizCreate = async (quizObject) => {
const requiredSchema = Joi.object(validationSchema).fork(['title', 'description', 'createdBy.userId', 'createdBy.fullName', 'createdBy.email', 'updatedBy.userId', 'updatedBy.email', 'updatedBy.email'], item => item.required());
return await requiredSchema.validateAsync(quizObject);
};
module.exports = mongoose.model('quiz', QuizSchema);
I want to access these Question fields using the Fork method as in the joiValidationForQuizCreate method. Is this possible or is there a better method available? I don't want to write schematics over and over on a case-by-case basis.

What is the correct way to enter an Array?

I want to enter data via Postman into an array form using id Collection in the database also how can I write a valid JSON script for the modal for the modal
Add data using id Collection
controller
const addAcademicExperience = async (req, res, next) => {
//const id = req.params.id;
const {AcademicExperience} = req.body;
let academicexperience;
try {
academicexperience = await AcademicExperience.findByIdAndadd(id, {
AcademicExperience
});
await academicexperience.save();
} catch (err) {
console.log(err);
}
if (!academicexperience) {
return res.status(404).json({ message: 'Unable to Add' })
}
return res.status(200).json({academicexperience});
model Schema
Some data is required in an array and some are not
per user per user
To clarify, the site is similar to LinkedIn
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const FacultySchema = new Schema({
Faculty_ID: {
type: Number,
required: true,
unique: true,
},
Name: {
type: String,
required: true,
},
Phone_Number: {
type: String,
required: true,
},
Email: {
type: String,
required: true,
},
AcademicExperience: [{
institution: { type: String, required: true },
rank: { type: String, required: true },
title: { type: String, required: true },
working: { type: Boolean, required: true },
}
],
Certifications:
{
type: String,
require: true,
},
Currentm_embership:
{
type: String,
require: true,
},
Servicea_ctivites:
{
type: String,
require: true,
},
Professional:
{
type: String,
require: true,
},
Education:[ {
degree: { type: String, required: true },
discpilne: { type: String, required: true },
institution: { type: String, required: true },
year: { type: Date, required: true },
}
],
Non_academic_experines:[
{
Company: { type: String, required: true },
title: { type: String, required: true },
working: { type: Boolean, required: true },
Description_of_position: { type: String, required: true },
}
],
Honoers_and_awards:
{
type: String,
require: true,
},
Puplications_and_presentation:
{
type: String,
require: true,
},
});
module.exports = mongoose.model("Faculty", FacultySchema);

How to prevent changes from services

I'm using Feathers.js with Mongoose and I want to create a field that cannot be changed by the services.
// account-model.js - A mongoose model
//
// See http://mongoosejs.com/docs/models.html
// for more of what you can do here.
const mongoose = require('mongoose');
require('mongoose-type-email');
module.exports = function(app) {
const mongooseClient = app.get('mongooseClient');
const recovery = new mongooseClient.Schema({
token: { type: String, required: true }
}, {
timestamps: true
});
const account = new mongooseClient.Schema({
firstName: { type: String, required: true },
surname: { type: String, require: true },
phone: { type: String, require: true },
email: { type: mongoose.SchemaTypes.Email, required: true, unique: true },
birth: { type: Date, required: true },
gender: { type: String, required: true },
country: { type: String, required: true },
address: { type: String, required: true },
address2: { type: String, required: false },
city: { type: String, required: true },
postcode: { type: String, required: true },
password: { type: String, required: true },
status: { type: String, required true }
}, {
timestamps: true
});
return mongooseClient.model('account', account);
};
No one can make a post at /account/<id> and change the field status.
This field should only be changed when internally. When some approval service request.
How can I implement this behavior?
This is a perfect use case for Feathers hooks. When accessed externally, in a service method call params.provider will be set so you can check for it and remove the field from data if it is:
module.exports = function() {
return async context => {
if(context.params.provider) {
delete context.data.status;
}
}
}
This hook will be a before hook for the create, update and patch method.

Resources