| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import Core from 'overleaf-editor-core'
- import { Readable as StringStream } from 'stream'
- import BPromise from 'bluebird'
- import OError from '@overleaf/o-error'
- import * as HistoryStoreManager from './HistoryStoreManager.js'
- import * as WebApiManager from './WebApiManager.js'
- import * as Errors from './Errors.js'
- StringStream.prototype._read = function () {}
- const MAX_REQUESTS = 4 // maximum number of parallel requests to v1 history service
- export function getFileSnapshotStream(projectId, version, pathname, callback) {
- _getSnapshotAtVersion(projectId, version, (error, snapshot) => {
- if (error) {
- return callback(OError.tag(error))
- }
- const file = snapshot.getFile(pathname)
- if (file == null) {
- error = new Errors.NotFoundError(`${pathname} not found`, {
- projectId,
- version,
- pathname,
- })
- return callback(error)
- }
- WebApiManager.getHistoryId(projectId, (err, historyId) => {
- if (err) {
- return callback(OError.tag(err))
- }
- if (file.isEditable()) {
- file
- .load('eager', HistoryStoreManager.getBlobStore(historyId))
- .then(() => {
- const stream = new StringStream()
- stream.push(file.getContent())
- stream.push(null)
- callback(null, stream)
- })
- .catch(err => callback(err))
- } else {
- HistoryStoreManager.getProjectBlobStream(
- historyId,
- file.getHash(),
- callback
- )
- }
- })
- })
- }
- // Returns project snapshot containing the document content for files with
- // text operations in the relevant chunk, and hashes for unmodified/binary
- // files. Used by git bridge to get the state of the project.
- export function getProjectSnapshot(projectId, version, callback) {
- _getSnapshotAtVersion(projectId, version, (error, snapshot) => {
- if (error) {
- return callback(OError.tag(error))
- }
- WebApiManager.getHistoryId(projectId, (err, historyId) => {
- if (err) {
- return callback(OError.tag(err))
- }
- _loadFilesLimit(
- snapshot,
- 'eager',
- HistoryStoreManager.getBlobStore(historyId)
- )
- .then(() => {
- const data = {
- projectId,
- files: snapshot.getFileMap().files,
- }
- callback(null, data)
- })
- .catch(callback)
- })
- })
- }
- function _getSnapshotAtVersion(projectId, version, callback) {
- WebApiManager.getHistoryId(projectId, (error, historyId) => {
- if (error) {
- return callback(OError.tag(error))
- }
- HistoryStoreManager.getChunkAtVersion(
- projectId,
- historyId,
- version,
- (error, data) => {
- if (error) {
- return callback(OError.tag(error))
- }
- const chunk = Core.Chunk.fromRaw(data.chunk)
- const snapshot = chunk.getSnapshot()
- const changes = chunk
- .getChanges()
- .slice(0, version - chunk.getStartVersion())
- snapshot.applyAll(changes)
- callback(null, snapshot)
- }
- )
- })
- }
- export function getLatestSnapshot(projectId, historyId, callback) {
- HistoryStoreManager.getMostRecentChunk(projectId, historyId, (err, data) => {
- if (err) {
- return callback(err)
- }
- if (data == null || data.chunk == null) {
- return callback(new OError('undefined chunk'))
- }
- // apply all the changes in the chunk to get the current snapshot
- const chunk = Core.Chunk.fromRaw(data.chunk)
- const snapshot = chunk.getSnapshot()
- const changes = chunk.getChanges()
- snapshot.applyAll(changes)
- snapshot
- .loadFiles('lazy', HistoryStoreManager.getBlobStore(historyId))
- .then(snapshotFiles => callback(null, snapshotFiles))
- .catch(err => callback(err))
- })
- }
- function _loadFilesLimit(snapshot, kind, blobStore) {
- // bluebird promises only support a limit on concurrency for map()
- // so make an array of the files we need to load
- const fileList = []
- snapshot.fileMap.map(file => fileList.push(file))
- // load the files in parallel with a limit on the concurrent requests
- return BPromise.map(
- fileList,
- file => {
- // only load changed files, others can be dereferenced from their
- // blobs (this method is only used by the git bridge which
- // understands how to load blobs).
- if (!file.isEditable() || file.getHash()) {
- return
- }
- return file.load(kind, blobStore)
- },
- { concurrency: MAX_REQUESTS }
- )
- }
|