mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #17642 from overleaf/mj-optimize-range-fetch
[overleaf-editor-core] Load ranges and content in parallel GitOrigin-RevId: aa949ae7532965c5476c58bacf4bf068620791c0
This commit is contained in:
parent
bedc54e632
commit
178485715f
3 changed files with 147 additions and 17 deletions
|
@ -86,14 +86,16 @@ class HashFileData extends FileData {
|
||||||
* @returns {Promise<LazyStringFileData>}
|
* @returns {Promise<LazyStringFileData>}
|
||||||
*/
|
*/
|
||||||
async toLazy(blobStore) {
|
async toLazy(blobStore) {
|
||||||
const blob = await blobStore.getBlob(this.hash)
|
const [blob, rangesBlob] = await Promise.all([
|
||||||
let rangesBlob
|
blobStore.getBlob(this.hash),
|
||||||
if (this.rangesHash) {
|
this.rangesHash
|
||||||
rangesBlob = await blobStore.getBlob(this.rangesHash)
|
? blobStore.getBlob(this.rangesHash)
|
||||||
if (!rangesBlob) {
|
: Promise.resolve(undefined),
|
||||||
|
])
|
||||||
|
if (rangesBlob === null) {
|
||||||
|
// We attempted to look up the blob, but none was found
|
||||||
throw new Error('Failed to look up rangesHash in blobStore')
|
throw new Error('Failed to look up rangesHash in blobStore')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!blob) throw new Error('blob not found: ' + this.hash)
|
if (!blob) throw new Error('blob not found: ' + this.hash)
|
||||||
return FileData.createLazyFromBlobs(blob, rangesBlob)
|
return FileData.createLazyFromBlobs(blob, rangesBlob)
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,16 +110,19 @@ class LazyStringFileData extends FileData {
|
||||||
* @returns {Promise<EagerStringFileData>}
|
* @returns {Promise<EagerStringFileData>}
|
||||||
*/
|
*/
|
||||||
async toEager(blobStore) {
|
async toEager(blobStore) {
|
||||||
const content = await blobStore.getString(this.hash)
|
const [content, ranges] = await Promise.all([
|
||||||
let comments
|
blobStore.getString(this.hash),
|
||||||
let trackedChanges
|
this.rangesHash
|
||||||
if (this.rangesHash) {
|
? /** @type {Promise<RangesBlob>} */ (
|
||||||
/** @type {RangesBlob} */
|
blobStore.getObject(this.rangesHash)
|
||||||
const ranges = await blobStore.getObject(this.rangesHash)
|
)
|
||||||
comments = ranges.comments
|
: Promise.resolve(undefined),
|
||||||
trackedChanges = ranges.trackedChanges
|
])
|
||||||
}
|
const file = new EagerStringFileData(
|
||||||
const file = new EagerStringFileData(content, comments, trackedChanges)
|
content,
|
||||||
|
ranges?.comments,
|
||||||
|
ranges?.trackedChanges
|
||||||
|
)
|
||||||
applyOperations(this.operations, file)
|
applyOperations(this.operations, file)
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
125
libraries/overleaf-editor-core/test/hash_file_data.test.js
Normal file
125
libraries/overleaf-editor-core/test/hash_file_data.test.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
const HashFileData = require('../lib/file_data/hash_file_data')
|
||||||
|
const { expect } = require('chai')
|
||||||
|
const StringFileData = require('../lib/file_data/string_file_data')
|
||||||
|
const sinon = require('sinon')
|
||||||
|
const Blob = require('../lib/blob')
|
||||||
|
|
||||||
|
describe('HashFileData', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.fileHash = 'a5675307b61ec2517330622a6e649b4ca1ee5612'
|
||||||
|
this.rangesHash = '380de212d09bf8498065833dbf242aaf11184316'
|
||||||
|
this.blobStore = {
|
||||||
|
getString: sinon.stub(),
|
||||||
|
getObject: sinon.stub(),
|
||||||
|
getBlob: sinon.stub(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('constructor', function () {
|
||||||
|
it('should create a new instance of HashFileData from content hash and ranges hash', function () {
|
||||||
|
const fileData = new HashFileData(this.fileHash, this.rangesHash)
|
||||||
|
|
||||||
|
expect(fileData).to.be.instanceOf(HashFileData)
|
||||||
|
expect(fileData.getHash()).to.equal(this.fileHash)
|
||||||
|
expect(fileData.getRangesHash()).to.equal(this.rangesHash)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a new instance of HashFileData with no ranges hash', function () {
|
||||||
|
const fileData = new HashFileData(this.fileHash)
|
||||||
|
expect(fileData).to.be.instanceOf(HashFileData)
|
||||||
|
expect(fileData.getHash()).to.equal(this.fileHash)
|
||||||
|
expect(fileData.getRangesHash()).to.be.undefined
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('fromRaw', function () {
|
||||||
|
it('should create a new instance of HashFileData from raw data', function () {
|
||||||
|
const raw = { hash: this.fileHash, rangesHash: this.rangesHash }
|
||||||
|
const fileData = HashFileData.fromRaw(raw)
|
||||||
|
|
||||||
|
expect(fileData).to.be.instanceOf(HashFileData)
|
||||||
|
expect(fileData.getHash()).to.equal(raw.hash)
|
||||||
|
expect(fileData.getRangesHash()).to.equal(raw.rangesHash)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a new instance of HashFileData from raw data without ranges hash', function () {
|
||||||
|
const raw = { hash: this.fileHash }
|
||||||
|
const fileData = HashFileData.fromRaw(raw)
|
||||||
|
|
||||||
|
expect(fileData).to.be.instanceOf(HashFileData)
|
||||||
|
expect(fileData.getHash()).to.equal(raw.hash)
|
||||||
|
expect(fileData.getRangesHash()).to.equal(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('toRaw', function () {
|
||||||
|
it('should include ranges hash when present', function () {
|
||||||
|
const fileData = new HashFileData(this.fileHash, this.rangesHash)
|
||||||
|
const raw = fileData.toRaw()
|
||||||
|
expect(raw).to.deep.equal({
|
||||||
|
hash: this.fileHash,
|
||||||
|
rangesHash: this.rangesHash,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should omit ranges hash when not present', function () {
|
||||||
|
const fileData = new HashFileData(this.fileHash)
|
||||||
|
const raw = fileData.toRaw()
|
||||||
|
expect(raw).to.deep.equal({
|
||||||
|
hash: this.fileHash,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('toEager', function () {
|
||||||
|
it('should convert HashFileData to StringFileData including ranges', async function () {
|
||||||
|
const trackedChanges = [
|
||||||
|
{
|
||||||
|
range: { pos: 5, length: 10 },
|
||||||
|
tracking: {
|
||||||
|
userId: 'foo',
|
||||||
|
type: 'insert',
|
||||||
|
ts: '2024-01-01T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const comments = [
|
||||||
|
{
|
||||||
|
id: 'comment-1',
|
||||||
|
ranges: [{ pos: 1, length: 4 }],
|
||||||
|
resolved: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const fileData = new HashFileData(this.fileHash, this.rangesHash)
|
||||||
|
this.blobStore.getString.withArgs(this.fileHash).resolves('content')
|
||||||
|
this.blobStore.getObject.withArgs(this.rangesHash).resolves({
|
||||||
|
trackedChanges,
|
||||||
|
comments,
|
||||||
|
})
|
||||||
|
this.blobStore.getBlob
|
||||||
|
.withArgs(this.rangesHash)
|
||||||
|
.resolves(new Blob(this.rangesHash, 20, 20))
|
||||||
|
this.blobStore.getBlob
|
||||||
|
.withArgs(this.fileHash)
|
||||||
|
.resolves(new Blob(this.fileHash, 20, 20))
|
||||||
|
const eagerFileData = await fileData.toEager(this.blobStore)
|
||||||
|
expect(eagerFileData).to.be.instanceOf(StringFileData)
|
||||||
|
expect(eagerFileData.getContent()).to.equal('content')
|
||||||
|
expect(eagerFileData.trackedChanges.toRaw()).to.deep.equal(trackedChanges)
|
||||||
|
expect(eagerFileData.getComments()).to.deep.equal(comments)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should convert HashFileData to StringFileData without ranges', async function () {
|
||||||
|
const fileData = new HashFileData(this.fileHash, undefined)
|
||||||
|
this.blobStore.getString.withArgs(this.fileHash).resolves('content')
|
||||||
|
this.blobStore.getBlob
|
||||||
|
.withArgs(this.fileHash)
|
||||||
|
.resolves(new Blob(this.fileHash, 20, 20))
|
||||||
|
const eagerFileData = await fileData.toEager(this.blobStore)
|
||||||
|
expect(eagerFileData).to.be.instanceOf(StringFileData)
|
||||||
|
expect(eagerFileData.getContent()).to.equal('content')
|
||||||
|
expect(eagerFileData.trackedChanges.toRaw()).to.deep.equal([])
|
||||||
|
expect(eagerFileData.getComments()).to.deep.equal([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue