chore: Refactor backlinks and revisions (#1611)

* Update backlinks service to not rely on revisions

* fix: Add missing index for finding backlinks

* Debounce revision creation (#1616)

* refactor debounce logic to service

* Debounce slack notification

* Revisions created by service

* fix: Revision sidebar latest

* test: Add tests for notifications
This commit is contained in:
Tom Moor
2020-11-01 10:26:39 -08:00
committed by GitHub
parent 7735aa12d7
commit 3d09c8f655
20 changed files with 487 additions and 246 deletions

View File

@@ -1,15 +1,17 @@
// @flow
import { difference } from "lodash";
import type { DocumentEvent } from "../events";
import { Document, Revision, Backlink } from "../models";
import type { DocumentEvent, RevisionEvent } from "../events";
import { Document, Backlink } from "../models";
import { Op } from "../sequelize";
import parseDocumentIds from "../utils/parseDocumentIds";
import slugify from "../utils/slugify";
export default class Backlinks {
async on(event: DocumentEvent) {
async on(event: DocumentEvent | RevisionEvent) {
switch (event.name) {
case "documents.publish": {
const document = await Document.findByPk(event.documentId);
if (!document) return;
const linkIds = parseDocumentIds(document.text);
await Promise.all(
@@ -32,36 +34,18 @@ export default class Backlinks {
break;
}
case "documents.update": {
// no-op for now
if (event.data.autosave) return;
// no-op for drafts
const document = await Document.findByPk(event.documentId);
if (!document) return;
// backlinks are only created for published documents
if (!document.publishedAt) return;
const [currentRevision, previousRevision] = await Revision.findAll({
where: { documentId: event.documentId },
order: [["createdAt", "desc"]],
limit: 2,
});
const linkIds = parseDocumentIds(document.text);
const linkedDocumentIds = [];
// before parsing document text we must make sure it's been migrated to
// the latest version or the parser may fail on version differences
await currentRevision.migrateVersion();
if (previousRevision) {
await previousRevision.migrateVersion();
}
const previousLinkIds = previousRevision
? parseDocumentIds(previousRevision.text)
: [];
const currentLinkIds = parseDocumentIds(currentRevision.text);
const addedLinkIds = difference(currentLinkIds, previousLinkIds);
const removedLinkIds = difference(previousLinkIds, currentLinkIds);
// add any new backlinks that were created
// create or find existing backlink records for referenced docs
await Promise.all(
addedLinkIds.map(async (linkId) => {
linkIds.map(async (linkId) => {
const linkedDocument = await Document.findByPk(linkId);
if (!linkedDocument || linkedDocument.id === event.documentId) {
return;
@@ -73,35 +57,31 @@ export default class Backlinks {
reverseDocumentId: event.documentId,
},
defaults: {
userId: currentRevision.userId,
userId: document.lastModifiedById,
},
});
linkedDocumentIds.push(linkedDocument.id);
})
);
// delete any backlinks that were removed
await Promise.all(
removedLinkIds.map(async (linkId) => {
const document = await Document.findByPk(linkId, {
paranoid: false,
});
if (document) {
await Backlink.destroy({
where: {
documentId: document.id,
reverseDocumentId: event.documentId,
},
});
}
})
);
// delete any backlinks that no longer exist
await Backlink.destroy({
where: {
documentId: {
[Op.notIn]: linkedDocumentIds,
},
reverseDocumentId: event.documentId,
},
});
break;
}
case "documents.title_change": {
const document = await Document.findByPk(event.documentId);
if (!document) return;
if (
!previousRevision ||
currentRevision.title === previousRevision.title
) {
break;
}
// might as well check
const { title, previousTitle } = event.data;
if (!previousTitle || title === previousTitle) break;
// update any link titles in documents that lead to this one
const backlinks = await Backlink.findAll({
@@ -113,7 +93,7 @@ export default class Backlinks {
await Promise.all(
backlinks.map(async (backlink) => {
const previousUrl = `/doc/${slugify(previousRevision.title)}-${
const previousUrl = `/doc/${slugify(previousTitle)}-${
document.urlId
}`;
@@ -121,8 +101,8 @@ export default class Backlinks {
// the old title as anchor text. Go ahead and update those to the
// new title automatically
backlink.reverseDocument.text = backlink.reverseDocument.text.replace(
`[${previousRevision.title}](${previousUrl})`,
`[${document.title}](${document.url})`
`[${previousTitle}](${previousUrl})`,
`[${title}](${document.url})`
);
await backlink.reverseDocument.save({
silent: true,
@@ -136,12 +116,10 @@ export default class Backlinks {
case "documents.delete": {
await Backlink.destroy({
where: {
reverseDocumentId: event.documentId,
},
});
await Backlink.destroy({
where: {
documentId: event.documentId,
[Op.or]: [
{ reverseDocumentId: event.documentId },
{ documentId: event.documentId },
],
},
});
break;