Mongoose schema getter recursion - node.js

I'm having trouble understanding how to use a getter with a mongoose schema I'm using for an API. I'm using the ipaddr.js module for the ipaddr methods.
I have this field defined in my schema:
ipv4_address: {
type: Buffer,
required: false,
get: function () {
//For debugging, actually want it returned
console.log(this.ipv4_address.toString());
},
set: function (v) {
return ipaddr.parse(v).toByteArray();
},
select: false,
index: true
}
I've set getters to true so that I can see what's going on in the console, ultimately I just want the ipv4_address returned in the JSON result.
ClientSchema.set('toObject', {
getters: true
});
ClientSchema.set('toJSON', {
getters: true
});
I understand that this line:
console.log(this.ipv4_address.toString());
causes the setter to be called recursively resulting in a RangeError: Maximum call stack size exceeded. The reason for this I understand completely. If I were reading from _ipv4_address this wouldn't happen, but the schema defines the variable name.
This is apart of a REST API and I'd like the POSTs to write the ipv4_address as that field name. I'd like the field name to be the same when GETing the result.
I'm just very confused on how I should get around this.

The getter function is passed the current field value, so you don't need to pull it out of this, and you can avoid the recursion:
get: function (me) {
//For debugging, actually want it returned
console.log(me.toString());
},

Related

Check if a document has already been created in mongoose validator?

In one of my conditional schema validation I need to know if the current document has already
been created before returning true or false.
This is the kind of logic I'm looking for (in pseudo code):
password: {
type: String,
trim: true,
required: function () {
if (this.password == '') {
// Pseudo code:
return document.exists ? false : true;
}
}
},
How can I do that with real code and is it the correct way or should I look for a different approach ?
note: I'm aware of the exists() method but I don't know how to use it in a schema validation situation.

Mongoose post hook Type error in TypeScript

I am using mongoose and typescript, when I call updateOne() on the model I want to use
someSchema.post<Query>('updateOne', function(doc: IsomeDoc) {
console.log(this)
}
The problem is that this is of type Query if I suppress typescript checker and ignore as it give me an error:
Generic type 'Query<ResultType, DocType, THelpers>' requires between 2 and 3 type arguments.
This is my schema
const someSchema = new Schema(
{
_id: { type: String, required: true },
status: { type: String },
},
{ timestamps: true }
)
How can I get the correct type for this inside the function? After lots of searching there is barely any use of post hook with typescript and mongoose.
What is ResultType?
Edit:
After seeing #gregooroo's answer I was able to get past Query by making it Query<IsomeDoc, IsomeDoc> but it does not give me the correct type for this object.
For query middlewares you need to construct a Query type with a generic type of what this query should return
someSchema.post<Query<IsomeDoc, IsomeDoc>>('updateOne', function(doc) {
console.log(this)
}

How to get only required fileds from Mongoose Schema?

I'm looking for a way to send a custom error message to the client when a required input is not set.
A sample Mongoose Schema:
this._schema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
}
});
I use a this._schema.pre('validate') function to test if a required input is missing and, if it is missing, pass a custom AppError to my next() function which will handle the rest.
The pre-validation:
this._schema.pre('validate', function(next) {
if(!this.email) return next(new AppError('Email not set'));
next();
});
I don't want to check for each field manually using a list of if statements. Rather I would like to loop over all required fields and throw the error if one of them is missing (loop should be recursive too for nested fields).
Question:
How can I loop over required fields and pass the first missing field to an Error?
Note: I'm not sure if this is the right way, so other interpretations are welcome! Found something here, but I'm not sure that's the correct answer.

Check with Chai if an object doesn't contain any additional properties

I'm working on an API and should return based on permissions only a subset of the actual object's properties. I'm writing my tests in mocha and chai and would like to test for something like this (given res is the response object from the server and res.body contains the received JSON data):
res.body.should.not.contain.properties.except(['prop1', 'prop2.subprop'])
in which case res.body.prop1 can be any kind of object, and res.body.prop2 is only allowed to contain the property subprop - which again could be any kind of object.
Now, I could write custom functions to test this, but I thought someone else had already a similar problem and there is an extension for chai for it maybe or some other library I could use instead.
Out of the box, I do not think Chai offers a way to build a query like this. However, a JSON Schema is a perfect fit for testing if an object matches a certain format. Luckily, a Chai JSON Schema Plugin exists. Using that, the result looks like this:
chai.use(require('chai-json-schema'));
var bodySchema = {
title: 'response body',
type: 'object',
required: ['prop1', 'prop2'],
additionalProperties: false,
properties: {
prop1: {},
prop2: {
type: 'object',
required: ['subprop'],
additionalProperties: false,
properties: {
subprop: {}
}
}
}
};
res.body.should.be.jsonSchema(bodySchema);
A short explanation:
The required property takes an array of required properties. If prop1 or prop2 are actually optional, remove them from this array (or leave it out alltogether).
The additionalProperties: false ensures no properties other than the ones defined in the properties hash are allowed.
prop2 contains a subschema, which can contain the same fields as the root schema and specifies the format of the sub-property.
Granted, these schema's can grow a bit large, but so would your validation function. Of course you can make the schema a true JSON file to separate it from the rest of your code.

Validation errors in custom instance or static methods in a Mongoose model

I have a basic Mongoose model with a Meeting and Participants array:
var MeetingSchema = new Schema({
description: {
type: String
},
maxNumberOfParticipants: {
type: Number
},
participants: [ {
type: Schema.ObjectId,
ref: 'User'
} ]
});
Let's say I want to validate that the number of participants added doesn't exceed the maxNumberOfParticipants for that meeting.
I've thought through a few options:
Custom Validator - which I can't do because I have to validate one attribute (participants length) against another (maxNumberOfParticipants).
Middleware - i.e., pre-save. I can't do this either because my addition of participants occurs via a findOneAndUpdate (and these don't get called unless I use save).
Add validation as part of my addParticipants method. This seems reasonable, but I'm not sure how to pass back a validation error from the model.
Note that I don't want to implement the validation in the controller (express, MEAN.js stack) because I'd like to keep all logic and validations on the model.
Here is my addParticipants method:
MeetingSchema.methods.addParticipant = function addParticipant(params, callback) {
var Meeting = mongoose.model('Meeting');
if (this.participants.length == this.maxNumberOfParticipants) {
// since we already have the max length then don't add one more
return ????
}
return Meeting.findOneAndUpdate({ _id: this.id },
{ $addToSet: { participants: params.id } },
{new: true})
.populate('participants', 'displayName')
.exec(callback);
};
Not sure how to return a validation error in this case or even if this pattern is a recommended approach.
I wouldn't think that's it's common practice for this to be done at the mongoose schema level. Typically you will have something in between the function getting called and the database layer (your schema) that performs some kind of validation (such as checking max count). You would want your database layer to be in charge of just doing simple/basic data manipulation that way you don't have to worry about any extra dependencies when/if anything else calls it. This may mean you'd need to go with route 1 that you suggested, yes you would need to perform a database request to find out what your current number of participants but I think it the long run it will help you :)

Resources