201 lines
6.0 KiB
TypeScript
201 lines
6.0 KiB
TypeScript
import {
|
|
Args,
|
|
ID,
|
|
Mutation,
|
|
Parent,
|
|
Query,
|
|
ResolveField,
|
|
Resolver,
|
|
Subscription,
|
|
} from '@nestjs/graphql';
|
|
import { TeamInvitation } from './team-invitation.model';
|
|
import { TeamInvitationService } from './team-invitation.service';
|
|
import { pipe } from 'fp-ts/function';
|
|
import * as TE from 'fp-ts/TaskEither';
|
|
import * as E from 'fp-ts/Either';
|
|
import * as O from 'fp-ts/Option';
|
|
import { Team, TeamMember, TeamMemberRole } from 'src/team/team.model';
|
|
import { TEAM_INVITE_NO_INVITE_FOUND, USER_NOT_FOUND } from 'src/errors';
|
|
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
|
import { User } from 'src/user/user.model';
|
|
import { UseGuards } from '@nestjs/common';
|
|
import { GqlAuthGuard } from '../guards/gql-auth.guard';
|
|
import { TeamService } from 'src/team/team.service';
|
|
import { throwErr } from 'src/utils';
|
|
import { TeamInviteeGuard } from './team-invitee.guard';
|
|
import { GqlTeamMemberGuard } from 'src/team/guards/gql-team-member.guard';
|
|
import { RequiresTeamRole } from 'src/team/decorators/requires-team-role.decorator';
|
|
import { TeamInviteViewerGuard } from './team-invite-viewer.guard';
|
|
import { TeamInviteTeamOwnerGuard } from './team-invite-team-owner.guard';
|
|
import { UserService } from 'src/user/user.service';
|
|
import { PubSubService } from 'src/pubsub/pubsub.service';
|
|
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
|
import { SkipThrottle } from '@nestjs/throttler';
|
|
import { AuthUser } from 'src/types/AuthUser';
|
|
import { CreateTeamInvitationArgs } from './input-type.args';
|
|
|
|
@UseGuards(GqlThrottlerGuard)
|
|
@Resolver(() => TeamInvitation)
|
|
export class TeamInvitationResolver {
|
|
constructor(
|
|
private readonly userService: UserService,
|
|
private readonly teamService: TeamService,
|
|
private readonly teamInvitationService: TeamInvitationService,
|
|
|
|
private readonly pubsub: PubSubService,
|
|
) {}
|
|
|
|
@ResolveField(() => Team, {
|
|
complexity: 5,
|
|
description: 'Get the team associated to the invite',
|
|
})
|
|
async team(@Parent() teamInvitation: TeamInvitation): Promise<Team> {
|
|
return pipe(
|
|
this.teamService.getTeamWithIDTE(teamInvitation.teamID),
|
|
TE.getOrElse(throwErr),
|
|
)();
|
|
}
|
|
|
|
@ResolveField(() => User, {
|
|
complexity: 5,
|
|
description: 'Get the creator of the invite',
|
|
})
|
|
async creator(@Parent() teamInvitation: TeamInvitation): Promise<User> {
|
|
const user = await this.userService.findUserById(teamInvitation.creatorUid);
|
|
if (O.isNone(user)) throwErr(USER_NOT_FOUND);
|
|
|
|
return {
|
|
...user.value,
|
|
currentGQLSession: JSON.stringify(user.value.currentGQLSession),
|
|
currentRESTSession: JSON.stringify(user.value.currentRESTSession),
|
|
};
|
|
}
|
|
|
|
@Query(() => TeamInvitation, {
|
|
description:
|
|
'Gets the Team Invitation with the given ID, or null if not exists',
|
|
})
|
|
@UseGuards(GqlAuthGuard, TeamInviteViewerGuard)
|
|
async teamInvitation(
|
|
@GqlUser() user: AuthUser,
|
|
@Args({
|
|
name: 'inviteID',
|
|
description: 'ID of the Team Invitation to lookup',
|
|
type: () => ID,
|
|
})
|
|
inviteID: string,
|
|
): Promise<TeamInvitation> {
|
|
const teamInvitation = await this.teamInvitationService.getInvitation(
|
|
inviteID,
|
|
);
|
|
if (O.isNone(teamInvitation)) throwErr(TEAM_INVITE_NO_INVITE_FOUND);
|
|
return teamInvitation.value;
|
|
}
|
|
|
|
@Mutation(() => TeamInvitation, {
|
|
description: 'Creates a Team Invitation',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
async createTeamInvitation(
|
|
@GqlUser() user: AuthUser,
|
|
@Args() args: CreateTeamInvitationArgs,
|
|
): Promise<TeamInvitation> {
|
|
const teamInvitation = await this.teamInvitationService.createInvitation(
|
|
user,
|
|
args.teamID,
|
|
args.inviteeEmail,
|
|
args.inviteeRole,
|
|
);
|
|
|
|
if (E.isLeft(teamInvitation)) throwErr(teamInvitation.left);
|
|
return teamInvitation.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Revokes an invitation and deletes it',
|
|
})
|
|
@UseGuards(GqlAuthGuard, TeamInviteTeamOwnerGuard)
|
|
@RequiresTeamRole(TeamMemberRole.OWNER)
|
|
async revokeTeamInvitation(
|
|
@Args({
|
|
name: 'inviteID',
|
|
type: () => ID,
|
|
description: 'ID of the invite to revoke',
|
|
})
|
|
inviteID: string,
|
|
): Promise<true> {
|
|
const isRevoked = await this.teamInvitationService.revokeInvitation(
|
|
inviteID,
|
|
);
|
|
if (E.isLeft(isRevoked)) throwErr(isRevoked.left);
|
|
return true;
|
|
}
|
|
|
|
@Mutation(() => TeamMember, {
|
|
description: 'Accept an Invitation',
|
|
})
|
|
@UseGuards(GqlAuthGuard, TeamInviteeGuard)
|
|
async acceptTeamInvitation(
|
|
@GqlUser() user: AuthUser,
|
|
@Args({
|
|
name: 'inviteID',
|
|
type: () => ID,
|
|
description: 'ID of the Invite to accept',
|
|
})
|
|
inviteID: string,
|
|
): Promise<TeamMember> {
|
|
const teamMember = await this.teamInvitationService.acceptInvitation(
|
|
inviteID,
|
|
user,
|
|
);
|
|
if (E.isLeft(teamMember)) throwErr(teamMember.left);
|
|
return teamMember.right;
|
|
}
|
|
|
|
// Subscriptions
|
|
@Subscription(() => TeamInvitation, {
|
|
description: 'Listens to when a Team Invitation is added',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
teamInvitationAdded(
|
|
@Args({
|
|
name: 'teamID',
|
|
type: () => ID,
|
|
description: 'ID of the Team to listen to',
|
|
})
|
|
teamID: string,
|
|
): AsyncIterator<TeamInvitation> {
|
|
return this.pubsub.asyncIterator(`team/${teamID}/invite_added`);
|
|
}
|
|
|
|
@Subscription(() => ID, {
|
|
description: 'Listens to when a Team Invitation is removed',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
teamInvitationRemoved(
|
|
@Args({
|
|
name: 'teamID',
|
|
type: () => ID,
|
|
description: 'ID of the Team to listen to',
|
|
})
|
|
teamID: string,
|
|
): AsyncIterator<string> {
|
|
return this.pubsub.asyncIterator(`team/${teamID}/invite_removed`);
|
|
}
|
|
}
|