Merge main
This commit is contained in:
@@ -9,7 +9,7 @@ Object {
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"isSuspended": false,
|
||||
"language": null,
|
||||
"language": "en_US",
|
||||
"lastActiveAt": null,
|
||||
"name": "User 1",
|
||||
},
|
||||
@@ -45,7 +45,7 @@ Object {
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"isSuspended": false,
|
||||
"language": null,
|
||||
"language": "en_US",
|
||||
"lastActiveAt": null,
|
||||
"name": "User 1",
|
||||
},
|
||||
@@ -81,7 +81,7 @@ Object {
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": true,
|
||||
"isSuspended": false,
|
||||
"language": null,
|
||||
"language": "en_US",
|
||||
"lastActiveAt": null,
|
||||
"name": "User 1",
|
||||
},
|
||||
@@ -126,7 +126,7 @@ Object {
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"isSuspended": true,
|
||||
"language": null,
|
||||
"language": "en_US",
|
||||
"lastActiveAt": null,
|
||||
"name": "User 1",
|
||||
},
|
||||
|
||||
43
server/api/auth.test.js
Normal file
43
server/api/auth.test.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { buildUser, buildTeam } from "../test/factories";
|
||||
import { flushdb } from "../test/support";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(() => flushdb());
|
||||
afterAll(() => server.close());
|
||||
|
||||
describe("#auth.info", () => {
|
||||
it("should return current authentication", async () => {
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({ teamId: team.id });
|
||||
|
||||
const res = await server.post("/api/auth.info", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.user.name).toBe(user.name);
|
||||
expect(body.data.team.name).toBe(team.name);
|
||||
});
|
||||
|
||||
it("should require the team to not be deleted", async () => {
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({ teamId: team.id });
|
||||
|
||||
await team.destroy();
|
||||
|
||||
const res = await server.post("/api/auth.info", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/auth.info");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
@@ -31,7 +31,13 @@ const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post("collections.create", auth(), async (ctx) => {
|
||||
const { name, color, description, icon } = ctx.body;
|
||||
const {
|
||||
name,
|
||||
color,
|
||||
description,
|
||||
icon,
|
||||
sort = Collection.DEFAULT_SORT,
|
||||
} = ctx.body;
|
||||
const isPrivate = ctx.body.private;
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
@@ -48,8 +54,9 @@ router.post("collections.create", auth(), async (ctx) => {
|
||||
icon,
|
||||
color,
|
||||
teamId: user.teamId,
|
||||
creatorId: user.id,
|
||||
createdById: user.id,
|
||||
private: isPrivate,
|
||||
sort,
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
@@ -484,16 +491,14 @@ router.post("collections.export_all", auth(), async (ctx) => {
|
||||
});
|
||||
|
||||
router.post("collections.update", auth(), async (ctx) => {
|
||||
const { id, name, description, icon, color } = ctx.body;
|
||||
let { id, name, description, icon, color, sort } = ctx.body;
|
||||
const isPrivate = ctx.body.private;
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
if (color) {
|
||||
ctx.assertHexColor(color, "Invalid hex value (please use format #FFFFFF)");
|
||||
}
|
||||
|
||||
const user = ctx.state.user;
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
@@ -517,11 +522,24 @@ router.post("collections.update", auth(), async (ctx) => {
|
||||
|
||||
const isPrivacyChanged = isPrivate !== collection.private;
|
||||
|
||||
collection.name = name;
|
||||
collection.description = description;
|
||||
collection.icon = icon;
|
||||
collection.color = color;
|
||||
collection.private = isPrivate;
|
||||
if (name !== undefined) {
|
||||
collection.name = name;
|
||||
}
|
||||
if (description !== undefined) {
|
||||
collection.description = description;
|
||||
}
|
||||
if (icon !== undefined) {
|
||||
collection.icon = icon;
|
||||
}
|
||||
if (color !== undefined) {
|
||||
collection.color = color;
|
||||
}
|
||||
if (isPrivate !== undefined) {
|
||||
collection.private = isPrivate;
|
||||
}
|
||||
if (sort !== undefined) {
|
||||
collection.sort = sort;
|
||||
}
|
||||
|
||||
await collection.save();
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { Collection, CollectionUser, CollectionGroup } from "../models";
|
||||
import { buildUser, buildGroup, buildCollection } from "../test/factories";
|
||||
import { Document, CollectionUser, CollectionGroup } from "../models";
|
||||
import {
|
||||
buildUser,
|
||||
buildGroup,
|
||||
buildCollection,
|
||||
buildDocument,
|
||||
} from "../test/factories";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
@@ -885,6 +890,8 @@ describe("#collections.create", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toBeTruthy();
|
||||
expect(body.data.name).toBe("Test");
|
||||
expect(body.data.sort.field).toBe("index");
|
||||
expect(body.data.sort.direction).toBe("asc");
|
||||
expect(body.policies.length).toBe(1);
|
||||
expect(body.policies[0].abilities.read).toBeTruthy();
|
||||
expect(body.policies[0].abilities.export).toBeTruthy();
|
||||
@@ -937,6 +944,29 @@ describe("#collections.update", () => {
|
||||
expect(body.policies.length).toBe(1);
|
||||
});
|
||||
|
||||
it("allows editing sort", async () => {
|
||||
const { user, collection } = await seed();
|
||||
const sort = { field: "index", direction: "desc" };
|
||||
const res = await server.post("/api/collections.update", {
|
||||
body: { token: user.getJwtToken(), id: collection.id, sort },
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.sort.field).toBe("index");
|
||||
expect(body.data.sort.direction).toBe("desc");
|
||||
});
|
||||
|
||||
it("allows editing individual fields", async () => {
|
||||
const { user, collection } = await seed();
|
||||
const res = await server.post("/api/collections.update", {
|
||||
body: { token: user.getJwtToken(), id: collection.id, private: true },
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.private).toBe(true);
|
||||
expect(body.data.name).toBe(collection.name);
|
||||
});
|
||||
|
||||
it("allows editing from non-private to private collection", async () => {
|
||||
const { user, collection } = await seed();
|
||||
const res = await server.post("/api/collections.update", {
|
||||
@@ -1048,6 +1078,24 @@ describe("#collections.update", () => {
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it("does not allow setting unknown sort fields", async () => {
|
||||
const { user, collection } = await seed();
|
||||
const sort = { field: "blah", direction: "desc" };
|
||||
const res = await server.post("/api/collections.update", {
|
||||
body: { token: user.getJwtToken(), id: collection.id, sort },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it("does not allow setting unknown sort directions", async () => {
|
||||
const { user, collection } = await seed();
|
||||
const sort = { field: "title", direction: "blah" };
|
||||
const res = await server.post("/api/collections.update", {
|
||||
body: { token: user.getJwtToken(), id: collection.id, sort },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#collections.delete", () => {
|
||||
@@ -1078,11 +1126,11 @@ describe("#collections.delete", () => {
|
||||
|
||||
it("should delete collection", async () => {
|
||||
const { user, collection } = await seed();
|
||||
await Collection.create({
|
||||
name: "Blah",
|
||||
urlId: "blah",
|
||||
|
||||
// to ensure it isn't the last collection
|
||||
await buildCollection({
|
||||
teamId: user.teamId,
|
||||
creatorId: user.id,
|
||||
createdById: user.id,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/collections.delete", {
|
||||
@@ -1094,6 +1142,37 @@ describe("#collections.delete", () => {
|
||||
expect(body.success).toBe(true);
|
||||
});
|
||||
|
||||
it("should delete published documents", async () => {
|
||||
const { user, collection } = await seed();
|
||||
|
||||
// to ensure it isn't the last collection
|
||||
await buildCollection({
|
||||
teamId: user.teamId,
|
||||
createdById: user.id,
|
||||
});
|
||||
|
||||
// archived document should not be deleted
|
||||
await buildDocument({
|
||||
collectionId: collection.id,
|
||||
archivedAt: new Date(),
|
||||
});
|
||||
|
||||
const res = await server.post("/api/collections.delete", {
|
||||
body: { token: user.getJwtToken(), id: collection.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.success).toBe(true);
|
||||
expect(
|
||||
await Document.count({
|
||||
where: {
|
||||
collectionId: collection.id,
|
||||
},
|
||||
})
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it("allows deleting by read-write collection group user", async () => {
|
||||
const user = await buildUser();
|
||||
const collection = await buildCollection({
|
||||
|
||||
@@ -38,7 +38,7 @@ const { authorize, cannot } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
const {
|
||||
let {
|
||||
sort = "updatedAt",
|
||||
template,
|
||||
backlinkDocumentId,
|
||||
@@ -71,6 +71,7 @@ router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
where = { ...where, createdById };
|
||||
}
|
||||
|
||||
let documentIds = [];
|
||||
// if a specific collection is passed then we need to check auth to view it
|
||||
if (collectionId) {
|
||||
ctx.assertUuid(collectionId, "collection must be a UUID");
|
||||
@@ -81,6 +82,15 @@ router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
}).findByPk(collectionId);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
// index sort is special because it uses the order of the documents in the
|
||||
// collection.documentStructure rather than a database column
|
||||
if (sort === "index") {
|
||||
documentIds = collection.documentStructure
|
||||
.map((node) => node.id)
|
||||
.slice(ctx.state.pagination.offset, ctx.state.pagination.limit);
|
||||
where = { ...where, id: documentIds };
|
||||
}
|
||||
|
||||
// otherwise, filter by all collections the user has access to
|
||||
} else {
|
||||
const collectionIds = await user.collectionIds();
|
||||
@@ -92,6 +102,12 @@ router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
where = { ...where, parentDocumentId };
|
||||
}
|
||||
|
||||
// Explicitly passing 'null' as the parentDocumentId allows listing documents
|
||||
// that have no parent document (aka they are at the root of the collection)
|
||||
if (parentDocumentId === null) {
|
||||
where = { ...where, parentDocumentId: { [Op.eq]: null } };
|
||||
}
|
||||
|
||||
if (backlinkDocumentId) {
|
||||
ctx.assertUuid(backlinkDocumentId, "backlinkDocumentId must be a UUID");
|
||||
|
||||
@@ -108,6 +124,10 @@ router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (sort === "index") {
|
||||
sort = "updatedAt";
|
||||
}
|
||||
|
||||
// add the users starred state to the response by default
|
||||
const starredScope = { method: ["withStarred", user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
@@ -124,6 +144,14 @@ router.post("documents.list", auth(), pagination(), async (ctx) => {
|
||||
limit: ctx.state.pagination.limit,
|
||||
});
|
||||
|
||||
// index sort is special because it uses the order of the documents in the
|
||||
// collection.documentStructure rather than a database column
|
||||
if (documentIds.length) {
|
||||
documents.sort(
|
||||
(a, b) => documentIds.indexOf(a.id) - documentIds.indexOf(b.id)
|
||||
);
|
||||
}
|
||||
|
||||
const data = await Promise.all(
|
||||
documents.map((document) => presentDocument(document))
|
||||
);
|
||||
@@ -523,18 +551,27 @@ router.post("documents.restore", auth(), async (ctx) => {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
|
||||
// Passing collectionId allows restoring to a different collection than the
|
||||
// document was originally within
|
||||
if (collectionId) {
|
||||
ctx.assertUuid(collectionId, "collectionId must be a uuid");
|
||||
authorize(user, "restore", document);
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId);
|
||||
authorize(user, "update", collection);
|
||||
|
||||
document.collectionId = collectionId;
|
||||
}
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(document.collectionId);
|
||||
|
||||
// if the collectionId was provided in the request and isn't valid then it will
|
||||
// be caught as a 403 on the authorize call below. Otherwise we're checking here
|
||||
// that the original collection still exists and advising to pass collectionId
|
||||
// if not.
|
||||
if (!collectionId) {
|
||||
ctx.assertPresent(collection, "collectionId is required");
|
||||
}
|
||||
|
||||
authorize(user, "update", collection);
|
||||
|
||||
if (document.deletedAt) {
|
||||
authorize(user, "restore", document);
|
||||
|
||||
@@ -910,7 +947,7 @@ router.post("documents.update", auth(), async (ctx) => {
|
||||
transaction = await sequelize.transaction();
|
||||
|
||||
if (publish) {
|
||||
await document.publish({ transaction });
|
||||
await document.publish(user.id, { transaction });
|
||||
} else {
|
||||
await document.save({ autosave, transaction });
|
||||
}
|
||||
@@ -1087,7 +1124,7 @@ router.post("documents.unpublish", auth(), async (ctx) => {
|
||||
|
||||
authorize(user, "unpublish", document);
|
||||
|
||||
await document.unpublish();
|
||||
await document.unpublish(user.id);
|
||||
|
||||
await Event.create({
|
||||
name: "documents.unpublish",
|
||||
|
||||
@@ -433,7 +433,27 @@ describe("#documents.list", () => {
|
||||
expect(body.data[0].id).toEqual(document.id);
|
||||
});
|
||||
|
||||
it("should not return unpublished documents", async () => {
|
||||
it("should allow filtering documents with no parent", async () => {
|
||||
const { user, document } = await seed();
|
||||
await buildDocument({
|
||||
title: "child document",
|
||||
text: "random text",
|
||||
parentDocumentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/documents.list", {
|
||||
body: { token: user.getJwtToken(), parentDocumentId: null },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.length).toEqual(1);
|
||||
expect(body.data[0].id).toEqual(document.id);
|
||||
});
|
||||
|
||||
it("should not return draft documents", async () => {
|
||||
const { user, document } = await seed();
|
||||
document.publishedAt = null;
|
||||
await document.save();
|
||||
@@ -493,6 +513,32 @@ describe("#documents.list", () => {
|
||||
expect(body.data[1].id).toEqual(anotherDoc.id);
|
||||
});
|
||||
|
||||
it("should allow sorting by collection index", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
const anotherDoc = await buildDocument({
|
||||
title: "another document",
|
||||
text: "random text",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
collectionId: collection.id,
|
||||
});
|
||||
await collection.addDocumentToStructure(anotherDoc, 0);
|
||||
|
||||
const res = await server.post("/api/documents.list", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
collectionId: collection.id,
|
||||
sort: "index",
|
||||
direction: "ASC",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data[0].id).toEqual(anotherDoc.id);
|
||||
expect(body.data[1].id).toEqual(document.id);
|
||||
});
|
||||
|
||||
it("should allow filtering by collection", async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post("/api/documents.list", {
|
||||
@@ -1334,7 +1380,22 @@ describe("#documents.restore", () => {
|
||||
expect(body.data.collectionId).toEqual(collection.id);
|
||||
});
|
||||
|
||||
it("should now allow restore of trashed documents to collection user cannot access", async () => {
|
||||
it("should not allow restore of documents in deleted collection", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
|
||||
await document.destroy(user.id);
|
||||
await collection.destroy();
|
||||
|
||||
const res = await server.post("/api/documents.restore", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: document.id,
|
||||
},
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it("should not allow restore of trashed documents to collection user cannot access", async () => {
|
||||
const { user, document } = await seed();
|
||||
const collection = await buildCollection();
|
||||
|
||||
@@ -1955,7 +2016,7 @@ describe("#documents.delete", () => {
|
||||
|
||||
describe("#documents.unpublish", () => {
|
||||
it("should unpublish a document", async () => {
|
||||
const { user, document } = await seed();
|
||||
let { user, document } = await seed();
|
||||
const res = await server.post("/api/documents.unpublish", {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
@@ -1964,6 +2025,28 @@ describe("#documents.unpublish", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.publishedAt).toBeNull();
|
||||
|
||||
document = await Document.unscoped().findByPk(document.id);
|
||||
expect(document.userId).toEqual(user.id);
|
||||
});
|
||||
|
||||
it("should unpublish another users document", async () => {
|
||||
const { user, collection } = await seed();
|
||||
let document = await buildDocument({
|
||||
teamId: user.teamId,
|
||||
collectionId: collection.id,
|
||||
});
|
||||
const res = await server.post("/api/documents.unpublish", {
|
||||
body: { token: user.getJwtToken(), id: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.publishedAt).toBeNull();
|
||||
|
||||
document = await Document.unscoped().findByPk(document.id);
|
||||
expect(document.userId).toEqual(user.id);
|
||||
});
|
||||
|
||||
it("should fail to unpublish a draft document", async () => {
|
||||
@@ -1989,7 +2072,7 @@ describe("#documents.unpublish", () => {
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it("should fail to unpublish a archived document", async () => {
|
||||
it("should fail to unpublish an archived document", async () => {
|
||||
const { user, document } = await seed();
|
||||
await document.archive();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import Router from "koa-router";
|
||||
import Sequelize from "sequelize";
|
||||
import auth from "../middlewares/authentication";
|
||||
import { Event, Team, User } from "../models";
|
||||
import { Event, Team, User, Collection } from "../models";
|
||||
import policy from "../policies";
|
||||
import { presentEvent } from "../presenters";
|
||||
import pagination from "./middlewares/pagination";
|
||||
@@ -12,30 +12,62 @@ const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post("events.list", auth(), pagination(), async (ctx) => {
|
||||
let { sort = "createdAt", direction, auditLog = false } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds({ paranoid: false });
|
||||
let {
|
||||
sort = "createdAt",
|
||||
actorId,
|
||||
collectionId,
|
||||
direction,
|
||||
name,
|
||||
auditLog = false,
|
||||
} = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
let where = {
|
||||
name: Event.ACTIVITY_EVENTS,
|
||||
teamId: user.teamId,
|
||||
[Op.or]: [
|
||||
{ collectionId: collectionIds },
|
||||
{
|
||||
collectionId: {
|
||||
[Op.eq]: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (actorId) {
|
||||
ctx.assertUuid(actorId, "actorId must be a UUID");
|
||||
where = {
|
||||
...where,
|
||||
actorId,
|
||||
};
|
||||
}
|
||||
|
||||
if (collectionId) {
|
||||
ctx.assertUuid(collectionId, "collection must be a UUID");
|
||||
|
||||
where = { ...where, collectionId };
|
||||
const collection = await Collection.scope({
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId);
|
||||
authorize(user, "read", collection);
|
||||
} else {
|
||||
const collectionIds = await user.collectionIds({ paranoid: false });
|
||||
where = {
|
||||
...where,
|
||||
[Op.or]: [
|
||||
{ collectionId: collectionIds },
|
||||
{
|
||||
collectionId: {
|
||||
[Op.eq]: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (auditLog) {
|
||||
authorize(user, "auditLog", Team);
|
||||
where.name = Event.AUDIT_EVENTS;
|
||||
}
|
||||
|
||||
if (name && where.name.includes(name)) {
|
||||
where.name = name;
|
||||
}
|
||||
|
||||
const events = await Event.findAll({
|
||||
where,
|
||||
order: [[sort, direction]],
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("#events.list", () => {
|
||||
it("should only return activity events", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// private event
|
||||
// audit event
|
||||
await buildEvent({
|
||||
name: "users.promote",
|
||||
teamId: user.teamId,
|
||||
@@ -29,6 +29,7 @@ describe("#events.list", () => {
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
@@ -39,6 +40,100 @@ describe("#events.list", () => {
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
});
|
||||
|
||||
it("should return audit events", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// audit event
|
||||
const auditEvent = await buildEvent({
|
||||
name: "users.promote",
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
// event viewable in activity stream
|
||||
const event = await buildEvent({
|
||||
name: "documents.publish",
|
||||
collectionId: collection.id,
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: admin.getJwtToken(), auditLog: true },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.length).toEqual(2);
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
expect(body.data[1].id).toEqual(auditEvent.id);
|
||||
});
|
||||
|
||||
it("should allow filtering by actorId", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// audit event
|
||||
const auditEvent = await buildEvent({
|
||||
name: "users.promote",
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
// event viewable in activity stream
|
||||
await buildEvent({
|
||||
name: "documents.publish",
|
||||
collectionId: collection.id,
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: admin.getJwtToken(), auditLog: true, actorId: admin.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.length).toEqual(1);
|
||||
expect(body.data[0].id).toEqual(auditEvent.id);
|
||||
});
|
||||
|
||||
it("should allow filtering by event name", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// audit event
|
||||
await buildEvent({
|
||||
name: "users.promote",
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
// event viewable in activity stream
|
||||
const event = await buildEvent({
|
||||
name: "documents.publish",
|
||||
collectionId: collection.id,
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
});
|
||||
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
name: "documents.publish",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.length).toEqual(1);
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
});
|
||||
|
||||
it("should return events with deleted actors", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
@@ -64,6 +159,15 @@ describe("#events.list", () => {
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
});
|
||||
|
||||
it("should require authorization for audit events", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: user.getJwtToken(), auditLog: true },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/events.list");
|
||||
const body = await res.json();
|
||||
|
||||
Reference in New Issue
Block a user