setupTimestamps.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. 'use strict';
  2. const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
  3. const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
  4. const get = require('../get');
  5. const handleTimestampOption = require('../schema/handleTimestampOption');
  6. const symbols = require('../../schema/symbols');
  7. module.exports = function setupTimestamps(schema, timestamps) {
  8. const childHasTimestamp = schema.childSchemas.find(withTimestamp);
  9. function withTimestamp(s) {
  10. const ts = s.schema.options.timestamps;
  11. return !!ts;
  12. }
  13. if (!timestamps && !childHasTimestamp) {
  14. return;
  15. }
  16. const createdAt = handleTimestampOption(timestamps, 'createdAt');
  17. const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
  18. const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ?
  19. timestamps.currentTime :
  20. null;
  21. const schemaAdditions = {};
  22. schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt };
  23. if (updatedAt && !schema.paths[updatedAt]) {
  24. schemaAdditions[updatedAt] = Date;
  25. }
  26. if (createdAt && !schema.paths[createdAt]) {
  27. const baseImmutableCreatedAt = schema.base.get('timestamps.createdAt.immutable');
  28. const immutable = baseImmutableCreatedAt != null ? baseImmutableCreatedAt : true;
  29. schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable };
  30. }
  31. schema.add(schemaAdditions);
  32. schema.pre('save', function(next) {
  33. const timestampOption = get(this, '$__.saveOptions.timestamps');
  34. if (timestampOption === false) {
  35. return next();
  36. }
  37. const skipUpdatedAt = timestampOption != null && timestampOption.updatedAt === false;
  38. const skipCreatedAt = timestampOption != null && timestampOption.createdAt === false;
  39. const defaultTimestamp = currentTime != null ?
  40. currentTime() :
  41. this.ownerDocument().constructor.base.now();
  42. if (!skipCreatedAt && (this.isNew || this.$isSubdocument) && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
  43. this.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true });
  44. }
  45. if (!skipUpdatedAt && updatedAt && (this.isNew || this.$isModified())) {
  46. let ts = defaultTimestamp;
  47. if (this.isNew && createdAt != null) {
  48. ts = this.$__getValue(createdAt);
  49. }
  50. this.$set(updatedAt, ts);
  51. }
  52. next();
  53. });
  54. schema.methods.initializeTimestamps = function() {
  55. const ts = currentTime != null ?
  56. currentTime() :
  57. this.constructor.base.now();
  58. if (createdAt && !this.get(createdAt)) {
  59. this.$set(createdAt, ts);
  60. }
  61. if (updatedAt && !this.get(updatedAt)) {
  62. this.$set(updatedAt, ts);
  63. }
  64. return this;
  65. };
  66. _setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
  67. const opts = { query: true, model: false };
  68. schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate);
  69. schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
  70. schema.pre('replaceOne', opts, _setTimestampsOnUpdate);
  71. schema.pre('update', opts, _setTimestampsOnUpdate);
  72. schema.pre('updateOne', opts, _setTimestampsOnUpdate);
  73. schema.pre('updateMany', opts, _setTimestampsOnUpdate);
  74. function _setTimestampsOnUpdate(next) {
  75. const now = currentTime != null ?
  76. currentTime() :
  77. this.model.base.now();
  78. // Replacing with null update should still trigger timestamps
  79. if (this.op === 'findOneAndReplace' && this.getUpdate() == null) {
  80. this.setUpdate({});
  81. }
  82. applyTimestampsToUpdate(now, createdAt, updatedAt, this.getUpdate(),
  83. this.options, this.schema);
  84. applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
  85. next();
  86. }
  87. };