Merge pull request #17128 from overleaf/em-filter-out-comments

Do not send comments to project-history when ranges support is disabled

GitOrigin-RevId: 0c5e5e2c98ea3c2830ba4d5d114bf4730b440440
This commit is contained in:
Eric Mc Sween 2024-02-16 07:40:13 -05:00 committed by Copybot
parent 8c339045b3
commit b55bdf7ced
6 changed files with 226 additions and 181 deletions

View file

@ -1,3 +1,4 @@
// @ts-check
/** /**
* The purpose of this class is to track a set of inserts and deletes to a document, like * The purpose of this class is to track a set of inserts and deletes to a document, like
* track changes in Word. We store these as a set of ShareJs style ranges: * track changes in Word. We store these as a set of ShareJs style ranges:
@ -44,8 +45,21 @@ class RangesTracker {
comments = [] comments = []
} }
this.comments = comments this.comments = comments
this.setIdSeed(RangesTracker.generateIdSeed()) this.track_changes = false
this.resetDirtyState() this.id_seed = RangesTracker.generateIdSeed()
this.id_increment = 0
this._dirtyState = {
comment: {
moved: {},
removed: {},
added: {},
},
change: {
moved: {},
removed: {},
added: {},
},
}
} }
getIdSeed() { getIdSeed() {

View file

@ -37,9 +37,11 @@ const RangesManager = {
* @param {Ranges} ranges - ranges before the updates were applied * @param {Ranges} ranges - ranges before the updates were applied
* @param {Update[]} updates * @param {Update[]} updates
* @param {string[]} newDocLines - the document lines after the updates were applied * @param {string[]} newDocLines - the document lines after the updates were applied
* @param {object} opts
* @param {boolean} [opts.historyRangesSupport] - whether history ranges support is enabled
* @returns {{ newRanges: Ranges, rangesWereCollapsed: boolean, historyUpdates: HistoryUpdate[] }} * @returns {{ newRanges: Ranges, rangesWereCollapsed: boolean, historyUpdates: HistoryUpdate[] }}
*/ */
applyUpdate(projectId, docId, ranges, updates, newDocLines) { applyUpdate(projectId, docId, ranges, updates, newDocLines, opts = {}) {
if (ranges == null) { if (ranges == null) {
ranges = {} ranges = {}
} }
@ -58,9 +60,13 @@ const RangesManager = {
} }
const historyOps = [] const historyOps = []
for (const op of update.op) { for (const op of update.op) {
historyOps.push( if (opts.historyRangesSupport) {
getHistoryOp(op, rangesTracker.comments, rangesTracker.changes) historyOps.push(
) getHistoryOp(op, rangesTracker.comments, rangesTracker.changes)
)
} else if (isInsert(op) || isDelete(op)) {
historyOps.push(op)
}
rangesTracker.applyOp(op, { user_id: update.meta?.user_id }) rangesTracker.applyOp(op, { user_id: update.meta?.user_id })
} }
historyUpdates.push({ ...update, op: historyOps }) historyUpdates.push({ ...update, op: historyOps })

View file

@ -141,20 +141,15 @@ const UpdateManager = {
sync: incomingUpdateVersion === previousVersion, sync: incomingUpdateVersion === previousVersion,
}) })
let { newRanges, rangesWereCollapsed, historyUpdates } = const { newRanges, rangesWereCollapsed, historyUpdates } =
RangesManager.applyUpdate( RangesManager.applyUpdate(
projectId, projectId,
docId, docId,
ranges, ranges,
appliedOps, appliedOps,
updatedDocLines updatedDocLines,
{ historyRangesSupport }
) )
if (!historyRangesSupport) {
// The document has not been transitioned to include comments and
// tracked changes in its history. Send regular updates rather than the
// full history updates.
historyUpdates = appliedOps
}
profile.log('RangesManager.applyUpdate', { sync: true }) profile.log('RangesManager.applyUpdate', { sync: true })
await RedisManager.promises.updateDocument( await RedisManager.promises.updateDocument(

View file

@ -237,25 +237,13 @@ describe('RangesManager', function () {
}) })
}) })
describe('inserts among tracked deletes', function () { describe('with comment updates', function () {
beforeEach(function () { beforeEach(function () {
// original text is "on[1]e[22] [333](three) fo[4444]ur five"
// [] denotes tracked deletes
// () denotes tracked inserts
this.ranges = {
changes: makeRanges([
{ d: '1', p: 2 },
{ d: '22', p: 3 },
{ d: '333', p: 4 },
{ i: 'three', p: 4 },
{ d: '4444', p: 12 },
]),
}
this.updates = makeUpdates([ this.updates = makeUpdates([
{ i: 'zero ', p: 0 }, { i: 'two ', p: 4 },
{ i: 'two ', p: 9, u: true }, { c: 'one', p: 0 },
]) ])
this.newDocLines = ['zero one two three four five'] this.ranges = {}
this.result = this.RangesManager.applyUpdate( this.result = this.RangesManager.applyUpdate(
this.project_id, this.project_id,
this.doc_id, this.doc_id,
@ -265,168 +253,210 @@ describe('RangesManager', function () {
) )
}) })
it('should offset the hpos by the length of tracked deletes before the insert', function () { it('should not send comments to the history', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([ expect(this.result.historyUpdates[0].op).to.deep.equal([
[{ i: 'zero ', p: 0 }], { i: 'two ', p: 4 },
// 'two' is added just before the "333" tracked delete
[{ i: 'two ', p: 9, u: true, hpos: 12 }],
]) ])
}) })
}) })
describe('tracked delete rejections', function () { describe('with history ranges support', function () {
beforeEach(function () { describe('inserts among tracked deletes', function () {
// original text is "one [two ]three four five" beforeEach(function () {
// [] denotes tracked deletes // original text is "on[1]e[22] [333](three) fo[4444]ur five"
this.ranges = { // [] denotes tracked deletes
changes: makeRanges([{ d: 'two ', p: 4 }]), // () denotes tracked inserts
} this.ranges = {
this.updates = makeUpdates([{ i: 'tw', p: 4, u: true }]) changes: makeRanges([
this.newDocLines = ['one twthree four five'] { d: '1', p: 2 },
this.result = this.RangesManager.applyUpdate( { d: '22', p: 3 },
this.project_id, { d: '333', p: 4 },
this.doc_id, { i: 'three', p: 4 },
this.ranges, { d: '4444', p: 12 },
this.updates, ]),
this.newDocLines }
) this.updates = makeUpdates([
{ i: 'zero ', p: 0 },
{ i: 'two ', p: 9, u: true },
])
this.newDocLines = ['zero one two three four five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines,
{ historyRangesSupport: true }
)
})
it('should offset the hpos by the length of tracked deletes before the insert', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([
[{ i: 'zero ', p: 0 }],
// 'two' is added just before the "333" tracked delete
[{ i: 'two ', p: 9, u: true, hpos: 12 }],
])
})
}) })
it('should mark the insert as a tracked delete rejection where appropriate', function () { describe('tracked delete rejections', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([ beforeEach(function () {
[{ i: 'tw', p: 4, u: true, trackedDeleteRejection: true }], // original text is "one [two ]three four five"
]) // [] denotes tracked deletes
}) this.ranges = {
}) changes: makeRanges([{ d: 'two ', p: 4 }]),
}
this.updates = makeUpdates([{ i: 'tw', p: 4, u: true }])
this.newDocLines = ['one twthree four five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines,
{ historyRangesSupport: true }
)
})
describe('deletes among tracked deletes', function () { it('should mark the insert as a tracked delete rejection where appropriate', function () {
beforeEach(function () { expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([
// original text is "on[1]e [22](three) f[333]ou[4444]r [55555]five" [{ i: 'tw', p: 4, u: true, trackedDeleteRejection: true }],
// [] denotes tracked deletes ])
// () denotes tracked inserts })
this.ranges = {
comments: [],
changes: makeRanges([
{ d: '1', p: 2 },
{ d: '22', p: 4 },
{ i: 'three', p: 4 },
{ d: '333', p: 11 },
{ d: '4444', p: 13 },
{ d: '55555', p: 15 },
]),
}
this.updates = makeUpdates([
{ d: 'four ', p: 10 },
{ d: 'three ', p: 4 },
])
this.newDocLines = ['one five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines
)
}) })
it('should split and offset deletes appropriately', function () { describe('deletes among tracked deletes', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([ beforeEach(function () {
[ // original text is "on[1]e [22](three) f[333]ou[4444]r [55555]five"
// the "four" delete has tracked deletes inside it, add splits // [] denotes tracked deletes
{ // () denotes tracked inserts
d: 'four ', this.ranges = {
p: 10, comments: [],
hpos: 13, changes: makeRanges([
hsplits: [ { d: '1', p: 2 },
{ offset: 1, length: 3 }, { d: '22', p: 4 },
{ offset: 3, length: 4 }, { i: 'three', p: 4 },
], { d: '333', p: 11 },
}, { d: '4444', p: 13 },
], { d: '55555', p: 15 },
]),
}
this.updates = makeUpdates([
{ d: 'four ', p: 10 },
{ d: 'three ', p: 4 },
])
this.newDocLines = ['one five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines,
{ historyRangesSupport: true }
)
})
// the "three" delete is offset to the right by the two first tracked it('should split and offset deletes appropriately', function () {
// deletes expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([
[{ d: 'three ', p: 4, hpos: 7 }], [
]) // the "four" delete has tracked deletes inside it, add splits
}) {
}) d: 'four ',
p: 10,
hpos: 13,
hsplits: [
{ offset: 1, length: 3 },
{ offset: 3, length: 4 },
],
},
],
describe('comments among tracked deletes', function () { // the "three" delete is offset to the right by the two first tracked
beforeEach(function () { // deletes
// original text is "on[1]e[22] [333](three) fo[4444]ur five" [{ d: 'three ', p: 4, hpos: 7 }],
// [] denotes tracked deletes ])
// () denotes tracked inserts })
this.ranges = {
changes: makeRanges([
{ d: '1', p: 2 },
{ d: '22', p: 3 },
{ d: '333', p: 4 },
{ i: 'three', p: 4 },
{ d: '4444', p: 12 },
]),
}
this.updates = makeUpdates([
{ c: 'three ', p: 4 },
{ c: 'four ', p: 10 },
])
this.newDocLines = ['one three four five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines
)
}) })
it('should offset the hpos by the length of tracked deletes before the insert', function () { describe('comments among tracked deletes', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([ beforeEach(function () {
[{ c: 'three ', p: 4, hpos: 10 }], // original text is "on[1]e[22] [333](three) fo[4444]ur five"
[{ c: 'four ', p: 10, hpos: 16, hlen: 9 }], // [] denotes tracked deletes
]) // () denotes tracked inserts
}) this.ranges = {
}) changes: makeRanges([
{ d: '1', p: 2 },
{ d: '22', p: 3 },
{ d: '333', p: 4 },
{ i: 'three', p: 4 },
{ d: '4444', p: 12 },
]),
}
this.updates = makeUpdates([
{ c: 'three ', p: 4 },
{ c: 'four ', p: 10 },
])
this.newDocLines = ['one three four five']
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines,
{ historyRangesSupport: true }
)
})
describe('inserts inside comments', function () { it('should offset the hpos by the length of tracked deletes before the insert', function () {
beforeEach(function () { expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([
// original text is "one three four five" [{ c: 'three ', p: 4, hpos: 10 }],
this.ranges = { [{ c: 'four ', p: 10, hpos: 16, hlen: 9 }],
comments: makeRanges([ ])
{ c: 'three', p: 4, t: 'comment-id-1' }, })
{ c: 'ree four', p: 6, t: 'comment-id-2' },
]),
}
this.updates = makeUpdates([
{ i: '[before]', p: 4 },
{ i: '[inside]', p: 13 }, // 4 + 8 + 1
{ i: '[overlap]', p: 23 }, // 13 + 8 + 2
{ i: '[after]', p: 39 }, // 23 + 9 + 7
])
this.newDocLines = [
'one [before]t[inside]hr[overlap]ee four[after] five',
]
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines
)
}) })
it('should add the proper commentIds properties to ops', function () { describe('inserts inside comments', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([ beforeEach(function () {
[{ i: '[before]', p: 4 }], // original text is "one three four five"
[{ i: '[inside]', p: 13, commentIds: ['comment-id-1'] }], this.ranges = {
[ comments: makeRanges([
{ { c: 'three', p: 4, t: 'comment-id-1' },
i: '[overlap]', { c: 'ree four', p: 6, t: 'comment-id-2' },
p: 23, ]),
commentIds: ['comment-id-1', 'comment-id-2'], }
}, this.updates = makeUpdates([
], { i: '[before]', p: 4 },
[{ i: '[after]', p: 39 }], { i: '[inside]', p: 13 }, // 4 + 8 + 1
]) { i: '[overlap]', p: 23 }, // 13 + 8 + 2
{ i: '[after]', p: 39 }, // 23 + 9 + 7
])
this.newDocLines = [
'one [before]t[inside]hr[overlap]ee four[after] five',
]
this.result = this.RangesManager.applyUpdate(
this.project_id,
this.doc_id,
this.ranges,
this.updates,
this.newDocLines,
{ historyRangesSupport: true }
)
})
it('should add the proper commentIds properties to ops', function () {
expect(this.result.historyUpdates.map(x => x.op)).to.deep.equal([
[{ i: '[before]', p: 4 }],
[{ i: '[inside]', p: 13, commentIds: ['comment-id-1'] }],
[
{
i: '[overlap]',
p: 23,
commentIds: ['comment-id-1', 'comment-id-2'],
},
],
[{ i: '[after]', p: 39 }],
])
})
}) })
}) })
}) })

View file

@ -396,7 +396,7 @@ describe('UpdateManager', function () {
it('should add metadata to the ops', function () { it('should add metadata to the ops', function () {
this.UpdateManager.promises._addMetadataToHistoryUpdates.should.have.been.calledWith( this.UpdateManager.promises._addMetadataToHistoryUpdates.should.have.been.calledWith(
this.appliedOps, this.historyUpdates,
this.pathname, this.pathname,
this.projectHistoryId, this.projectHistoryId,
this.lines this.lines
@ -406,12 +406,12 @@ describe('UpdateManager', function () {
it('should push the applied ops into the history queue', function () { it('should push the applied ops into the history queue', function () {
this.ProjectHistoryRedisManager.promises.queueOps.should.have.been.calledWith( this.ProjectHistoryRedisManager.promises.queueOps.should.have.been.calledWith(
this.project_id, this.project_id,
...this.appliedOps.map(op => JSON.stringify(op)) ...this.historyUpdates.map(op => JSON.stringify(op))
) )
this.HistoryManager.recordAndFlushHistoryOps.should.have.been.calledWith( this.HistoryManager.recordAndFlushHistoryOps.should.have.been.calledWith(
this.project_id, this.project_id,
this.appliedOps, this.historyUpdates,
this.appliedOps.length this.historyUpdates.length
) )
}) })
}) })

View file

@ -722,7 +722,7 @@ export class DocumentContainer extends EventEmitter {
this.emit('ranges:clear') this.emit('ranges:clear')
this.ranges!.changes = changes this.ranges!.changes = changes
this.ranges!.comments = comments this.ranges!.comments = comments
this.ranges!.track_changes = this.doc?.track_changes this.ranges!.track_changes = this.doc?.track_changes ?? false
for (const op of this.filterOps(this.doc?.getInflightOp() || [])) { for (const op of this.filterOps(this.doc?.getInflightOp() || [])) {
this.ranges!.setIdSeed(this.doc?.track_changes_id_seeds?.inflight) this.ranges!.setIdSeed(this.doc?.track_changes_id_seeds?.inflight)
this.ranges!.applyOp(op, { user_id: this.track_changes_as }) this.ranges!.applyOp(op, { user_id: this.track_changes_as })