diff --git a/app/models/Document.js b/app/models/Document.js index c67b1c58..5bd9392f 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -39,6 +39,7 @@ class Document extends BaseModel { updatedBy: User; url: string; views: number; + revision: number; data: Object; @@ -168,6 +169,7 @@ class Document extends BaseModel { id: this.id, title: this.title, text: this.text, + lastRevision: this.revision, }); } else { if (!this.title) { diff --git a/app/scenes/Document/Document.js b/app/scenes/Document/Document.js index 8e75a484..7aa08b9c 100644 --- a/app/scenes/Document/Document.js +++ b/app/scenes/Document/Document.js @@ -232,7 +232,7 @@ class DocumentScene extends Component { message={DISCARD_CHANGES} /> { }); router.post('documents.update', auth(), async ctx => { - const { id, title, text } = ctx.body; + const { id, title, text, lastRevision } = ctx.body; ctx.assertPresent(id, 'id is required'); ctx.assertPresent(title || text, 'title or text is required'); @@ -250,6 +250,10 @@ router.post('documents.update', auth(), async ctx => { const document = await Document.findById(id); const collection = document.collection; + if (lastRevision && lastRevision !== document.revisionCount) { + throw httpErrors.BadRequest('Document has changed since last revision'); + } + authDocumentForUser(ctx, document); // Update document diff --git a/server/api/documents.test.js b/server/api/documents.test.js index 82380eed..fb5759ac 100644 --- a/server/api/documents.test.js +++ b/server/api/documents.test.js @@ -277,6 +277,7 @@ describe('#documents.update', async () => { id: document.id, title: 'Updated title', text: 'Updated text', + lastRevision: document.revision, }, }); const body = await res.json(); @@ -287,6 +288,23 @@ describe('#documents.update', async () => { expect(body.data.collection.documents[1].title).toBe('Updated title'); }); + it('should fail if document lastRevision does not match', async () => { + const { user, document } = await seed(); + + const res = await server.post('/api/documents.update', { + body: { + token: user.getJwtToken(), + id: document.id, + text: 'Updated text', + lastRevision: 123, + }, + }); + const body = await res.json(); + + expect(res.status).toEqual(400); + expect(body).toMatchSnapshot(); + }); + it('should update document details for children', async () => { const { user, document, collection } = await seed(); collection.documentStructure = [ diff --git a/server/presenters/document.js b/server/presenters/document.js index 29ddff81..a665b307 100644 --- a/server/presenters/document.js +++ b/server/presenters/document.js @@ -36,6 +36,7 @@ async function present(ctx: Object, document: Document, options: ?Options) { team: document.teamId, collaborators: [], starred: !!(document.starred && document.starred.length), + revision: document.revisionCount, collectionId: document.atlasId, collaboratorCount: undefined, collection: undefined,