Add routes for managing users in teams
This commit is contained in:
parent
dd358154b6
commit
46e56b7ad4
@ -3,6 +3,7 @@ import {INewUser} from './INewUser'
|
|||||||
import {ITeam} from './ITeam'
|
import {ITeam} from './ITeam'
|
||||||
import {IUserTeam} from './IUserTeam'
|
import {IUserTeam} from './IUserTeam'
|
||||||
import {IUser} from './IUser'
|
import {IUser} from './IUser'
|
||||||
|
import {IUserInTeam} from './IUserInTeam'
|
||||||
|
|
||||||
export interface IAPIDef {
|
export interface IAPIDef {
|
||||||
'/auth/register': {
|
'/auth/register': {
|
||||||
@ -70,6 +71,30 @@ export interface IAPIDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'/teams/:teamId/users': {
|
||||||
|
get: {
|
||||||
|
params: {
|
||||||
|
teamId: number
|
||||||
|
}
|
||||||
|
response: IUserInTeam[] // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'/teams/:teamId/users/:userId': {
|
||||||
|
post: {
|
||||||
|
params: {
|
||||||
|
teamId: number
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
params: {
|
||||||
|
teamId: number
|
||||||
|
userId: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
'/my/teams': {
|
'/my/teams': {
|
||||||
get: {
|
get: {
|
||||||
response: IUserTeam[]
|
response: IUserTeam[]
|
||||||
|
|||||||
7
packages/common/src/IUserInTeam.ts
Normal file
7
packages/common/src/IUserInTeam.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface IUserInTeam {
|
||||||
|
teamId: number
|
||||||
|
userId: number
|
||||||
|
displayName: string
|
||||||
|
roleId: number
|
||||||
|
roleName: string
|
||||||
|
}
|
||||||
@ -6,4 +6,7 @@ export * from './IRole'
|
|||||||
export * from './IRoutes'
|
export * from './IRoutes'
|
||||||
export * from './ITeam'
|
export * from './ITeam'
|
||||||
export * from './IUser'
|
export * from './IUser'
|
||||||
|
export * from './IUser'
|
||||||
|
export * from './IUserInTeam'
|
||||||
|
export * from './IUserTeam'
|
||||||
export * from './URLFormatter'
|
export * from './URLFormatter'
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {Team} from '../entities/Team'
|
import {Team} from '../entities/Team'
|
||||||
import {UserTeam} from '../entities/UserTeam'
|
import {UserTeam} from '../entities/UserTeam'
|
||||||
import {IUserTeamParams} from './IUserTeamParams'
|
import {IUserTeamParams} from './IUserTeamParams'
|
||||||
|
import {IUserInTeam} from '@rondo/common'
|
||||||
|
|
||||||
export interface ITeamService {
|
export interface ITeamService {
|
||||||
create(params: {name: string, userId: number}): Promise<Team>
|
create(params: {name: string, userId: number}): Promise<Team>
|
||||||
@ -17,5 +18,7 @@ export interface ITeamService {
|
|||||||
|
|
||||||
find(userId: number): Promise<UserTeam[]>
|
find(userId: number): Promise<UserTeam[]>
|
||||||
|
|
||||||
|
findUsers(teamId: number): Promise<IUserInTeam[]>
|
||||||
|
|
||||||
// TODO add other methods
|
// TODO add other methods
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {test} from '../test'
|
import {test} from '../test'
|
||||||
import {createTeam} from './TeamTestUtils'
|
import {addUser, removeUser, findUsers, createTeam} from './TeamTestUtils'
|
||||||
|
|
||||||
describe('team', () => {
|
describe('team', () => {
|
||||||
|
|
||||||
@ -8,10 +8,12 @@ describe('team', () => {
|
|||||||
|
|
||||||
let cookie!: string
|
let cookie!: string
|
||||||
let token!: string
|
let token!: string
|
||||||
|
let mainUserId: number
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const session = await test.registerAccount()
|
const session = await test.registerAccount()
|
||||||
cookie = session.cookie
|
cookie = session.cookie
|
||||||
token = session.token
|
token = session.token
|
||||||
|
mainUserId = session.userId
|
||||||
t.setHeaders({ cookie, 'x-csrf-token': token })
|
t.setHeaders({ cookie, 'x-csrf-token': token })
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -78,6 +80,49 @@ describe('team', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('POST /teams/:teamId/users/:userId', () => {
|
||||||
|
it('adds a user to the team', async () => {
|
||||||
|
const team = await createTeam(t, 'test')
|
||||||
|
const teamId = team.id
|
||||||
|
const {userId} = await test.registerAccount('test2@user.com')
|
||||||
|
await addUser(t, {userId, teamId})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('DELETE /teams/:teamId/users/:userId', () => {
|
||||||
|
it('removes the user from the team', async () => {
|
||||||
|
const team = await createTeam(t, 'test')
|
||||||
|
const teamId = team.id
|
||||||
|
const {userId} = await test.registerAccount('test2@user.com')
|
||||||
|
await addUser(t, {userId, teamId})
|
||||||
|
await removeUser(t, {userId, teamId})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('GET /teams/:teamId/users', () => {
|
||||||
|
it('fetches team members user info', async () => {
|
||||||
|
const team = await createTeam(t, 'test')
|
||||||
|
const teamId = team.id
|
||||||
|
const {userId} = await test.registerAccount('test2@user.com')
|
||||||
|
await addUser(t, {userId, teamId})
|
||||||
|
const users = await findUsers(t, {teamId})
|
||||||
|
expect(users.length).toBe(2)
|
||||||
|
expect(users).toEqual([{
|
||||||
|
teamId,
|
||||||
|
userId: mainUserId,
|
||||||
|
displayName: jasmine.any(String),
|
||||||
|
roleId: 1,
|
||||||
|
roleName: 'ADMIN',
|
||||||
|
}, {
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
displayName: jasmine.any(String),
|
||||||
|
roleId: 1,
|
||||||
|
roleName: 'ADMIN',
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// describe('GET /my/teams', () => {
|
// describe('GET /my/teams', () => {
|
||||||
|
|
||||||
// })
|
// })
|
||||||
|
|||||||
@ -64,6 +64,49 @@ export class TeamRoutes extends BaseRoute<IAPIDef> {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.get('/teams/:teamId/users', async req => {
|
||||||
|
const teamId = Number(req.params.teamId)
|
||||||
|
|
||||||
|
await this.permissions.belongsToTeam({
|
||||||
|
teamId,
|
||||||
|
userId: req.user!.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this.teamService.findUsers(teamId)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.post('/teams/:teamId/users/:userId', async req => {
|
||||||
|
const teamId = Number(req.params.teamId)
|
||||||
|
const userId = Number(req.params.userId)
|
||||||
|
|
||||||
|
await this.permissions.belongsToTeam({
|
||||||
|
teamId,
|
||||||
|
userId: req.user!.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.teamService.addUser({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
roleId: 1, // TODO customize roles
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.delete('/teams/:teamId/users/:userId', async req => {
|
||||||
|
const teamId = Number(req.params.teamId)
|
||||||
|
const userId = Number(req.params.userId)
|
||||||
|
|
||||||
|
await this.permissions.belongsToTeam({
|
||||||
|
teamId,
|
||||||
|
userId: req.user!.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.teamService.removeUser({
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
roleId: 1, // TODO customzie roles
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,10 +49,31 @@ export class TeamService extends BaseService implements ITeamService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeUser({teamId, userId, roleId}: IUserTeamParams) {
|
async removeUser({teamId, userId, roleId}: IUserTeamParams) {
|
||||||
|
// TODO check if this is the last admin team member
|
||||||
await this.getRepository(UserTeam)
|
await this.getRepository(UserTeam)
|
||||||
.delete({userId, teamId, roleId})
|
.delete({userId, teamId, roleId})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findUsers(teamId: number) {
|
||||||
|
const userTeams = await this.getRepository(UserTeam)
|
||||||
|
.createQueryBuilder('ut')
|
||||||
|
.select('ut')
|
||||||
|
.innerJoinAndSelect('ut.user', 'user')
|
||||||
|
.innerJoinAndSelect('ut.role', 'role')
|
||||||
|
.where('ut.teamId = :teamId', {
|
||||||
|
teamId,
|
||||||
|
})
|
||||||
|
.getMany()
|
||||||
|
|
||||||
|
return userTeams.map(ut => ({
|
||||||
|
teamId,
|
||||||
|
userId: ut.userId,
|
||||||
|
displayName: `${ut.user.firstName} ${ut.user.lastName}`,
|
||||||
|
roleId: ut.role!.id,
|
||||||
|
roleName: ut.role!.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
async findOne(id: number) {
|
async findOne(id: number) {
|
||||||
return this.getRepository(Team).findOne(id)
|
return this.getRepository(Team).findOne(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,3 +11,44 @@ export async function createTeam(t: RequestTester<IAPIDef>, name: string) {
|
|||||||
expect(response.body.id).toBeTruthy()
|
expect(response.body.id).toBeTruthy()
|
||||||
return response.body
|
return response.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addUser(t: RequestTester<IAPIDef>, params: {
|
||||||
|
teamId: number,
|
||||||
|
userId: number,
|
||||||
|
}) {
|
||||||
|
await t
|
||||||
|
.post('/teams/:teamId/users/:userId', {
|
||||||
|
params: {
|
||||||
|
teamId: params.teamId,
|
||||||
|
userId: params.userId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeUser(t: RequestTester<IAPIDef>, params: {
|
||||||
|
teamId: number,
|
||||||
|
userId: number,
|
||||||
|
}) {
|
||||||
|
await t
|
||||||
|
.delete('/teams/:teamId/users/:userId', {
|
||||||
|
params: {
|
||||||
|
teamId: params.teamId,
|
||||||
|
userId: params.userId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findUsers(t: RequestTester<IAPIDef>, params: {
|
||||||
|
teamId: number,
|
||||||
|
}) {
|
||||||
|
const response = await t
|
||||||
|
.get('/teams/:teamId/users', {
|
||||||
|
params: {
|
||||||
|
teamId: params.teamId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.expect(200)
|
||||||
|
return response.body
|
||||||
|
}
|
||||||
|
|||||||
@ -89,12 +89,16 @@ export class TestUtils<T extends IRoutes> {
|
|||||||
return match![1]
|
return match![1]
|
||||||
}
|
}
|
||||||
|
|
||||||
getLoginBody(csrfToken: string) {
|
getLoginBody(csrfToken: string, username?: string) {
|
||||||
const {username, password} = this
|
const {password} = this
|
||||||
return {username, password, _csrf: csrfToken}
|
return {
|
||||||
|
username: username || this.username,
|
||||||
|
password,
|
||||||
|
_csrf: csrfToken,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerAccount() {
|
async registerAccount(username?: string) {
|
||||||
const {context} = this
|
const {context} = this
|
||||||
const {cookie, token} = await this.getCsrf()
|
const {cookie, token} = await this.getCsrf()
|
||||||
|
|
||||||
@ -104,7 +108,7 @@ export class TestUtils<T extends IRoutes> {
|
|||||||
.send({
|
.send({
|
||||||
firstName: 'test',
|
firstName: 'test',
|
||||||
lastName: 'test',
|
lastName: 'test',
|
||||||
...this.getLoginBody(token),
|
...this.getLoginBody(token, username),
|
||||||
})
|
})
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user