* feat: introducing get team info by id in admin module as a query * chore: adding resolve field for admin * chore: remove nullable false * refactor: rename getTeamInfo to teamInfo * refactor: make myRole nullable
336 lines
9.1 KiB
TypeScript
336 lines
9.1 KiB
TypeScript
import { Team, TeamMember, TeamMemberRole } from './team.model';
|
|
import {
|
|
Resolver,
|
|
ResolveField,
|
|
Args,
|
|
Parent,
|
|
Query,
|
|
Mutation,
|
|
Int,
|
|
Subscription,
|
|
ID,
|
|
} from '@nestjs/graphql';
|
|
import { TeamService } from './team.service';
|
|
import { GqlAuthGuard } from '../guards/gql-auth.guard';
|
|
import { GqlUser } from '../decorators/gql-user.decorator';
|
|
import { UseGuards } from '@nestjs/common';
|
|
import { RequiresTeamRole } from './decorators/requires-team-role.decorator';
|
|
import { GqlTeamMemberGuard } from './guards/gql-team-member.guard';
|
|
import { PubSubService } from '../pubsub/pubsub.service';
|
|
import * as E from 'fp-ts/Either';
|
|
import { throwErr } from 'src/utils';
|
|
import { AuthUser } from 'src/types/AuthUser';
|
|
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
|
import { SkipThrottle } from '@nestjs/throttler';
|
|
|
|
@UseGuards(GqlThrottlerGuard)
|
|
@Resolver(() => Team)
|
|
export class TeamResolver {
|
|
constructor(
|
|
private readonly teamService: TeamService,
|
|
private readonly pubsub: PubSubService,
|
|
) {}
|
|
|
|
// Field Resolvers
|
|
// TODO: Deprecate this
|
|
@ResolveField(() => [TeamMember], {
|
|
description: 'Returns the list of members of a team',
|
|
complexity: 10,
|
|
})
|
|
members(
|
|
@Parent() team: Team,
|
|
@Args({
|
|
name: 'cursor',
|
|
type: () => ID,
|
|
description:
|
|
'The ID of the last returned team member entry (used for pagination)',
|
|
nullable: true,
|
|
})
|
|
cursor?: string,
|
|
): Promise<TeamMember[]> {
|
|
return this.teamService.getMembersOfTeam(team.id, cursor ?? null);
|
|
}
|
|
|
|
@ResolveField(() => [TeamMember], {
|
|
description: 'Returns the list of members of a team',
|
|
complexity: 10,
|
|
})
|
|
teamMembers(@Parent() team: Team): Promise<TeamMember[]> {
|
|
return this.teamService.getTeamMembers(team.id);
|
|
}
|
|
|
|
@ResolveField(() => TeamMemberRole, {
|
|
description: 'The role of the current user in the team',
|
|
nullable: true,
|
|
})
|
|
@UseGuards(GqlAuthGuard)
|
|
myRole(
|
|
@Parent() team: Team,
|
|
@GqlUser() user: AuthUser,
|
|
): Promise<TeamMemberRole | null> {
|
|
return this.teamService.getRoleOfUserInTeam(team.id, user.uid);
|
|
}
|
|
|
|
@ResolveField(() => Int, {
|
|
description: 'The number of users with the OWNER role in the team',
|
|
})
|
|
ownersCount(@Parent() team: Team): Promise<number> {
|
|
return this.teamService.getCountOfUsersWithRoleInTeam(
|
|
team.id,
|
|
TeamMemberRole.OWNER,
|
|
);
|
|
}
|
|
|
|
@ResolveField(() => Int, {
|
|
description: 'The number of users with the EDITOR role in the team',
|
|
})
|
|
editorsCount(@Parent() team: Team): Promise<number> {
|
|
return this.teamService.getCountOfUsersWithRoleInTeam(
|
|
team.id,
|
|
TeamMemberRole.EDITOR,
|
|
);
|
|
}
|
|
|
|
@ResolveField(() => Int, {
|
|
description: 'The number of users with the VIEWER role in the team',
|
|
})
|
|
viewersCount(@Parent() team: Team): Promise<number> {
|
|
return this.teamService.getCountOfUsersWithRoleInTeam(
|
|
team.id,
|
|
TeamMemberRole.VIEWER,
|
|
);
|
|
}
|
|
|
|
// Query
|
|
@Query(() => [Team], {
|
|
description: 'List of teams that the executing user belongs to.',
|
|
})
|
|
@UseGuards(GqlAuthGuard)
|
|
myTeams(
|
|
@GqlUser() user: AuthUser,
|
|
@Args({
|
|
name: 'cursor',
|
|
type: () => ID,
|
|
description:
|
|
'The ID of the last returned team entry (used for pagination)',
|
|
nullable: true,
|
|
})
|
|
cursor?: string,
|
|
): Promise<Team[]> {
|
|
return this.teamService.getTeamsOfUser(user.uid, cursor ?? null);
|
|
}
|
|
|
|
@Query(() => Team, {
|
|
description: 'Returns the detail of the team with the given ID',
|
|
nullable: true,
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
team(
|
|
@Args({
|
|
name: 'teamID',
|
|
type: () => ID,
|
|
description: 'ID of the team to check',
|
|
})
|
|
teamID: string,
|
|
): Promise<Team | null> {
|
|
return this.teamService.getTeamWithID(teamID);
|
|
}
|
|
|
|
// Mutation
|
|
@Mutation(() => Team, {
|
|
description: 'Creates a team owned by the executing user',
|
|
})
|
|
@UseGuards(GqlAuthGuard)
|
|
async createTeam(
|
|
@GqlUser() user: AuthUser,
|
|
@Args({ name: 'name', description: 'Displayed name of the team' })
|
|
name: string,
|
|
): Promise<Team> {
|
|
const team = await this.teamService.createTeam(name, user.uid);
|
|
if (E.isLeft(team)) throwErr(team.left);
|
|
return team.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Leaves a team the executing user is a part of',
|
|
})
|
|
@UseGuards(GqlAuthGuard)
|
|
async leaveTeam(
|
|
@GqlUser() user: AuthUser,
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the Team to leave',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
): Promise<boolean> {
|
|
const isUserLeft = await this.teamService.leaveTeam(teamID, user.uid);
|
|
if (E.isLeft(isUserLeft)) throwErr(isUserLeft.left);
|
|
return isUserLeft.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Removes the team member from the team',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
async removeTeamMember(
|
|
@GqlUser() _user: AuthUser,
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the Team to remove user from',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
@Args({
|
|
name: 'userUid',
|
|
description: 'ID of the user to remove from the given team',
|
|
type: () => ID,
|
|
})
|
|
userUid: string,
|
|
): Promise<boolean> {
|
|
const isRemoved = await this.teamService.leaveTeam(teamID, userUid);
|
|
if (E.isLeft(isRemoved)) throwErr(isRemoved.left);
|
|
return isRemoved.right;
|
|
}
|
|
|
|
@Mutation(() => Team, {
|
|
description: 'Renames a team',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
async renameTeam(
|
|
@Args({ name: 'teamID', description: 'ID of the team', type: () => ID })
|
|
teamID: string,
|
|
@Args({ name: 'newName', description: 'The updated name of the team' })
|
|
newName: string,
|
|
): Promise<Team> {
|
|
const team = await this.teamService.renameTeam(teamID, newName);
|
|
if (E.isLeft(team)) throwErr(team.left);
|
|
return team.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Deletes the team',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
async deleteTeam(
|
|
@Args({ name: 'teamID', description: 'ID of the team', type: () => ID })
|
|
teamID: string,
|
|
): Promise<boolean> {
|
|
const isDeleted = await this.teamService.deleteTeam(teamID);
|
|
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
|
|
return isDeleted.right;
|
|
}
|
|
|
|
@Mutation(() => TeamMember, {
|
|
description: 'Update role of a team member the executing user owns',
|
|
})
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
async updateTeamMemberRole(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the affected team',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
@Args({
|
|
name: 'userUid',
|
|
description: 'UID of the affected user',
|
|
type: () => ID,
|
|
})
|
|
userUid: string,
|
|
@Args({
|
|
name: 'newRole',
|
|
description: 'Updated role value',
|
|
type: () => TeamMemberRole,
|
|
})
|
|
newRole: TeamMemberRole,
|
|
): Promise<TeamMember> {
|
|
const teamMember = await this.teamService.updateTeamMemberRole(
|
|
teamID,
|
|
userUid,
|
|
newRole,
|
|
);
|
|
if (E.isLeft(teamMember)) throwErr(teamMember.left);
|
|
return teamMember.right;
|
|
}
|
|
|
|
// Subscriptions
|
|
@Subscription(() => TeamMember, {
|
|
description:
|
|
'Listen to when a new team member being added to the team. The emitted value is the new team member added.',
|
|
resolve: (value) => value,
|
|
})
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
teamMemberAdded(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
): AsyncIterator<TeamMember> {
|
|
return this.pubsub.asyncIterator(`team/${teamID}/member_added`);
|
|
}
|
|
|
|
@Subscription(() => TeamMember, {
|
|
description:
|
|
'Listen to when a team member status has been updated. The emitted value is the new team member status',
|
|
resolve: (value) => value,
|
|
})
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
teamMemberUpdated(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
): AsyncIterator<TeamMember> {
|
|
return this.pubsub.asyncIterator(`team/${teamID}/member_updated`);
|
|
}
|
|
|
|
@Subscription(() => ID, {
|
|
description:
|
|
'Listen to when a team member has been removed. The emitted value is the uid of the user removed',
|
|
resolve: (value) => value,
|
|
})
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
teamMemberRemoved(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
): AsyncIterator<string> {
|
|
return this.pubsub.asyncIterator(`team/${teamID}/member_removed`);
|
|
}
|
|
}
|