mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -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,13 +86,15 @@ class HashFileData extends FileData {
|
|||
* @returns {Promise<LazyStringFileData>}
|
||||
*/
|
||||
async toLazy(blobStore) {
|
||||
const blob = await blobStore.getBlob(this.hash)
|
||||
let rangesBlob
|
||||
if (this.rangesHash) {
|
||||
rangesBlob = await blobStore.getBlob(this.rangesHash)
|
||||
if (!rangesBlob) {
|
||||
throw new Error('Failed to look up rangesHash in blobStore')
|
||||
}
|
||||
const [blob, rangesBlob] = await Promise.all([
|
||||
blobStore.getBlob(this.hash),
|
||||
this.rangesHash
|
||||
? blobStore.getBlob(this.rangesHash)
|
||||
: 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')
|
||||
}
|
||||
if (!blob) throw new Error('blob not found: ' + this.hash)
|
||||
return FileData.createLazyFromBlobs(blob, rangesBlob)
|
||||
|
|
|
@ -110,16 +110,19 @@ class LazyStringFileData extends FileData {
|
|||
* @returns {Promise<EagerStringFileData>}
|
||||
*/
|
||||
async toEager(blobStore) {
|
||||
const content = await blobStore.getString(this.hash)
|
||||
let comments
|
||||
let trackedChanges
|
||||
if (this.rangesHash) {
|
||||
/** @type {RangesBlob} */
|
||||
const ranges = await blobStore.getObject(this.rangesHash)
|
||||
comments = ranges.comments
|
||||
trackedChanges = ranges.trackedChanges
|
||||
}
|
||||
const file = new EagerStringFileData(content, comments, trackedChanges)
|
||||
const [content, ranges] = await Promise.all([
|
||||
blobStore.getString(this.hash),
|
||||
this.rangesHash
|
||||
? /** @type {Promise<RangesBlob>} */ (
|
||||
blobStore.getObject(this.rangesHash)
|
||||
)
|
||||
: Promise.resolve(undefined),
|
||||
])
|
||||
const file = new EagerStringFileData(
|
||||
content,
|
||||
ranges?.comments,
|
||||
ranges?.trackedChanges
|
||||
)
|
||||
applyOperations(this.operations, 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