@@ -33,7 +33,7 @@ describe("documents.publish", () => {
|
||||
const otherDocument = await buildDocument();
|
||||
await otherDocument.destroy();
|
||||
const document = await buildDocument({
|
||||
version: null,
|
||||
version: 0,
|
||||
text: `[ ] checklist item`,
|
||||
});
|
||||
document.text = `[this is a link](${otherDocument.url})`;
|
||||
@@ -54,6 +54,7 @@ describe("documents.publish", () => {
|
||||
expect(backlinks.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("documents.update", () => {
|
||||
test("should not fail on a document with no previous revisions", async () => {
|
||||
const otherDocument = await buildDocument();
|
||||
@@ -79,7 +80,7 @@ describe("documents.update", () => {
|
||||
test("should not fail when previous revision is different document version", async () => {
|
||||
const otherDocument = await buildDocument();
|
||||
const document = await buildDocument({
|
||||
version: null,
|
||||
version: undefined,
|
||||
text: `[ ] checklist item`,
|
||||
});
|
||||
document.text = `[this is a link](${otherDocument.url})`;
|
||||
@@ -158,6 +159,7 @@ describe("documents.update", () => {
|
||||
expect(backlinks[0].documentId).toBe(yetAnotherDocument.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("documents.delete", () => {
|
||||
test("should destroy related backlinks", async () => {
|
||||
const otherDocument = await buildDocument();
|
||||
@@ -188,6 +190,7 @@ describe("documents.delete", () => {
|
||||
expect(backlinks.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("documents.title_change", () => {
|
||||
test("should update titles in backlinked documents", async () => {
|
||||
const newTitle = "test";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Op } from "sequelize";
|
||||
import { Document, Backlink, Team } from "@server/models";
|
||||
import { Op } from "@server/sequelize";
|
||||
import parseDocumentIds from "@server/utils/parseDocumentIds";
|
||||
import slugify from "@server/utils/slugify";
|
||||
import { DocumentEvent, RevisionEvent } from "../../types";
|
||||
@@ -105,7 +105,6 @@ export default class BacklinksProcessor {
|
||||
],
|
||||
});
|
||||
await Promise.all(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'backlink' implicitly has an 'any' type.
|
||||
backlinks.map(async (backlink) => {
|
||||
const previousUrl = `/doc/${slugify(previousTitle)}-${
|
||||
document.urlId
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Document } from "@server/models";
|
||||
import Document from "@server/models/Document";
|
||||
import { globalEventQueue } from "../../queues";
|
||||
import { Event } from "../../types";
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class DebounceProcessor {
|
||||
|
||||
case "documents.update.delayed": {
|
||||
const document = await Document.findByPk(event.documentId, {
|
||||
fields: ["updatedAt"],
|
||||
attributes: ["updatedAt"],
|
||||
});
|
||||
|
||||
// If the document has been deleted then prevent further processing
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import fs from "fs";
|
||||
import invariant from "invariant";
|
||||
import Logger from "@server/logging/logger";
|
||||
import mailer from "@server/mailer";
|
||||
import { FileOperation, Collection, Event, Team, User } from "@server/models";
|
||||
import { Event as TEvent } from "@server/types";
|
||||
import { uploadToS3FromBuffer } from "@server/utils/s3";
|
||||
import { archiveCollections } from "@server/utils/zip";
|
||||
import Logger from "../../logging/logger";
|
||||
import mailer from "../../mailer";
|
||||
import { Event as TEvent } from "../../types";
|
||||
|
||||
export default class ExportsProcessor {
|
||||
async on(event: TEvent) {
|
||||
@@ -13,8 +14,14 @@ export default class ExportsProcessor {
|
||||
case "collections.export_all": {
|
||||
const { actorId, teamId } = event;
|
||||
const team = await Team.findByPk(teamId);
|
||||
invariant(team, "team operation not found");
|
||||
|
||||
const user = await User.findByPk(actorId);
|
||||
invariant(user, "user operation not found");
|
||||
|
||||
const exportData = await FileOperation.findByPk(event.modelId);
|
||||
invariant(exportData, "exportData not found");
|
||||
|
||||
const collectionIds =
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'collectionId' does not exist on type 'Co... Remove this comment to see the full error message
|
||||
event.collectionId || (await user.collectionIds());
|
||||
@@ -91,7 +98,6 @@ export default class ExportsProcessor {
|
||||
}
|
||||
|
||||
async updateFileOperation(
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'FileOperation' refers to a value, but is being us... Remove this comment to see the full error message
|
||||
fileOperation: FileOperation,
|
||||
actorId: string,
|
||||
teamId: string,
|
||||
@@ -102,6 +108,7 @@ export default class ExportsProcessor {
|
||||
name: "fileOperations.update",
|
||||
teamId,
|
||||
actorId,
|
||||
// @ts-expect-error dataValues exists
|
||||
data: fileOperation.dataValues,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import File from "formidable/lib/file";
|
||||
import invariant from "invariant";
|
||||
import collectionImporter from "@server/commands/collectionImporter";
|
||||
import { Attachment, User } from "@server/models";
|
||||
import { Event } from "../../types";
|
||||
@@ -11,14 +12,18 @@ export default class ImportsProcessor {
|
||||
case "collections.import": {
|
||||
const { type } = event.data;
|
||||
const attachment = await Attachment.findByPk(event.modelId);
|
||||
invariant(attachment, "attachment not found");
|
||||
|
||||
const user = await User.findByPk(event.actorId);
|
||||
const buffer = await attachment.buffer;
|
||||
invariant(user, "user not found");
|
||||
|
||||
const buffer: any = await attachment.buffer;
|
||||
const tmpDir = os.tmpdir();
|
||||
const tmpFilePath = `${tmpDir}/upload-${event.modelId}`;
|
||||
await fs.promises.writeFile(tmpFilePath, buffer);
|
||||
const file = new File({
|
||||
name: attachment.name,
|
||||
type: attachment.type,
|
||||
type: attachment.contentType,
|
||||
path: tmpFilePath,
|
||||
});
|
||||
await collectionImporter({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import mailer from "@server/mailer";
|
||||
import { View, NotificationSetting } from "@server/models";
|
||||
import {
|
||||
buildDocument,
|
||||
@@ -5,13 +6,14 @@ import {
|
||||
buildUser,
|
||||
} from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import mailer from "../../mailer";
|
||||
import NotificationsService from "./notifications";
|
||||
|
||||
jest.mock("../../mailer");
|
||||
jest.mock("@server/mailer");
|
||||
|
||||
const Notifications = new NotificationsService();
|
||||
beforeEach(() => flushdb());
|
||||
beforeEach(jest.resetAllMocks);
|
||||
|
||||
describe("documents.publish", () => {
|
||||
test("should not send a notification to author", async () => {
|
||||
const user = await buildUser();
|
||||
@@ -24,13 +26,16 @@ describe("documents.publish", () => {
|
||||
teamId: user.teamId,
|
||||
event: "documents.publish",
|
||||
});
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "documents.publish"; doc... Remove this comment to see the full error message
|
||||
await Notifications.on({
|
||||
name: "documents.publish",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
actorId: document.createdById,
|
||||
ip: "127.0.0.1",
|
||||
data: {
|
||||
title: document.title,
|
||||
},
|
||||
});
|
||||
expect(mailer.documentNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -45,13 +50,17 @@ describe("documents.publish", () => {
|
||||
teamId: user.teamId,
|
||||
event: "documents.publish",
|
||||
});
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "documents.publish"; doc... Remove this comment to see the full error message
|
||||
|
||||
await Notifications.on({
|
||||
name: "documents.publish",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
actorId: document.createdById,
|
||||
ip: "127.0.0.1",
|
||||
data: {
|
||||
title: document.title,
|
||||
},
|
||||
});
|
||||
expect(mailer.documentNotification).toHaveBeenCalled();
|
||||
});
|
||||
@@ -71,17 +80,21 @@ describe("documents.publish", () => {
|
||||
teamId: user.teamId,
|
||||
event: "documents.publish",
|
||||
});
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "documents.publish"; doc... Remove this comment to see the full error message
|
||||
await Notifications.on({
|
||||
name: "documents.publish",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
actorId: document.createdById,
|
||||
ip: "127.0.0.1",
|
||||
data: {
|
||||
title: document.title,
|
||||
},
|
||||
});
|
||||
expect(mailer.documentNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("revisions.create", () => {
|
||||
test("should send a notification to other collaborators", async () => {
|
||||
const document = await buildDocument();
|
||||
@@ -100,8 +113,6 @@ describe("revisions.create", () => {
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "revisions.create"; docu... Remove this comment to see the full error message
|
||||
actorId: document.createdById,
|
||||
});
|
||||
expect(mailer.documentNotification).toHaveBeenCalled();
|
||||
});
|
||||
@@ -124,8 +135,6 @@ describe("revisions.create", () => {
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "revisions.create"; docu... Remove this comment to see the full error message
|
||||
actorId: document.createdById,
|
||||
});
|
||||
expect(mailer.documentNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -146,8 +155,6 @@ describe("revisions.create", () => {
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: "revisions.create"; docu... Remove this comment to see the full error message
|
||||
actorId: document.createdById,
|
||||
});
|
||||
expect(mailer.documentNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Op } from "sequelize";
|
||||
import Logger from "@server/logging/logger";
|
||||
import mailer from "@server/mailer";
|
||||
import {
|
||||
View,
|
||||
Document,
|
||||
@@ -6,15 +9,12 @@ import {
|
||||
User,
|
||||
NotificationSetting,
|
||||
} from "@server/models";
|
||||
import { Op } from "@server/sequelize";
|
||||
import Logger from "../../logging/logger";
|
||||
import mailer from "../../mailer";
|
||||
import {
|
||||
DocumentEvent,
|
||||
CollectionEvent,
|
||||
RevisionEvent,
|
||||
Event,
|
||||
} from "../../types";
|
||||
} from "@server/types";
|
||||
|
||||
export default class NotificationsProcessor {
|
||||
async on(event: Event) {
|
||||
@@ -106,6 +106,10 @@ export default class NotificationsProcessor {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!setting.user.email) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mailer.documentNotification({
|
||||
to: setting.user.email,
|
||||
eventName,
|
||||
@@ -149,7 +153,7 @@ export default class NotificationsProcessor {
|
||||
|
||||
for (const setting of notificationSettings) {
|
||||
// Suppress notifications for suspended users
|
||||
if (setting.user.isSuspended) {
|
||||
if (setting.user.isSuspended || !setting.user.email) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export default class SlackProcessor {
|
||||
Document.findByPk(event.documentId),
|
||||
Team.findByPk(event.teamId),
|
||||
]);
|
||||
if (!document) return;
|
||||
if (!document || !team) return;
|
||||
|
||||
// never send notifications for draft documents
|
||||
if (!document.publishedAt) return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { subHours } from "date-fns";
|
||||
import { Op } from "sequelize";
|
||||
import {
|
||||
Document,
|
||||
Collection,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
Pin,
|
||||
} from "@server/models";
|
||||
import { presentPin } from "@server/presenters";
|
||||
import { Op } from "@server/sequelize";
|
||||
import { Event } from "../../types";
|
||||
|
||||
export default class WebsocketsProcessor {
|
||||
@@ -21,6 +21,10 @@ export default class WebsocketsProcessor {
|
||||
const document = await Document.findByPk(event.documentId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = document.publishedAt
|
||||
? `collection-${document.collectionId}`
|
||||
: `user-${event.actorId}`;
|
||||
@@ -44,6 +48,9 @@ export default class WebsocketsProcessor {
|
||||
const document = await Document.findByPk(event.documentId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!document.publishedAt) {
|
||||
return socketio.to(`user-${document.createdById}`).emit("entities", {
|
||||
@@ -87,6 +94,9 @@ export default class WebsocketsProcessor {
|
||||
const document = await Document.findByPk(event.documentId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
const channel = document.publishedAt
|
||||
? `collection-${document.collectionId}`
|
||||
: `user-${event.actorId}`;
|
||||
@@ -103,6 +113,9 @@ export default class WebsocketsProcessor {
|
||||
|
||||
case "documents.create": {
|
||||
const document = await Document.findByPk(event.documentId);
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
return socketio.to(`user-${event.actorId}`).emit("entities", {
|
||||
event: event.name,
|
||||
documentIds: [
|
||||
@@ -133,7 +146,6 @@ export default class WebsocketsProcessor {
|
||||
},
|
||||
paranoid: false,
|
||||
});
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'document' implicitly has an 'any' type.
|
||||
documents.forEach((document) => {
|
||||
socketio.to(`collection-${document.collectionId}`).emit("entities", {
|
||||
event: event.name,
|
||||
@@ -162,6 +174,9 @@ export default class WebsocketsProcessor {
|
||||
const collection = await Collection.findByPk(event.collectionId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
socketio
|
||||
.to(
|
||||
collection.permission
|
||||
@@ -194,6 +209,9 @@ export default class WebsocketsProcessor {
|
||||
const collection = await Collection.findByPk(event.collectionId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!collection) {
|
||||
return;
|
||||
}
|
||||
return socketio.to(`team-${collection.teamId}`).emit("entities", {
|
||||
event: event.name,
|
||||
collectionIds: [
|
||||
@@ -270,6 +288,9 @@ export default class WebsocketsProcessor {
|
||||
|
||||
case "collections.add_group": {
|
||||
const group = await Group.findByPk(event.data.groupId);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the users being added are not yet in the websocket channel for the collection
|
||||
// so they need to be notified separately
|
||||
@@ -293,6 +314,10 @@ export default class WebsocketsProcessor {
|
||||
|
||||
case "collections.remove_group": {
|
||||
const group = await Group.findByPk(event.data.groupId);
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const membershipUserIds = await Collection.membershipUserIds(
|
||||
event.collectionId
|
||||
);
|
||||
@@ -337,6 +362,9 @@ export default class WebsocketsProcessor {
|
||||
case "pins.create":
|
||||
case "pins.update": {
|
||||
const pin = await Pin.findByPk(event.modelId);
|
||||
if (!pin) {
|
||||
return;
|
||||
}
|
||||
return socketio
|
||||
.to(
|
||||
pin.collectionId
|
||||
@@ -363,6 +391,9 @@ export default class WebsocketsProcessor {
|
||||
const group = await Group.findByPk(event.modelId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
return socketio.to(`team-${group.teamId}`).emit("entities", {
|
||||
event: event.name,
|
||||
groupIds: [
|
||||
@@ -462,6 +493,10 @@ export default class WebsocketsProcessor {
|
||||
const group = await Group.findByPk(event.modelId, {
|
||||
paranoid: false,
|
||||
});
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
socketio.to(`team-${group.teamId}`).emit("entities", {
|
||||
event: event.name,
|
||||
groupIds: [
|
||||
|
||||
Reference in New Issue
Block a user