diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ad98e77aa83..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: node_js -sudo: false -node_js: [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4] -install: - - travis_retry npm install -before_script: - - wget http://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.0.2.tgz - - tar -zxvf mongodb-linux-x86_64-ubuntu1604-4.0.2.tgz - - mkdir -p ./data/db/27017 - - mkdir -p ./data/db/27000 - - printf "\n--timeout 8000" >> ./test/mocha.opts - - ./mongodb-linux-x86_64-ubuntu1604-4.0.2/bin/mongod --fork --dbpath ./data/db/27017 --syslog --port 27017 - - export PATH=`pwd`/mongodb-linux-x86_64-ubuntu1604-4.0.2/bin/:$PATH - - sleep 2 - - mongod --version - - mkdir ./test/typescript/node_modules - - ln -s `pwd` ./test/typescript/node_modules/mongoose -matrix: - include: - - name: "đŸ‘•Linter" - node_js: 10 - before_script: skip - script: npm run lint -notifications: - email: false diff --git a/History.md b/History.md index c4948fa7b07..6e890ba35d5 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,15 @@ +5.11.18 / 2021-02-23 +==================== + * fix(connection): set connection state to `disconnected` if connecting string failed to parse #9921 + * fix(connection): remove `db` events deprecation warning if `useUnifiedTopology = true` #9930 + * fix(connection): fix promise chaining for openUri #9960 [lantw44](https://github.com/lantw44) + * fix(index.d.ts): add `PopulatedDoc` type to make it easier to define populated docs in interfaces #9818 + * fix(index.d.ts): allow explicitly overwriting `toObject()` return type for backwards compatibility #9944 + * fix(index.d.ts): correctly throw error when interface path type doesn't line up with schema path type #9958 [ShadiestGoat](https://github.com/ShadiestGoat) + * fix(index.d.ts): remove `any` from `deleteX()` and `updateX()` query params and return values #9959 [btd](https://github.com/btd) + * fix(index.d.ts): add non-generic versions of `Model.create()` for better autocomplete #9928 + * docs: correctly handle multiple `>` in API descriptions #9940 + 5.11.17 / 2021-02-17 ==================== * fix(populate): handle `perDocumentLimit` when multiple documents reference the same populated doc #9906 diff --git a/docs/populate.pug b/docs/populate.pug index a9cee2d1031..b0c193d3f98 100644 --- a/docs/populate.pug +++ b/docs/populate.pug @@ -220,7 +220,7 @@ block content // prints "The author is Ian Fleming" console.log('The authors age is %s', story.author.age); - // prints "The authors age is null' + // prints "The authors age is null" }); ``` diff --git a/index.d.ts b/index.d.ts index 8e64f799d7e..9e87d6bc8a7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1189,9 +1189,9 @@ declare module 'mongoose' { } type SchemaDefinitionWithBuiltInClass = T extends number - ? (typeof Number | 'number' | 'Number') + ? (typeof Number | 'number' | 'Number' | typeof Schema.Types.Number) : T extends string - ? (typeof String | 'string' | 'String') + ? (typeof String | 'string' | 'String' | typeof Schema.Types.String) : (Function | string); type SchemaDefinitionProperty = T extends string | number | Function @@ -1371,8 +1371,13 @@ declare module 'mongoose' { currentTime?: () => (Date | number); } + type Unpacked = T extends (infer U)[] ? U : T; + interface SchemaTypeOptions { - type?: T extends string | number | Function ? SchemaDefinitionWithBuiltInClass : T; + type?: + T extends string | number | Function ? SchemaDefinitionWithBuiltInClass : + T extends object[] ? T | Schema & Document>[] : + T; /** Defines a virtual with the given name that gets/sets this path. */ alias?: string; diff --git a/index.pug b/index.pug index 75ffbb0dfb7..b975e44bdba 100644 --- a/index.pug +++ b/index.pug @@ -169,9 +169,6 @@ html(lang='en') - - - @@ -259,9 +256,6 @@ html(lang='en') - - - diff --git a/lib/document.js b/lib/document.js index 042b48ed342..9a9c630e342 100644 --- a/lib/document.js +++ b/lib/document.js @@ -2500,7 +2500,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) { const doValidateOptions = { skipSchemaValidators: skipSchemaValidators[path], - path: path + path: path, + validateModifiedOnly: shouldValidateModifiedOnly }; schemaType.doValidate(val, function(err) { if (err && (!schemaType.$isMongooseDocumentArray || err.$isArrayValidatorError)) { @@ -2626,7 +2627,8 @@ Document.prototype.validateSync = function(pathsToValidate, options) { const val = _this.$__getValue(path); const err = p.doValidateSync(val, _this, { skipSchemaValidators: skipSchemaValidators[path], - path: path + path: path, + validateModifiedOnly: shouldValidateModifiedOnly }); if (err && (!p.$isMongooseDocumentArray || err.$isArrayValidatorError)) { if (p.$isSingleNested && diff --git a/lib/helpers/timestamps/setupTimestamps.js b/lib/helpers/timestamps/setupTimestamps.js index 76140099df8..904485b0ba8 100644 --- a/lib/helpers/timestamps/setupTimestamps.js +++ b/lib/helpers/timestamps/setupTimestamps.js @@ -86,6 +86,7 @@ module.exports = function setupTimestamps(schema, timestamps) { _setTimestampsOnUpdate[symbols.builtInMiddleware] = true; const opts = { query: true, model: false }; + schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate); schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate); schema.pre('replaceOne', opts, _setTimestampsOnUpdate); schema.pre('update', opts, _setTimestampsOnUpdate); @@ -96,6 +97,12 @@ module.exports = function setupTimestamps(schema, timestamps) { const now = currentTime != null ? currentTime() : this.model.base.now(); + + // Replacing with null update should still trigger timestamps + if (this.op === 'findOneAndReplace' && this.getUpdate() == null) { + this.setUpdate({}); + } + applyTimestampsToUpdate(now, createdAt, updatedAt, this.getUpdate(), this.options, this.schema); applyTimestampsToChildren(now, this.getUpdate(), this.model.schema); diff --git a/lib/query.js b/lib/query.js index 3e1943e3cc0..9d3df0931c6 100644 --- a/lib/query.js +++ b/lib/query.js @@ -3331,6 +3331,7 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options, callb } this.setOptions(options); + this.setOptions({ overwrite: true }); if (!callback) { return this; diff --git a/lib/schema/documentarray.js b/lib/schema/documentarray.js index 1d50941f239..196269437ce 100644 --- a/lib/schema/documentarray.js +++ b/lib/schema/documentarray.js @@ -251,6 +251,11 @@ DocumentArrayPath.prototype.doValidate = function(array, fn, scope, options) { doc = array[i] = new Constructor(doc, array, undefined, undefined, i); } + if (options.validateModifiedOnly && !doc.isModified()) { + --count || fn(error); + continue; + } + doc.$__validate(callback); } } @@ -267,7 +272,7 @@ DocumentArrayPath.prototype.doValidate = function(array, fn, scope, options) { * @api private */ -DocumentArrayPath.prototype.doValidateSync = function(array, scope) { +DocumentArrayPath.prototype.doValidateSync = function(array, scope, options) { const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope); if (schemaTypeError != null) { schemaTypeError.$isArrayValidatorError = true; @@ -299,6 +304,10 @@ DocumentArrayPath.prototype.doValidateSync = function(array, scope) { doc = array[i] = new Constructor(doc, array, undefined, undefined, i); } + if (options != null && options.validateModifiedOnly && !doc.isModified()) { + continue; + } + const subdocValidateError = doc.validateSync(); if (subdocValidateError && resultError == null) { diff --git a/package.json b/package.json index 9ac4f5ecb4d..a18c2af0db4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose", "description": "Mongoose MongoDB ODM", - "version": "5.11.17", + "version": "5.11.18", "author": "Guillermo Rauch ", "keywords": [ "mongodb", @@ -63,7 +63,7 @@ "pug": "2.0.3", "q": "1.5.1", "semver": "5.5.0", - "typescript": "4.x", + "typescript": "4.1.x", "uuid": "2.0.3", "uuid-parse": "1.0.0", "validator": "10.8.0", diff --git a/test/document.test.js b/test/document.test.js index 27b9b83c16f..7f0a225531b 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -2519,15 +2519,21 @@ describe('document', function() { }); }); - it('filters out validation on unmodified paths when validateModifiedOnly set (gh-7421)', function(done) { - const testSchema = new Schema({ title: { type: String, required: true }, other: String }); + it('filters out validation on unmodified paths when validateModifiedOnly set (gh-7421) (gh-9963)', function(done) { + const testSchema = new Schema({ + title: { type: String, required: true }, + other: String, + subdocs: [{ name: { type: String, required: true } }] + }); const Test = db.model('Test', testSchema); - Test.create([{}], { validateBeforeSave: false }, function(createError, docs) { + const doc = { subdocs: [{ name: null }, { name: 'test' }] }; + Test.create([doc], { validateBeforeSave: false }, function(createError, docs) { assert.equal(createError, null); const doc = docs[0]; doc.other = 'something'; + doc.subdocs[1].name = 'test2'; assert.equal(doc.validateSync(undefined, { validateModifiedOnly: true }), null); doc.save({ validateModifiedOnly: true }, function(error) { assert.equal(error, null); diff --git a/test/timestamps.test.js b/test/timestamps.test.js index d37558cd3d2..b31369d6089 100644 --- a/test/timestamps.test.js +++ b/test/timestamps.test.js @@ -582,7 +582,7 @@ describe('timestamps', function() { }); }); - it('should have fields when create with findOneAndUpdate', function(done) { + it('sets timestamps on findOneAndUpdate', function(done) { Cat.findOneAndUpdate({ name: 'notexistname' }, { $set: {} }, { upsert: true, new: true }, function(err, doc) { assert.ok(doc.createdAt); assert.ok(doc.updatedAt); @@ -591,6 +591,14 @@ describe('timestamps', function() { }); }); + it('sets timestamps on findOneAndReplace (gh-9951)', function() { + return Cat.findOneAndReplace({ name: 'notexistname' }, {}, { upsert: true, new: true }).then(doc => { + assert.ok(doc.createdAt); + assert.ok(doc.updatedAt); + assert.ok(doc.createdAt.getTime() === doc.updatedAt.getTime()); + }); + }); + it('should change updatedAt when save', function(done) { Cat.findOne({ name: 'newcat' }, function(err, doc) { const old = doc.updatedAt; diff --git a/test/typescript/schema.ts b/test/typescript/schema.ts index 43528dc4611..a5561b49363 100644 --- a/test/typescript/schema.ts +++ b/test/typescript/schema.ts @@ -6,7 +6,23 @@ enum Genre { Comedy } -const movieSchema: Schema = new Schema({ +interface Actor { + name: string, + age: number +} +const actorSchema = + new Schema, Actor>({ name: { type: String }, age: { type: Number } }); + +interface Movie { + title?: string, + featuredIn?: string, + rating?: number, + genre?: string, + actionIntensity?: number, + actors?: Actor[] +} + +const movieSchema = new Schema, Model>, Movie>({ title: String, featuredIn: { type: String, @@ -32,6 +48,10 @@ const movieSchema: Schema = new Schema({ }, 'Action intensity required for action genre' ] + }, + actors: { + type: [actorSchema], + default: undefined } });