Admin endpoints
This commit is contained in:
88
server/api/__snapshots__/team.test.js.snap
Normal file
88
server/api/__snapshots__/team.test.js.snap
Normal file
@@ -0,0 +1,88 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#team.addAdmin should promote a new admin 1`] = `
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"email": "user1@example.com",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": true,
|
||||
"name": "User 1",
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
"username": "user1",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.addAdmin should require admin 1`] = `
|
||||
Object {
|
||||
"error": "only_available_for_admins",
|
||||
"message": "Only available for admins",
|
||||
"ok": false,
|
||||
"status": 403,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.removeAdmin should demote an admin 1`] = `
|
||||
Object {
|
||||
"avatarUrl": null,
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.removeAdmin should require admin 1`] = `
|
||||
Object {
|
||||
"error": "only_available_for_admins",
|
||||
"message": "Only available for admins",
|
||||
"ok": false,
|
||||
"status": 403,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.removeAdmin shouldn't demote admins if only one available 1`] = `
|
||||
Object {
|
||||
"error": "at_least_one_admin_is_required",
|
||||
"message": "At least one admin is required",
|
||||
"ok": false,
|
||||
"status": 400,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.users should require admin 1`] = `
|
||||
Object {
|
||||
"error": "only_available_for_admins",
|
||||
"message": "Only available for admins",
|
||||
"ok": false,
|
||||
"status": 403,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.users should return teams paginated user list 1`] = `
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"email": "admin@example.com",
|
||||
"id": "fa952cff-fa64-4d42-a6ea-6955c9689046",
|
||||
"isAdmin": true,
|
||||
"name": "Admin User",
|
||||
"username": "admin",
|
||||
},
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"email": "user1@example.com",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
},
|
||||
],
|
||||
"ok": true,
|
||||
"pagination": Object {
|
||||
"limit": 15,
|
||||
"nextPath": "/api/team.users?limit=15&offset=15",
|
||||
"offset": 0,
|
||||
},
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
@@ -13,7 +13,6 @@ exports[`#user.info should return known user 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"email": "user1@example.com",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
|
||||
@@ -13,7 +13,7 @@ router.post('auth.info', auth(), async ctx => {
|
||||
|
||||
ctx.body = {
|
||||
data: {
|
||||
user: await presentUser(ctx, user),
|
||||
user: await presentUser(ctx, user, { includeDetails: true }),
|
||||
team: await presentTeam(ctx, team),
|
||||
},
|
||||
};
|
||||
@@ -51,9 +51,14 @@ router.post('auth.slack', async ctx => {
|
||||
name: data.user.name,
|
||||
email: data.user.email,
|
||||
teamId: team.id,
|
||||
isAdmin: !teamExisted,
|
||||
slackData: data.user,
|
||||
slackAccessToken: data.access_token,
|
||||
});
|
||||
|
||||
// Set initial avatar
|
||||
await user.updateAvatar();
|
||||
await user.save();
|
||||
}
|
||||
|
||||
if (!teamExisted) {
|
||||
@@ -68,10 +73,6 @@ router.post('auth.slack', async ctx => {
|
||||
expires: new Date('2100'),
|
||||
});
|
||||
|
||||
// Update user's avatar
|
||||
await user.updateAvatar();
|
||||
await user.save();
|
||||
|
||||
ctx.body = {
|
||||
data: {
|
||||
user: await presentUser(ctx, user),
|
||||
|
||||
@@ -59,6 +59,8 @@ router.post('hooks.slack', async ctx => {
|
||||
});
|
||||
|
||||
if (!user) throw httpErrors.BadRequest('Invalid user');
|
||||
if (!user.isAdmin)
|
||||
throw httpErrors.BadRequest('Only admins can add integrations');
|
||||
|
||||
const documents = await Document.searchForUser(user, text, {
|
||||
limit: 5,
|
||||
|
||||
70
server/api/team.js
Normal file
70
server/api/team.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import httpErrors from 'http-errors';
|
||||
|
||||
import User from '../models/User';
|
||||
import Team from '../models/Team';
|
||||
|
||||
import auth from './middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentUser } from '../presenters';
|
||||
|
||||
const router = new Router();
|
||||
router.use(auth({ adminOnly: true }));
|
||||
|
||||
router.post('team.users', pagination(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
|
||||
const users = await User.findAll({
|
||||
where: {
|
||||
teamId: user.teamId,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
});
|
||||
|
||||
ctx.body = {
|
||||
pagination: ctx.state.pagination,
|
||||
data: users.map(user => presentUser(ctx, user, { includeDetails: true })),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('team.addAdmin', async ctx => {
|
||||
const { user } = ctx.body;
|
||||
const admin = ctx.state.user;
|
||||
ctx.assertPresent(user, 'id is required');
|
||||
|
||||
const team = await Team.findById(admin.teamId);
|
||||
const promotedUser = await User.findOne({
|
||||
where: { id: user, teamId: admin.teamId },
|
||||
});
|
||||
|
||||
if (!promotedUser) throw httpErrors.NotFound();
|
||||
|
||||
await team.addAdmin(promotedUser);
|
||||
|
||||
ctx.body = presentUser(ctx, promotedUser, { includeDetails: true });
|
||||
});
|
||||
|
||||
router.post('team.removeAdmin', async ctx => {
|
||||
const { user } = ctx.body;
|
||||
const admin = ctx.state.user;
|
||||
ctx.assertPresent(user, 'id is required');
|
||||
|
||||
const team = await Team.findById(admin.teamId);
|
||||
const demotedUser = await User.findOne({
|
||||
where: { id: user, teamId: admin.teamId },
|
||||
});
|
||||
|
||||
if (!demotedUser) throw httpErrors.NotFound();
|
||||
|
||||
try {
|
||||
await team.removeAdmin(demotedUser);
|
||||
ctx.body = presentUser(ctx, user, { includeDetails: true });
|
||||
} catch (e) {
|
||||
throw httpErrors.BadRequest(e.message);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
108
server/api/team.test.js
Normal file
108
server/api/team.test.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
|
||||
import app from '..';
|
||||
|
||||
import { flushdb, seed } from '../test/support';
|
||||
|
||||
const server = new TestServer(app.callback());
|
||||
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#team.users', async () => {
|
||||
it('should return teams paginated user list', async () => {
|
||||
const { admin } = await seed();
|
||||
|
||||
const res = await server.post('/api/team.users', {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/team.users', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#team.addAdmin', async () => {
|
||||
it('should promote a new admin', async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/team.addAdmin', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
user: user.id,
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/team.addAdmin', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#team.removeAdmin', 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/team.removeAdmin', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
user: user.id,
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shouldn't demote admins if only one available ", async () => {
|
||||
const { admin } = await seed();
|
||||
|
||||
const res = await server.post('/api/team.removeAdmin', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
user: admin.id,
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin', async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/team.addAdmin', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(403);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user