'use strict'; const applyTimestampsToChildren = require('../update/applyTimestampsToChildren'); const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate'); const get = require('../get'); const handleTimestampOption = require('../schema/handleTimestampOption'); const symbols = require('../../schema/symbols'); module.exports = function setupTimestamps(schema, timestamps) { const childHasTimestamp = schema.childSchemas.find(withTimestamp); function withTimestamp(s) { const ts = s.schema.options.timestamps; return !!ts; } if (!timestamps && !childHasTimestamp) { return; } const createdAt = handleTimestampOption(timestamps, 'createdAt'); const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ? timestamps.currentTime : null; const schemaAdditions = {}; schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt }; if (updatedAt && !schema.paths[updatedAt]) { schemaAdditions[updatedAt] = Date; } if (createdAt && !schema.paths[createdAt]) { const baseImmutableCreatedAt = schema.base.get('timestamps.createdAt.immutable'); const immutable = baseImmutableCreatedAt != null ? baseImmutableCreatedAt : true; schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable }; } schema.add(schemaAdditions); schema.pre('save', function(next) { const timestampOption = get(this, '$__.saveOptions.timestamps'); if (timestampOption === false) { return next(); } const skipUpdatedAt = timestampOption != null && timestampOption.updatedAt === false; const skipCreatedAt = timestampOption != null && timestampOption.createdAt === false; const defaultTimestamp = currentTime != null ? currentTime() : this.ownerDocument().constructor.base.now(); if (!skipCreatedAt && (this.isNew || this.$isSubdocument) && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) { this.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true }); } if (!skipUpdatedAt && updatedAt && (this.isNew || this.$isModified())) { let ts = defaultTimestamp; if (this.isNew && createdAt != null) { ts = this.$__getValue(createdAt); } this.$set(updatedAt, ts); } next(); }); schema.methods.initializeTimestamps = function() { const ts = currentTime != null ? currentTime() : this.constructor.base.now(); if (createdAt && !this.get(createdAt)) { this.$set(createdAt, ts); } if (updatedAt && !this.get(updatedAt)) { this.$set(updatedAt, ts); } return this; }; _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); schema.pre('updateOne', opts, _setTimestampsOnUpdate); schema.pre('updateMany', opts, _setTimestampsOnUpdate); function _setTimestampsOnUpdate(next) { 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); next(); } };