chore: Move to prettier standard double quotes (#1309)
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import Router from "koa-router";
|
||||
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentApiKey } from '../presenters';
|
||||
import { ApiKey, Event } from '../models';
|
||||
import policy from '../policies';
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import { presentApiKey } from "../presenters";
|
||||
import { ApiKey, Event } from "../models";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('apiKeys.create', auth(), async ctx => {
|
||||
router.post("apiKeys.create", auth(), async ctx => {
|
||||
const { name } = ctx.body;
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', ApiKey);
|
||||
authorize(user, "create", ApiKey);
|
||||
|
||||
const key = await ApiKey.create({
|
||||
name,
|
||||
@@ -23,7 +23,7 @@ router.post('apiKeys.create', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'api_keys.create',
|
||||
name: "api_keys.create",
|
||||
modelId: key.id,
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
@@ -36,13 +36,13 @@ router.post('apiKeys.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('apiKeys.list', auth(), pagination(), async ctx => {
|
||||
router.post("apiKeys.list", auth(), pagination(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
const keys = await ApiKey.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
});
|
||||
@@ -53,18 +53,18 @@ router.post('apiKeys.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('apiKeys.delete', auth(), async ctx => {
|
||||
router.post("apiKeys.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const key = await ApiKey.findByPk(id);
|
||||
authorize(user, 'delete', key);
|
||||
authorize(user, "delete", key);
|
||||
|
||||
await key.destroy();
|
||||
|
||||
await Event.create({
|
||||
name: 'api_keys.delete',
|
||||
name: "api_keys.delete",
|
||||
modelId: key.id,
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import uuid from 'uuid';
|
||||
import format from 'date-fns/format';
|
||||
import { Attachment, Document, Event } from '../models';
|
||||
import Router from "koa-router";
|
||||
import uuid from "uuid";
|
||||
import format from "date-fns/format";
|
||||
import { Attachment, Document, Event } from "../models";
|
||||
import {
|
||||
makePolicy,
|
||||
getSignature,
|
||||
publicS3Endpoint,
|
||||
makeCredential,
|
||||
getSignedImageUrl,
|
||||
} from '../utils/s3';
|
||||
import auth from '../middlewares/authentication';
|
||||
import { NotFoundError } from '../errors';
|
||||
import policy from '../policies';
|
||||
} from "../utils/s3";
|
||||
import auth from "../middlewares/authentication";
|
||||
import { NotFoundError } from "../errors";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
const AWS_S3_ACL = process.env.AWS_S3_ACL || 'private';
|
||||
const AWS_S3_ACL = process.env.AWS_S3_ACL || "private";
|
||||
|
||||
router.post('attachments.create', auth(), async ctx => {
|
||||
router.post("attachments.create", auth(), async ctx => {
|
||||
let { name, documentId, contentType, size } = ctx.body;
|
||||
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertPresent(contentType, 'contentType is required');
|
||||
ctx.assertPresent(size, 'size is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
ctx.assertPresent(contentType, "contentType is required");
|
||||
ctx.assertPresent(size, "size is required");
|
||||
|
||||
const { user } = ctx.state;
|
||||
const s3Key = uuid.v4();
|
||||
@@ -31,16 +31,16 @@ router.post('attachments.create', auth(), async ctx => {
|
||||
const acl =
|
||||
ctx.body.public === undefined
|
||||
? AWS_S3_ACL
|
||||
: ctx.body.public ? 'public-read' : 'private';
|
||||
: ctx.body.public ? "public-read" : "private";
|
||||
const credential = makeCredential();
|
||||
const longDate = format(new Date(), 'YYYYMMDDTHHmmss\\Z');
|
||||
const longDate = format(new Date(), "YYYYMMDDTHHmmss\\Z");
|
||||
const policy = makePolicy(credential, longDate, acl);
|
||||
const endpoint = publicS3Endpoint();
|
||||
const url = `${endpoint}/${key}`;
|
||||
|
||||
if (documentId) {
|
||||
const document = await Document.findByPk(documentId, { userId: user.id });
|
||||
authorize(user, 'update', document);
|
||||
authorize(user, "update", document);
|
||||
}
|
||||
|
||||
const attachment = await Attachment.create({
|
||||
@@ -55,7 +55,7 @@ router.post('attachments.create', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'attachments.create',
|
||||
name: "attachments.create",
|
||||
data: { name },
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
@@ -67,15 +67,15 @@ router.post('attachments.create', auth(), async ctx => {
|
||||
maxUploadSize: process.env.AWS_S3_UPLOAD_MAX_SIZE,
|
||||
uploadUrl: endpoint,
|
||||
form: {
|
||||
'Cache-Control': 'max-age=31557600',
|
||||
'Content-Type': contentType,
|
||||
"Cache-Control": "max-age=31557600",
|
||||
"Content-Type": contentType,
|
||||
acl,
|
||||
key,
|
||||
policy,
|
||||
'x-amz-algorithm': 'AWS4-HMAC-SHA256',
|
||||
'x-amz-credential': credential,
|
||||
'x-amz-date': longDate,
|
||||
'x-amz-signature': getSignature(policy),
|
||||
"x-amz-algorithm": "AWS4-HMAC-SHA256",
|
||||
"x-amz-credential": credential,
|
||||
"x-amz-date": longDate,
|
||||
"x-amz-signature": getSignature(policy),
|
||||
},
|
||||
attachment: {
|
||||
documentId,
|
||||
@@ -88,9 +88,9 @@ router.post('attachments.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('attachments.redirect', auth(), async ctx => {
|
||||
router.post("attachments.redirect", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const attachment = await Attachment.findByPk(id);
|
||||
@@ -103,7 +103,7 @@ router.post('attachments.redirect', auth(), async ctx => {
|
||||
const document = await Document.findByPk(attachment.documentId, {
|
||||
userId: user.id,
|
||||
});
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
}
|
||||
|
||||
const accessUrl = await getSignedImageUrl(attachment.key);
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { flushdb } from '../test/support';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { flushdb } from "../test/support";
|
||||
import {
|
||||
buildUser,
|
||||
buildCollection,
|
||||
buildAttachment,
|
||||
buildDocument,
|
||||
} from '../test/factories';
|
||||
} from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#attachments.redirect', async () => {
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/attachments.redirect');
|
||||
describe("#attachments.redirect", async () => {
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/attachments.redirect");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
|
||||
it('should return a redirect for an attachment belonging to a document user has access to', async () => {
|
||||
it("should return a redirect for an attachment belonging to a document user has access to", async () => {
|
||||
const user = await buildUser();
|
||||
const attachment = await buildAttachment({
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
const res = await server.post('/api/attachments.redirect', {
|
||||
const res = await server.post("/api/attachments.redirect", {
|
||||
body: { token: user.getJwtToken(), id: attachment.id },
|
||||
redirect: 'manual',
|
||||
redirect: "manual",
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(302);
|
||||
});
|
||||
|
||||
it('should always return a redirect for a public attachment', async () => {
|
||||
it("should always return a redirect for a public attachment", async () => {
|
||||
const user = await buildUser();
|
||||
const collection = await buildCollection({
|
||||
teamId: user.teamId,
|
||||
@@ -52,15 +52,15 @@ describe('#attachments.redirect', async () => {
|
||||
documentId: document.id,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/attachments.redirect', {
|
||||
const res = await server.post("/api/attachments.redirect", {
|
||||
body: { token: user.getJwtToken(), id: attachment.id },
|
||||
redirect: 'manual',
|
||||
redirect: "manual",
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(302);
|
||||
});
|
||||
|
||||
it('should not return a redirect for a private attachment belonging to a document user does not have access to', async () => {
|
||||
it("should not return a redirect for a private attachment belonging to a document user does not have access to", async () => {
|
||||
const user = await buildUser();
|
||||
const collection = await buildCollection({
|
||||
private: true,
|
||||
@@ -74,10 +74,10 @@ describe('#attachments.redirect', async () => {
|
||||
teamId: document.teamId,
|
||||
userId: document.userId,
|
||||
documentId: document.id,
|
||||
acl: 'private',
|
||||
acl: "private",
|
||||
});
|
||||
|
||||
const res = await server.post('/api/attachments.redirect', {
|
||||
const res = await server.post("/api/attachments.redirect", {
|
||||
body: { token: user.getJwtToken(), id: attachment.id },
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import auth from '../middlewares/authentication';
|
||||
import { presentUser, presentTeam, presentPolicies } from '../presenters';
|
||||
import { Team } from '../models';
|
||||
import Router from "koa-router";
|
||||
import auth from "../middlewares/authentication";
|
||||
import { presentUser, presentTeam, presentPolicies } from "../presenters";
|
||||
import { Team } from "../models";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
router.post('auth.info', auth(), async ctx => {
|
||||
router.post("auth.info", auth(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// @flow
|
||||
import fs from 'fs';
|
||||
import Router from 'koa-router';
|
||||
import { Op } from '../sequelize';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import fs from "fs";
|
||||
import Router from "koa-router";
|
||||
import { Op } from "../sequelize";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import {
|
||||
presentCollection,
|
||||
presentUser,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
presentMembership,
|
||||
presentGroup,
|
||||
presentCollectionGroupMembership,
|
||||
} from '../presenters';
|
||||
} from "../presenters";
|
||||
import {
|
||||
Collection,
|
||||
CollectionUser,
|
||||
@@ -20,40 +20,40 @@ import {
|
||||
Event,
|
||||
User,
|
||||
Group,
|
||||
} from '../models';
|
||||
import { ValidationError } from '../errors';
|
||||
import { exportCollections } from '../logistics';
|
||||
import { archiveCollection, archiveCollections } from '../utils/zip';
|
||||
import policy from '../policies';
|
||||
} from "../models";
|
||||
import { ValidationError } from "../errors";
|
||||
import { exportCollections } from "../logistics";
|
||||
import { archiveCollection, archiveCollections } from "../utils/zip";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('collections.create', auth(), async ctx => {
|
||||
router.post("collections.create", auth(), async ctx => {
|
||||
const { name, color, description, icon, type } = ctx.body;
|
||||
const isPrivate = ctx.body.private;
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
if (color) {
|
||||
ctx.assertHexColor(color, 'Invalid hex value (please use format #FFFFFF)');
|
||||
ctx.assertHexColor(color, "Invalid hex value (please use format #FFFFFF)");
|
||||
}
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', Collection);
|
||||
authorize(user, "create", Collection);
|
||||
|
||||
let collection = await Collection.create({
|
||||
name,
|
||||
description,
|
||||
icon,
|
||||
color,
|
||||
type: type || 'atlas',
|
||||
type: type || "atlas",
|
||||
teamId: user.teamId,
|
||||
creatorId: user.id,
|
||||
private: isPrivate,
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.create',
|
||||
name: "collections.create",
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
actorId: user.id,
|
||||
@@ -64,7 +64,7 @@ router.post('collections.create', auth(), async ctx => {
|
||||
// we must reload the collection to get memberships for policy presenter
|
||||
if (isPrivate) {
|
||||
collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collection.id);
|
||||
}
|
||||
|
||||
@@ -74,15 +74,15 @@ router.post('collections.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.info', auth(), async ctx => {
|
||||
router.post("collections.info", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
ctx.body = {
|
||||
data: presentCollection(collection),
|
||||
@@ -90,18 +90,18 @@ router.post('collections.info', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.add_group', auth(), async ctx => {
|
||||
const { id, groupId, permission = 'read_write' } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(groupId, 'groupId is required');
|
||||
router.post("collections.add_group", auth(), async ctx => {
|
||||
const { id, groupId, permission = "read_write" } = ctx.body;
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(groupId, "groupId is required");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', ctx.state.user.id],
|
||||
method: ["withMembership", ctx.state.user.id],
|
||||
}).findByPk(id);
|
||||
authorize(ctx.state.user, 'update', collection);
|
||||
authorize(ctx.state.user, "update", collection);
|
||||
|
||||
const group = await Group.findByPk(groupId);
|
||||
authorize(ctx.state.user, 'read', group);
|
||||
authorize(ctx.state.user, "read", group);
|
||||
|
||||
let membership = await CollectionGroup.findOne({
|
||||
where: {
|
||||
@@ -123,7 +123,7 @@ router.post('collections.add_group', auth(), async ctx => {
|
||||
}
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.add_group',
|
||||
name: "collections.add_group",
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
actorId: ctx.state.user.id,
|
||||
@@ -140,23 +140,23 @@ router.post('collections.add_group', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.remove_group', auth(), async ctx => {
|
||||
router.post("collections.remove_group", auth(), async ctx => {
|
||||
const { id, groupId } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(groupId, 'groupId is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(groupId, "groupId is required");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', ctx.state.user.id],
|
||||
method: ["withMembership", ctx.state.user.id],
|
||||
}).findByPk(id);
|
||||
authorize(ctx.state.user, 'update', collection);
|
||||
authorize(ctx.state.user, "update", collection);
|
||||
|
||||
const group = await Group.findByPk(groupId);
|
||||
authorize(ctx.state.user, 'read', group);
|
||||
authorize(ctx.state.user, "read", group);
|
||||
|
||||
await collection.removeGroup(group);
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.remove_group',
|
||||
name: "collections.remove_group",
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
actorId: ctx.state.user.id,
|
||||
@@ -170,19 +170,19 @@ router.post('collections.remove_group', auth(), async ctx => {
|
||||
});
|
||||
|
||||
router.post(
|
||||
'collections.group_memberships',
|
||||
"collections.group_memberships",
|
||||
auth(),
|
||||
pagination(),
|
||||
async ctx => {
|
||||
const { id, query, permission } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
let where = {
|
||||
collectionId: id,
|
||||
@@ -207,13 +207,13 @@ router.post(
|
||||
|
||||
const memberships = await CollectionGroup.findAll({
|
||||
where,
|
||||
order: [['createdAt', 'DESC']],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
include: [
|
||||
{
|
||||
model: Group,
|
||||
as: 'group',
|
||||
as: "group",
|
||||
where: groupWhere,
|
||||
required: true,
|
||||
},
|
||||
@@ -232,18 +232,18 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
router.post('collections.add_user', auth(), async ctx => {
|
||||
const { id, userId, permission = 'read_write' } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(userId, 'userId is required');
|
||||
router.post("collections.add_user", auth(), async ctx => {
|
||||
const { id, userId, permission = "read_write" } = ctx.body;
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(userId, "userId is required");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', ctx.state.user.id],
|
||||
method: ["withMembership", ctx.state.user.id],
|
||||
}).findByPk(id);
|
||||
authorize(ctx.state.user, 'update', collection);
|
||||
authorize(ctx.state.user, "update", collection);
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'read', user);
|
||||
authorize(ctx.state.user, "read", user);
|
||||
|
||||
let membership = await CollectionUser.findOne({
|
||||
where: {
|
||||
@@ -265,7 +265,7 @@ router.post('collections.add_user', auth(), async ctx => {
|
||||
}
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.add_user',
|
||||
name: "collections.add_user",
|
||||
userId,
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
@@ -282,23 +282,23 @@ router.post('collections.add_user', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.remove_user', auth(), async ctx => {
|
||||
router.post("collections.remove_user", auth(), async ctx => {
|
||||
const { id, userId } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(userId, 'userId is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(userId, "userId is required");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', ctx.state.user.id],
|
||||
method: ["withMembership", ctx.state.user.id],
|
||||
}).findByPk(id);
|
||||
authorize(ctx.state.user, 'update', collection);
|
||||
authorize(ctx.state.user, "update", collection);
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'read', user);
|
||||
authorize(ctx.state.user, "read", user);
|
||||
|
||||
await collection.removeUser(user);
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.remove_user',
|
||||
name: "collections.remove_user",
|
||||
userId,
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
@@ -313,15 +313,15 @@ router.post('collections.remove_user', auth(), async ctx => {
|
||||
});
|
||||
|
||||
// DEPRECATED: Use collection.memberships which has pagination, filtering and permissions
|
||||
router.post('collections.users', auth(), async ctx => {
|
||||
router.post("collections.users", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
const users = await collection.getUsers();
|
||||
|
||||
@@ -330,15 +330,15 @@ router.post('collections.users', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.memberships', auth(), pagination(), async ctx => {
|
||||
router.post("collections.memberships", auth(), pagination(), async ctx => {
|
||||
const { id, query, permission } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
let where = {
|
||||
collectionId: id,
|
||||
@@ -363,13 +363,13 @@ router.post('collections.memberships', auth(), pagination(), async ctx => {
|
||||
|
||||
const memberships = await CollectionUser.findAll({
|
||||
where,
|
||||
order: [['createdAt', 'DESC']],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
as: "user",
|
||||
where: userWhere,
|
||||
required: true,
|
||||
},
|
||||
@@ -385,20 +385,20 @@ router.post('collections.memberships', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.export', auth(), async ctx => {
|
||||
router.post("collections.export", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
authorize(user, 'export', collection);
|
||||
authorize(user, "export", collection);
|
||||
|
||||
const filePath = await archiveCollection(collection);
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.export',
|
||||
name: "collections.export",
|
||||
collectionId: collection.id,
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
@@ -407,19 +407,19 @@ router.post('collections.export', auth(), async ctx => {
|
||||
});
|
||||
|
||||
ctx.attachment(`${collection.name}.zip`);
|
||||
ctx.set('Content-Type', 'application/force-download');
|
||||
ctx.set("Content-Type", "application/force-download");
|
||||
ctx.body = fs.createReadStream(filePath);
|
||||
});
|
||||
|
||||
router.post('collections.export_all', auth(), async ctx => {
|
||||
router.post("collections.export_all", auth(), async ctx => {
|
||||
const { download = false } = ctx.body;
|
||||
|
||||
const user = ctx.state.user;
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
authorize(user, 'export', team);
|
||||
authorize(user, "export", team);
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.export',
|
||||
name: "collections.export",
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
ip: ctx.request.ip,
|
||||
@@ -428,12 +428,12 @@ router.post('collections.export_all', auth(), async ctx => {
|
||||
if (download) {
|
||||
const collections = await Collection.findAll({
|
||||
where: { teamId: team.id },
|
||||
order: [['name', 'ASC']],
|
||||
order: [["name", "ASC"]],
|
||||
});
|
||||
const filePath = await archiveCollections(collections);
|
||||
|
||||
ctx.attachment(`${team.name}.zip`);
|
||||
ctx.set('Content-Type', 'application/force-download');
|
||||
ctx.set("Content-Type", "application/force-download");
|
||||
ctx.body = fs.createReadStream(filePath);
|
||||
} else {
|
||||
// async operation to create zip archive and email user
|
||||
@@ -445,22 +445,22 @@ router.post('collections.export_all', auth(), async ctx => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('collections.update', auth(), async ctx => {
|
||||
router.post("collections.update", auth(), async ctx => {
|
||||
const { id, name, description, icon, color } = ctx.body;
|
||||
const isPrivate = ctx.body.private;
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
if (color) {
|
||||
ctx.assertHexColor(color, 'Invalid hex value (please use format #FFFFFF)');
|
||||
ctx.assertHexColor(color, "Invalid hex value (please use format #FFFFFF)");
|
||||
}
|
||||
|
||||
const user = ctx.state.user;
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
|
||||
authorize(user, 'update', collection);
|
||||
authorize(user, "update", collection);
|
||||
|
||||
// we're making this collection private right now, ensure that the current
|
||||
// user has a read-write membership so that at least they can edit it
|
||||
@@ -471,7 +471,7 @@ router.post('collections.update', auth(), async ctx => {
|
||||
userId: user.id,
|
||||
},
|
||||
defaults: {
|
||||
permission: 'read_write',
|
||||
permission: "read_write",
|
||||
createdById: user.id,
|
||||
},
|
||||
});
|
||||
@@ -488,7 +488,7 @@ router.post('collections.update', auth(), async ctx => {
|
||||
await collection.save();
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.update',
|
||||
name: "collections.update",
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
actorId: user.id,
|
||||
@@ -508,17 +508,17 @@ router.post('collections.update', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.list', auth(), pagination(), async ctx => {
|
||||
router.post("collections.list", auth(), pagination(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
let collections = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findAll({
|
||||
where: {
|
||||
teamId: user.teamId,
|
||||
id: collectionIds,
|
||||
},
|
||||
order: [['updatedAt', 'DESC']],
|
||||
order: [["updatedAt", "DESC"]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
});
|
||||
@@ -530,24 +530,24 @@ router.post('collections.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('collections.delete', auth(), async ctx => {
|
||||
router.post("collections.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(id);
|
||||
|
||||
authorize(user, 'delete', collection);
|
||||
authorize(user, "delete", collection);
|
||||
|
||||
const total = await Collection.count();
|
||||
if (total === 1) throw new ValidationError('Cannot delete last collection');
|
||||
if (total === 1) throw new ValidationError("Cannot delete last collection");
|
||||
|
||||
await collection.destroy();
|
||||
|
||||
await Event.create({
|
||||
name: 'collections.delete',
|
||||
name: "collections.delete",
|
||||
collectionId: collection.id,
|
||||
teamId: collection.teamId,
|
||||
actorId: user.id,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import Sequelize from 'sequelize';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import documentMover from '../commands/documentMover';
|
||||
import Router from "koa-router";
|
||||
import Sequelize from "sequelize";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import documentMover from "../commands/documentMover";
|
||||
import {
|
||||
presentDocument,
|
||||
presentCollection,
|
||||
presentPolicies,
|
||||
} from '../presenters';
|
||||
} from "../presenters";
|
||||
import {
|
||||
Collection,
|
||||
Document,
|
||||
@@ -19,23 +19,23 @@ import {
|
||||
Revision,
|
||||
Backlink,
|
||||
User,
|
||||
} from '../models';
|
||||
import { InvalidRequestError } from '../errors';
|
||||
import policy from '../policies';
|
||||
import { sequelize } from '../sequelize';
|
||||
} from "../models";
|
||||
import { InvalidRequestError } from "../errors";
|
||||
import policy from "../policies";
|
||||
import { sequelize } from "../sequelize";
|
||||
|
||||
const Op = Sequelize.Op;
|
||||
const { authorize, cannot } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('documents.list', auth(), pagination(), async ctx => {
|
||||
const { sort = 'updatedAt', backlinkDocumentId, parentDocumentId } = ctx.body;
|
||||
router.post("documents.list", auth(), pagination(), async ctx => {
|
||||
const { sort = "updatedAt", backlinkDocumentId, parentDocumentId } = ctx.body;
|
||||
|
||||
// collection and user are here for backwards compatablity
|
||||
const collectionId = ctx.body.collectionId || ctx.body.collection;
|
||||
const createdById = ctx.body.userId || ctx.body.user;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
// always filter by the current team
|
||||
const user = ctx.state.user;
|
||||
@@ -44,19 +44,19 @@ router.post('documents.list', auth(), pagination(), async ctx => {
|
||||
// if a specific user is passed then add to filters. If the user doesn't
|
||||
// exist in the team then nothing will be returned, so no need to check auth
|
||||
if (createdById) {
|
||||
ctx.assertUuid(createdById, 'user must be a UUID');
|
||||
ctx.assertUuid(createdById, "user must be a UUID");
|
||||
where = { ...where, createdById };
|
||||
}
|
||||
|
||||
// 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');
|
||||
ctx.assertUuid(collectionId, "collection must be a UUID");
|
||||
|
||||
where = { ...where, collectionId };
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId);
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
// otherwise, filter by all collections the user has access to
|
||||
} else {
|
||||
@@ -65,15 +65,15 @@ router.post('documents.list', auth(), pagination(), async ctx => {
|
||||
}
|
||||
|
||||
if (parentDocumentId) {
|
||||
ctx.assertUuid(parentDocumentId, 'parentDocumentId must be a UUID');
|
||||
ctx.assertUuid(parentDocumentId, "parentDocumentId must be a UUID");
|
||||
where = { ...where, parentDocumentId };
|
||||
}
|
||||
|
||||
if (backlinkDocumentId) {
|
||||
ctx.assertUuid(backlinkDocumentId, 'backlinkDocumentId must be a UUID');
|
||||
ctx.assertUuid(backlinkDocumentId, "backlinkDocumentId must be a UUID");
|
||||
|
||||
const backlinks = await Backlink.findAll({
|
||||
attributes: ['reverseDocumentId'],
|
||||
attributes: ["reverseDocumentId"],
|
||||
where: {
|
||||
documentId: backlinkDocumentId,
|
||||
},
|
||||
@@ -86,10 +86,10 @@ router.post('documents.list', auth(), pagination(), async ctx => {
|
||||
}
|
||||
|
||||
// add the users starred state to the response by default
|
||||
const starredScope = { method: ['withStarred', user.id] };
|
||||
const collectionScope = { method: ['withCollection', user.id] };
|
||||
const starredScope = { method: ["withStarred", user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
const documents = await Document.scope(
|
||||
'defaultScope',
|
||||
"defaultScope",
|
||||
starredScope,
|
||||
collectionScope
|
||||
).findAll({
|
||||
@@ -112,23 +112,23 @@ router.post('documents.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.pinned', auth(), pagination(), async ctx => {
|
||||
const { collectionId, sort = 'updatedAt' } = ctx.body;
|
||||
router.post("documents.pinned", auth(), pagination(), async ctx => {
|
||||
const { collectionId, sort = "updatedAt" } = ctx.body;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
ctx.assertUuid(collectionId, 'collectionId is required');
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
ctx.assertUuid(collectionId, "collectionId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId);
|
||||
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
|
||||
const starredScope = { method: ['withStarred', user.id] };
|
||||
const collectionScope = { method: ['withCollection', user.id] };
|
||||
const starredScope = { method: ["withStarred", user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
const documents = await Document.scope(
|
||||
'defaultScope',
|
||||
"defaultScope",
|
||||
starredScope,
|
||||
collectionScope
|
||||
).findAll({
|
||||
@@ -157,17 +157,17 @@ router.post('documents.pinned', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.archived', auth(), pagination(), async ctx => {
|
||||
const { sort = 'updatedAt' } = ctx.body;
|
||||
router.post("documents.archived", auth(), pagination(), async ctx => {
|
||||
const { sort = "updatedAt" } = ctx.body;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
|
||||
const collectionScope = { method: ['withCollection', user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
const documents = await Document.scope(
|
||||
'defaultScope',
|
||||
"defaultScope",
|
||||
collectionScope
|
||||
).findAll({
|
||||
where: {
|
||||
@@ -195,15 +195,15 @@ router.post('documents.archived', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.deleted', auth(), pagination(), async ctx => {
|
||||
const { sort = 'deletedAt' } = ctx.body;
|
||||
router.post("documents.deleted", auth(), pagination(), async ctx => {
|
||||
const { sort = "deletedAt" } = ctx.body;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
|
||||
const collectionScope = { method: ['withCollection', user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
const documents = await Document.scope(collectionScope).findAll({
|
||||
where: {
|
||||
teamId: user.teamId,
|
||||
@@ -213,8 +213,8 @@ router.post('documents.deleted', auth(), pagination(), async ctx => {
|
||||
},
|
||||
},
|
||||
include: [
|
||||
{ model: User, as: 'createdBy', paranoid: false },
|
||||
{ model: User, as: 'updatedBy', paranoid: false },
|
||||
{ model: User, as: "createdBy", paranoid: false },
|
||||
{ model: User, as: "updatedBy", paranoid: false },
|
||||
],
|
||||
paranoid: false,
|
||||
order: [[sort, direction]],
|
||||
@@ -235,9 +235,9 @@ router.post('documents.deleted', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.viewed', auth(), pagination(), async ctx => {
|
||||
let { sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
router.post("documents.viewed", auth(), pagination(), async ctx => {
|
||||
let { sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
@@ -255,7 +255,7 @@ router.post('documents.viewed', auth(), pagination(), async ctx => {
|
||||
include: [
|
||||
{
|
||||
model: Star,
|
||||
as: 'starred',
|
||||
as: "starred",
|
||||
where: { userId: user.id },
|
||||
required: false,
|
||||
},
|
||||
@@ -280,9 +280,9 @@ router.post('documents.viewed', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.starred', auth(), pagination(), async ctx => {
|
||||
let { sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
router.post("documents.starred", auth(), pagination(), async ctx => {
|
||||
let { sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
@@ -301,11 +301,11 @@ router.post('documents.starred', auth(), pagination(), async ctx => {
|
||||
include: [
|
||||
{
|
||||
model: Collection,
|
||||
as: 'collection',
|
||||
as: "collection",
|
||||
},
|
||||
{
|
||||
model: Star,
|
||||
as: 'starred',
|
||||
as: "starred",
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
@@ -331,16 +331,16 @@ router.post('documents.starred', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.drafts', auth(), pagination(), async ctx => {
|
||||
let { sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
router.post("documents.drafts", auth(), pagination(), async ctx => {
|
||||
let { sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionIds = await user.collectionIds();
|
||||
|
||||
const collectionScope = { method: ['withCollection', user.id] };
|
||||
const collectionScope = { method: ["withCollection", user.id] };
|
||||
const documents = await Document.scope(
|
||||
'defaultScope',
|
||||
"defaultScope",
|
||||
collectionScope
|
||||
).findAll({
|
||||
where: {
|
||||
@@ -366,9 +366,9 @@ router.post('documents.drafts', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.info', auth({ required: false }), async ctx => {
|
||||
router.post("documents.info", auth({ required: false }), async ctx => {
|
||||
const { id, shareId } = ctx.body;
|
||||
ctx.assertPresent(id || shareId, 'id or shareId is required');
|
||||
ctx.assertPresent(id || shareId, "id or shareId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
let document;
|
||||
@@ -384,16 +384,16 @@ router.post('documents.info', auth({ required: false }), async ctx => {
|
||||
// unscoping here allows us to return unpublished documents
|
||||
model: Document.unscoped(),
|
||||
include: [
|
||||
{ model: User, as: 'createdBy', paranoid: false },
|
||||
{ model: User, as: 'updatedBy', paranoid: false },
|
||||
{ model: User, as: "createdBy", paranoid: false },
|
||||
{ model: User, as: "updatedBy", paranoid: false },
|
||||
],
|
||||
required: true,
|
||||
as: 'document',
|
||||
as: "document",
|
||||
},
|
||||
],
|
||||
});
|
||||
if (!share || share.document.archivedAt) {
|
||||
throw new InvalidRequestError('Document could not be found for shareId');
|
||||
throw new InvalidRequestError("Document could not be found for shareId");
|
||||
}
|
||||
document = share.document;
|
||||
} else {
|
||||
@@ -401,10 +401,10 @@ router.post('documents.info', auth({ required: false }), async ctx => {
|
||||
id,
|
||||
user ? { userId: user.id } : undefined
|
||||
);
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
}
|
||||
|
||||
const isPublic = cannot(user, 'read', document);
|
||||
const isPublic = cannot(user, "read", document);
|
||||
|
||||
ctx.body = {
|
||||
data: await presentDocument(document, { isPublic }),
|
||||
@@ -412,9 +412,9 @@ router.post('documents.info', auth({ required: false }), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.restore', auth(), async ctx => {
|
||||
router.post("documents.restore", auth(), async ctx => {
|
||||
const { id, revisionId } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, {
|
||||
@@ -423,13 +423,13 @@ router.post('documents.restore', auth(), async ctx => {
|
||||
});
|
||||
|
||||
if (document.deletedAt) {
|
||||
authorize(user, 'restore', document);
|
||||
authorize(user, "restore", document);
|
||||
|
||||
// restore a previously deleted document
|
||||
await document.unarchive(user.id);
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.restore',
|
||||
name: "documents.restore",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -438,13 +438,13 @@ router.post('documents.restore', auth(), async ctx => {
|
||||
ip: ctx.request.ip,
|
||||
});
|
||||
} else if (document.archivedAt) {
|
||||
authorize(user, 'unarchive', document);
|
||||
authorize(user, "unarchive", document);
|
||||
|
||||
// restore a previously archived document
|
||||
await document.unarchive(user.id);
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.unarchive',
|
||||
name: "documents.unarchive",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -454,17 +454,17 @@ router.post('documents.restore', auth(), async ctx => {
|
||||
});
|
||||
} else if (revisionId) {
|
||||
// restore a document to a specific revision
|
||||
authorize(user, 'update', document);
|
||||
authorize(user, "update", document);
|
||||
|
||||
const revision = await Revision.findByPk(revisionId);
|
||||
authorize(document, 'restore', revision);
|
||||
authorize(document, "restore", revision);
|
||||
|
||||
document.text = revision.text;
|
||||
document.title = revision.title;
|
||||
await document.save();
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.restore',
|
||||
name: "documents.restore",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -473,7 +473,7 @@ router.post('documents.restore', auth(), async ctx => {
|
||||
ip: ctx.request.ip,
|
||||
});
|
||||
} else {
|
||||
ctx.assertPresent(revisionId, 'revisionId is required');
|
||||
ctx.assertPresent(revisionId, "revisionId is required");
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
@@ -482,7 +482,7 @@ router.post('documents.restore', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.search', auth(), pagination(), async ctx => {
|
||||
router.post("documents.search", auth(), pagination(), async ctx => {
|
||||
const {
|
||||
query,
|
||||
includeArchived,
|
||||
@@ -493,34 +493,34 @@ router.post('documents.search', auth(), pagination(), async ctx => {
|
||||
} = ctx.body;
|
||||
const { offset, limit } = ctx.state.pagination;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertPresent(query, 'query is required');
|
||||
ctx.assertPresent(query, "query is required");
|
||||
|
||||
if (collectionId) {
|
||||
ctx.assertUuid(collectionId, 'collectionId must be a UUID');
|
||||
ctx.assertUuid(collectionId, "collectionId must be a UUID");
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findByPk(collectionId);
|
||||
authorize(user, 'read', collection);
|
||||
authorize(user, "read", collection);
|
||||
}
|
||||
|
||||
let collaboratorIds = undefined;
|
||||
if (userId) {
|
||||
ctx.assertUuid(userId, 'userId must be a UUID');
|
||||
ctx.assertUuid(userId, "userId must be a UUID");
|
||||
collaboratorIds = [userId];
|
||||
}
|
||||
|
||||
if (dateFilter) {
|
||||
ctx.assertIn(
|
||||
dateFilter,
|
||||
['day', 'week', 'month', 'year'],
|
||||
'dateFilter must be one of day,week,month,year'
|
||||
["day", "week", "month", "year"],
|
||||
"dateFilter must be one of day,week,month,year"
|
||||
);
|
||||
}
|
||||
|
||||
const results = await Document.searchForUser(user, query, {
|
||||
includeArchived: includeArchived === 'true',
|
||||
includeDrafts: includeDrafts === 'true',
|
||||
includeArchived: includeArchived === "true",
|
||||
includeDrafts: includeDrafts === "true",
|
||||
collaboratorIds,
|
||||
collectionId,
|
||||
dateFilter,
|
||||
@@ -545,19 +545,19 @@ router.post('documents.search', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.pin', auth(), async ctx => {
|
||||
router.post("documents.pin", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'pin', document);
|
||||
authorize(user, "pin", document);
|
||||
|
||||
document.pinnedById = user.id;
|
||||
await document.save();
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.pin',
|
||||
name: "documents.pin",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -572,19 +572,19 @@ router.post('documents.pin', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.unpin', auth(), async ctx => {
|
||||
router.post("documents.unpin", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'unpin', document);
|
||||
authorize(user, "unpin", document);
|
||||
|
||||
document.pinnedById = null;
|
||||
await document.save();
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.unpin',
|
||||
name: "documents.unpin",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -599,20 +599,20 @@ router.post('documents.unpin', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.star', auth(), async ctx => {
|
||||
router.post("documents.star", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
await Star.findOrCreate({
|
||||
where: { documentId: document.id, userId: user.id },
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.star',
|
||||
name: "documents.star",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -626,20 +626,20 @@ router.post('documents.star', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.unstar', auth(), async ctx => {
|
||||
router.post("documents.unstar", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
await Star.destroy({
|
||||
where: { documentId: document.id, userId: user.id },
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.unstar',
|
||||
name: "documents.unstar",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -653,46 +653,46 @@ router.post('documents.unstar', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.create', auth(), async ctx => {
|
||||
router.post("documents.create", auth(), async ctx => {
|
||||
const {
|
||||
title = '',
|
||||
text = '',
|
||||
title = "",
|
||||
text = "",
|
||||
publish,
|
||||
collectionId,
|
||||
parentDocumentId,
|
||||
index,
|
||||
} = ctx.body;
|
||||
const editorVersion = ctx.headers['x-editor-version'];
|
||||
const editorVersion = ctx.headers["x-editor-version"];
|
||||
|
||||
ctx.assertUuid(collectionId, 'collectionId must be an uuid');
|
||||
ctx.assertUuid(collectionId, "collectionId must be an uuid");
|
||||
if (parentDocumentId) {
|
||||
ctx.assertUuid(parentDocumentId, 'parentDocumentId must be an uuid');
|
||||
ctx.assertUuid(parentDocumentId, "parentDocumentId must be an uuid");
|
||||
}
|
||||
|
||||
if (index) ctx.assertPositiveInteger(index, 'index must be an integer (>=0)');
|
||||
if (index) ctx.assertPositiveInteger(index, "index must be an integer (>=0)");
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', Document);
|
||||
authorize(user, "create", Document);
|
||||
|
||||
const collection = await Collection.scope({
|
||||
method: ['withMembership', user.id],
|
||||
method: ["withMembership", user.id],
|
||||
}).findOne({
|
||||
where: {
|
||||
id: collectionId,
|
||||
teamId: user.teamId,
|
||||
},
|
||||
});
|
||||
authorize(user, 'publish', collection);
|
||||
authorize(user, "publish", collection);
|
||||
|
||||
let parentDocument;
|
||||
if (parentDocumentId && collection.type === 'atlas') {
|
||||
if (parentDocumentId && collection.type === "atlas") {
|
||||
parentDocument = await Document.findOne({
|
||||
where: {
|
||||
id: parentDocumentId,
|
||||
collectionId: collection.id,
|
||||
},
|
||||
});
|
||||
authorize(user, 'read', parentDocument, { collection });
|
||||
authorize(user, "read", parentDocument, { collection });
|
||||
}
|
||||
|
||||
let document = await Document.create({
|
||||
@@ -708,7 +708,7 @@ router.post('documents.create', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.create',
|
||||
name: "documents.create",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -721,7 +721,7 @@ router.post('documents.create', auth(), async ctx => {
|
||||
await document.publish();
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.publish',
|
||||
name: "documents.publish",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -745,7 +745,7 @@ router.post('documents.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.update', auth(), async ctx => {
|
||||
router.post("documents.update", auth(), async ctx => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
@@ -756,18 +756,18 @@ router.post('documents.update', auth(), async ctx => {
|
||||
lastRevision,
|
||||
append,
|
||||
} = ctx.body;
|
||||
const editorVersion = ctx.headers['x-editor-version'];
|
||||
const editorVersion = ctx.headers["x-editor-version"];
|
||||
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(title || text, 'title or text is required');
|
||||
if (append) ctx.assertPresent(text, 'Text is required while appending');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
ctx.assertPresent(title || text, "title or text is required");
|
||||
if (append) ctx.assertPresent(text, "Text is required while appending");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'update', document);
|
||||
authorize(user, "update", document);
|
||||
|
||||
if (lastRevision && lastRevision !== document.revisionCount) {
|
||||
throw new InvalidRequestError('Document has changed since last revision');
|
||||
throw new InvalidRequestError("Document has changed since last revision");
|
||||
}
|
||||
|
||||
// Update document
|
||||
@@ -801,7 +801,7 @@ router.post('documents.update', auth(), async ctx => {
|
||||
|
||||
if (publish) {
|
||||
await Event.create({
|
||||
name: 'documents.publish',
|
||||
name: "documents.publish",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -811,7 +811,7 @@ router.post('documents.update', auth(), async ctx => {
|
||||
});
|
||||
} else {
|
||||
await Event.create({
|
||||
name: 'documents.update',
|
||||
name: "documents.update",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -834,31 +834,31 @@ router.post('documents.update', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.move', auth(), async ctx => {
|
||||
router.post("documents.move", auth(), async ctx => {
|
||||
const { id, collectionId, parentDocumentId, index } = ctx.body;
|
||||
ctx.assertUuid(id, 'id must be a uuid');
|
||||
ctx.assertUuid(collectionId, 'collectionId must be a uuid');
|
||||
ctx.assertUuid(id, "id must be a uuid");
|
||||
ctx.assertUuid(collectionId, "collectionId must be a uuid");
|
||||
|
||||
if (parentDocumentId) {
|
||||
ctx.assertUuid(parentDocumentId, 'parentDocumentId must be a uuid');
|
||||
ctx.assertUuid(parentDocumentId, "parentDocumentId must be a uuid");
|
||||
}
|
||||
if (index) {
|
||||
ctx.assertPositiveInteger(index, 'index must be a positive integer');
|
||||
ctx.assertPositiveInteger(index, "index must be a positive integer");
|
||||
}
|
||||
if (parentDocumentId === id) {
|
||||
throw new InvalidRequestError(
|
||||
'Infinite loop detected, cannot nest a document inside itself'
|
||||
"Infinite loop detected, cannot nest a document inside itself"
|
||||
);
|
||||
}
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'move', document);
|
||||
authorize(user, "move", document);
|
||||
|
||||
const { collection } = document;
|
||||
if (collection.type !== 'atlas' && parentDocumentId) {
|
||||
if (collection.type !== "atlas" && parentDocumentId) {
|
||||
throw new InvalidRequestError(
|
||||
'Document cannot be nested in this collection type'
|
||||
"Document cannot be nested in this collection type"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -866,7 +866,7 @@ router.post('documents.move', auth(), async ctx => {
|
||||
const parent = await Document.findByPk(parentDocumentId, {
|
||||
userId: user.id,
|
||||
});
|
||||
authorize(user, 'update', parent);
|
||||
authorize(user, "update", parent);
|
||||
}
|
||||
|
||||
const { documents, collections } = await documentMover({
|
||||
@@ -891,18 +891,18 @@ router.post('documents.move', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.archive', auth(), async ctx => {
|
||||
router.post("documents.archive", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'archive', document);
|
||||
authorize(user, "archive", document);
|
||||
|
||||
await document.archive(user.id);
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.archive',
|
||||
name: "documents.archive",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
@@ -917,18 +917,18 @@ router.post('documents.archive', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('documents.delete', auth(), async ctx => {
|
||||
router.post("documents.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(id, { userId: user.id });
|
||||
authorize(user, 'delete', document);
|
||||
authorize(user, "delete", document);
|
||||
|
||||
await document.delete();
|
||||
|
||||
await Event.create({
|
||||
name: 'documents.delete',
|
||||
name: "documents.delete",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
teamId: document.teamId,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,19 @@
|
||||
// @flow
|
||||
import Sequelize from 'sequelize';
|
||||
import Router from 'koa-router';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentEvent } from '../presenters';
|
||||
import { Event, Team, User } from '../models';
|
||||
import policy from '../policies';
|
||||
import Sequelize from "sequelize";
|
||||
import Router from "koa-router";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import { presentEvent } from "../presenters";
|
||||
import { Event, Team, User } from "../models";
|
||||
import policy from "../policies";
|
||||
|
||||
const Op = Sequelize.Op;
|
||||
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';
|
||||
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 paranoid = false;
|
||||
@@ -33,7 +33,7 @@ router.post('events.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
|
||||
if (auditLog) {
|
||||
authorize(user, 'auditLog', Team);
|
||||
authorize(user, "auditLog", Team);
|
||||
where.name = Event.AUDIT_EVENTS;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ router.post('events.list', auth(), pagination(), async ctx => {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'actor',
|
||||
as: "actor",
|
||||
paranoid: false,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildEvent } from '../test/factories';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildEvent } from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#events.list', async () => {
|
||||
it('should only return activity events', async () => {
|
||||
describe("#events.list", async () => {
|
||||
it("should only return activity events", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// private event
|
||||
await buildEvent({
|
||||
name: 'users.promote',
|
||||
name: "users.promote",
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
userId: user.id,
|
||||
@@ -23,13 +23,13 @@ describe('#events.list', async () => {
|
||||
|
||||
// event viewable in activity stream
|
||||
const event = await buildEvent({
|
||||
name: 'documents.publish',
|
||||
name: "documents.publish",
|
||||
collectionId: collection.id,
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
actorId: admin.id,
|
||||
});
|
||||
const res = await server.post('/api/events.list', {
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -39,12 +39,12 @@ describe('#events.list', async () => {
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
});
|
||||
|
||||
it('should return events with deleted actors', async () => {
|
||||
it("should return events with deleted actors", async () => {
|
||||
const { user, admin, document, collection } = await seed();
|
||||
|
||||
// event viewable in activity stream
|
||||
const event = await buildEvent({
|
||||
name: 'documents.publish',
|
||||
name: "documents.publish",
|
||||
collectionId: collection.id,
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
@@ -53,7 +53,7 @@ describe('#events.list', async () => {
|
||||
|
||||
await user.destroy();
|
||||
|
||||
const res = await server.post('/api/events.list', {
|
||||
const res = await server.post("/api/events.list", {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
|
||||
@@ -64,8 +64,8 @@ describe('#events.list', async () => {
|
||||
expect(body.data[0].id).toEqual(event.id);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/events.list');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/events.list");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { Op } from '../sequelize';
|
||||
import { MAX_AVATAR_DISPLAY } from '../../shared/constants';
|
||||
import Router from "koa-router";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import { Op } from "../sequelize";
|
||||
import { MAX_AVATAR_DISPLAY } from "../../shared/constants";
|
||||
|
||||
import {
|
||||
presentGroup,
|
||||
presentPolicies,
|
||||
presentUser,
|
||||
presentGroupMembership,
|
||||
} from '../presenters';
|
||||
import { User, Event, Group, GroupUser } from '../models';
|
||||
import policy from '../policies';
|
||||
} from "../presenters";
|
||||
import { User, Event, Group, GroupUser } from "../models";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('groups.list', auth(), pagination(), async ctx => {
|
||||
const { sort = 'updatedAt' } = ctx.body;
|
||||
router.post("groups.list", auth(), pagination(), async ctx => {
|
||||
const { sort = "updatedAt" } = ctx.body;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
const user = ctx.state.user;
|
||||
|
||||
let groups = await Group.findAll({
|
||||
@@ -55,13 +55,13 @@ router.post('groups.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.info', auth(), async ctx => {
|
||||
router.post("groups.info", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const group = await Group.findByPk(id);
|
||||
authorize(user, 'read', group);
|
||||
authorize(user, "read", group);
|
||||
|
||||
ctx.body = {
|
||||
data: presentGroup(group),
|
||||
@@ -69,13 +69,13 @@ router.post('groups.info', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.create', auth(), async ctx => {
|
||||
router.post("groups.create", auth(), async ctx => {
|
||||
const { name } = ctx.body;
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
|
||||
authorize(user, 'create', Group);
|
||||
authorize(user, "create", Group);
|
||||
let group = await Group.create({
|
||||
name,
|
||||
teamId: user.teamId,
|
||||
@@ -86,7 +86,7 @@ router.post('groups.create', auth(), async ctx => {
|
||||
group = await Group.findByPk(group.id);
|
||||
|
||||
await Event.create({
|
||||
name: 'groups.create',
|
||||
name: "groups.create",
|
||||
actorId: user.id,
|
||||
teamId: user.teamId,
|
||||
modelId: group.id,
|
||||
@@ -100,22 +100,22 @@ router.post('groups.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.update', auth(), async ctx => {
|
||||
router.post("groups.update", auth(), async ctx => {
|
||||
const { id, name } = ctx.body;
|
||||
ctx.assertPresent(name, 'name is required');
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertPresent(name, "name is required");
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const group = await Group.findByPk(id);
|
||||
|
||||
authorize(user, 'update', group);
|
||||
authorize(user, "update", group);
|
||||
|
||||
group.name = name;
|
||||
|
||||
if (group.changed()) {
|
||||
await group.save();
|
||||
await Event.create({
|
||||
name: 'groups.update',
|
||||
name: "groups.update",
|
||||
teamId: user.teamId,
|
||||
actorId: user.id,
|
||||
modelId: group.id,
|
||||
@@ -130,18 +130,18 @@ router.post('groups.update', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.delete', auth(), async ctx => {
|
||||
router.post("groups.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const { user } = ctx.state;
|
||||
const group = await Group.findByPk(id);
|
||||
|
||||
authorize(user, 'delete', group);
|
||||
authorize(user, "delete", group);
|
||||
await group.destroy();
|
||||
|
||||
await Event.create({
|
||||
name: 'groups.delete',
|
||||
name: "groups.delete",
|
||||
actorId: user.id,
|
||||
modelId: group.id,
|
||||
teamId: group.teamId,
|
||||
@@ -154,13 +154,13 @@ router.post('groups.delete', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.memberships', auth(), pagination(), async ctx => {
|
||||
router.post("groups.memberships", auth(), pagination(), async ctx => {
|
||||
const { id, query } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const group = await Group.findByPk(id);
|
||||
authorize(user, 'read', group);
|
||||
authorize(user, "read", group);
|
||||
|
||||
let userWhere;
|
||||
if (query) {
|
||||
@@ -173,13 +173,13 @@ router.post('groups.memberships', auth(), pagination(), async ctx => {
|
||||
|
||||
const memberships = await GroupUser.findAll({
|
||||
where: { groupId: id },
|
||||
order: [['createdAt', 'DESC']],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
as: "user",
|
||||
where: userWhere,
|
||||
required: true,
|
||||
},
|
||||
@@ -195,16 +195,16 @@ router.post('groups.memberships', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.add_user', auth(), async ctx => {
|
||||
router.post("groups.add_user", auth(), async ctx => {
|
||||
const { id, userId } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(userId, 'userId is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(userId, "userId is required");
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'read', user);
|
||||
authorize(ctx.state.user, "read", user);
|
||||
|
||||
let group = await Group.findByPk(id);
|
||||
authorize(ctx.state.user, 'update', group);
|
||||
authorize(ctx.state.user, "update", group);
|
||||
|
||||
let membership = await GroupUser.findOne({
|
||||
where: {
|
||||
@@ -230,7 +230,7 @@ router.post('groups.add_user', auth(), async ctx => {
|
||||
group = await Group.findByPk(id);
|
||||
|
||||
await Event.create({
|
||||
name: 'groups.add_user',
|
||||
name: "groups.add_user",
|
||||
userId,
|
||||
teamId: user.teamId,
|
||||
modelId: group.id,
|
||||
@@ -249,21 +249,21 @@ router.post('groups.add_user', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('groups.remove_user', auth(), async ctx => {
|
||||
router.post("groups.remove_user", auth(), async ctx => {
|
||||
const { id, userId } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(userId, 'userId is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertUuid(userId, "userId is required");
|
||||
|
||||
let group = await Group.findByPk(id);
|
||||
authorize(ctx.state.user, 'update', group);
|
||||
authorize(ctx.state.user, "update", group);
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'read', user);
|
||||
authorize(ctx.state.user, "read", user);
|
||||
|
||||
await group.removeUser(user);
|
||||
|
||||
await Event.create({
|
||||
name: 'groups.remove_user',
|
||||
name: "groups.remove_user",
|
||||
userId,
|
||||
modelId: group.id,
|
||||
teamId: user.teamId,
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { flushdb } from '../test/support';
|
||||
import { buildUser, buildGroup } from '../test/factories';
|
||||
import { Event } from '../models';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { flushdb } from "../test/support";
|
||||
import { buildUser, buildGroup } from "../test/factories";
|
||||
import { Event } from "../models";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#groups.create', async () => {
|
||||
it('should create a group', async () => {
|
||||
const name = 'hello I am a group';
|
||||
describe("#groups.create", async () => {
|
||||
it("should create a group", async () => {
|
||||
const name = "hello I am a group";
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
|
||||
const res = await server.post('/api/groups.create', {
|
||||
const res = await server.post("/api/groups.create", {
|
||||
body: { token: user.getJwtToken(), name },
|
||||
});
|
||||
|
||||
@@ -26,11 +26,11 @@ describe('#groups.create', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.update', async () => {
|
||||
it('should require authentication', async () => {
|
||||
describe("#groups.update", async () => {
|
||||
it("should require authentication", async () => {
|
||||
const group = await buildGroup();
|
||||
const res = await server.post('/api/groups.update', {
|
||||
body: { id: group.id, name: 'Test' },
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: { id: group.id, name: "Test" },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
@@ -38,26 +38,26 @@ describe('#groups.update', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const group = await buildGroup();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/groups.update', {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: 'Test' },
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: "Test" },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const group = await buildGroup();
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
|
||||
const res = await server.post('/api/groups.update', {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: 'Test' },
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: "Test" },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
describe('when user is admin', async () => {
|
||||
describe("when user is admin", async () => {
|
||||
let user, group;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -65,9 +65,9 @@ describe('#groups.update', async () => {
|
||||
group = await buildGroup({ teamId: user.teamId });
|
||||
});
|
||||
|
||||
it('allows admin to edit a group', async () => {
|
||||
const res = await server.post('/api/groups.update', {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: 'Test' },
|
||||
it("allows admin to edit a group", async () => {
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: "Test" },
|
||||
});
|
||||
|
||||
const events = await Event.findAll();
|
||||
@@ -75,11 +75,11 @@ describe('#groups.update', async () => {
|
||||
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.name).toBe('Test');
|
||||
expect(body.data.name).toBe("Test");
|
||||
});
|
||||
|
||||
it('does not create an event if the update is a noop', async () => {
|
||||
const res = await server.post('/api/groups.update', {
|
||||
it("does not create an event if the update is a noop", async () => {
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: { token: user.getJwtToken(), id: group.id, name: group.name },
|
||||
});
|
||||
|
||||
@@ -91,17 +91,17 @@ describe('#groups.update', async () => {
|
||||
expect(body.data.name).toBe(group.name);
|
||||
});
|
||||
|
||||
it('fails with validation error when name already taken', async () => {
|
||||
it("fails with validation error when name already taken", async () => {
|
||||
await buildGroup({
|
||||
teamId: user.teamId,
|
||||
name: 'test',
|
||||
name: "test",
|
||||
});
|
||||
|
||||
const res = await server.post('/api/groups.update', {
|
||||
const res = await server.post("/api/groups.update", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
name: 'TEST',
|
||||
name: "TEST",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -112,40 +112,40 @@ describe('#groups.update', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.list', async () => {
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/groups.list');
|
||||
describe("#groups.list", async () => {
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/groups.list");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should return groups with memberships preloaded', async () => {
|
||||
it("should return groups with memberships preloaded", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
await group.addUser(user, { through: { createdById: user.id } });
|
||||
|
||||
const res = await server.post('/api/groups.list', {
|
||||
const res = await server.post("/api/groups.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
expect(body.data['groups'].length).toEqual(1);
|
||||
expect(body.data['groups'][0].id).toEqual(group.id);
|
||||
expect(body.data["groups"].length).toEqual(1);
|
||||
expect(body.data["groups"][0].id).toEqual(group.id);
|
||||
|
||||
expect(body.data['groupMemberships'].length).toEqual(1);
|
||||
expect(body.data['groupMemberships'][0].groupId).toEqual(group.id);
|
||||
expect(body.data['groupMemberships'][0].user.id).toEqual(user.id);
|
||||
expect(body.data["groupMemberships"].length).toEqual(1);
|
||||
expect(body.data["groupMemberships"][0].groupId).toEqual(group.id);
|
||||
expect(body.data["groupMemberships"][0].user.id).toEqual(user.id);
|
||||
|
||||
expect(body.policies.length).toEqual(1);
|
||||
expect(body.policies[0].abilities.read).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return groups when membership user is deleted', async () => {
|
||||
it("should return groups when membership user is deleted", async () => {
|
||||
const me = await buildUser();
|
||||
const user = await buildUser({ teamId: me.teamId });
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
@@ -154,31 +154,31 @@ describe('#groups.list', async () => {
|
||||
await group.addUser(me, { through: { createdById: me.id } });
|
||||
await user.destroy();
|
||||
|
||||
const res = await server.post('/api/groups.list', {
|
||||
const res = await server.post("/api/groups.list", {
|
||||
body: { token: me.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
expect(body.data['groups'].length).toEqual(1);
|
||||
expect(body.data['groups'][0].id).toEqual(group.id);
|
||||
expect(body.data["groups"].length).toEqual(1);
|
||||
expect(body.data["groups"][0].id).toEqual(group.id);
|
||||
|
||||
expect(body.data['groupMemberships'].length).toEqual(1);
|
||||
expect(body.data['groupMemberships'][0].groupId).toEqual(group.id);
|
||||
expect(body.data['groupMemberships'][0].user.id).toEqual(me.id);
|
||||
expect(body.data["groupMemberships"].length).toEqual(1);
|
||||
expect(body.data["groupMemberships"][0].groupId).toEqual(group.id);
|
||||
expect(body.data["groupMemberships"][0].user.id).toEqual(me.id);
|
||||
|
||||
expect(body.policies.length).toEqual(1);
|
||||
expect(body.policies[0].abilities.read).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.info', async () => {
|
||||
it('should return group if admin', async () => {
|
||||
describe("#groups.info", async () => {
|
||||
it("should return group if admin", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
const res = await server.post('/api/groups.info', {
|
||||
const res = await server.post("/api/groups.info", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
|
||||
@@ -188,12 +188,12 @@ describe('#groups.info', async () => {
|
||||
expect(body.data.id).toEqual(group.id);
|
||||
});
|
||||
|
||||
it('should return group if member', async () => {
|
||||
it("should return group if member", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
await group.addUser(user, { through: { createdById: user.id } });
|
||||
|
||||
const res = await server.post('/api/groups.info', {
|
||||
const res = await server.post("/api/groups.info", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
|
||||
@@ -203,39 +203,39 @@ describe('#groups.info', async () => {
|
||||
expect(body.data.id).toEqual(group.id);
|
||||
});
|
||||
|
||||
it('should not return group if non-member, non-admin', async () => {
|
||||
it("should not return group if non-member, non-admin", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
const res = await server.post('/api/groups.info', {
|
||||
const res = await server.post("/api/groups.info", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/groups.info');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/groups.info");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup();
|
||||
const res = await server.post('/api/groups.info', {
|
||||
const res = await server.post("/api/groups.info", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.delete', async () => {
|
||||
it('should require authentication', async () => {
|
||||
describe("#groups.delete", async () => {
|
||||
it("should require authentication", async () => {
|
||||
const group = await buildGroup();
|
||||
const res = await server.post('/api/groups.delete', {
|
||||
const res = await server.post("/api/groups.delete", {
|
||||
body: { id: group.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -244,30 +244,30 @@ describe('#groups.delete', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const group = await buildGroup();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/groups.delete', {
|
||||
const res = await server.post("/api/groups.delete", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const group = await buildGroup();
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
|
||||
const res = await server.post('/api/groups.delete', {
|
||||
const res = await server.post("/api/groups.delete", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('allows admin to delete a group', async () => {
|
||||
it("allows admin to delete a group", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
const res = await server.post('/api/groups.delete', {
|
||||
const res = await server.post("/api/groups.delete", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
|
||||
@@ -277,14 +277,14 @@ describe('#groups.delete', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.memberships', async () => {
|
||||
it('should return members in a group', async () => {
|
||||
describe("#groups.memberships", async () => {
|
||||
it("should return members in a group", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
await group.addUser(user, { through: { createdById: user.id } });
|
||||
|
||||
const res = await server.post('/api/groups.memberships', {
|
||||
const res = await server.post("/api/groups.memberships", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
|
||||
@@ -297,10 +297,10 @@ describe('#groups.memberships', async () => {
|
||||
expect(body.data.groupMemberships[0].user.id).toEqual(user.id);
|
||||
});
|
||||
|
||||
it('should allow filtering members in group by name', async () => {
|
||||
it("should allow filtering members in group by name", async () => {
|
||||
const user = await buildUser();
|
||||
const user2 = await buildUser({ name: "Won't find" });
|
||||
const user3 = await buildUser({ teamId: user.teamId, name: 'Deleted' });
|
||||
const user3 = await buildUser({ teamId: user.teamId, name: "Deleted" });
|
||||
const group = await buildGroup({ teamId: user.teamId });
|
||||
|
||||
await group.addUser(user, { through: { createdById: user.id } });
|
||||
@@ -309,7 +309,7 @@ describe('#groups.memberships', async () => {
|
||||
|
||||
await user3.destroy();
|
||||
|
||||
const res = await server.post('/api/groups.memberships', {
|
||||
const res = await server.post("/api/groups.memberships", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -323,33 +323,33 @@ describe('#groups.memberships', async () => {
|
||||
expect(body.data.users[0].id).toEqual(user.id);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/groups.memberships');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/groups.memberships");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup();
|
||||
|
||||
const res = await server.post('/api/groups.memberships', {
|
||||
const res = await server.post("/api/groups.memberships", {
|
||||
body: { token: user.getJwtToken(), id: group.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.add_user', async () => {
|
||||
it('should add user to group', async () => {
|
||||
describe("#groups.add_user", async () => {
|
||||
it("should add user to group", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/groups.add_user', {
|
||||
const res = await server.post("/api/groups.add_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -362,19 +362,19 @@ describe('#groups.add_user', async () => {
|
||||
expect(users.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/groups.add_user');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/groups.add_user");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
|
||||
it('should require user in team', async () => {
|
||||
it("should require user in team", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const anotherUser = await buildUser();
|
||||
|
||||
const res = await server.post('/api/groups.add_user', {
|
||||
const res = await server.post("/api/groups.add_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -388,14 +388,14 @@ describe('#groups.add_user', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const anotherUser = await buildUser({ teamId: user.teamId });
|
||||
|
||||
const res = await server.post('/api/groups.add_user', {
|
||||
const res = await server.post("/api/groups.add_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -410,14 +410,14 @@ describe('#groups.add_user', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#groups.remove_user', async () => {
|
||||
it('should remove user from group', async () => {
|
||||
describe("#groups.remove_user", async () => {
|
||||
it("should remove user from group", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
await server.post('/api/groups.add_user', {
|
||||
await server.post("/api/groups.add_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -428,7 +428,7 @@ describe('#groups.remove_user', async () => {
|
||||
const users = await group.getUsers();
|
||||
expect(users.length).toEqual(1);
|
||||
|
||||
const res = await server.post('/api/groups.remove_user', {
|
||||
const res = await server.post("/api/groups.remove_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -441,20 +441,20 @@ describe('#groups.remove_user', async () => {
|
||||
expect(users1.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/groups.remove_user');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/groups.remove_user");
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
|
||||
it('should require user in team', async () => {
|
||||
it("should require user in team", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const anotherUser = await buildUser();
|
||||
|
||||
const res = await server.post('/api/groups.remove_user', {
|
||||
const res = await server.post("/api/groups.remove_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
@@ -467,7 +467,7 @@ describe('#groups.remove_user', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const group = await buildGroup({
|
||||
teamId: user.teamId,
|
||||
@@ -476,7 +476,7 @@ describe('#groups.remove_user', async () => {
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/groups.remove_user', {
|
||||
const res = await server.post("/api/groups.remove_user", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: group.id,
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import { escapeRegExp } from 'lodash';
|
||||
import { AuthenticationError, InvalidRequestError } from '../errors';
|
||||
import { Authentication, Document, User, Team, Collection } from '../models';
|
||||
import { presentSlackAttachment } from '../presenters';
|
||||
import * as Slack from '../slack';
|
||||
import Router from "koa-router";
|
||||
import { escapeRegExp } from "lodash";
|
||||
import { AuthenticationError, InvalidRequestError } from "../errors";
|
||||
import { Authentication, Document, User, Team, Collection } from "../models";
|
||||
import { presentSlackAttachment } from "../presenters";
|
||||
import * as Slack from "../slack";
|
||||
const router = new Router();
|
||||
|
||||
// triggered by a user posting a getoutline.com link in Slack
|
||||
router.post('hooks.unfurl', async ctx => {
|
||||
router.post("hooks.unfurl", async ctx => {
|
||||
const { challenge, token, event } = ctx.body;
|
||||
if (challenge) return (ctx.body = ctx.body.challenge);
|
||||
|
||||
if (token !== process.env.SLACK_VERIFICATION_TOKEN) {
|
||||
throw new AuthenticationError('Invalid token');
|
||||
throw new AuthenticationError("Invalid token");
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
where: { service: 'slack', serviceId: event.user },
|
||||
where: { service: "slack", serviceId: event.user },
|
||||
});
|
||||
if (!user) return;
|
||||
|
||||
const auth = await Authentication.findOne({
|
||||
where: { service: 'slack', teamId: user.teamId },
|
||||
where: { service: "slack", teamId: user.teamId },
|
||||
});
|
||||
if (!auth) return;
|
||||
|
||||
// get content for unfurled links
|
||||
let unfurls = {};
|
||||
for (let link of event.links) {
|
||||
const id = link.url.substr(link.url.lastIndexOf('/') + 1);
|
||||
const id = link.url.substr(link.url.lastIndexOf("/") + 1);
|
||||
const doc = await Document.findByPk(id);
|
||||
if (!doc || doc.teamId !== user.teamId) continue;
|
||||
|
||||
@@ -40,7 +40,7 @@ router.post('hooks.unfurl', async ctx => {
|
||||
};
|
||||
}
|
||||
|
||||
await Slack.post('chat.unfurl', {
|
||||
await Slack.post("chat.unfurl", {
|
||||
token: auth.token,
|
||||
channel: event.channel,
|
||||
ts: event.message_ts,
|
||||
@@ -49,17 +49,17 @@ router.post('hooks.unfurl', async ctx => {
|
||||
});
|
||||
|
||||
// triggered by interactions with actions, dialogs, message buttons in Slack
|
||||
router.post('hooks.interactive', async ctx => {
|
||||
router.post("hooks.interactive", async ctx => {
|
||||
const { payload } = ctx.body;
|
||||
ctx.assertPresent(payload, 'payload is required');
|
||||
ctx.assertPresent(payload, "payload is required");
|
||||
|
||||
const data = JSON.parse(payload);
|
||||
const { callback_id, token } = data;
|
||||
ctx.assertPresent(token, 'token is required');
|
||||
ctx.assertPresent(callback_id, 'callback_id is required');
|
||||
ctx.assertPresent(token, "token is required");
|
||||
ctx.assertPresent(callback_id, "callback_id is required");
|
||||
|
||||
if (token !== process.env.SLACK_VERIFICATION_TOKEN) {
|
||||
throw new AuthenticationError('Invalid verification token');
|
||||
throw new AuthenticationError("Invalid verification token");
|
||||
}
|
||||
|
||||
const team = await Team.findOne({
|
||||
@@ -69,8 +69,8 @@ router.post('hooks.interactive', async ctx => {
|
||||
if (!team) {
|
||||
ctx.body = {
|
||||
text:
|
||||
'Sorry, we couldn’t find an integration for your team. Head to your Outline settings to set one up.',
|
||||
response_type: 'ephemeral',
|
||||
"Sorry, we couldn’t find an integration for your team. Head to your Outline settings to set one up.",
|
||||
response_type: "ephemeral",
|
||||
replace_original: false,
|
||||
};
|
||||
return;
|
||||
@@ -83,13 +83,13 @@ router.post('hooks.interactive', async ctx => {
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
if (!document) throw new InvalidRequestError('Invalid document');
|
||||
if (!document) throw new InvalidRequestError("Invalid document");
|
||||
|
||||
const collection = await Collection.findByPk(document.collectionId);
|
||||
|
||||
// respond with a public message that will be posted in the original channel
|
||||
ctx.body = {
|
||||
response_type: 'in_channel',
|
||||
response_type: "in_channel",
|
||||
replace_original: false,
|
||||
attachments: [
|
||||
presentSlackAttachment(document, collection, team, document.getSummary()),
|
||||
@@ -98,25 +98,25 @@ router.post('hooks.interactive', async ctx => {
|
||||
});
|
||||
|
||||
// triggered by the /outline command in Slack
|
||||
router.post('hooks.slack', async ctx => {
|
||||
const { token, team_id, user_id, text = '' } = ctx.body;
|
||||
ctx.assertPresent(token, 'token is required');
|
||||
ctx.assertPresent(team_id, 'team_id is required');
|
||||
ctx.assertPresent(user_id, 'user_id is required');
|
||||
router.post("hooks.slack", async ctx => {
|
||||
const { token, team_id, user_id, text = "" } = ctx.body;
|
||||
ctx.assertPresent(token, "token is required");
|
||||
ctx.assertPresent(team_id, "team_id is required");
|
||||
ctx.assertPresent(user_id, "user_id is required");
|
||||
|
||||
if (token !== process.env.SLACK_VERIFICATION_TOKEN) {
|
||||
throw new AuthenticationError('Invalid verification token');
|
||||
throw new AuthenticationError("Invalid verification token");
|
||||
}
|
||||
|
||||
// Handle "help" command or no input
|
||||
if (text.trim() === 'help' || !text.trim()) {
|
||||
if (text.trim() === "help" || !text.trim()) {
|
||||
ctx.body = {
|
||||
response_type: 'ephemeral',
|
||||
text: 'How to use /outline',
|
||||
response_type: "ephemeral",
|
||||
text: "How to use /outline",
|
||||
attachments: [
|
||||
{
|
||||
text:
|
||||
'To search your knowledgebase use `/outline keyword`. \nYou’ve already learned how to get help with `/outline help`.',
|
||||
"To search your knowledgebase use `/outline keyword`. \nYou’ve already learned how to get help with `/outline help`.",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -128,9 +128,9 @@ router.post('hooks.slack', async ctx => {
|
||||
});
|
||||
if (!team) {
|
||||
ctx.body = {
|
||||
response_type: 'ephemeral',
|
||||
response_type: "ephemeral",
|
||||
text:
|
||||
'Sorry, we couldn’t find an integration for your team. Head to your Outline settings to set one up.',
|
||||
"Sorry, we couldn’t find an integration for your team. Head to your Outline settings to set one up.",
|
||||
};
|
||||
return;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ router.post('hooks.slack', async ctx => {
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
teamId: team.id,
|
||||
service: 'slack',
|
||||
service: "slack",
|
||||
serviceId: user_id,
|
||||
},
|
||||
});
|
||||
@@ -166,9 +166,9 @@ router.post('hooks.slack', async ctx => {
|
||||
process.env.SLACK_MESSAGE_ACTIONS
|
||||
? [
|
||||
{
|
||||
name: 'post',
|
||||
text: 'Post to Channel',
|
||||
type: 'button',
|
||||
name: "post",
|
||||
text: "Post to Channel",
|
||||
type: "button",
|
||||
value: result.document.id,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { Authentication } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildDocument } from '../test/factories';
|
||||
import * as Slack from '../slack';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { Authentication } from "../models";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildDocument } from "../test/factories";
|
||||
import * as Slack from "../slack";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
jest.mock('../slack', () => ({
|
||||
jest.mock("../slack", () => ({
|
||||
post: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('#hooks.unfurl', async () => {
|
||||
it('should return documents', async () => {
|
||||
describe("#hooks.unfurl", async () => {
|
||||
it("should return documents", async () => {
|
||||
const { user, document } = await seed();
|
||||
await Authentication.create({
|
||||
service: 'slack',
|
||||
service: "slack",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: '',
|
||||
token: "",
|
||||
});
|
||||
|
||||
const res = await server.post('/api/hooks.unfurl', {
|
||||
const res = await server.post("/api/hooks.unfurl", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
team_id: 'TXXXXXXXX',
|
||||
api_app_id: 'AXXXXXXXXX',
|
||||
team_id: "TXXXXXXXX",
|
||||
api_app_id: "AXXXXXXXXX",
|
||||
event: {
|
||||
type: 'link_shared',
|
||||
channel: 'Cxxxxxx',
|
||||
type: "link_shared",
|
||||
channel: "Cxxxxxx",
|
||||
user: user.serviceId,
|
||||
message_ts: '123456789.9875',
|
||||
message_ts: "123456789.9875",
|
||||
links: [
|
||||
{
|
||||
domain: 'getoutline.com',
|
||||
domain: "getoutline.com",
|
||||
url: document.url,
|
||||
},
|
||||
],
|
||||
@@ -49,16 +49,16 @@ describe('#hooks.unfurl', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#hooks.slack', async () => {
|
||||
it('should return no matches', async () => {
|
||||
describe("#hooks.slack", async () => {
|
||||
it("should return no matches", async () => {
|
||||
const { user, team } = await seed();
|
||||
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: 'dsfkndfskndsfkn',
|
||||
text: "dsfkndfskndsfkn",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -66,19 +66,19 @@ describe('#hooks.slack', async () => {
|
||||
expect(body.attachments).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return search results with summary if query is in title', async () => {
|
||||
it("should return search results with summary if query is in title", async () => {
|
||||
const { user, team } = await seed();
|
||||
const document = await buildDocument({
|
||||
title: 'This title contains a search term',
|
||||
title: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: 'contains',
|
||||
text: "contains",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -88,19 +88,19 @@ describe('#hooks.slack', async () => {
|
||||
expect(body.attachments[0].text).toEqual(document.getSummary());
|
||||
});
|
||||
|
||||
it('should return search results if query is regex-like', async () => {
|
||||
it("should return search results if query is regex-like", async () => {
|
||||
const { user, team } = await seed();
|
||||
await buildDocument({
|
||||
title: 'This title contains a search term',
|
||||
title: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: '*contains',
|
||||
text: "*contains",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -108,19 +108,19 @@ describe('#hooks.slack', async () => {
|
||||
expect(body.attachments.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return search results with snippet if query is in text', async () => {
|
||||
it("should return search results with snippet if query is in text", async () => {
|
||||
const { user, team } = await seed();
|
||||
const document = await buildDocument({
|
||||
text: 'This title contains a search term',
|
||||
text: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: 'contains',
|
||||
text: "contains",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -128,62 +128,62 @@ describe('#hooks.slack', async () => {
|
||||
expect(body.attachments.length).toEqual(1);
|
||||
expect(body.attachments[0].title).toEqual(document.title);
|
||||
expect(body.attachments[0].text).toEqual(
|
||||
'This title *contains* a search term'
|
||||
"This title *contains* a search term"
|
||||
);
|
||||
});
|
||||
|
||||
it('should respond with help content for help keyword', async () => {
|
||||
it("should respond with help content for help keyword", async () => {
|
||||
const { user, team } = await seed();
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: 'help',
|
||||
text: "help",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.text.includes('How to use')).toEqual(true);
|
||||
expect(body.text.includes("How to use")).toEqual(true);
|
||||
});
|
||||
|
||||
it('should respond with help content for no keyword', async () => {
|
||||
it("should respond with help content for no keyword", async () => {
|
||||
const { user, team } = await seed();
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.serviceId,
|
||||
team_id: team.slackId,
|
||||
text: '',
|
||||
text: "",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.text.includes('How to use')).toEqual(true);
|
||||
expect(body.text.includes("How to use")).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return search results with snippet for unknown user', async () => {
|
||||
it("should return search results with snippet for unknown user", async () => {
|
||||
const { user, team } = await seed();
|
||||
|
||||
// unpublished document will not be returned
|
||||
await buildDocument({
|
||||
text: 'This title contains a search term',
|
||||
text: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
publishedAt: null,
|
||||
});
|
||||
|
||||
const document = await buildDocument({
|
||||
text: 'This title contains a search term',
|
||||
text: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: 'unknown-slack-user-id',
|
||||
user_id: "unknown-slack-user-id",
|
||||
team_id: team.slackId,
|
||||
text: 'contains',
|
||||
text: "contains",
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -191,30 +191,30 @@ describe('#hooks.slack', async () => {
|
||||
expect(body.attachments.length).toEqual(1);
|
||||
expect(body.attachments[0].title).toEqual(document.title);
|
||||
expect(body.attachments[0].text).toEqual(
|
||||
'This title *contains* a search term'
|
||||
"This title *contains* a search term"
|
||||
);
|
||||
});
|
||||
|
||||
it('should error if incorrect verification token', async () => {
|
||||
it("should error if incorrect verification token", async () => {
|
||||
const { user, team } = await seed();
|
||||
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
const res = await server.post("/api/hooks.slack", {
|
||||
body: {
|
||||
token: 'wrong-verification-token',
|
||||
token: "wrong-verification-token",
|
||||
team_id: team.slackId,
|
||||
user_id: user.serviceId,
|
||||
text: 'Welcome',
|
||||
text: "Welcome",
|
||||
},
|
||||
});
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#hooks.interactive', async () => {
|
||||
it('should respond with replacement message', async () => {
|
||||
describe("#hooks.interactive", async () => {
|
||||
it("should respond with replacement message", async () => {
|
||||
const { user, team } = await seed();
|
||||
const document = await buildDocument({
|
||||
title: 'This title contains a search term',
|
||||
title: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
@@ -225,48 +225,48 @@ describe('#hooks.interactive', async () => {
|
||||
team: { id: team.slackId },
|
||||
callback_id: document.id,
|
||||
});
|
||||
const res = await server.post('/api/hooks.interactive', {
|
||||
const res = await server.post("/api/hooks.interactive", {
|
||||
body: { payload },
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.response_type).toEqual('in_channel');
|
||||
expect(body.response_type).toEqual("in_channel");
|
||||
expect(body.attachments.length).toEqual(1);
|
||||
expect(body.attachments[0].title).toEqual(document.title);
|
||||
});
|
||||
|
||||
it('should respond with replacement message if unknown user', async () => {
|
||||
it("should respond with replacement message if unknown user", async () => {
|
||||
const { user, team } = await seed();
|
||||
const document = await buildDocument({
|
||||
title: 'This title contains a search term',
|
||||
title: "This title contains a search term",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
const payload = JSON.stringify({
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user: { id: 'unknown-slack-user-id' },
|
||||
user: { id: "unknown-slack-user-id" },
|
||||
team: { id: team.slackId },
|
||||
callback_id: document.id,
|
||||
});
|
||||
const res = await server.post('/api/hooks.interactive', {
|
||||
const res = await server.post("/api/hooks.interactive", {
|
||||
body: { payload },
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.response_type).toEqual('in_channel');
|
||||
expect(body.response_type).toEqual("in_channel");
|
||||
expect(body.attachments.length).toEqual(1);
|
||||
expect(body.attachments[0].title).toEqual(document.title);
|
||||
});
|
||||
|
||||
it('should error if incorrect verification token', async () => {
|
||||
it("should error if incorrect verification token", async () => {
|
||||
const { user } = await seed();
|
||||
const payload = JSON.stringify({
|
||||
token: 'wrong-verification-token',
|
||||
token: "wrong-verification-token",
|
||||
user: { id: user.serviceId, name: user.name },
|
||||
callback_id: 'doesnt-matter',
|
||||
callback_id: "doesnt-matter",
|
||||
});
|
||||
const res = await server.post('/api/hooks.interactive', {
|
||||
const res = await server.post("/api/hooks.interactive", {
|
||||
body: { payload },
|
||||
});
|
||||
expect(res.status).toEqual(401);
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
// @flow
|
||||
import bodyParser from 'koa-bodyparser';
|
||||
import Koa from 'koa';
|
||||
import Router from 'koa-router';
|
||||
import bodyParser from "koa-bodyparser";
|
||||
import Koa from "koa";
|
||||
import Router from "koa-router";
|
||||
|
||||
import auth from './auth';
|
||||
import events from './events';
|
||||
import users from './users';
|
||||
import collections from './collections';
|
||||
import documents from './documents';
|
||||
import revisions from './revisions';
|
||||
import views from './views';
|
||||
import hooks from './hooks';
|
||||
import apiKeys from './apiKeys';
|
||||
import shares from './shares';
|
||||
import groups from './groups';
|
||||
import team from './team';
|
||||
import integrations from './integrations';
|
||||
import notificationSettings from './notificationSettings';
|
||||
import utils from './utils';
|
||||
import attachments from './attachments';
|
||||
import auth from "./auth";
|
||||
import events from "./events";
|
||||
import users from "./users";
|
||||
import collections from "./collections";
|
||||
import documents from "./documents";
|
||||
import revisions from "./revisions";
|
||||
import views from "./views";
|
||||
import hooks from "./hooks";
|
||||
import apiKeys from "./apiKeys";
|
||||
import shares from "./shares";
|
||||
import groups from "./groups";
|
||||
import team from "./team";
|
||||
import integrations from "./integrations";
|
||||
import notificationSettings from "./notificationSettings";
|
||||
import utils from "./utils";
|
||||
import attachments from "./attachments";
|
||||
|
||||
import { NotFoundError } from '../errors';
|
||||
import errorHandling from '../middlewares/errorHandling';
|
||||
import validation from '../middlewares/validation';
|
||||
import methodOverride from '../middlewares/methodOverride';
|
||||
import cache from './middlewares/cache';
|
||||
import apiWrapper from './middlewares/apiWrapper';
|
||||
import editor from './middlewares/editor';
|
||||
import { NotFoundError } from "../errors";
|
||||
import errorHandling from "../middlewares/errorHandling";
|
||||
import validation from "../middlewares/validation";
|
||||
import methodOverride from "../middlewares/methodOverride";
|
||||
import cache from "./middlewares/cache";
|
||||
import apiWrapper from "./middlewares/apiWrapper";
|
||||
import editor from "./middlewares/editor";
|
||||
|
||||
const api = new Koa();
|
||||
const router = new Router();
|
||||
@@ -41,25 +41,25 @@ api.use(apiWrapper());
|
||||
api.use(editor());
|
||||
|
||||
// routes
|
||||
router.use('/', auth.routes());
|
||||
router.use('/', events.routes());
|
||||
router.use('/', users.routes());
|
||||
router.use('/', collections.routes());
|
||||
router.use('/', documents.routes());
|
||||
router.use('/', revisions.routes());
|
||||
router.use('/', views.routes());
|
||||
router.use('/', hooks.routes());
|
||||
router.use('/', apiKeys.routes());
|
||||
router.use('/', shares.routes());
|
||||
router.use('/', team.routes());
|
||||
router.use('/', integrations.routes());
|
||||
router.use('/', notificationSettings.routes());
|
||||
router.use('/', attachments.routes());
|
||||
router.use('/', utils.routes());
|
||||
router.use('/', groups.routes());
|
||||
router.use("/", auth.routes());
|
||||
router.use("/", events.routes());
|
||||
router.use("/", users.routes());
|
||||
router.use("/", collections.routes());
|
||||
router.use("/", documents.routes());
|
||||
router.use("/", revisions.routes());
|
||||
router.use("/", views.routes());
|
||||
router.use("/", hooks.routes());
|
||||
router.use("/", apiKeys.routes());
|
||||
router.use("/", shares.routes());
|
||||
router.use("/", team.routes());
|
||||
router.use("/", integrations.routes());
|
||||
router.use("/", notificationSettings.routes());
|
||||
router.use("/", attachments.routes());
|
||||
router.use("/", utils.routes());
|
||||
router.use("/", groups.routes());
|
||||
|
||||
router.post('*', ctx => {
|
||||
ctx.throw(new NotFoundError('Endpoint not found'));
|
||||
router.post("*", ctx => {
|
||||
ctx.throw(new NotFoundError("Endpoint not found"));
|
||||
});
|
||||
|
||||
// Router is embedded in a Koa application wrapper, because koa-router does not
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { flushdb } from '../test/support';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { flushdb } from "../test/support";
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('POST unknown endpoint', async () => {
|
||||
it('should be not found', async () => {
|
||||
const res = await server.post('/api/blah');
|
||||
describe("POST unknown endpoint", async () => {
|
||||
it("should be not found", async () => {
|
||||
const res = await server.post("/api/blah");
|
||||
expect(res.status).toEqual(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET unknown endpoint', async () => {
|
||||
it('should be not found', async () => {
|
||||
const res = await server.get('/api/blah');
|
||||
describe("GET unknown endpoint", async () => {
|
||||
it("should be not found", async () => {
|
||||
const res = await server.get("/api/blah");
|
||||
expect(res.status).toEqual(404);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import Integration from '../models/Integration';
|
||||
import pagination from './middlewares/pagination';
|
||||
import auth from '../middlewares/authentication';
|
||||
import { Event } from '../models';
|
||||
import { presentIntegration } from '../presenters';
|
||||
import policy from '../policies';
|
||||
import Router from "koa-router";
|
||||
import Integration from "../models/Integration";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import auth from "../middlewares/authentication";
|
||||
import { Event } from "../models";
|
||||
import { presentIntegration } from "../presenters";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('integrations.list', auth(), pagination(), async ctx => {
|
||||
let { sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
router.post("integrations.list", auth(), pagination(), async ctx => {
|
||||
let { sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const integrations = await Integration.findAll({
|
||||
@@ -28,18 +28,18 @@ router.post('integrations.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('integrations.delete', auth(), async ctx => {
|
||||
router.post("integrations.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const integration = await Integration.findByPk(id);
|
||||
authorize(user, 'delete', integration);
|
||||
authorize(user, "delete", integration);
|
||||
|
||||
await integration.destroy();
|
||||
|
||||
await Event.create({
|
||||
name: 'integrations.delete',
|
||||
name: "integrations.delete",
|
||||
modelId: integration.id,
|
||||
teamId: integration.teamId,
|
||||
actorId: user.id,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import stream from 'stream';
|
||||
import { type Context } from 'koa';
|
||||
import stream from "stream";
|
||||
import { type Context } from "koa";
|
||||
|
||||
export default function apiWrapper() {
|
||||
return async function apiWrapperMiddleware(
|
||||
@@ -12,7 +12,7 @@ export default function apiWrapper() {
|
||||
const ok = ctx.status < 400;
|
||||
|
||||
if (
|
||||
typeof ctx.body !== 'string' &&
|
||||
typeof ctx.body !== "string" &&
|
||||
!(ctx.body instanceof stream.Readable)
|
||||
) {
|
||||
// $FlowFixMe
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// @flow
|
||||
import debug from 'debug';
|
||||
import { type Context } from 'koa';
|
||||
import debug from "debug";
|
||||
import { type Context } from "koa";
|
||||
|
||||
const log = debug('cache');
|
||||
const log = debug("cache");
|
||||
|
||||
export default function cache() {
|
||||
return async function cacheMiddleware(ctx: Context, next: () => Promise<*>) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// @flow
|
||||
import semver from 'semver';
|
||||
import { type Context } from 'koa';
|
||||
import pkg from 'rich-markdown-editor/package.json';
|
||||
import { EditorUpdateError } from '../../errors';
|
||||
import semver from "semver";
|
||||
import { type Context } from "koa";
|
||||
import pkg from "rich-markdown-editor/package.json";
|
||||
import { EditorUpdateError } from "../../errors";
|
||||
|
||||
export default function editor() {
|
||||
return async function editorMiddleware(ctx: Context, next: () => Promise<*>) {
|
||||
const clientVersion = ctx.headers['x-editor-version'];
|
||||
const clientVersion = ctx.headers["x-editor-version"];
|
||||
|
||||
// If the editor version on the client is behind the current version being
|
||||
// served in production by either a minor (new features), or major (breaking
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import querystring from 'querystring';
|
||||
import { InvalidRequestError } from '../../errors';
|
||||
import { type Context } from 'koa';
|
||||
import querystring from "querystring";
|
||||
import { InvalidRequestError } from "../../errors";
|
||||
import { type Context } from "koa";
|
||||
|
||||
export default function pagination(options?: Object) {
|
||||
return async function paginationMiddleware(
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../../app';
|
||||
import { flushdb, seed } from '../../test/support';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../../app";
|
||||
import { flushdb, seed } from "../../test/support";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#pagination', async () => {
|
||||
it('should allow offset and limit', async () => {
|
||||
describe("#pagination", async () => {
|
||||
it("should allow offset and limit", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken(), limit: 1, offset: 1 },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should not allow negative limit', async () => {
|
||||
it("should not allow negative limit", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken(), limit: -1 },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('should not allow non-integer limit', async () => {
|
||||
it("should not allow non-integer limit", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
body: { token: user.getJwtToken(), limit: 'blah' },
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken(), limit: "blah" },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('should not allow negative offset', async () => {
|
||||
it("should not allow negative offset", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken(), offset: -1 },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('should not allow non-integer offset', async () => {
|
||||
it("should not allow non-integer offset", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
body: { token: user.getJwtToken(), offset: 'blah' },
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken(), offset: "blah" },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import Router from "koa-router";
|
||||
|
||||
import auth from '../middlewares/authentication';
|
||||
import { NotificationSetting } from '../models';
|
||||
import { presentNotificationSetting } from '../presenters';
|
||||
import policy from '../policies';
|
||||
import auth from "../middlewares/authentication";
|
||||
import { NotificationSetting } from "../models";
|
||||
import { presentNotificationSetting } from "../presenters";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('notificationSettings.create', auth(), async ctx => {
|
||||
router.post("notificationSettings.create", auth(), async ctx => {
|
||||
const { event } = ctx.body;
|
||||
ctx.assertPresent(event, 'event is required');
|
||||
ctx.assertPresent(event, "event is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', NotificationSetting);
|
||||
authorize(user, "create", NotificationSetting);
|
||||
|
||||
const [setting] = await NotificationSetting.findOrCreate({
|
||||
where: {
|
||||
@@ -29,7 +29,7 @@ router.post('notificationSettings.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('notificationSettings.list', auth(), async ctx => {
|
||||
router.post("notificationSettings.list", auth(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
const settings = await NotificationSetting.findAll({
|
||||
where: {
|
||||
@@ -42,13 +42,13 @@ router.post('notificationSettings.list', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('notificationSettings.delete', auth(), async ctx => {
|
||||
router.post("notificationSettings.delete", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const setting = await NotificationSetting.findByPk(id);
|
||||
authorize(user, 'delete', setting);
|
||||
authorize(user, "delete", setting);
|
||||
|
||||
await setting.destroy();
|
||||
|
||||
@@ -57,10 +57,10 @@ router.post('notificationSettings.delete', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('notificationSettings.unsubscribe', async ctx => {
|
||||
router.post("notificationSettings.unsubscribe", async ctx => {
|
||||
const { id, token } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertPresent(token, 'token is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
ctx.assertPresent(token, "token is required");
|
||||
|
||||
const setting = await NotificationSetting.findByPk(id);
|
||||
if (setting) {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentRevision } from '../presenters';
|
||||
import { Document, Revision } from '../models';
|
||||
import { NotFoundError } from '../errors';
|
||||
import policy from '../policies';
|
||||
import Router from "koa-router";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import { presentRevision } from "../presenters";
|
||||
import { Document, Revision } from "../models";
|
||||
import { NotFoundError } from "../errors";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('revisions.info', auth(), async ctx => {
|
||||
router.post("revisions.info", auth(), async ctx => {
|
||||
let { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const revision = await Revision.findByPk(id);
|
||||
@@ -23,7 +23,7 @@ router.post('revisions.info', auth(), async ctx => {
|
||||
const document = await Document.findByPk(revision.documentId, {
|
||||
userId: user.id,
|
||||
});
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
ctx.body = {
|
||||
pagination: ctx.state.pagination,
|
||||
@@ -31,14 +31,14 @@ router.post('revisions.info', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('revisions.list', auth(), pagination(), async ctx => {
|
||||
let { documentId, sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
ctx.assertPresent(documentId, 'documentId is required');
|
||||
router.post("revisions.list", auth(), pagination(), async ctx => {
|
||||
let { documentId, sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
ctx.assertPresent(documentId, "documentId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(documentId, { userId: user.id });
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
const revisions = await Revision.findAll({
|
||||
where: { documentId: document.id },
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildDocument, buildUser } from '../test/factories';
|
||||
import Revision from '../models/Revision';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildDocument, buildUser } from "../test/factories";
|
||||
import Revision from "../models/Revision";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#revisions.info', async () => {
|
||||
it('should return a document revision', async () => {
|
||||
describe("#revisions.info", async () => {
|
||||
it("should return a document revision", async () => {
|
||||
const { user, document } = await seed();
|
||||
const revision = await Revision.findOne({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
},
|
||||
});
|
||||
const res = await server.post('/api/revisions.info', {
|
||||
const res = await server.post("/api/revisions.info", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: revision.id,
|
||||
@@ -31,7 +31,7 @@ describe('#revisions.info', async () => {
|
||||
expect(body.data.title).toEqual(document.title);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const document = await buildDocument();
|
||||
const revision = await Revision.findOne({
|
||||
where: {
|
||||
@@ -39,7 +39,7 @@ describe('#revisions.info', async () => {
|
||||
},
|
||||
});
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/revisions.info', {
|
||||
const res = await server.post("/api/revisions.info", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: revision.id,
|
||||
@@ -49,10 +49,10 @@ describe('#revisions.info', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#revisions.list', async () => {
|
||||
describe("#revisions.list", async () => {
|
||||
it("should return a document's revisions", async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post('/api/revisions.list', {
|
||||
const res = await server.post("/api/revisions.list", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
documentId: document.id,
|
||||
@@ -66,22 +66,22 @@ describe('#revisions.list', async () => {
|
||||
expect(body.data[0].title).toEqual(document.title);
|
||||
});
|
||||
|
||||
it('should not return revisions for document in collection not a member of', async () => {
|
||||
it("should not return revisions for document in collection not a member of", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
collection.private = true;
|
||||
await collection.save();
|
||||
|
||||
const res = await server.post('/api/revisions.list', {
|
||||
const res = await server.post("/api/revisions.list", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const document = await buildDocument();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/revisions.list', {
|
||||
const res = await server.post("/api/revisions.list", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
documentId: document.id,
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import Sequelize from 'sequelize';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentShare } from '../presenters';
|
||||
import { Document, User, Event, Share, Team } from '../models';
|
||||
import policy from '../policies';
|
||||
import Router from "koa-router";
|
||||
import Sequelize from "sequelize";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import { presentShare } from "../presenters";
|
||||
import { Document, User, Event, Share, Team } from "../models";
|
||||
import policy from "../policies";
|
||||
|
||||
const Op = Sequelize.Op;
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('shares.info', auth(), async ctx => {
|
||||
router.post("shares.info", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const share = await Share.findByPk(id);
|
||||
authorize(user, 'read', share);
|
||||
authorize(user, "read", share);
|
||||
|
||||
ctx.body = {
|
||||
data: presentShare(share),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('shares.list', auth(), pagination(), async ctx => {
|
||||
let { sort = 'updatedAt', direction } = ctx.body;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
router.post("shares.list", auth(), pagination(), async ctx => {
|
||||
let { sort = "updatedAt", direction } = ctx.body;
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
|
||||
const user = ctx.state.user;
|
||||
const where = {
|
||||
@@ -47,7 +47,7 @@ router.post('shares.list', auth(), pagination(), async ctx => {
|
||||
{
|
||||
model: Document,
|
||||
required: true,
|
||||
as: 'document',
|
||||
as: "document",
|
||||
where: {
|
||||
collectionId: collectionIds,
|
||||
},
|
||||
@@ -55,7 +55,7 @@ router.post('shares.list', auth(), pagination(), async ctx => {
|
||||
{
|
||||
model: User,
|
||||
required: true,
|
||||
as: 'user',
|
||||
as: "user",
|
||||
},
|
||||
],
|
||||
offset: ctx.state.pagination.offset,
|
||||
@@ -68,15 +68,15 @@ router.post('shares.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('shares.create', auth(), async ctx => {
|
||||
router.post("shares.create", auth(), async ctx => {
|
||||
const { documentId } = ctx.body;
|
||||
ctx.assertPresent(documentId, 'documentId is required');
|
||||
ctx.assertPresent(documentId, "documentId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(documentId, { userId: user.id });
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
authorize(user, 'share', document);
|
||||
authorize(user, 'share', team);
|
||||
authorize(user, "share", document);
|
||||
authorize(user, "share", team);
|
||||
|
||||
const [share] = await Share.findOrCreate({
|
||||
where: {
|
||||
@@ -88,7 +88,7 @@ router.post('shares.create', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Event.create({
|
||||
name: 'shares.create',
|
||||
name: "shares.create",
|
||||
documentId,
|
||||
collectionId: document.collectionId,
|
||||
modelId: share.id,
|
||||
@@ -106,20 +106,20 @@ router.post('shares.create', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('shares.revoke', auth(), async ctx => {
|
||||
router.post("shares.revoke", auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertUuid(id, 'id is required');
|
||||
ctx.assertUuid(id, "id is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const share = await Share.findByPk(id);
|
||||
authorize(user, 'revoke', share);
|
||||
authorize(user, "revoke", share);
|
||||
|
||||
await share.revoke(user.id);
|
||||
|
||||
const document = await Document.findByPk(share.documentId);
|
||||
|
||||
await Event.create({
|
||||
name: 'shares.revoke',
|
||||
name: "shares.revoke",
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
modelId: share.id,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { CollectionUser } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser, buildShare } from '../test/factories';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { CollectionUser } from "../models";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildUser, buildShare } from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#shares.list', async () => {
|
||||
it('should only return shares created by user', async () => {
|
||||
describe("#shares.list", async () => {
|
||||
it("should only return shares created by user", async () => {
|
||||
const { user, admin, document } = await seed();
|
||||
await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -23,7 +23,7 @@ describe('#shares.list', async () => {
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
const res = await server.post('/api/shares.list', {
|
||||
const res = await server.post("/api/shares.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -34,7 +34,7 @@ describe('#shares.list', async () => {
|
||||
expect(body.data[0].documentTitle).toBe(document.title);
|
||||
});
|
||||
|
||||
it('should not return revoked shares', async () => {
|
||||
it("should not return revoked shares", async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -43,7 +43,7 @@ describe('#shares.list', async () => {
|
||||
});
|
||||
await share.revoke(user.id);
|
||||
|
||||
const res = await server.post('/api/shares.list', {
|
||||
const res = await server.post("/api/shares.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -52,14 +52,14 @@ describe('#shares.list', async () => {
|
||||
expect(body.data.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('admins should return shares created by all users', async () => {
|
||||
it("admins should return shares created by all users", async () => {
|
||||
const { user, admin, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
teamId: admin.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
const res = await server.post('/api/shares.list', {
|
||||
const res = await server.post("/api/shares.list", {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -70,7 +70,7 @@ describe('#shares.list', async () => {
|
||||
expect(body.data[0].documentTitle).toBe(document.title);
|
||||
});
|
||||
|
||||
it('admins should not return shares in collection not a member of', async () => {
|
||||
it("admins should not return shares in collection not a member of", async () => {
|
||||
const { admin, document, collection } = await seed();
|
||||
await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -81,7 +81,7 @@ describe('#shares.list', async () => {
|
||||
collection.private = true;
|
||||
await collection.save();
|
||||
|
||||
const res = await server.post('/api/shares.list', {
|
||||
const res = await server.post("/api/shares.list", {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -90,8 +90,8 @@ describe('#shares.list', async () => {
|
||||
expect(body.data.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/shares.list');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/shares.list");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
@@ -99,10 +99,10 @@ describe('#shares.list', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#shares.create', async () => {
|
||||
it('should allow creating a share record for document', async () => {
|
||||
describe("#shares.create", async () => {
|
||||
it("should allow creating a share record for document", async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -111,7 +111,7 @@ describe('#shares.create', async () => {
|
||||
expect(body.data.documentTitle).toBe(document.title);
|
||||
});
|
||||
|
||||
it('should allow creating a share record for document in read-only collection', async () => {
|
||||
it("should allow creating a share record for document in read-only collection", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
collection.private = true;
|
||||
await collection.save();
|
||||
@@ -120,10 +120,10 @@ describe('#shares.create', async () => {
|
||||
createdById: user.id,
|
||||
collectionId: collection.id,
|
||||
userId: user.id,
|
||||
permission: 'read',
|
||||
permission: "read",
|
||||
});
|
||||
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -132,7 +132,7 @@ describe('#shares.create', async () => {
|
||||
expect(body.data.documentTitle).toBe(document.title);
|
||||
});
|
||||
|
||||
it('should allow creating a share record if link previously revoked', async () => {
|
||||
it("should allow creating a share record if link previously revoked", async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -140,7 +140,7 @@ describe('#shares.create', async () => {
|
||||
userId: user.id,
|
||||
});
|
||||
await share.revoke();
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -150,14 +150,14 @@ describe('#shares.create', async () => {
|
||||
expect(body.data.documentTitle).toBe(document.title);
|
||||
});
|
||||
|
||||
it('should return existing share link for document and user', async () => {
|
||||
it("should return existing share link for document and user", async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -166,18 +166,18 @@ describe('#shares.create', async () => {
|
||||
expect(body.data.id).toBe(share.id);
|
||||
});
|
||||
|
||||
it('should not allow creating a share record if disabled', async () => {
|
||||
it("should not allow creating a share record if disabled", async () => {
|
||||
const { user, document, team } = await seed();
|
||||
await team.update({ sharing: false });
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
it("should require authentication", async () => {
|
||||
const { document } = await seed();
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -186,18 +186,18 @@ describe('#shares.create', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#shares.revoke', async () => {
|
||||
it('should allow author to revoke a share', async () => {
|
||||
describe("#shares.revoke", async () => {
|
||||
it("should allow author to revoke a share", async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -205,13 +205,13 @@ describe('#shares.revoke', async () => {
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/shares.revoke', {
|
||||
const res = await server.post("/api/shares.revoke", {
|
||||
body: { token: user.getJwtToken(), id: share.id },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should allow admin to revoke a share', async () => {
|
||||
it("should allow admin to revoke a share", async () => {
|
||||
const { user, admin, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
@@ -219,20 +219,20 @@ describe('#shares.revoke', async () => {
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
const res = await server.post('/api/shares.revoke', {
|
||||
const res = await server.post("/api/shares.revoke", {
|
||||
body: { token: admin.getJwtToken(), id: share.id },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
it("should require authentication", async () => {
|
||||
const { user, document } = await seed();
|
||||
const share = await buildShare({
|
||||
documentId: document.id,
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
const res = await server.post('/api/shares.revoke', {
|
||||
const res = await server.post("/api/shares.revoke", {
|
||||
body: { id: share.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -241,10 +241,10 @@ describe('#shares.revoke', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/shares.create', {
|
||||
const res = await server.post("/api/shares.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import { Team } from '../models';
|
||||
import Router from "koa-router";
|
||||
import { Team } from "../models";
|
||||
|
||||
import auth from '../middlewares/authentication';
|
||||
import { presentTeam, presentPolicies } from '../presenters';
|
||||
import policy from '../policies';
|
||||
import auth from "../middlewares/authentication";
|
||||
import { presentTeam, presentPolicies } from "../presenters";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('team.update', auth(), async ctx => {
|
||||
router.post("team.update", auth(), async ctx => {
|
||||
const {
|
||||
name,
|
||||
avatarUrl,
|
||||
@@ -20,10 +20,10 @@ router.post('team.update', auth(), async ctx => {
|
||||
} = ctx.body;
|
||||
const user = ctx.state.user;
|
||||
const team = await Team.findByPk(user.teamId);
|
||||
authorize(user, 'update', team);
|
||||
authorize(user, "update", team);
|
||||
|
||||
if (subdomain !== undefined && process.env.SUBDOMAINS_ENABLED === 'true') {
|
||||
team.subdomain = subdomain === '' ? null : subdomain;
|
||||
if (subdomain !== undefined && process.env.SUBDOMAINS_ENABLED === "true") {
|
||||
team.subdomain = subdomain === "" ? null : subdomain;
|
||||
}
|
||||
|
||||
if (name) team.name = name;
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { flushdb, seed } from "../test/support";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#team.update', async () => {
|
||||
it('should update team details', async () => {
|
||||
describe("#team.update", async () => {
|
||||
it("should update team details", async () => {
|
||||
const { admin } = await seed();
|
||||
const res = await server.post('/api/team.update', {
|
||||
body: { token: admin.getJwtToken(), name: 'New name' },
|
||||
const res = await server.post("/api/team.update", {
|
||||
body: { token: admin.getJwtToken(), name: "New name" },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.name).toEqual('New name');
|
||||
expect(body.data.name).toEqual("New name");
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/team.update', {
|
||||
body: { token: user.getJwtToken(), name: 'New name' },
|
||||
const res = await server.post("/api/team.update", {
|
||||
body: { token: user.getJwtToken(), name: "New name" },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
it("should require authentication", async () => {
|
||||
await seed();
|
||||
const res = await server.post('/api/team.update');
|
||||
const res = await server.post("/api/team.update");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import { Op } from '../sequelize';
|
||||
import { Event, User, Team } from '../models';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import userInviter from '../commands/userInviter';
|
||||
import { presentUser } from '../presenters';
|
||||
import policy from '../policies';
|
||||
import Router from "koa-router";
|
||||
import { Op } from "../sequelize";
|
||||
import { Event, User, Team } from "../models";
|
||||
import auth from "../middlewares/authentication";
|
||||
import pagination from "./middlewares/pagination";
|
||||
import userInviter from "../commands/userInviter";
|
||||
import { presentUser } from "../presenters";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('users.list', auth(), pagination(), async ctx => {
|
||||
const { sort = 'createdAt', query, includeSuspended = false } = ctx.body;
|
||||
router.post("users.list", auth(), pagination(), async ctx => {
|
||||
const { sort = "createdAt", query, includeSuspended = false } = ctx.body;
|
||||
let direction = ctx.body.direction;
|
||||
if (direction !== 'ASC') direction = 'DESC';
|
||||
if (direction !== "ASC") direction = "DESC";
|
||||
const user = ctx.state.user;
|
||||
|
||||
let where = {
|
||||
@@ -54,13 +54,13 @@ router.post('users.list', auth(), pagination(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.info', auth(), async ctx => {
|
||||
router.post("users.info", auth(), async ctx => {
|
||||
ctx.body = {
|
||||
data: presentUser(ctx.state.user),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.update', auth(), async ctx => {
|
||||
router.post("users.update", auth(), async ctx => {
|
||||
const { user } = ctx.state;
|
||||
const { name, avatarUrl } = ctx.body;
|
||||
|
||||
@@ -76,19 +76,19 @@ router.post('users.update', auth(), async ctx => {
|
||||
|
||||
// Admin specific
|
||||
|
||||
router.post('users.promote', auth(), async ctx => {
|
||||
router.post("users.promote", auth(), async ctx => {
|
||||
const userId = ctx.body.id;
|
||||
const teamId = ctx.state.user.teamId;
|
||||
ctx.assertPresent(userId, 'id is required');
|
||||
ctx.assertPresent(userId, "id is required");
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'promote', user);
|
||||
authorize(ctx.state.user, "promote", user);
|
||||
|
||||
const team = await Team.findByPk(teamId);
|
||||
await team.addAdmin(user);
|
||||
|
||||
await Event.create({
|
||||
name: 'users.promote',
|
||||
name: "users.promote",
|
||||
actorId: ctx.state.user.id,
|
||||
userId,
|
||||
teamId,
|
||||
@@ -101,19 +101,19 @@ router.post('users.promote', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.demote', auth(), async ctx => {
|
||||
router.post("users.demote", auth(), async ctx => {
|
||||
const userId = ctx.body.id;
|
||||
const teamId = ctx.state.user.teamId;
|
||||
ctx.assertPresent(userId, 'id is required');
|
||||
ctx.assertPresent(userId, "id is required");
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'demote', user);
|
||||
authorize(ctx.state.user, "demote", user);
|
||||
|
||||
const team = await Team.findByPk(teamId);
|
||||
await team.removeAdmin(user);
|
||||
|
||||
await Event.create({
|
||||
name: 'users.demote',
|
||||
name: "users.demote",
|
||||
actorId: ctx.state.user.id,
|
||||
userId,
|
||||
teamId,
|
||||
@@ -126,20 +126,20 @@ router.post('users.demote', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.suspend', auth(), async ctx => {
|
||||
router.post("users.suspend", auth(), async ctx => {
|
||||
const admin = ctx.state.user;
|
||||
const userId = ctx.body.id;
|
||||
const teamId = ctx.state.user.teamId;
|
||||
ctx.assertPresent(userId, 'id is required');
|
||||
ctx.assertPresent(userId, "id is required");
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'suspend', user);
|
||||
authorize(ctx.state.user, "suspend", user);
|
||||
|
||||
const team = await Team.findByPk(teamId);
|
||||
await team.suspendUser(user, admin);
|
||||
|
||||
await Event.create({
|
||||
name: 'users.suspend',
|
||||
name: "users.suspend",
|
||||
actorId: ctx.state.user.id,
|
||||
userId,
|
||||
teamId,
|
||||
@@ -152,20 +152,20 @@ router.post('users.suspend', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.activate', auth(), async ctx => {
|
||||
router.post("users.activate", auth(), async ctx => {
|
||||
const admin = ctx.state.user;
|
||||
const userId = ctx.body.id;
|
||||
const teamId = ctx.state.user.teamId;
|
||||
ctx.assertPresent(userId, 'id is required');
|
||||
ctx.assertPresent(userId, "id is required");
|
||||
|
||||
const user = await User.findByPk(userId);
|
||||
authorize(ctx.state.user, 'activate', user);
|
||||
authorize(ctx.state.user, "activate", user);
|
||||
|
||||
const team = await Team.findByPk(teamId);
|
||||
await team.activateUser(user, admin);
|
||||
|
||||
await Event.create({
|
||||
name: 'users.activate',
|
||||
name: "users.activate",
|
||||
actorId: ctx.state.user.id,
|
||||
userId,
|
||||
teamId,
|
||||
@@ -178,12 +178,12 @@ router.post('users.activate', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.invite', auth(), async ctx => {
|
||||
router.post("users.invite", auth(), async ctx => {
|
||||
const { invites } = ctx.body;
|
||||
ctx.assertPresent(invites, 'invites is required');
|
||||
ctx.assertPresent(invites, "invites is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'invite', User);
|
||||
authorize(user, "invite", User);
|
||||
|
||||
const response = await userInviter({ user, invites, ip: ctx.request.ip });
|
||||
|
||||
@@ -195,17 +195,17 @@ router.post('users.invite', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.delete', auth(), async ctx => {
|
||||
router.post("users.delete", auth(), async ctx => {
|
||||
const { confirmation, id } = ctx.body;
|
||||
ctx.assertPresent(confirmation, 'confirmation is required');
|
||||
ctx.assertPresent(confirmation, "confirmation is required");
|
||||
|
||||
let user = ctx.state.user;
|
||||
if (id) user = await User.findByPk(id);
|
||||
authorize(ctx.state.user, 'delete', user);
|
||||
authorize(ctx.state.user, "delete", user);
|
||||
|
||||
await user.destroy();
|
||||
await Event.create({
|
||||
name: 'users.delete',
|
||||
name: "users.delete",
|
||||
actorId: user.id,
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildUser } from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#users.list', async () => {
|
||||
it('should allow filtering by user name', async () => {
|
||||
const user = await buildUser({ name: 'Tester' });
|
||||
describe("#users.list", async () => {
|
||||
it("should allow filtering by user name", async () => {
|
||||
const user = await buildUser({ name: "Tester" });
|
||||
|
||||
// suspended user should not be returned
|
||||
await buildUser({
|
||||
name: 'Tester',
|
||||
name: "Tester",
|
||||
teamId: user.teamId,
|
||||
suspendedAt: new Date(),
|
||||
});
|
||||
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: {
|
||||
query: 'test',
|
||||
query: "test",
|
||||
token: user.getJwtToken(),
|
||||
},
|
||||
});
|
||||
@@ -34,17 +34,17 @@ describe('#users.list', async () => {
|
||||
expect(body.data[0].id).toEqual(user.id);
|
||||
});
|
||||
|
||||
it('should allow including suspended', async () => {
|
||||
const user = await buildUser({ name: 'Tester' });
|
||||
it("should allow including suspended", async () => {
|
||||
const user = await buildUser({ name: "Tester" });
|
||||
await buildUser({
|
||||
name: 'Tester',
|
||||
name: "Tester",
|
||||
teamId: user.teamId,
|
||||
suspendedAt: new Date(),
|
||||
});
|
||||
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: {
|
||||
query: 'test',
|
||||
query: "test",
|
||||
includeSuspended: true,
|
||||
token: user.getJwtToken(),
|
||||
},
|
||||
@@ -55,10 +55,10 @@ describe('#users.list', async () => {
|
||||
expect(body.data.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should return teams paginated user list', async () => {
|
||||
it("should return teams paginated user list", async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -69,9 +69,9 @@ describe('#users.list', async () => {
|
||||
expect(body.data[1].id).toEqual(admin.id);
|
||||
});
|
||||
|
||||
it('should require admin for detailed info', async () => {
|
||||
it("should require admin for detailed info", async () => {
|
||||
const { user, admin } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
const res = await server.post("/api/users.list", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -85,10 +85,10 @@ describe('#users.list', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.info', async () => {
|
||||
it('should return known user', async () => {
|
||||
describe("#users.info", async () => {
|
||||
it("should return known user", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.info', {
|
||||
const res = await server.post("/api/users.info", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -98,19 +98,19 @@ describe('#users.info', async () => {
|
||||
expect(body.data.name).toEqual(user.name);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/users.info');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/users.info");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.invite', async () => {
|
||||
it('should return sent invites', async () => {
|
||||
describe("#users.invite", async () => {
|
||||
it("should return sent invites", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.invite', {
|
||||
const res = await server.post("/api/users.invite", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
invites: [{ email: 'test@example.com', name: 'Test', guest: false }],
|
||||
invites: [{ email: "test@example.com", name: "Test", guest: false }],
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -118,70 +118,70 @@ describe('#users.invite', async () => {
|
||||
expect(body.data.sent.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/users.invite');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/users.invite");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.delete', async () => {
|
||||
it('should not allow deleting without confirmation', async () => {
|
||||
describe("#users.delete", async () => {
|
||||
it("should not allow deleting without confirmation", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('should allow deleting last admin if only user', async () => {
|
||||
it("should allow deleting last admin if only user", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should not allow deleting last admin if many users', async () => {
|
||||
it("should not allow deleting last admin if many users", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
await buildUser({ teamId: user.teamId, isAdmin: false });
|
||||
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('should allow deleting user account with confirmation', async () => {
|
||||
it("should allow deleting user account with confirmation", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should allow deleting pending user account with admin', async () => {
|
||||
it("should allow deleting pending user account with admin", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const pending = await buildUser({
|
||||
teamId: user.teamId,
|
||||
lastActiveAt: null,
|
||||
});
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken(), id: pending.id, confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should not allow deleting another user account', async () => {
|
||||
it("should not allow deleting another user account", async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const user2 = await buildUser({ teamId: user.teamId });
|
||||
const res = await server.post('/api/users.delete', {
|
||||
const res = await server.post("/api/users.delete", {
|
||||
body: { token: user.getJwtToken(), id: user2.id, confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/users.delete');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/users.delete");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
@@ -189,20 +189,20 @@ describe('#users.delete', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.update', async () => {
|
||||
it('should update user profile information', async () => {
|
||||
describe("#users.update", async () => {
|
||||
it("should update user profile information", async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.update', {
|
||||
body: { token: user.getJwtToken(), name: 'New name' },
|
||||
const res = await server.post("/api/users.update", {
|
||||
body: { token: user.getJwtToken(), name: "New name" },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.name).toEqual('New name');
|
||||
expect(body.data.name).toEqual("New name");
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/users.update');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/users.update");
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
@@ -210,11 +210,11 @@ describe('#users.update', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.promote', async () => {
|
||||
it('should promote a new admin', async () => {
|
||||
describe("#users.promote", async () => {
|
||||
it("should promote a new admin", async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/users.promote', {
|
||||
const res = await server.post("/api/users.promote", {
|
||||
body: { token: admin.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -223,9 +223,9 @@ describe('#users.promote', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.promote', {
|
||||
const res = await server.post("/api/users.promote", {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -235,12 +235,12 @@ describe('#users.promote', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.demote', async () => {
|
||||
it('should demote an admin', async () => {
|
||||
describe("#users.demote", async () => {
|
||||
it("should demote an admin", async () => {
|
||||
const { admin, user } = await seed();
|
||||
await user.update({ isAdmin: true }); // Make another admin
|
||||
|
||||
const res = await server.post('/api/users.demote', {
|
||||
const res = await server.post("/api/users.demote", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -252,10 +252,10 @@ describe('#users.demote', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not demote admins if only one available', async () => {
|
||||
it("should not demote admins if only one available", async () => {
|
||||
const admin = await buildUser({ isAdmin: true });
|
||||
|
||||
const res = await server.post('/api/users.demote', {
|
||||
const res = await server.post("/api/users.demote", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: admin.id,
|
||||
@@ -267,9 +267,9 @@ describe('#users.demote', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.promote', {
|
||||
const res = await server.post("/api/users.promote", {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -279,11 +279,11 @@ describe('#users.demote', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.suspend', async () => {
|
||||
it('should suspend an user', async () => {
|
||||
describe("#users.suspend", async () => {
|
||||
it("should suspend an user", async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
const res = await server.post("/api/users.suspend", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -295,9 +295,9 @@ describe('#users.suspend', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not allow suspending the user themselves', async () => {
|
||||
it("should not allow suspending the user themselves", async () => {
|
||||
const admin = await buildUser({ isAdmin: true });
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
const res = await server.post("/api/users.suspend", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: admin.id,
|
||||
@@ -309,9 +309,9 @@ describe('#users.suspend', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
const res = await server.post("/api/users.suspend", {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -321,8 +321,8 @@ describe('#users.suspend', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.activate', async () => {
|
||||
it('should activate a suspended user', async () => {
|
||||
describe("#users.activate", async () => {
|
||||
it("should activate a suspended user", async () => {
|
||||
const { admin, user } = await seed();
|
||||
await user.update({
|
||||
suspendedById: admin.id,
|
||||
@@ -330,7 +330,7 @@ describe('#users.activate', async () => {
|
||||
});
|
||||
|
||||
expect(user.isSuspended).toBe(true);
|
||||
const res = await server.post('/api/users.activate', {
|
||||
const res = await server.post("/api/users.activate", {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -342,9 +342,9 @@ describe('#users.activate', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
it("should require admin", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/users.activate', {
|
||||
const res = await server.post("/api/users.activate", {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
// @flow
|
||||
import debug from 'debug';
|
||||
import Router from 'koa-router';
|
||||
import subDays from 'date-fns/sub_days';
|
||||
import { AuthenticationError } from '../errors';
|
||||
import { Document, Attachment } from '../models';
|
||||
import { Op } from '../sequelize';
|
||||
import debug from "debug";
|
||||
import Router from "koa-router";
|
||||
import subDays from "date-fns/sub_days";
|
||||
import { AuthenticationError } from "../errors";
|
||||
import { Document, Attachment } from "../models";
|
||||
import { Op } from "../sequelize";
|
||||
|
||||
const router = new Router();
|
||||
const log = debug('utils');
|
||||
const log = debug("utils");
|
||||
|
||||
router.post('utils.gc', async ctx => {
|
||||
router.post("utils.gc", async ctx => {
|
||||
const { token } = ctx.body;
|
||||
|
||||
if (process.env.UTILS_SECRET !== token) {
|
||||
throw new AuthenticationError('Invalid secret token');
|
||||
throw new AuthenticationError("Invalid secret token");
|
||||
}
|
||||
|
||||
log('Permanently deleting documents older than 30 days…');
|
||||
log("Permanently deleting documents older than 30 days…");
|
||||
|
||||
const where = {
|
||||
deletedAt: {
|
||||
@@ -24,8 +24,8 @@ router.post('utils.gc', async ctx => {
|
||||
},
|
||||
};
|
||||
|
||||
const documents = await Document.scope('withUnpublished').findAll({
|
||||
attributes: ['id'],
|
||||
const documents = await Document.scope("withUnpublished").findAll({
|
||||
attributes: ["id"],
|
||||
where,
|
||||
});
|
||||
const documentIds = documents.map(d => d.id);
|
||||
@@ -36,7 +36,7 @@ router.post('utils.gc', async ctx => {
|
||||
},
|
||||
});
|
||||
|
||||
await Document.scope('withUnpublished').destroy({
|
||||
await Document.scope("withUnpublished").destroy({
|
||||
where,
|
||||
force: true,
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import subDays from 'date-fns/sub_days';
|
||||
import app from '../app';
|
||||
import { Document } from '../models';
|
||||
import { sequelize } from '../sequelize';
|
||||
import { flushdb } from '../test/support';
|
||||
import { buildDocument } from '../test/factories';
|
||||
import TestServer from "fetch-test-server";
|
||||
import subDays from "date-fns/sub_days";
|
||||
import app from "../app";
|
||||
import { Document } from "../models";
|
||||
import { sequelize } from "../sequelize";
|
||||
import { flushdb } from "../test/support";
|
||||
import { buildDocument } from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#utils.gc', async () => {
|
||||
it('should destroy documents deleted more than 30 days ago', async () => {
|
||||
describe("#utils.gc", async () => {
|
||||
it("should destroy documents deleted more than 30 days ago", async () => {
|
||||
const document = await buildDocument({
|
||||
publishedAt: new Date(),
|
||||
});
|
||||
@@ -25,7 +25,7 @@ describe('#utils.gc', async () => {
|
||||
).toISOString()}' WHERE id = '${document.id}'`
|
||||
);
|
||||
|
||||
const res = await server.post('/api/utils.gc', {
|
||||
const res = await server.post("/api/utils.gc", {
|
||||
body: {
|
||||
token: process.env.UTILS_SECRET,
|
||||
},
|
||||
@@ -40,7 +40,7 @@ describe('#utils.gc', async () => {
|
||||
expect(reloaded).toBe(null);
|
||||
});
|
||||
|
||||
it('should destroy draft documents deleted more than 30 days ago', async () => {
|
||||
it("should destroy draft documents deleted more than 30 days ago", async () => {
|
||||
const document = await buildDocument({
|
||||
publishedAt: undefined,
|
||||
});
|
||||
@@ -52,7 +52,7 @@ describe('#utils.gc', async () => {
|
||||
).toISOString()}' WHERE id = '${document.id}'`
|
||||
);
|
||||
|
||||
const res = await server.post('/api/utils.gc', {
|
||||
const res = await server.post("/api/utils.gc", {
|
||||
body: {
|
||||
token: process.env.UTILS_SECRET,
|
||||
},
|
||||
@@ -67,8 +67,8 @@ describe('#utils.gc', async () => {
|
||||
expect(reloaded).toBe(null);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/utils.gc');
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/utils.gc");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import auth from '../middlewares/authentication';
|
||||
import { presentView } from '../presenters';
|
||||
import { View, Document, Event } from '../models';
|
||||
import policy from '../policies';
|
||||
import Router from "koa-router";
|
||||
import auth from "../middlewares/authentication";
|
||||
import { presentView } from "../presenters";
|
||||
import { View, Document, Event } from "../models";
|
||||
import policy from "../policies";
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('views.list', auth(), async ctx => {
|
||||
router.post("views.list", auth(), async ctx => {
|
||||
const { documentId } = ctx.body;
|
||||
ctx.assertUuid(documentId, 'documentId is required');
|
||||
ctx.assertUuid(documentId, "documentId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(documentId, { userId: user.id });
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
const views = await View.findByDocument(documentId);
|
||||
|
||||
@@ -23,18 +23,18 @@ router.post('views.list', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('views.create', auth(), async ctx => {
|
||||
router.post("views.create", auth(), async ctx => {
|
||||
const { documentId } = ctx.body;
|
||||
ctx.assertUuid(documentId, 'documentId is required');
|
||||
ctx.assertUuid(documentId, "documentId is required");
|
||||
|
||||
const user = ctx.state.user;
|
||||
const document = await Document.findByPk(documentId, { userId: user.id });
|
||||
authorize(user, 'read', document);
|
||||
authorize(user, "read", document);
|
||||
|
||||
const view = await View.increment({ documentId, userId: user.id });
|
||||
|
||||
await Event.create({
|
||||
name: 'views.create',
|
||||
name: "views.create",
|
||||
actorId: user.id,
|
||||
documentId: document.id,
|
||||
collectionId: document.collectionId,
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '../app';
|
||||
import { View, CollectionUser } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
import TestServer from "fetch-test-server";
|
||||
import app from "../app";
|
||||
import { View, CollectionUser } from "../models";
|
||||
import { flushdb, seed } from "../test/support";
|
||||
import { buildUser } from "../test/factories";
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#views.list', async () => {
|
||||
it('should return views for a document', async () => {
|
||||
describe("#views.list", async () => {
|
||||
it("should return views for a document", async () => {
|
||||
const { user, document } = await seed();
|
||||
await View.increment({ documentId: document.id, userId: user.id });
|
||||
|
||||
const res = await server.post('/api/views.list', {
|
||||
const res = await server.post("/api/views.list", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -25,7 +25,7 @@ describe('#views.list', async () => {
|
||||
expect(body.data[0].user.name).toBe(user.name);
|
||||
});
|
||||
|
||||
it('should return views for a document in read-only collection', async () => {
|
||||
it("should return views for a document in read-only collection", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
collection.private = true;
|
||||
await collection.save();
|
||||
@@ -34,12 +34,12 @@ describe('#views.list', async () => {
|
||||
createdById: user.id,
|
||||
collectionId: collection.id,
|
||||
userId: user.id,
|
||||
permission: 'read',
|
||||
permission: "read",
|
||||
});
|
||||
|
||||
await View.increment({ documentId: document.id, userId: user.id });
|
||||
|
||||
const res = await server.post('/api/views.list', {
|
||||
const res = await server.post("/api/views.list", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -49,9 +49,9 @@ describe('#views.list', async () => {
|
||||
expect(body.data[0].user.name).toBe(user.name);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
it("should require authentication", async () => {
|
||||
const { document } = await seed();
|
||||
const res = await server.post('/api/views.list', {
|
||||
const res = await server.post("/api/views.list", {
|
||||
body: { documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -60,20 +60,20 @@ describe('#views.list', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/views.list', {
|
||||
const res = await server.post("/api/views.list", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#views.create', async () => {
|
||||
it('should allow creating a view record for document', async () => {
|
||||
describe("#views.create", async () => {
|
||||
it("should allow creating a view record for document", async () => {
|
||||
const { user, document } = await seed();
|
||||
const res = await server.post('/api/views.create', {
|
||||
const res = await server.post("/api/views.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -82,7 +82,7 @@ describe('#views.create', async () => {
|
||||
expect(body.data.count).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow creating a view record for document in read-only collection', async () => {
|
||||
it("should allow creating a view record for document in read-only collection", async () => {
|
||||
const { user, document, collection } = await seed();
|
||||
collection.private = true;
|
||||
await collection.save();
|
||||
@@ -91,10 +91,10 @@ describe('#views.create', async () => {
|
||||
createdById: user.id,
|
||||
collectionId: collection.id,
|
||||
userId: user.id,
|
||||
permission: 'read',
|
||||
permission: "read",
|
||||
});
|
||||
|
||||
const res = await server.post('/api/views.create', {
|
||||
const res = await server.post("/api/views.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -103,9 +103,9 @@ describe('#views.create', async () => {
|
||||
expect(body.data.count).toBe(1);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
it("should require authentication", async () => {
|
||||
const { document } = await seed();
|
||||
const res = await server.post('/api/views.create', {
|
||||
const res = await server.post("/api/views.create", {
|
||||
body: { documentId: document.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -114,10 +114,10 @@ describe('#views.create', async () => {
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require authorization', async () => {
|
||||
it("should require authorization", async () => {
|
||||
const { document } = await seed();
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/views.create', {
|
||||
const res = await server.post("/api/views.create", {
|
||||
body: { token: user.getJwtToken(), documentId: document.id },
|
||||
});
|
||||
expect(res.status).toEqual(403);
|
||||
|
||||
Reference in New Issue
Block a user