2023-01-13 07:42:29 -05:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const assert = require('check-types').assert
|
|
|
|
const OError = require('@overleaf/o-error')
|
|
|
|
|
|
|
|
const History = require('./history')
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {import("./types").BlobStore} BlobStore
|
|
|
|
* @typedef {import("./change")} Change
|
|
|
|
* @typedef {import("./snapshot")} Snapshot
|
|
|
|
*/
|
|
|
|
class ConflictingEndVersion extends OError {
|
|
|
|
constructor(clientEndVersion, latestEndVersion) {
|
|
|
|
const message =
|
|
|
|
'client sent updates with end_version ' +
|
|
|
|
clientEndVersion +
|
|
|
|
' but latest chunk has end_version ' +
|
|
|
|
latestEndVersion
|
|
|
|
super(message, { clientEndVersion, latestEndVersion })
|
|
|
|
this.clientEndVersion = clientEndVersion
|
|
|
|
this.latestEndVersion = latestEndVersion
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class NotFoundError extends OError {
|
|
|
|
// `message` and `info` optional arguments allow children classes to override
|
|
|
|
// these values, ensuring backwards compatibility with previous implementation
|
|
|
|
// based on the `overleaf-error-type` library
|
|
|
|
constructor(projectId, message, info) {
|
|
|
|
const errorMessage = message || `no chunks for project ${projectId}`
|
|
|
|
const errorInfo = info || { projectId }
|
|
|
|
super(errorMessage, errorInfo)
|
|
|
|
this.projectId = projectId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class VersionNotFoundError extends NotFoundError {
|
|
|
|
constructor(projectId, version) {
|
|
|
|
super(projectId, `chunk for ${projectId} v ${version} not found`, {
|
|
|
|
projectId,
|
|
|
|
version,
|
|
|
|
})
|
|
|
|
this.projectId = projectId
|
|
|
|
this.version = version
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class BeforeTimestampNotFoundError extends NotFoundError {
|
|
|
|
constructor(projectId, timestamp) {
|
|
|
|
super(projectId, `chunk for ${projectId} timestamp ${timestamp} not found`)
|
|
|
|
this.projectId = projectId
|
|
|
|
this.timestamp = timestamp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class NotPersistedError extends NotFoundError {
|
|
|
|
constructor(projectId) {
|
|
|
|
super(projectId, `chunk for ${projectId} not persisted yet`)
|
|
|
|
this.projectId = projectId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-08-22 10:48:04 -04:00
|
|
|
* A Chunk is a {@link History} that is part of a project's overall history. It
|
|
|
|
* has a start and an end version that place its History in context.
|
2023-01-13 07:42:29 -05:00
|
|
|
*/
|
2023-08-22 10:48:04 -04:00
|
|
|
class Chunk {
|
|
|
|
static ConflictingEndVersion = ConflictingEndVersion
|
|
|
|
static NotFoundError = NotFoundError
|
|
|
|
static VersionNotFoundError = VersionNotFoundError
|
|
|
|
static BeforeTimestampNotFoundError = BeforeTimestampNotFoundError
|
|
|
|
static NotPersistedError = NotPersistedError
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {History} history
|
|
|
|
* @param {number} startVersion
|
|
|
|
*/
|
|
|
|
constructor(history, startVersion) {
|
|
|
|
assert.instance(history, History, 'bad history')
|
|
|
|
assert.integer(startVersion, 'bad startVersion')
|
|
|
|
|
|
|
|
this.history = history
|
|
|
|
this.startVersion = startVersion
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
static fromRaw(raw) {
|
|
|
|
return new Chunk(History.fromRaw(raw.history), raw.startVersion)
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
toRaw() {
|
|
|
|
return { history: this.history.toRaw(), startVersion: this.startVersion }
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* The history for this chunk.
|
|
|
|
*
|
|
|
|
* @return {History}
|
|
|
|
*/
|
|
|
|
getHistory() {
|
|
|
|
return this.history
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* {@see History#getSnapshot}
|
|
|
|
* @return {Snapshot}
|
|
|
|
*/
|
|
|
|
getSnapshot() {
|
|
|
|
return this.history.getSnapshot()
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* {@see History#getChanges}
|
|
|
|
* @return {Array.<Change>}
|
|
|
|
*/
|
|
|
|
getChanges() {
|
|
|
|
return this.history.getChanges()
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* {@see History#pushChanges}
|
|
|
|
* @param {Array.<Change>} changes
|
|
|
|
*/
|
|
|
|
pushChanges(changes) {
|
|
|
|
this.history.pushChanges(changes)
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* The version of the project after applying all changes in this chunk.
|
|
|
|
*
|
|
|
|
* @return {number} non-negative, greater than or equal to start version
|
|
|
|
*/
|
|
|
|
getEndVersion() {
|
|
|
|
return this.startVersion + this.history.countChanges()
|
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
|
2023-08-22 10:48:04 -04:00
|
|
|
/**
|
|
|
|
* The timestamp of the last change in this chunk
|
|
|
|
*/
|
|
|
|
|
|
|
|
getEndTimestamp() {
|
|
|
|
if (!this.history.countChanges()) return null
|
|
|
|
return this.history.getChanges().slice(-1)[0].getTimestamp()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The version of the project before applying all changes in this chunk.
|
|
|
|
*
|
|
|
|
* @return {number} non-negative, less than or equal to end version
|
|
|
|
*/
|
|
|
|
getStartVersion() {
|
|
|
|
return this.startVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@see History#loadFiles}
|
|
|
|
*
|
|
|
|
* @param {string} kind
|
|
|
|
* @param {BlobStore} blobStore
|
2023-08-22 10:48:24 -04:00
|
|
|
* @return {Promise<void>}
|
2023-08-22 10:48:04 -04:00
|
|
|
*/
|
2023-08-22 10:48:24 -04:00
|
|
|
async loadFiles(kind, blobStore) {
|
|
|
|
await this.history.loadFiles(kind, blobStore)
|
2023-08-22 10:48:04 -04:00
|
|
|
}
|
2023-01-13 07:42:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Chunk
|