diff --git a/services/docstore/app/js/schemas.js b/services/docstore/app/js/schemas.js index e57dfdd660..805193dc39 100644 --- a/services/docstore/app/js/schemas.js +++ b/services/docstore/app/js/schemas.js @@ -14,17 +14,22 @@ import { z } from '@overleaf/validation-tools' // - history restores send id-less changes and comment ops with a `resolved` // flag (stripped again by RangeManager), and detached comments without an // id, see overleaf-editor-core/lib/doc_updater_compatible_ranges.js +// - old tracked changes created while the fixedRemoveChange flag existed +// (removed in https://github.com/overleaf/internal/pull/23993) still carry +// it in their op until accepted/rejected const insertOp = z.strictObject({ i: z.string(), p: z.number().int().min(0), u: z.boolean().optional(), + fixedRemoveChange: z.boolean().optional(), }) const deleteOp = z.strictObject({ d: z.string(), p: z.number().int().min(0), u: z.boolean().optional(), + fixedRemoveChange: z.boolean().optional(), }) const commentOp = z.strictObject({ diff --git a/services/docstore/test/unit/js/schemas.test.js b/services/docstore/test/unit/js/schemas.test.js new file mode 100644 index 0000000000..8998a9e514 --- /dev/null +++ b/services/docstore/test/unit/js/schemas.test.js @@ -0,0 +1,27 @@ +import { describe, it, expect } from 'vitest' +import { ranges } from '../../../app/js/schemas.js' + +describe('schemas', function () { + describe('ranges', function () { + it('accepts a legacy fixedRemoveChange flag on an insert op', function () { + const result = ranges.safeParse({ + changes: [{ op: { i: 'foo', p: 0, fixedRemoveChange: true } }], + }) + expect(result.success).to.equal(true) + }) + + it('accepts a legacy fixedRemoveChange flag on a delete op', function () { + const result = ranges.safeParse({ + changes: [{ op: { d: 'foo', p: 0, fixedRemoveChange: true } }], + }) + expect(result.success).to.equal(true) + }) + + it('rejects an unrecognized key on an insert op', function () { + const result = ranges.safeParse({ + changes: [{ op: { i: 'foo', p: 0, somethingElse: true } }], + }) + expect(result.success).to.equal(false) + }) + }) +}) diff --git a/services/document-updater/app/js/schemas.js b/services/document-updater/app/js/schemas.js index bb3dd03299..594c0bce5a 100644 --- a/services/document-updater/app/js/schemas.js +++ b/services/document-updater/app/js/schemas.js @@ -11,17 +11,23 @@ const { z, zz } = require('@overleaf/validation-tools') // schemas model them explicitly: comments/changes preloaded from web may lack `id`. // Tightening these is tracked as part of moving the shared editor payload // schemas into overleaf-editor-core. +// +// Old tracked changes created while the fixedRemoveChange flag existed +// (removed in https://github.com/overleaf/internal/pull/23993) still carry +// it in their op until accepted/rejected. const insertOp = z.strictObject({ i: z.string(), p: z.number().int().min(0), u: z.boolean().optional(), + fixedRemoveChange: z.boolean().optional(), }) const deleteOp = z.strictObject({ d: z.string(), p: z.number().int().min(0), u: z.boolean().optional(), + fixedRemoveChange: z.boolean().optional(), }) const commentOp = z.strictObject({ diff --git a/services/document-updater/test/unit/js/schemas.test.js b/services/document-updater/test/unit/js/schemas.test.js new file mode 100644 index 0000000000..f3727f737c --- /dev/null +++ b/services/document-updater/test/unit/js/schemas.test.js @@ -0,0 +1,36 @@ +const { expect } = require('chai') +const { insertOp, deleteOp } = require('../../../app/js/schemas') + +describe('schemas', function () { + describe('insertOp', function () { + it('accepts a legacy fixedRemoveChange flag', function () { + const result = insertOp.safeParse({ + i: 'foo', + p: 0, + fixedRemoveChange: true, + }) + expect(result.success).to.equal(true) + }) + + it('rejects an unrecognized key', function () { + const result = insertOp.safeParse({ i: 'foo', p: 0, somethingElse: true }) + expect(result.success).to.equal(false) + }) + }) + + describe('deleteOp', function () { + it('accepts a legacy fixedRemoveChange flag', function () { + const result = deleteOp.safeParse({ + d: 'foo', + p: 0, + fixedRemoveChange: true, + }) + expect(result.success).to.equal(true) + }) + + it('rejects an unrecognized key', function () { + const result = deleteOp.safeParse({ d: 'foo', p: 0, somethingElse: true }) + expect(result.success).to.equal(false) + }) + }) +})