@@ -1,30 +1,19 @@
|
||||
import { Collection, UserAuthentication } from "@server/models";
|
||||
import mailer from "@server/mailer";
|
||||
import Collection from "@server/models/Collection";
|
||||
import UserAuthentication from "@server/models/UserAuthentication";
|
||||
import { buildUser, buildTeam } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import mailer from "../mailer";
|
||||
import accountProvisioner from "./accountProvisioner";
|
||||
|
||||
jest.mock("../mailer");
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = {
|
||||
createPresignedPost: jest.fn(),
|
||||
putObject: jest.fn().mockReturnThis(),
|
||||
promise: jest.fn(),
|
||||
};
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'mockReset' does not exist on type '(type... Remove this comment to see the full error message
|
||||
mailer.sendTemplate.mockReset();
|
||||
return flushdb();
|
||||
});
|
||||
|
||||
describe("accountProvisioner", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
it("should create a new user and team", async () => {
|
||||
const spy = jest.spyOn(mailer, "sendTemplate");
|
||||
const { user, team, isNewTeam, isNewUser } = await accountProvisioner({
|
||||
ip,
|
||||
user: {
|
||||
@@ -48,7 +37,7 @@ describe("accountProvisioner", () => {
|
||||
scopes: ["read"],
|
||||
},
|
||||
});
|
||||
const authentications = await user.getAuthentications();
|
||||
const authentications = await user.$get("authentications");
|
||||
const auth = authentications[0];
|
||||
expect(auth.accessToken).toEqual("123");
|
||||
expect(auth.scopes.length).toEqual(1);
|
||||
@@ -58,19 +47,22 @@ describe("accountProvisioner", () => {
|
||||
expect(user.username).toEqual("jtester");
|
||||
expect(isNewUser).toEqual(true);
|
||||
expect(isNewTeam).toEqual(true);
|
||||
expect(mailer.sendTemplate).toHaveBeenCalled();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
const collectionCount = await Collection.count();
|
||||
expect(collectionCount).toEqual(1);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it("should update exising user and authentication", async () => {
|
||||
const spy = jest.spyOn(mailer, "sendTemplate");
|
||||
const existingTeam = await buildTeam();
|
||||
const providers = await existingTeam.getAuthenticationProviders();
|
||||
const providers = await existingTeam.$get("authenticationProviders");
|
||||
const authenticationProvider = providers[0];
|
||||
const existing = await buildUser({
|
||||
teamId: existingTeam.id,
|
||||
});
|
||||
const authentications = await existing.getAuthentications();
|
||||
const authentications = await existing.$get("authentications");
|
||||
const authentication = authentications[0];
|
||||
const newEmail = "test@example.com";
|
||||
const newUsername = "tname";
|
||||
@@ -98,21 +90,23 @@ describe("accountProvisioner", () => {
|
||||
},
|
||||
});
|
||||
const auth = await UserAuthentication.findByPk(authentication.id);
|
||||
expect(auth.accessToken).toEqual("123");
|
||||
expect(auth.scopes.length).toEqual(1);
|
||||
expect(auth.scopes[0]).toEqual("read");
|
||||
expect(auth?.accessToken).toEqual("123");
|
||||
expect(auth?.scopes.length).toEqual(1);
|
||||
expect(auth?.scopes[0]).toEqual("read");
|
||||
expect(user.email).toEqual(newEmail);
|
||||
expect(user.username).toEqual(newUsername);
|
||||
expect(isNewTeam).toEqual(false);
|
||||
expect(isNewUser).toEqual(false);
|
||||
expect(mailer.sendTemplate).not.toHaveBeenCalled();
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
const collectionCount = await Collection.count();
|
||||
expect(collectionCount).toEqual(0);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it("should throw an error when authentication provider is disabled", async () => {
|
||||
const existingTeam = await buildTeam();
|
||||
const providers = await existingTeam.getAuthenticationProviders();
|
||||
const providers = await existingTeam.$get("authenticationProviders");
|
||||
const authenticationProvider = providers[0];
|
||||
await authenticationProvider.update({
|
||||
enabled: false,
|
||||
@@ -120,7 +114,7 @@ describe("accountProvisioner", () => {
|
||||
const existing = await buildUser({
|
||||
teamId: existingTeam.id,
|
||||
});
|
||||
const authentications = await existing.getAuthentications();
|
||||
const authentications = await existing.$get("authentications");
|
||||
const authentication = authentications[0];
|
||||
let error;
|
||||
|
||||
@@ -129,7 +123,7 @@ describe("accountProvisioner", () => {
|
||||
ip,
|
||||
user: {
|
||||
name: existing.name,
|
||||
email: existing.email,
|
||||
email: existing.email!,
|
||||
avatarUrl: existing.avatarUrl,
|
||||
},
|
||||
team: {
|
||||
@@ -155,8 +149,9 @@ describe("accountProvisioner", () => {
|
||||
});
|
||||
|
||||
it("should create a new user in an existing team", async () => {
|
||||
const spy = jest.spyOn(mailer, "sendTemplate");
|
||||
const team = await buildTeam();
|
||||
const authenticationProviders = await team.getAuthenticationProviders();
|
||||
const authenticationProviders = await team.$get("authenticationProviders");
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const { user, isNewUser } = await accountProvisioner({
|
||||
ip,
|
||||
@@ -181,7 +176,7 @@ describe("accountProvisioner", () => {
|
||||
scopes: ["read"],
|
||||
},
|
||||
});
|
||||
const authentications = await user.getAuthentications();
|
||||
const authentications = await user.$get("authentications");
|
||||
const auth = authentications[0];
|
||||
expect(auth.accessToken).toEqual("123");
|
||||
expect(auth.scopes.length).toEqual(1);
|
||||
@@ -189,9 +184,11 @@ describe("accountProvisioner", () => {
|
||||
expect(user.email).toEqual("jenny@example.com");
|
||||
expect(user.username).toEqual("jtester");
|
||||
expect(isNewUser).toEqual(true);
|
||||
expect(mailer.sendTemplate).toHaveBeenCalled();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
// should provision welcome collection
|
||||
const collectionCount = await Collection.count();
|
||||
expect(collectionCount).toEqual(1);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import invariant from "invariant";
|
||||
import Sequelize from "sequelize";
|
||||
import { Collection, Team, User } from "@server/models";
|
||||
import { UniqueConstraintError } from "sequelize";
|
||||
import {
|
||||
AuthenticationError,
|
||||
EmailAuthenticationRequiredError,
|
||||
AuthenticationProviderDisabledError,
|
||||
} from "../errors";
|
||||
import mailer from "../mailer";
|
||||
} from "@server/errors";
|
||||
import mailer from "@server/mailer";
|
||||
import { Collection, Team, User } from "@server/models";
|
||||
import teamCreator from "./teamCreator";
|
||||
import userCreator from "./userCreator";
|
||||
|
||||
@@ -15,14 +15,14 @@ type Props = {
|
||||
user: {
|
||||
name: string;
|
||||
email: string;
|
||||
avatarUrl?: string;
|
||||
avatarUrl?: string | null;
|
||||
username?: string;
|
||||
};
|
||||
team: {
|
||||
name: string;
|
||||
domain?: string;
|
||||
subdomain: string;
|
||||
avatarUrl?: string;
|
||||
avatarUrl?: string | null;
|
||||
};
|
||||
authenticationProvider: {
|
||||
name: string;
|
||||
@@ -37,9 +37,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export type AccountProvisionerResult = {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Team' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
team: Team;
|
||||
isNewTeam: boolean;
|
||||
isNewUser: boolean;
|
||||
@@ -123,7 +121,7 @@ export default async function accountProvisioner({
|
||||
isNewTeam,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof Sequelize.UniqueConstraintError) {
|
||||
if (err instanceof UniqueConstraintError) {
|
||||
const exists = await User.findOne({
|
||||
where: {
|
||||
email: userParams.email,
|
||||
|
||||
@@ -13,7 +13,6 @@ export default async function attachmentCreator({
|
||||
name: string;
|
||||
type: string;
|
||||
buffer: Buffer;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
source?: "import";
|
||||
ip: string;
|
||||
|
||||
@@ -7,11 +7,8 @@ export default async function collectionExporter({
|
||||
user,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Collection' refers to a value, but is being used ... Remove this comment to see the full error message
|
||||
collection?: Collection;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Team' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
team: Team;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
ip: string;
|
||||
}) {
|
||||
@@ -39,6 +36,10 @@ export default async function collectionExporter({
|
||||
});
|
||||
|
||||
fileOperation.user = user;
|
||||
fileOperation.collection = collection;
|
||||
|
||||
if (collection) {
|
||||
fileOperation.collection = collection;
|
||||
}
|
||||
|
||||
return fileOperation;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import collectionImporter from "./collectionImporter";
|
||||
|
||||
jest.mock("../utils/s3");
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("collectionImporter", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ export default async function collectionImporter({
|
||||
ip,
|
||||
}: {
|
||||
file: FileWithPath;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
type: "outline";
|
||||
ip: string;
|
||||
@@ -48,7 +47,6 @@ export default async function collectionImporter({
|
||||
// store progress and pointers
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'string' is missing in type '{}' but requ... Remove this comment to see the full error message
|
||||
const collections: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Collection' refers to a value, but is being used ... Remove this comment to see the full error message
|
||||
string: Collection;
|
||||
} = {};
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'string' is missing in type '{}' but requ... Remove this comment to see the full error message
|
||||
@@ -57,7 +55,6 @@ export default async function collectionImporter({
|
||||
} = {};
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'string' is missing in type '{}' but requ... Remove this comment to see the full error message
|
||||
const attachments: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Attachment' refers to a value, but is being used ... Remove this comment to see the full error message
|
||||
string: Attachment;
|
||||
} = {};
|
||||
|
||||
@@ -186,13 +183,13 @@ export default async function collectionImporter({
|
||||
/(.*)uploads\//,
|
||||
"uploads/"
|
||||
);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'text' does not exist on type 'Document'.
|
||||
|
||||
document.text = document.text
|
||||
.replace(attachmentPath, attachment.redirectUrl)
|
||||
.replace(normalizedAttachmentPath, attachment.redirectUrl)
|
||||
.replace(`/${normalizedAttachmentPath}`, attachment.redirectUrl);
|
||||
|
||||
// does nothing if the document text is unchanged
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'save' does not exist on type 'Document'.
|
||||
await document.save({
|
||||
fields: ["text"],
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import invariant from "invariant";
|
||||
import { Document, Event, User } from "@server/models";
|
||||
|
||||
export default async function documentCreator({
|
||||
@@ -21,18 +22,16 @@ export default async function documentCreator({
|
||||
publish?: boolean;
|
||||
collectionId: string;
|
||||
parentDocumentId?: string;
|
||||
templateDocument?: Document;
|
||||
templateDocument?: Document | null;
|
||||
template?: boolean;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
index?: number;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
editorVersion?: string;
|
||||
source?: "import";
|
||||
ip: string;
|
||||
}): Promise<Document> {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
const templateId = templateDocument ? templateDocument.id : undefined;
|
||||
const document = await Document.create({
|
||||
parentDocumentId,
|
||||
@@ -47,7 +46,6 @@ export default async function documentCreator({
|
||||
template,
|
||||
templateId,
|
||||
title: templateDocument ? templateDocument.title : title,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'text' does not exist on type 'Document'.
|
||||
text: templateDocument ? templateDocument.text : text,
|
||||
});
|
||||
await Event.create({
|
||||
@@ -83,10 +81,13 @@ export default async function documentCreator({
|
||||
// reload to get all of the data needed to present (user, collection etc)
|
||||
// we need to specify publishedAt to bypass default scope that only returns
|
||||
// published documents
|
||||
return Document.findOne({
|
||||
const doc = await Document.findOne({
|
||||
where: {
|
||||
id: document.id,
|
||||
publishedAt: document.publishedAt,
|
||||
},
|
||||
});
|
||||
invariant(doc, "Document must exist");
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import path from "path";
|
||||
import File from "formidable/lib/file";
|
||||
import { Attachment } from "@server/models";
|
||||
import Attachment from "@server/models/Attachment";
|
||||
import { buildUser } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import documentImporter from "./documentImporter";
|
||||
|
||||
jest.mock("../utils/s3");
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("documentImporter", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
|
||||
@@ -144,7 +144,6 @@ export default async function documentImporter({
|
||||
user,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
file: File;
|
||||
ip: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Attachment } from "@server/models";
|
||||
import Attachment from "@server/models/Attachment";
|
||||
import {
|
||||
buildDocument,
|
||||
buildAttachment,
|
||||
@@ -10,6 +10,7 @@ import parseAttachmentIds from "@server/utils/parseAttachmentIds";
|
||||
import documentMover from "./documentMover";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("documentMover", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
@@ -33,7 +34,7 @@ describe("documentMover", () => {
|
||||
const document = await buildDocument({
|
||||
collectionId: collection.id,
|
||||
});
|
||||
await document.archive();
|
||||
await document.archive(user.id);
|
||||
const response = await documentMover({
|
||||
user,
|
||||
document,
|
||||
@@ -63,13 +64,11 @@ describe("documentMover", () => {
|
||||
index: 0,
|
||||
ip,
|
||||
});
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'documentStructure' does not exist on typ... Remove this comment to see the full error message
|
||||
expect(response.collections[0].documentStructure[0].children[0].id).toBe(
|
||||
expect(response.collections[0].documentStructure![0].children[0].id).toBe(
|
||||
newDocument.id
|
||||
);
|
||||
expect(response.collections.length).toEqual(1);
|
||||
expect(response.documents.length).toEqual(1);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'collection' does not exist on type 'neve... Remove this comment to see the full error message
|
||||
expect(response.documents[0].collection.id).toEqual(collection.id);
|
||||
});
|
||||
|
||||
@@ -98,22 +97,17 @@ describe("documentMover", () => {
|
||||
// check document ids where updated
|
||||
await newDocument.reload();
|
||||
expect(newDocument.collectionId).toBe(newCollection.id);
|
||||
await document.reload();
|
||||
expect(document.collectionId).toBe(newCollection.id);
|
||||
|
||||
// check collection structure updated
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||
expect(response.collections[0].id).toBe(collection.id);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||
expect(response.collections[1].id).toBe(newCollection.id);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'documentStructure' does not exist on typ... Remove this comment to see the full error message
|
||||
expect(response.collections[1].documentStructure[0].children[0].id).toBe(
|
||||
expect(response.collections[1].documentStructure![0].children[0].id).toBe(
|
||||
newDocument.id
|
||||
);
|
||||
expect(response.collections.length).toEqual(2);
|
||||
expect(response.documents.length).toEqual(2);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'collection' does not exist on type 'neve... Remove this comment to see the full error message
|
||||
|
||||
expect(response.documents[0].collection.id).toEqual(newCollection.id);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'collection' does not exist on type 'neve... Remove this comment to see the full error message
|
||||
expect(response.documents[1].collection.id).toEqual(newCollection.id);
|
||||
});
|
||||
|
||||
@@ -152,8 +146,8 @@ describe("documentMover", () => {
|
||||
// check new attachment was created pointint to same key
|
||||
const attachmentIds = parseAttachmentIds(newDocument.text);
|
||||
const newAttachment = await Attachment.findByPk(attachmentIds[0]);
|
||||
expect(newAttachment.documentId).toBe(newDocument.id);
|
||||
expect(newAttachment.key).toBe(attachment.key);
|
||||
expect(newAttachment?.documentId).toBe(newDocument.id);
|
||||
expect(newAttachment?.key).toBe(attachment.key);
|
||||
await document.reload();
|
||||
expect(document.collectionId).toBe(newCollection.id);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import invariant from "invariant";
|
||||
import { Transaction } from "sequelize";
|
||||
import { Document, Attachment, Collection, Pin, Event } from "@server/models";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import {
|
||||
User,
|
||||
Document,
|
||||
Attachment,
|
||||
Collection,
|
||||
Pin,
|
||||
Event,
|
||||
} from "@server/models";
|
||||
import parseAttachmentIds from "@server/utils/parseAttachmentIds";
|
||||
import { sequelize } from "../sequelize";
|
||||
import pinDestroyer from "./pinDestroyer";
|
||||
|
||||
async function copyAttachments(
|
||||
document: Document,
|
||||
options?: { transaction?: Transaction }
|
||||
) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'text' does not exist on type 'Document'.
|
||||
let text = document.text;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
const documentId = document.id;
|
||||
// find any image attachments that are in this documents text
|
||||
const attachmentIds = parseAttachmentIds(text);
|
||||
@@ -18,7 +24,6 @@ async function copyAttachments(
|
||||
for (const id of attachmentIds) {
|
||||
const existing = await Attachment.findOne({
|
||||
where: {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'teamId' does not exist on type 'Document... Remove this comment to see the full error message
|
||||
teamId: document.teamId,
|
||||
id,
|
||||
},
|
||||
@@ -29,6 +34,7 @@ async function copyAttachments(
|
||||
// then create a new attachment pointed to this doc and update the reference
|
||||
// in the text so that it gets the moved documents permissions
|
||||
if (existing && existing.documentId !== documentId) {
|
||||
// @ts-expect-error dataValues exists
|
||||
const { id, ...rest } = existing.dataValues;
|
||||
const attachment = await Attachment.create(
|
||||
{ ...rest, documentId },
|
||||
@@ -41,6 +47,21 @@ async function copyAttachments(
|
||||
return text;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
user: User;
|
||||
document: Document;
|
||||
collectionId: string;
|
||||
parentDocumentId?: string | null;
|
||||
index?: number;
|
||||
ip: string;
|
||||
};
|
||||
|
||||
type Result = {
|
||||
collections: Collection[];
|
||||
documents: Document[];
|
||||
collectionChanged: boolean;
|
||||
};
|
||||
|
||||
export default async function documentMover({
|
||||
user,
|
||||
document,
|
||||
@@ -49,19 +70,11 @@ export default async function documentMover({
|
||||
// convert undefined to null so parentId comparison treats them as equal
|
||||
index,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
document: any;
|
||||
collectionId: string;
|
||||
parentDocumentId?: string | null;
|
||||
index?: number;
|
||||
ip: string;
|
||||
}) {
|
||||
}: Props): Promise<Result> {
|
||||
let transaction: Transaction | undefined;
|
||||
const collectionChanged = collectionId !== document.collectionId;
|
||||
const previousCollectionId = document.collectionId;
|
||||
const result = {
|
||||
const result: Result = {
|
||||
collections: [],
|
||||
documents: [],
|
||||
collectionChanged,
|
||||
@@ -77,7 +90,6 @@ export default async function documentMover({
|
||||
document.lastModifiedById = user.id;
|
||||
document.updatedBy = user;
|
||||
await document.save();
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Document' is not assignable to p... Remove this comment to see the full error message
|
||||
result.documents.push(document);
|
||||
} else {
|
||||
try {
|
||||
@@ -88,12 +100,13 @@ export default async function documentMover({
|
||||
transaction,
|
||||
paranoid: false,
|
||||
});
|
||||
const [
|
||||
documentJson,
|
||||
fromIndex,
|
||||
] = (await collection.removeDocumentInStructure(document, {
|
||||
|
||||
const response = await collection?.removeDocumentInStructure(document, {
|
||||
save: false,
|
||||
})) || [undefined, index];
|
||||
});
|
||||
|
||||
const documentJson = response?.[0];
|
||||
const fromIndex = response?.[1] || 0;
|
||||
|
||||
// if we're reordering from within the same parent
|
||||
// the original and destination collection are the same,
|
||||
@@ -110,7 +123,7 @@ export default async function documentMover({
|
||||
// if the collection is the same then it will get saved below, this
|
||||
// line prevents a pointless intermediate save from occurring.
|
||||
if (collectionChanged) {
|
||||
await collection.save({
|
||||
await collection?.save({
|
||||
transaction,
|
||||
});
|
||||
document.text = await copyAttachments(document, {
|
||||
@@ -124,36 +137,37 @@ export default async function documentMover({
|
||||
document.lastModifiedById = user.id;
|
||||
document.updatedBy = user;
|
||||
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Collection' refers to a value, but is being used ... Remove this comment to see the full error message
|
||||
const newCollection: Collection = collectionChanged
|
||||
const newCollection = collectionChanged
|
||||
? await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId, {
|
||||
transaction,
|
||||
})
|
||||
: collection;
|
||||
await newCollection.addDocumentToStructure(document, toIndex, {
|
||||
|
||||
invariant(newCollection, "collection should exist");
|
||||
|
||||
await newCollection?.addDocumentToStructure(document, toIndex, {
|
||||
documentJson,
|
||||
});
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message
|
||||
result.collections.push(collection);
|
||||
|
||||
if (collection) {
|
||||
result.collections.push(collection);
|
||||
}
|
||||
|
||||
// if collection does not remain the same loop through children and change their
|
||||
// collectionId and move any attachments they may have too. This includes
|
||||
// archived children, otherwise their collection would be wrong once restored.
|
||||
if (collectionChanged) {
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message
|
||||
result.collections.push(newCollection);
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'documentId' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
const loopChildren = async (documentId) => {
|
||||
const loopChildren = async (documentId: string) => {
|
||||
const childDocuments = await Document.findAll({
|
||||
where: {
|
||||
parentDocumentId: documentId,
|
||||
},
|
||||
});
|
||||
await Promise.all(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'child' implicitly has an 'any' type.
|
||||
childDocuments.map(async (child) => {
|
||||
await loopChildren(child.id);
|
||||
child.text = await copyAttachments(child, {
|
||||
@@ -161,8 +175,10 @@ export default async function documentMover({
|
||||
});
|
||||
child.collectionId = collectionId;
|
||||
await child.save();
|
||||
child.collection = newCollection;
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message
|
||||
|
||||
if (newCollection) {
|
||||
child.collection = newCollection;
|
||||
}
|
||||
result.documents.push(child);
|
||||
})
|
||||
);
|
||||
@@ -189,13 +205,13 @@ export default async function documentMover({
|
||||
await document.save({
|
||||
transaction,
|
||||
});
|
||||
document.collection = newCollection;
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Document' is not assignable to p... Remove this comment to see the full error message
|
||||
|
||||
if (newCollection) {
|
||||
document.collection = newCollection;
|
||||
}
|
||||
result.documents.push(document);
|
||||
|
||||
if (transaction) {
|
||||
await transaction.commit();
|
||||
}
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
if (transaction) {
|
||||
await transaction.rollback();
|
||||
@@ -213,9 +229,7 @@ export default async function documentMover({
|
||||
teamId: document.teamId,
|
||||
data: {
|
||||
title: document.title,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||
collectionIds: result.collections.map((c) => c.id),
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||
documentIds: result.documents.map((d) => d.id),
|
||||
},
|
||||
ip,
|
||||
|
||||
@@ -4,18 +4,8 @@ import { buildAttachment, buildDocument } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import documentPermanentDeleter from "./documentPermanentDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = {
|
||||
createPresignedPost: jest.fn(),
|
||||
deleteObject: jest.fn().mockReturnThis(),
|
||||
promise: jest.fn(),
|
||||
};
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("documentPermanentDeleter", () => {
|
||||
it("should destroy documents", async () => {
|
||||
const document = await buildDocument({
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { QueryTypes } from "sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import Logger from "@server/logging/logger";
|
||||
import { Document, Attachment } from "@server/models";
|
||||
import parseAttachmentIds from "@server/utils/parseAttachmentIds";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
export default async function documentPermanentDeleter(documents: Document[]) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'deletedAt' does not exist on type 'Docum... Remove this comment to see the full error message
|
||||
const activeDocument = documents.find((doc) => !doc.deletedAt);
|
||||
|
||||
if (activeDocument) {
|
||||
throw new Error(
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
`Cannot permanently delete ${activeDocument.id} document. Please delete it and try again.`
|
||||
);
|
||||
}
|
||||
@@ -23,16 +22,13 @@ export default async function documentPermanentDeleter(documents: Document[]) {
|
||||
`;
|
||||
|
||||
for (const document of documents) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'text' does not exist on type 'Document'.
|
||||
const attachmentIds = parseAttachmentIds(document.text);
|
||||
|
||||
for (const attachmentId of attachmentIds) {
|
||||
const [{ count }] = await sequelize.query(query, {
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
documentId: document.id,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'teamId' does not exist on type 'Document... Remove this comment to see the full error message
|
||||
teamId: document.teamId,
|
||||
query: attachmentId,
|
||||
},
|
||||
@@ -41,7 +37,6 @@ export default async function documentPermanentDeleter(documents: Document[]) {
|
||||
if (parseInt(count) === 0) {
|
||||
const attachment = await Attachment.findOne({
|
||||
where: {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'teamId' does not exist on type 'Document... Remove this comment to see the full error message
|
||||
teamId: document.teamId,
|
||||
id: attachmentId,
|
||||
},
|
||||
@@ -59,7 +54,6 @@ export default async function documentPermanentDeleter(documents: Document[]) {
|
||||
|
||||
return Document.scope("withUnpublished").destroy({
|
||||
where: {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
id: documents.map((document) => document.id),
|
||||
},
|
||||
force: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import invariant from "invariant";
|
||||
import { uniq } from "lodash";
|
||||
import { Node } from "prosemirror-model";
|
||||
import { schema, serializer } from "rich-markdown-editor";
|
||||
@@ -15,6 +16,8 @@ export default async function documentUpdater({
|
||||
userId?: string;
|
||||
}) {
|
||||
const document = await Document.findByPk(documentId);
|
||||
invariant(document, "document not found");
|
||||
|
||||
const state = Y.encodeStateAsUpdate(ydoc);
|
||||
const node = Node.fromJSON(schema, yDocToProsemirrorJSON(ydoc, "default"));
|
||||
const text = serializer.serialize(node, undefined);
|
||||
@@ -30,6 +33,7 @@ export default async function documentUpdater({
|
||||
const pudIds = Array.from(pud.clients.values());
|
||||
const existingIds = document.collaboratorIds;
|
||||
const collaboratorIds = uniq([...pudIds, ...existingIds]);
|
||||
|
||||
await Document.scope("withUnpublished").update(
|
||||
{
|
||||
text,
|
||||
|
||||
@@ -3,18 +3,8 @@ import { buildAdmin, buildFileOperation } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import fileOperationDeleter from "./fileOperationDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = {
|
||||
createPresignedPost: jest.fn(),
|
||||
deleteObject: jest.fn().mockReturnThis(),
|
||||
promise: jest.fn(),
|
||||
};
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("fileOperationDeleter", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { FileOperation, Event, User } from "@server/models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
export default async function fileOperationDeleter(
|
||||
// @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
|
||||
fileOp: FileOperation,
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User,
|
||||
ip: string
|
||||
) {
|
||||
@@ -19,6 +17,7 @@ export default async function fileOperationDeleter(
|
||||
name: "fileOperations.delete",
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
// @ts-expect-error dataValues does exist
|
||||
data: fileOp.dataValues,
|
||||
ip,
|
||||
},
|
||||
|
||||
@@ -25,8 +25,8 @@ describe("pinCreator", () => {
|
||||
expect(pin.collectionId).toEqual(null);
|
||||
expect(pin.createdById).toEqual(user.id);
|
||||
expect(pin.index).toEqual("P");
|
||||
expect(event.name).toEqual("pins.create");
|
||||
expect(event.modelId).toEqual(pin.id);
|
||||
expect(event!.name).toEqual("pins.create");
|
||||
expect(event!.modelId).toEqual(pin.id);
|
||||
});
|
||||
|
||||
it("should create pin to collection", async () => {
|
||||
@@ -48,8 +48,8 @@ describe("pinCreator", () => {
|
||||
expect(pin.collectionId).toEqual(document.collectionId);
|
||||
expect(pin.createdById).toEqual(user.id);
|
||||
expect(pin.index).toEqual("P");
|
||||
expect(event.name).toEqual("pins.create");
|
||||
expect(event.modelId).toEqual(pin.id);
|
||||
expect(event.collectionId).toEqual(pin.collectionId);
|
||||
expect(event!.name).toEqual("pins.create");
|
||||
expect(event!.modelId).toEqual(pin.id);
|
||||
expect(event!.collectionId).toEqual(pin.collectionId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import fractionalIndex from "fractional-index";
|
||||
import { Sequelize, Op, WhereOptions } from "sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { ValidationError } from "@server/errors";
|
||||
import { Pin, Event } from "@server/models";
|
||||
import { sequelize, Op } from "@server/sequelize";
|
||||
import { Pin, User, Event } from "@server/models";
|
||||
|
||||
const MAX_PINS = 8;
|
||||
|
||||
type Props = {
|
||||
/** The user creating the pin */
|
||||
user: any;
|
||||
user: User;
|
||||
/** The document to pin */
|
||||
documentId: string;
|
||||
/** The collection to pin the document in. If no collection is provided then it will be pinned to home */
|
||||
@@ -31,11 +32,11 @@ export default async function pinCreator({
|
||||
collectionId,
|
||||
ip,
|
||||
...rest
|
||||
}: Props): Promise<any> {
|
||||
}: Props): Promise<Pin> {
|
||||
let { index } = rest;
|
||||
const where = {
|
||||
const where: WhereOptions<Pin> = {
|
||||
teamId: user.teamId,
|
||||
...(collectionId ? { collectionId } : { collectionId: { [Op.eq]: null } }),
|
||||
...(collectionId ? { collectionId } : { collectionId: { [Op.is]: null } }),
|
||||
};
|
||||
|
||||
const count = await Pin.count({ where });
|
||||
@@ -51,7 +52,7 @@ export default async function pinCreator({
|
||||
order: [
|
||||
// using LC_COLLATE:"C" because we need byte order to drive the sorting
|
||||
// find only the last pin so we can create an index after it
|
||||
sequelize.literal('"pins"."index" collate "C" DESC'),
|
||||
Sequelize.literal('"pin"."index" collate "C" DESC'),
|
||||
["updatedAt", "ASC"],
|
||||
],
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("pinCreator", () => {
|
||||
expect(count).toEqual(0);
|
||||
|
||||
const event = await Event.findOne();
|
||||
expect(event.name).toEqual("pins.delete");
|
||||
expect(event.modelId).toEqual(pin.id);
|
||||
expect(event!.name).toEqual("pins.delete");
|
||||
expect(event!.modelId).toEqual(pin.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Transaction } from "sequelize";
|
||||
import { Event } from "@server/models";
|
||||
import { sequelize } from "@server/sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { Event, Pin, User } from "@server/models";
|
||||
|
||||
type Props = {
|
||||
/** The user destroying the pin */
|
||||
user: any;
|
||||
user: User;
|
||||
/** The pin to destroy */
|
||||
pin: any;
|
||||
pin: Pin;
|
||||
/** The IP address of the user creating the pin */
|
||||
ip: string;
|
||||
/** Optional existing transaction */
|
||||
@@ -25,7 +25,7 @@ export default async function pinDestroyer({
|
||||
pin,
|
||||
ip,
|
||||
transaction: t,
|
||||
}: Props): Promise<any> {
|
||||
}: Props): Promise<Pin> {
|
||||
const transaction = t || (await sequelize.transaction());
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Event } from "@server/models";
|
||||
import { sequelize } from "@server/sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { Event, Pin, User } from "@server/models";
|
||||
|
||||
type Props = {
|
||||
/** The user updating the pin */
|
||||
user: any;
|
||||
user: User;
|
||||
/** The existing pin */
|
||||
pin: any;
|
||||
pin: Pin;
|
||||
/** The index to pin the document at */
|
||||
index?: string;
|
||||
index: string;
|
||||
/** The IP address of the user creating the pin */
|
||||
ip: string;
|
||||
};
|
||||
@@ -24,7 +24,7 @@ export default async function pinUpdater({
|
||||
pin,
|
||||
index,
|
||||
ip,
|
||||
}: Props): Promise<any> {
|
||||
}: Props): Promise<Pin> {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { flushdb } from "@server/test/support";
|
||||
import revisionCreator from "./revisionCreator";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("revisionCreator", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
@@ -21,8 +22,8 @@ describe("revisionCreator", () => {
|
||||
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);
|
||||
expect(event.createdAt).toEqual(document.updatedAt);
|
||||
expect(event!.name).toEqual("revisions.create");
|
||||
expect(event!.modelId).toEqual(revision.id);
|
||||
expect(event!.createdAt).toEqual(document.updatedAt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { Document, User, Event, Revision } from "@server/models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
export default async function revisionCreator({
|
||||
document,
|
||||
@@ -7,7 +7,6 @@ export default async function revisionCreator({
|
||||
ip,
|
||||
}: {
|
||||
document: Document;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
ip?: string;
|
||||
}) {
|
||||
@@ -21,13 +20,10 @@ export default async function revisionCreator({
|
||||
await Event.create(
|
||||
{
|
||||
name: "revisions.create",
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'.
|
||||
documentId: document.id,
|
||||
modelId: revision.id,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'teamId' does not exist on type 'Document... Remove this comment to see the full error message
|
||||
teamId: document.teamId,
|
||||
actorId: user.id,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'updatedAt' does not exist on type 'Docum... Remove this comment to see the full error message
|
||||
createdAt: document.updatedAt,
|
||||
ip: ip || user.lastActiveIp,
|
||||
},
|
||||
|
||||
@@ -2,18 +2,8 @@ import { buildTeam } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import teamCreator from "./teamCreator";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = {
|
||||
createPresignedPost: jest.fn(),
|
||||
putObject: jest.fn().mockReturnThis(),
|
||||
promise: jest.fn(),
|
||||
};
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("teamCreator", () => {
|
||||
it("should create team and authentication provider", async () => {
|
||||
const result = await teamCreator({
|
||||
@@ -73,7 +63,7 @@ describe("teamCreator", () => {
|
||||
expect(authenticationProvider.name).toEqual("google");
|
||||
expect(authenticationProvider.providerId).toEqual("allowed-domain.com");
|
||||
expect(isNewTeam).toEqual(false);
|
||||
const providers = await team.getAuthenticationProviders();
|
||||
const providers = await team.$get("authenticationProviders");
|
||||
expect(providers.length).toEqual(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import invariant from "invariant";
|
||||
import Logger from "@server/logging/logger";
|
||||
import { Team, AuthenticationProvider } from "@server/models";
|
||||
import { getAllowedDomains } from "@server/utils/authentication";
|
||||
import { generateAvatarUrl } from "@server/utils/avatars";
|
||||
import { MaximumTeamsError } from "../errors";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
type TeamCreatorResult = {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Team' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
team: Team;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'AuthenticationProvider' refers to a value, but is... Remove this comment to see the full error message
|
||||
authenticationProvider: AuthenticationProvider;
|
||||
isNewTeam: boolean;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
domain?: string;
|
||||
subdomain: string;
|
||||
avatarUrl?: string | null;
|
||||
authenticationProvider: {
|
||||
name: string;
|
||||
providerId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default async function teamCreator({
|
||||
name,
|
||||
domain,
|
||||
subdomain,
|
||||
avatarUrl,
|
||||
authenticationProvider,
|
||||
}: {
|
||||
name: string;
|
||||
domain?: string;
|
||||
subdomain: string;
|
||||
avatarUrl?: string;
|
||||
authenticationProvider: {
|
||||
name: string;
|
||||
providerId: string;
|
||||
};
|
||||
}): Promise<TeamCreatorResult> {
|
||||
}: Props): Promise<TeamCreatorResult> {
|
||||
let authP = await AuthenticationProvider.findOne({
|
||||
where: authenticationProvider,
|
||||
include: [
|
||||
@@ -61,7 +61,12 @@ export default async function teamCreator({
|
||||
// authentication provider to the existing team
|
||||
if (teamCount === 1 && domain && getAllowedDomains().includes(domain)) {
|
||||
const team = await Team.findOne();
|
||||
authP = await team.createAuthenticationProvider(authenticationProvider);
|
||||
invariant(team, "Team should exist");
|
||||
|
||||
authP = await team.$create<AuthenticationProvider>(
|
||||
"authenticationProvider",
|
||||
authenticationProvider
|
||||
);
|
||||
return {
|
||||
authenticationProvider: authP,
|
||||
team,
|
||||
@@ -84,7 +89,7 @@ export default async function teamCreator({
|
||||
});
|
||||
}
|
||||
|
||||
const transaction = await sequelize.transaction();
|
||||
const transaction = await Team.sequelize!.transaction();
|
||||
let team;
|
||||
|
||||
try {
|
||||
|
||||
@@ -9,18 +9,8 @@ import {
|
||||
import { flushdb } from "@server/test/support";
|
||||
import teamPermanentDeleter from "./teamPermanentDeleter";
|
||||
|
||||
jest.mock("aws-sdk", () => {
|
||||
const mS3 = {
|
||||
createPresignedPost: jest.fn(),
|
||||
deleteObject: jest.fn().mockReturnThis(),
|
||||
promise: jest.fn(),
|
||||
};
|
||||
return {
|
||||
S3: jest.fn(() => mS3),
|
||||
Endpoint: jest.fn(),
|
||||
};
|
||||
});
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("teamPermanentDeleter", () => {
|
||||
it("should destroy related data", async () => {
|
||||
const team = await buildTeam({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import Logger from "@server/logging/logger";
|
||||
import {
|
||||
ApiKey,
|
||||
@@ -17,9 +18,7 @@ import {
|
||||
SearchQuery,
|
||||
Share,
|
||||
} from "@server/models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'Team' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
export default async function teamPermanentDeleter(team: Team) {
|
||||
if (!team.deletedAt) {
|
||||
throw new Error(
|
||||
@@ -45,16 +44,14 @@ export default async function teamPermanentDeleter(team: Team) {
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
},
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'attachments' implicitly has an 'any' ty... Remove this comment to see the full error message
|
||||
async (attachments, options) => {
|
||||
Logger.info(
|
||||
"commands",
|
||||
`Deleting attachments ${options.offset} – ${
|
||||
options.offset + options.limit
|
||||
(options.offset || 0) + (options?.limit || 0)
|
||||
}…`
|
||||
);
|
||||
await Promise.all(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'attachment' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
attachments.map((attachment) =>
|
||||
attachment.destroy({
|
||||
// @ts-expect-error ts-migrate(7005) FIXME: Variable 'transaction' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
@@ -74,9 +71,7 @@ export default async function teamPermanentDeleter(team: Team) {
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
},
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'users' implicitly has an 'any' type.
|
||||
async (users) => {
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'user' implicitly has an 'any' type.
|
||||
const userIds = users.map((user) => user.id);
|
||||
await UserAuthentication.destroy({
|
||||
where: {
|
||||
|
||||
@@ -3,12 +3,13 @@ import { flushdb } from "@server/test/support";
|
||||
import userCreator from "./userCreator";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("userCreator", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
it("should update exising user and authentication", async () => {
|
||||
const existing = await buildUser();
|
||||
const authentications = await existing.getAuthentications();
|
||||
const authentications = await existing.$get("authentications");
|
||||
const existingAuth = authentications[0];
|
||||
const newEmail = "test@example.com";
|
||||
const newUsername = "tname";
|
||||
@@ -37,7 +38,7 @@ describe("userCreator", () => {
|
||||
|
||||
it("should create user with deleted user matching providerId", async () => {
|
||||
const existing = await buildUser();
|
||||
const authentications = await existing.getAuthentications();
|
||||
const authentications = await existing.$get("authentications");
|
||||
const existingAuth = authentications[0];
|
||||
const newEmail = "test@example.com";
|
||||
await existing.destroy();
|
||||
@@ -63,7 +64,7 @@ describe("userCreator", () => {
|
||||
|
||||
it("should handle duplicate providerId for different iDP", async () => {
|
||||
const existing = await buildUser();
|
||||
const authentications = await existing.getAuthentications();
|
||||
const authentications = await existing.$get("authentications");
|
||||
const existingAuth = authentications[0];
|
||||
let error;
|
||||
|
||||
@@ -89,7 +90,7 @@ describe("userCreator", () => {
|
||||
|
||||
it("should create a new user", async () => {
|
||||
const team = await buildTeam();
|
||||
const authenticationProviders = await team.getAuthenticationProviders();
|
||||
const authenticationProviders = await team.$get("authenticationProviders");
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const result = await userCreator({
|
||||
name: "Test Name",
|
||||
@@ -119,7 +120,7 @@ describe("userCreator", () => {
|
||||
const team = await buildTeam({
|
||||
defaultUserRole: "viewer",
|
||||
});
|
||||
const authenticationProviders = await team.getAuthenticationProviders();
|
||||
const authenticationProviders = await team.$get("authenticationProviders");
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const result = await userCreator({
|
||||
name: "Test Name",
|
||||
@@ -143,7 +144,7 @@ describe("userCreator", () => {
|
||||
const team = await buildTeam({
|
||||
defaultUserRole: "viewer",
|
||||
});
|
||||
const authenticationProviders = await team.getAuthenticationProviders();
|
||||
const authenticationProviders = await team.$get("authenticationProviders");
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const result = await userCreator({
|
||||
name: "Test Name",
|
||||
@@ -187,11 +188,11 @@ describe("userCreator", () => {
|
||||
const invite = await buildInvite({
|
||||
teamId: team.id,
|
||||
});
|
||||
const authenticationProviders = await team.getAuthenticationProviders();
|
||||
const authenticationProviders = await team.$get("authenticationProviders");
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const result = await userCreator({
|
||||
name: invite.name,
|
||||
email: invite.email,
|
||||
email: invite.email!,
|
||||
teamId: invite.teamId,
|
||||
ip,
|
||||
authentication: {
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { Op } from "sequelize";
|
||||
import { Event, Team, User, UserAuthentication } from "@server/models";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
type UserCreatorResult = {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
isNewUser: boolean;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'UserAuthentication' refers to a value, but is bei... Remove this comment to see the full error message
|
||||
authentication: UserAuthentication;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
email: string;
|
||||
username?: string;
|
||||
isAdmin?: boolean;
|
||||
avatarUrl?: string | null;
|
||||
teamId: string;
|
||||
ip: string;
|
||||
authentication: {
|
||||
authenticationProviderId: string;
|
||||
providerId: string;
|
||||
scopes: string[];
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default async function userCreator({
|
||||
name,
|
||||
email,
|
||||
@@ -19,22 +33,7 @@ export default async function userCreator({
|
||||
teamId,
|
||||
authentication,
|
||||
ip,
|
||||
}: {
|
||||
name: string;
|
||||
email: string;
|
||||
username?: string;
|
||||
isAdmin?: boolean;
|
||||
avatarUrl?: string;
|
||||
teamId: string;
|
||||
ip: string;
|
||||
authentication: {
|
||||
authenticationProviderId: string;
|
||||
providerId: string;
|
||||
scopes: string[];
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
};
|
||||
}): Promise<UserCreatorResult> {
|
||||
}: Props): Promise<UserCreatorResult> {
|
||||
const { authenticationProviderId, providerId, ...rest } = authentication;
|
||||
const auth = await UserAuthentication.findOne({
|
||||
where: {
|
||||
@@ -90,7 +89,7 @@ export default async function userCreator({
|
||||
email,
|
||||
teamId,
|
||||
lastActiveAt: {
|
||||
[Op.eq]: null,
|
||||
[Op.is]: null,
|
||||
},
|
||||
},
|
||||
include: [
|
||||
@@ -105,7 +104,7 @@ export default async function userCreator({
|
||||
// We have an existing invite for his user, so we need to update it with our
|
||||
// new details and link up the authentication method
|
||||
if (invite && !invite.authentications.length) {
|
||||
const transaction = await sequelize.transaction();
|
||||
const transaction = await User.sequelize!.transaction();
|
||||
let auth;
|
||||
|
||||
try {
|
||||
@@ -118,9 +117,13 @@ export default async function userCreator({
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
auth = await invite.createAuthentication(authentication, {
|
||||
transaction,
|
||||
});
|
||||
auth = await invite.$create<UserAuthentication>(
|
||||
"authentication",
|
||||
authentication,
|
||||
{
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
await transaction.commit();
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
@@ -135,13 +138,15 @@ export default async function userCreator({
|
||||
}
|
||||
|
||||
// No auth, no user – this is an entirely new sign in.
|
||||
const transaction = await sequelize.transaction();
|
||||
const transaction = await User.sequelize!.transaction();
|
||||
|
||||
try {
|
||||
const { defaultUserRole } = await Team.findByPk(teamId, {
|
||||
const team = await Team.findByPk(teamId, {
|
||||
attributes: ["defaultUserRole"],
|
||||
transaction,
|
||||
});
|
||||
const defaultUserRole = team?.defaultUserRole;
|
||||
|
||||
const user = await User.create(
|
||||
{
|
||||
name,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { flushdb } from "@server/test/support";
|
||||
import userDestroyer from "./userDestroyer";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("userDestroyer", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Op } from "sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { Event, User } from "@server/models";
|
||||
import { ValidationError } from "../errors";
|
||||
import { Op, sequelize } from "../sequelize";
|
||||
|
||||
export default async function userDestroyer({
|
||||
user,
|
||||
actor,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
actor: User;
|
||||
ip: string;
|
||||
}) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { flushdb } from "@server/test/support";
|
||||
import userInviter from "./userInviter";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("userInviter", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
@@ -10,8 +11,8 @@ describe("userInviter", () => {
|
||||
const user = await buildUser();
|
||||
const response = await userInviter({
|
||||
invites: [
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: strin... Remove this comment to see the full error message
|
||||
{
|
||||
role: "member",
|
||||
email: "test@example.com",
|
||||
name: "Test",
|
||||
},
|
||||
@@ -26,8 +27,8 @@ describe("userInviter", () => {
|
||||
const user = await buildUser();
|
||||
const response = await userInviter({
|
||||
invites: [
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: strin... Remove this comment to see the full error message
|
||||
{
|
||||
role: "member",
|
||||
email: " ",
|
||||
name: "Test",
|
||||
},
|
||||
@@ -42,8 +43,8 @@ describe("userInviter", () => {
|
||||
const user = await buildUser();
|
||||
const response = await userInviter({
|
||||
invites: [
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: strin... Remove this comment to see the full error message
|
||||
{
|
||||
role: "member",
|
||||
email: "notanemail",
|
||||
name: "Test",
|
||||
},
|
||||
@@ -58,13 +59,13 @@ describe("userInviter", () => {
|
||||
const user = await buildUser();
|
||||
const response = await userInviter({
|
||||
invites: [
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: strin... Remove this comment to see the full error message
|
||||
{
|
||||
role: "member",
|
||||
email: "the@same.com",
|
||||
name: "Test",
|
||||
},
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: strin... Remove this comment to see the full error message
|
||||
{
|
||||
role: "member",
|
||||
email: "the@SAME.COM",
|
||||
name: "Test",
|
||||
},
|
||||
@@ -79,9 +80,9 @@ describe("userInviter", () => {
|
||||
const user = await buildUser();
|
||||
const response = await userInviter({
|
||||
invites: [
|
||||
// @ts-expect-error ts-migrate(2741) FIXME: Property 'role' is missing in type '{ email: any; ... Remove this comment to see the full error message
|
||||
{
|
||||
email: user.email,
|
||||
role: "member",
|
||||
email: user.email!,
|
||||
name: user.name,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import invariant from "invariant";
|
||||
import { uniqBy } from "lodash";
|
||||
import { Role } from "@shared/types";
|
||||
import mailer from "@server/mailer";
|
||||
import { User, Event, Team } from "@server/models";
|
||||
import mailer from "../mailer";
|
||||
|
||||
type Invite = {
|
||||
name: string;
|
||||
@@ -14,16 +15,16 @@ export default async function userInviter({
|
||||
invites,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
invites: Invite[];
|
||||
ip: string;
|
||||
}): Promise<{
|
||||
sent: Invite[];
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
users: User[];
|
||||
}> {
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
invariant(team, "team not found");
|
||||
|
||||
// filter out empties and obvious non-emails
|
||||
const compactedInvites = invites.filter(
|
||||
(invite) => !!invite.email.trim() && invite.email.match("@")
|
||||
@@ -44,7 +45,6 @@ export default async function userInviter({
|
||||
email: emails,
|
||||
},
|
||||
});
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'user' implicitly has an 'any' type.
|
||||
const existingEmails = existingUsers.map((user) => user.email);
|
||||
const filteredInvites = normalizedInvites.filter(
|
||||
(invite) => !existingEmails.includes(invite.email)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { GroupUser } from "@server/models";
|
||||
import GroupUser from "@server/models/GroupUser";
|
||||
import { buildGroup, buildAdmin, buildUser } from "@server/test/factories";
|
||||
import { flushdb } from "@server/test/support";
|
||||
import userSuspender from "./userSuspender";
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
|
||||
describe("userSuspender", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
@@ -46,7 +47,7 @@ describe("userSuspender", () => {
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await group.addUser(user, {
|
||||
await group.$add("user", user, {
|
||||
through: {
|
||||
createdById: user.id,
|
||||
},
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Transaction } from "sequelize";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import { User, Event, GroupUser } from "@server/models";
|
||||
import { ValidationError } from "../errors";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
type Props = {
|
||||
user: User;
|
||||
actorId: string;
|
||||
ip: string;
|
||||
};
|
||||
|
||||
export default async function userSuspender({
|
||||
user,
|
||||
actorId,
|
||||
ip,
|
||||
}: {
|
||||
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
|
||||
user: User;
|
||||
actorId: string;
|
||||
ip: string;
|
||||
}): Promise<void> {
|
||||
}: Props): Promise<void> {
|
||||
if (user.id === actorId) {
|
||||
throw ValidationError("Unable to suspend the current user");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user