Base model refactor (#810)
* Big upgrades * WIP: Stash * Stash, 30 flow errors left * Downgrade mobx * WIP * When I understand the difference between class and instance methods * 💚 * Fixes: File import Model saving edge cases pinning and starring docs Collection editing Upgrade mobx devtools * Notification settings saving works * Disabled settings * Document mailer * Working notifications * Colletion created notification Ensure not notified for own actions * Tidy up * Document updated event only for document creation Add indexes Notification setting on user creation * Commentary * Fixed: Notification setting on signup * Fix document move / duplicate stale data Add BaseModel.refresh method * Fixes: Title in sidebar not updated after editing document * 💚 * Improve / restore error handling Better handle offline errors * 👕
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#team.users should require admin for detailed info 1`] = `
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"id": "fa952cff-fa64-4d42-a6ea-6955c9689046",
|
||||
"name": "Admin User",
|
||||
"username": "admin",
|
||||
},
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
},
|
||||
],
|
||||
"ok": true,
|
||||
"pagination": Object {
|
||||
"limit": 15,
|
||||
"nextPath": "/api/team.users?limit=15&offset=15",
|
||||
"offset": 0,
|
||||
},
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#team.users should return teams paginated user list 1`] = `
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"email": "user1@example.com",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"isSuspended": false,
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
},
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"email": "admin@example.com",
|
||||
"id": "fa952cff-fa64-4d42-a6ea-6955c9689046",
|
||||
"isAdmin": true,
|
||||
"isSuspended": false,
|
||||
"name": "Admin User",
|
||||
"username": "admin",
|
||||
},
|
||||
],
|
||||
"ok": true,
|
||||
"pagination": Object {
|
||||
"limit": 15,
|
||||
"nextPath": "/api/team.users?limit=15&offset=15",
|
||||
"offset": 0,
|
||||
},
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#user.activate should activate a suspended user 1`] = `
|
||||
exports[`#users.activate should activate a suspended user 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
@@ -17,7 +17,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.activate should require admin 1`] = `
|
||||
exports[`#users.activate should require admin 1`] = `
|
||||
Object {
|
||||
"error": "admin_required",
|
||||
"message": "An admin role is required to access this resource",
|
||||
@@ -26,7 +26,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.delete should require authentication 1`] = `
|
||||
exports[`#users.delete should require authentication 1`] = `
|
||||
Object {
|
||||
"error": "authentication_required",
|
||||
"message": "Authentication required",
|
||||
@@ -35,7 +35,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.demote should demote an admin 1`] = `
|
||||
exports[`#users.demote should demote an admin 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
@@ -52,7 +52,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.demote should require admin 1`] = `
|
||||
exports[`#users.demote should require admin 1`] = `
|
||||
Object {
|
||||
"error": "admin_required",
|
||||
"message": "An admin role is required to access this resource",
|
||||
@@ -61,7 +61,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.demote shouldn't demote admins if only one available 1`] = `
|
||||
exports[`#users.demote shouldn't demote admins if only one available 1`] = `
|
||||
Object {
|
||||
"error": "validation_error",
|
||||
"message": "At least one admin is required",
|
||||
@@ -70,7 +70,69 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.promote should promote a new admin 1`] = `
|
||||
exports[`#users.list should require admin for detailed info 1`] = `
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"id": "fa952cff-fa64-4d42-a6ea-6955c9689046",
|
||||
"name": "Admin User",
|
||||
"username": "admin",
|
||||
},
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
},
|
||||
],
|
||||
"ok": true,
|
||||
"pagination": Object {
|
||||
"limit": 15,
|
||||
"nextPath": "/api/users.list?limit=15&offset=15",
|
||||
"offset": 0,
|
||||
},
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#users.list should return teams paginated user list 1`] = `
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"email": "user1@example.com",
|
||||
"id": "46fde1d4-0050-428f-9f0b-0bf77f4bdf61",
|
||||
"isAdmin": false,
|
||||
"isSuspended": false,
|
||||
"name": "User 1",
|
||||
"username": "user1",
|
||||
},
|
||||
Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
"createdAt": "2018-01-01T00:00:00.000Z",
|
||||
"email": "admin@example.com",
|
||||
"id": "fa952cff-fa64-4d42-a6ea-6955c9689046",
|
||||
"isAdmin": true,
|
||||
"isSuspended": false,
|
||||
"name": "Admin User",
|
||||
"username": "admin",
|
||||
},
|
||||
],
|
||||
"ok": true,
|
||||
"pagination": Object {
|
||||
"limit": 15,
|
||||
"nextPath": "/api/users.list?limit=15&offset=15",
|
||||
"offset": 0,
|
||||
},
|
||||
"status": 200,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#users.promote should promote a new admin 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
@@ -87,7 +149,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.promote should require admin 1`] = `
|
||||
exports[`#users.promote should require admin 1`] = `
|
||||
Object {
|
||||
"error": "admin_required",
|
||||
"message": "An admin role is required to access this resource",
|
||||
@@ -96,7 +158,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.suspend should require admin 1`] = `
|
||||
exports[`#users.suspend should require admin 1`] = `
|
||||
Object {
|
||||
"error": "admin_required",
|
||||
"message": "An admin role is required to access this resource",
|
||||
@@ -105,7 +167,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.suspend should suspend an user 1`] = `
|
||||
exports[`#users.suspend should suspend an user 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
@@ -122,7 +184,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.suspend shouldn't allow suspending the user themselves 1`] = `
|
||||
exports[`#users.suspend shouldn't allow suspending the user themselves 1`] = `
|
||||
Object {
|
||||
"error": "validation_error",
|
||||
"message": "Unable to suspend the current user",
|
||||
@@ -131,7 +193,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.update should require authentication 1`] = `
|
||||
exports[`#users.update should require authentication 1`] = `
|
||||
Object {
|
||||
"error": "authentication_required",
|
||||
"message": "Authentication required",
|
||||
@@ -140,7 +202,7 @@ Object {
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#user.update should update user profile information 1`] = `
|
||||
exports[`#users.update should update user profile information 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"avatarUrl": "http://example.com/avatar.png",
|
||||
@@ -4,7 +4,7 @@ import Koa from 'koa';
|
||||
import Router from 'koa-router';
|
||||
|
||||
import auth from './auth';
|
||||
import user from './user';
|
||||
import users from './users';
|
||||
import collections from './collections';
|
||||
import documents from './documents';
|
||||
import views from './views';
|
||||
@@ -13,6 +13,7 @@ import apiKeys from './apiKeys';
|
||||
import shares from './shares';
|
||||
import team from './team';
|
||||
import integrations from './integrations';
|
||||
import notificationSettings from './notificationSettings';
|
||||
|
||||
import errorHandling from './middlewares/errorHandling';
|
||||
import validation from '../middlewares/validation';
|
||||
@@ -33,7 +34,7 @@ api.use(apiWrapper());
|
||||
|
||||
// routes
|
||||
router.use('/', auth.routes());
|
||||
router.use('/', user.routes());
|
||||
router.use('/', users.routes());
|
||||
router.use('/', collections.routes());
|
||||
router.use('/', documents.routes());
|
||||
router.use('/', views.routes());
|
||||
@@ -42,6 +43,7 @@ router.use('/', apiKeys.routes());
|
||||
router.use('/', shares.routes());
|
||||
router.use('/', team.routes());
|
||||
router.use('/', integrations.routes());
|
||||
router.use('/', notificationSettings.routes());
|
||||
|
||||
// Router is embedded in a Koa application wrapper, because koa-router does not
|
||||
// allow middleware to catch any routes which were not explicitly defined.
|
||||
|
||||
60
server/api/notificationSettings.js
Normal file
60
server/api/notificationSettings.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
|
||||
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 => {
|
||||
const { event } = ctx.body;
|
||||
ctx.assertPresent(event, 'event is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
authorize(user, 'create', NotificationSetting);
|
||||
|
||||
const [setting] = await NotificationSetting.findOrCreate({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
event,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.body = {
|
||||
data: presentNotificationSetting(ctx, setting),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('notificationSettings.list', auth(), async ctx => {
|
||||
const user = ctx.state.user;
|
||||
const settings = await NotificationSetting.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.body = {
|
||||
data: settings.map(setting => presentNotificationSetting(ctx, setting)),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('notificationSettings.delete', auth(), async ctx => {
|
||||
const { id } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
const setting = await NotificationSetting.findById(id);
|
||||
authorize(user, 'delete', setting);
|
||||
|
||||
await setting.destroy();
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,11 +1,10 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import { User, Team } from '../models';
|
||||
import { Team } from '../models';
|
||||
import { publicS3Endpoint } from '../utils/s3';
|
||||
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentUser, presentTeam } from '../presenters';
|
||||
import { presentTeam } from '../presenters';
|
||||
import policy from '../policies';
|
||||
|
||||
const { authorize } = policy;
|
||||
@@ -33,24 +32,4 @@ router.post('team.update', auth(), async ctx => {
|
||||
ctx.body = { data: await presentTeam(ctx, team) };
|
||||
});
|
||||
|
||||
router.post('team.users', auth(), 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(listUser =>
|
||||
presentUser(ctx, listUser, { includeDetails: user.isAdmin })
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
|
||||
import app from '..';
|
||||
|
||||
import { flushdb, seed } from '../test/support';
|
||||
@@ -10,31 +9,6 @@ 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 for detailed info', 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(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#team.update', async () => {
|
||||
it('should update team details', async () => {
|
||||
const { admin } = await seed();
|
||||
|
||||
@@ -5,17 +5,38 @@ import { makePolicy, signPolicy, publicS3Endpoint } from '../utils/s3';
|
||||
import { ValidationError } from '../errors';
|
||||
import { Event, User, Team } from '../models';
|
||||
import auth from '../middlewares/authentication';
|
||||
import pagination from './middlewares/pagination';
|
||||
import { presentUser } from '../presenters';
|
||||
import policy from '../policies';
|
||||
|
||||
const { authorize } = policy;
|
||||
const router = new Router();
|
||||
|
||||
router.post('user.info', auth(), async ctx => {
|
||||
router.post('users.list', auth(), 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(listUser =>
|
||||
presentUser(ctx, listUser, { includeDetails: user.isAdmin })
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
router.post('users.info', auth(), async ctx => {
|
||||
ctx.body = { data: await presentUser(ctx, ctx.state.user) };
|
||||
});
|
||||
|
||||
router.post('user.update', auth(), async ctx => {
|
||||
router.post('users.update', auth(), async ctx => {
|
||||
const { user } = ctx.state;
|
||||
const { name, avatarUrl } = ctx.body;
|
||||
const endpoint = publicS3Endpoint();
|
||||
@@ -30,7 +51,7 @@ router.post('user.update', auth(), async ctx => {
|
||||
ctx.body = { data: await presentUser(ctx, user, { includeDetails: true }) };
|
||||
});
|
||||
|
||||
router.post('user.s3Upload', auth(), async ctx => {
|
||||
router.post('users.s3Upload', auth(), async ctx => {
|
||||
const { filename, kind, size } = ctx.body;
|
||||
ctx.assertPresent(filename, 'filename is required');
|
||||
ctx.assertPresent(kind, 'kind is required');
|
||||
@@ -79,7 +100,7 @@ router.post('user.s3Upload', auth(), async ctx => {
|
||||
|
||||
// Admin specific
|
||||
|
||||
router.post('user.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');
|
||||
@@ -95,7 +116,7 @@ router.post('user.promote', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('user.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');
|
||||
@@ -120,7 +141,7 @@ router.post('user.demote', auth(), async ctx => {
|
||||
*
|
||||
* Admin can suspend users to reduce the number of accounts on their billing plan
|
||||
*/
|
||||
router.post('user.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;
|
||||
@@ -147,7 +168,7 @@ router.post('user.suspend', auth(), async ctx => {
|
||||
* Admin can activate users to let them access resources. These users will also
|
||||
* account towards the billing plan limits.
|
||||
*/
|
||||
router.post('user.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;
|
||||
@@ -164,7 +185,7 @@ router.post('user.activate', auth(), async ctx => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post('user.delete', auth(), async ctx => {
|
||||
router.post('users.delete', auth(), async ctx => {
|
||||
const { confirmation } = ctx.body;
|
||||
ctx.assertPresent(confirmation, 'confirmation is required');
|
||||
|
||||
@@ -10,10 +10,35 @@ const server = new TestServer(app.callback());
|
||||
beforeEach(flushdb);
|
||||
afterAll(server.close);
|
||||
|
||||
describe('#user.info', async () => {
|
||||
describe('#users.list', async () => {
|
||||
it('should return teams paginated user list', async () => {
|
||||
const { admin } = await seed();
|
||||
|
||||
const res = await server.post('/api/users.list', {
|
||||
body: { token: admin.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should require admin for detailed info', async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/users.list', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#users.info', async () => {
|
||||
it('should return known user', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.info', {
|
||||
const res = await server.post('/api/users.info', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -24,15 +49,15 @@ describe('#user.info', async () => {
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/user.info');
|
||||
const res = await server.post('/api/users.info');
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.delete', async () => {
|
||||
describe('#users.delete', async () => {
|
||||
it('should not allow deleting without confirmation', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.delete', {
|
||||
const res = await server.post('/api/users.delete', {
|
||||
body: { token: user.getJwtToken() },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
@@ -40,7 +65,7 @@ describe('#user.delete', async () => {
|
||||
|
||||
it('should allow deleting last admin if only user', async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
const res = await server.post('/api/user.delete', {
|
||||
const res = await server.post('/api/users.delete', {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
@@ -50,7 +75,7 @@ describe('#user.delete', async () => {
|
||||
const user = await buildUser({ isAdmin: true });
|
||||
await buildUser({ teamId: user.teamId, isAdmin: false });
|
||||
|
||||
const res = await server.post('/api/user.delete', {
|
||||
const res = await server.post('/api/users.delete', {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(400);
|
||||
@@ -58,14 +83,14 @@ describe('#user.delete', async () => {
|
||||
|
||||
it('should allow deleting user account with confirmation', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.delete', {
|
||||
const res = await server.post('/api/users.delete', {
|
||||
body: { token: user.getJwtToken(), confirmation: true },
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/user.delete');
|
||||
const res = await server.post('/api/users.delete');
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
@@ -73,10 +98,10 @@ describe('#user.delete', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.update', async () => {
|
||||
describe('#users.update', async () => {
|
||||
it('should update user profile information', async () => {
|
||||
const { user } = await seed();
|
||||
const res = await server.post('/api/user.update', {
|
||||
const res = await server.post('/api/users.update', {
|
||||
body: { token: user.getJwtToken(), name: 'New name' },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -86,7 +111,7 @@ describe('#user.update', async () => {
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const res = await server.post('/api/user.update');
|
||||
const res = await server.post('/api/users.update');
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(401);
|
||||
@@ -94,11 +119,11 @@ describe('#user.update', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.promote', async () => {
|
||||
describe('#users.promote', async () => {
|
||||
it('should promote a new admin', async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/user.promote', {
|
||||
const res = await server.post('/api/users.promote', {
|
||||
body: { token: admin.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -109,7 +134,7 @@ describe('#user.promote', async () => {
|
||||
|
||||
it('should require admin', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.promote', {
|
||||
const res = await server.post('/api/users.promote', {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -119,12 +144,12 @@ describe('#user.promote', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.demote', 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/user.demote', {
|
||||
const res = await server.post('/api/users.demote', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -139,7 +164,7 @@ describe('#user.demote', async () => {
|
||||
it("shouldn't demote admins if only one available ", async () => {
|
||||
const admin = await buildUser({ isAdmin: true });
|
||||
|
||||
const res = await server.post('/api/user.demote', {
|
||||
const res = await server.post('/api/users.demote', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: admin.id,
|
||||
@@ -153,7 +178,7 @@ describe('#user.demote', async () => {
|
||||
|
||||
it('should require admin', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.promote', {
|
||||
const res = await server.post('/api/users.promote', {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -163,11 +188,11 @@ describe('#user.demote', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.suspend', async () => {
|
||||
describe('#users.suspend', async () => {
|
||||
it('should suspend an user', async () => {
|
||||
const { admin, user } = await seed();
|
||||
|
||||
const res = await server.post('/api/user.suspend', {
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -181,7 +206,7 @@ describe('#user.suspend', async () => {
|
||||
|
||||
it("shouldn't allow suspending the user themselves", async () => {
|
||||
const admin = await buildUser({ isAdmin: true });
|
||||
const res = await server.post('/api/user.suspend', {
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: admin.id,
|
||||
@@ -195,7 +220,7 @@ describe('#user.suspend', async () => {
|
||||
|
||||
it('should require admin', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.suspend', {
|
||||
const res = await server.post('/api/users.suspend', {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
@@ -205,7 +230,7 @@ describe('#user.suspend', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#user.activate', async () => {
|
||||
describe('#users.activate', async () => {
|
||||
it('should activate a suspended user', async () => {
|
||||
const { admin, user } = await seed();
|
||||
await user.update({
|
||||
@@ -214,7 +239,7 @@ describe('#user.activate', async () => {
|
||||
});
|
||||
|
||||
expect(user.isSuspended).toBe(true);
|
||||
const res = await server.post('/api/user.activate', {
|
||||
const res = await server.post('/api/users.activate', {
|
||||
body: {
|
||||
token: admin.getJwtToken(),
|
||||
id: user.id,
|
||||
@@ -228,7 +253,7 @@ describe('#user.activate', async () => {
|
||||
|
||||
it('should require admin', async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post('/api/user.activate', {
|
||||
const res = await server.post('/api/users.activate', {
|
||||
body: { token: user.getJwtToken(), id: user.id },
|
||||
});
|
||||
const body = await res.json();
|
||||
Reference in New Issue
Block a user