history.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const BPromise = require('bluebird')
  4. const Change = require('./change')
  5. const Snapshot = require('./snapshot')
  6. /**
  7. * @typedef {import("./types").BlobStore} BlobStore
  8. */
  9. /**
  10. * @constructor
  11. * @param {Snapshot} snapshot
  12. * @param {Array.<Change>} changes
  13. *
  14. * @classdesc
  15. * A History is a {@link Snapshot} and a sequence of {@link Change}s that can
  16. * be applied to produce a new snapshot.
  17. */
  18. function History(snapshot, changes) {
  19. assert.instance(snapshot, Snapshot, 'bad snapshot')
  20. assert.maybe.array.of.instance(changes, Change, 'bad changes')
  21. this.snapshot = snapshot
  22. this.changes = changes || []
  23. }
  24. History.fromRaw = function historyFromRaw(raw) {
  25. return new History(
  26. Snapshot.fromRaw(raw.snapshot),
  27. raw.changes.map(Change.fromRaw)
  28. )
  29. }
  30. History.prototype.toRaw = function historyToRaw() {
  31. function changeToRaw(change) {
  32. return change.toRaw()
  33. }
  34. return {
  35. snapshot: this.snapshot.toRaw(),
  36. changes: this.changes.map(changeToRaw),
  37. }
  38. }
  39. History.prototype.getSnapshot = function () {
  40. return this.snapshot
  41. }
  42. History.prototype.getChanges = function () {
  43. return this.changes
  44. }
  45. History.prototype.countChanges = function historyCountChanges() {
  46. return this.changes.length
  47. }
  48. /**
  49. * Add changes to this history.
  50. *
  51. * @param {Array.<Change>} changes
  52. */
  53. History.prototype.pushChanges = function historyPushChanges(changes) {
  54. this.changes.push.apply(this.changes, changes)
  55. }
  56. /**
  57. * If this History references blob hashes, either in the Snapshot or the
  58. * Changes, add them to the given set.
  59. *
  60. * @param {Set.<String>} blobHashes
  61. */
  62. History.prototype.findBlobHashes = function historyFindBlobHashes(blobHashes) {
  63. function findChangeBlobHashes(change) {
  64. change.findBlobHashes(blobHashes)
  65. }
  66. this.snapshot.findBlobHashes(blobHashes)
  67. this.changes.forEach(findChangeBlobHashes)
  68. }
  69. /**
  70. * If this History contains any File objects, load them.
  71. *
  72. * @param {string} kind see {File#load}
  73. * @param {BlobStore} blobStore
  74. * @return {Promise}
  75. */
  76. History.prototype.loadFiles = function historyLoadFiles(kind, blobStore) {
  77. function loadChangeFiles(change) {
  78. return change.loadFiles(kind, blobStore)
  79. }
  80. return BPromise.join(
  81. this.snapshot.loadFiles(kind, blobStore),
  82. BPromise.each(this.changes, loadChangeFiles)
  83. )
  84. }
  85. /**
  86. * Return a version of this history that is suitable for long term storage.
  87. * This requires that we store the content of file objects in the provided
  88. * blobStore.
  89. *
  90. * @param {BlobStore} blobStore
  91. * @param {number} [concurrency] applies separately to files, changes and
  92. * operations
  93. * @return {Promise.<Object>}
  94. */
  95. History.prototype.store = function historyStoreFunc(blobStore, concurrency) {
  96. assert.maybe.number(concurrency, 'bad concurrency')
  97. function storeChange(change) {
  98. return change.store(blobStore, concurrency)
  99. }
  100. return BPromise.join(
  101. this.snapshot.store(blobStore, concurrency),
  102. BPromise.map(this.changes, storeChange, { concurrency: concurrency || 1 })
  103. ).then(([rawSnapshot, rawChanges]) => {
  104. return {
  105. snapshot: rawSnapshot,
  106. changes: rawChanges,
  107. }
  108. })
  109. }
  110. module.exports = History