diff --git a/server/commands/revisionCreator.js b/server/commands/revisionCreator.js new file mode 100644 index 00000000..ad00b87b --- /dev/null +++ b/server/commands/revisionCreator.js @@ -0,0 +1,43 @@ +// @flow +import { Document, User, Event, Revision } from "../models"; +import { sequelize } from "../sequelize"; + +export default async function revisionCreator({ + document, + user, + ip, +}: { + document: Document, + user: User, + ip?: string, +}) { + let transaction; + + try { + transaction = await sequelize.transaction(); + + const revision = await Revision.createFromDocument(document, { + transaction, + }); + + await Event.create( + { + name: "revisions.create", + documentId: document.id, + modelId: revision.id, + teamId: document.teamId, + actorId: user.id, + ip: ip || user.lastActiveIp, + }, + { transaction } + ); + await transaction.commit(); + + return revision; + } catch (err) { + if (transaction) { + await transaction.rollback(); + } + throw err; + } +} diff --git a/server/commands/revisionCreator.test.js b/server/commands/revisionCreator.test.js new file mode 100644 index 00000000..970e1f47 --- /dev/null +++ b/server/commands/revisionCreator.test.js @@ -0,0 +1,27 @@ +// @flow +import { Event } from "../models"; +import { buildDocument, buildUser } from "../test/factories"; +import { flushdb } from "../test/support"; +import revisionCreator from "./revisionCreator"; + +beforeEach(() => flushdb()); + +describe("revisionCreator", () => { + const ip = "127.0.0.1"; + + it("should create revision model from document", async () => { + const user = await buildUser(); + const document = await buildDocument({ + userId: user.id, + teamId: user.teamId, + }); + + const revision = await revisionCreator({ document, user, ip }); + const event = await Event.findOne(); + + expect(revision.documentId).toEqual(document.id); + expect(revision.userId).toEqual(user.id); + expect(event.name).toEqual("revisions.create"); + expect(event.modelId).toEqual(revision.id); + }); +}); diff --git a/server/models/Event.js b/server/models/Event.js index 3423658b..7adca478 100644 --- a/server/models/Event.js +++ b/server/models/Event.js @@ -67,6 +67,7 @@ Event.ACTIVITY_EVENTS = [ "documents.delete", "documents.permanent_delete", "documents.restore", + "revisions.create", "users.create", ]; @@ -96,6 +97,7 @@ Event.AUDIT_EVENTS = [ "groups.create", "groups.update", "groups.delete", + "revisions.create", "shares.create", "shares.update", "shares.revoke", diff --git a/server/models/Revision.js b/server/models/Revision.js index 707be02c..17281319 100644 --- a/server/models/Revision.js +++ b/server/models/Revision.js @@ -49,19 +49,22 @@ Revision.findLatest = function (documentId) { }); }; -Revision.createFromDocument = function (document) { - return Revision.create({ - title: document.title, - text: document.text, - userId: document.lastModifiedById, - editorVersion: document.editorVersion, - version: document.version, - documentId: document.id, +Revision.createFromDocument = function (document, options) { + return Revision.create( + { + title: document.title, + text: document.text, + userId: document.lastModifiedById, + editorVersion: document.editorVersion, + version: document.version, + documentId: document.id, - // revision time is set to the last time document was touched as this - // handler can be debounced in the case of an update - createdAt: document.updatedAt, - }); + // revision time is set to the last time document was touched as this + // handler can be debounced in the case of an update + createdAt: document.updatedAt, + }, + options + ); }; Revision.prototype.migrateVersion = function () { diff --git a/server/services/revisions.js b/server/services/revisions.js index 6508d02e..5aacf3e0 100644 --- a/server/services/revisions.js +++ b/server/services/revisions.js @@ -1,6 +1,8 @@ // @flow +import invariant from "invariant"; +import revisionCreator from "../commands/revisionCreator"; import type { DocumentEvent, RevisionEvent } from "../events"; -import { Revision, Document } from "../models"; +import { Revision, Document, User } from "../models"; export default class Revisions { async on(event: DocumentEvent | RevisionEvent) { @@ -8,7 +10,7 @@ export default class Revisions { case "documents.publish": case "documents.update.debounced": { const document = await Document.findByPk(event.documentId); - if (!document) return; + invariant(document, "Document should exist"); const previous = await Revision.findLatest(document.id); @@ -22,7 +24,10 @@ export default class Revisions { return; } - await Revision.createFromDocument(document); + const user = await User.findByPk(event.actorId); + invariant(user, "User should exist"); + + await revisionCreator({ user, document }); break; }