Skip to content

Instantly share code, notes, and snippets.

@Corky3892
Last active October 1, 2021 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Corky3892/2452203657e5a31423d990faddf4ebad to your computer and use it in GitHub Desktop.
Save Corky3892/2452203657e5a31423d990faddf4ebad to your computer and use it in GitHub Desktop.
Trying to pass the runValidators to the mongoose findOneAndUpdate() method does produce great results (as of 6.0.5). Here is a generic solution to the problem. PS: If any mongoose / typescript Guru's out there know how to make this truly generic (where type T can be inferred and not assigned) please do let me know.
import { FilterQuery, UpdateQuery, QueryOptions, Model, model } from 'mongoose';
/**
* As of mongoose 6.0.5 findOneAndUpdate does not handle the runValidators flag correctly.
* If you are running upsert in combination with this flag then the update will not get validated correctly.
* This is will fix that problem.
* @param filter The query which will be used to find the document.
* @param update The update which shall be applied.
* @param options The optional Query Options to extend the behavior.
*/
function findOneAndUpdateWithValidation<T>(
filter: FilterQuery<T>,
update: UpdateQuery<T>,
options?: QueryOptions
) {
const model: Model<T> = this;
const newDoc = new model(update);
if (options && options.runValidators) {
const error = newDoc.validateSync();
if (error) throw error;
}
return model.findOneAndUpdate(filter, update, options);
}
/**
* @jest-environment node
*/
import 'jest-extended';
import * as mongoose from 'mongoose';
import { Database } from '../../../global';
import { findOneAndUpdateWithValidation } from './findOneAndUpdateWithValidation';
interface Person {
firstName: string;
lastName: string;
age: number;
}
type PersonDoc = Person & mongoose.Document
interface PersonModel extends mongoose.Model<Person> {
findOneAndUpdateWithValidation: typeof findOneAndUpdateWithValidation
}
const PersonSchema = new mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
age: { type: Number, required: true },
})
PersonSchema.statics = {
findOneAndUpdateWithValidation,
}
const PersonModel = mongoose.model<PersonDoc, PersonModel>('person', PersonSchema);
describe('update validation tests', () => {
// References mongoose.connect()
new Database();
// If this test passed then it demonstrates that run validators does not work when inserting.
// Given that the age param is missing we expect an error, if you try however you will see the test passes.
test('findOneAndUpdate - runValidators does not work on upsert', async () => {
const rob = {
firstName: 'Rob',
lastName: 'Smith',
}
await expect(PersonModel.findOneAndUpdate({firstName: 'Rob'}, rob, {
runValidators: true,
upsert: true,
new: true
})).resolves.toMatchObject({
firstName: 'Rob',
lastName: 'Smith'
})
})
// If this test passed then it demonstrates that run validators does not work when updating.
// Given that the age param is missing we expect an error, if you try however you will see the test passes.
test('findOneAndUpdate - runValidators does not work on edit', async () => {
const rob = {
firstName: 'Rob',
lastName: 'Rogers',
}
await expect(PersonModel.findOneAndUpdate({firstName: 'Rob'}, rob, {
runValidators: true,
new: true
})).resolves.toMatchObject({
firstName: 'Rob',
lastName: 'Rogers'
})
})
// Test the custom method to see if validation works.
// The request now correctly returns an error.
test('findOneAndUpdateWithValidation - run Validators now gets processed', async () => {
const jason = {
firstName: 'Jason',
lastName: 'Jones',
}
expect(async () => await PersonModel.findOneAndUpdateWithValidation<PersonDoc>({firstName: 'Jason'}, jason, {
runValidators: true,
upsert: true,
new: true
})).rejects.toThrow();
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment