* feat: rate-limiting guard added and configured in app module * feat: rate-limit annotation added in controllers and resolvers (query, mutation, not subscription) * docs: added comments
357 lines
9.8 KiB
TypeScript
357 lines
9.8 KiB
TypeScript
import {
|
|
Resolver,
|
|
ResolveField,
|
|
Parent,
|
|
Args,
|
|
Query,
|
|
Mutation,
|
|
Subscription,
|
|
ID,
|
|
} from '@nestjs/graphql';
|
|
import { RequestReorderData, TeamRequest } from './team-request.model';
|
|
import {
|
|
CreateTeamRequestInput,
|
|
UpdateTeamRequestInput,
|
|
SearchTeamRequestArgs,
|
|
GetTeamRequestInCollectionArgs,
|
|
MoveTeamRequestArgs,
|
|
UpdateLookUpRequestOrderArgs,
|
|
} from './input-type.args';
|
|
import { Team, TeamMemberRole } from '../team/team.model';
|
|
import { TeamRequestService } from './team-request.service';
|
|
import { TeamCollection } from '../team-collection/team-collection.model';
|
|
import { UseGuards } from '@nestjs/common';
|
|
import { GqlAuthGuard } from '../guards/gql-auth.guard';
|
|
import { GqlRequestTeamMemberGuard } from './guards/gql-request-team-member.guard';
|
|
import { GqlCollectionTeamMemberGuard } from '../team-collection/guards/gql-collection-team-member.guard';
|
|
import { RequiresTeamRole } from '../team/decorators/requires-team-role.decorator';
|
|
import { GqlTeamMemberGuard } from '../team/guards/gql-team-member.guard';
|
|
import { PubSubService } from 'src/pubsub/pubsub.service';
|
|
import * as E from 'fp-ts/Either';
|
|
import * as O from 'fp-ts/Option';
|
|
import { throwErr } from 'src/utils';
|
|
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
|
import { SkipThrottle } from '@nestjs/throttler';
|
|
|
|
@UseGuards(GqlThrottlerGuard)
|
|
@Resolver(() => TeamRequest)
|
|
export class TeamRequestResolver {
|
|
constructor(
|
|
private readonly teamRequestService: TeamRequestService,
|
|
private readonly pubsub: PubSubService,
|
|
) {}
|
|
|
|
// Field resolvers
|
|
@ResolveField(() => Team, {
|
|
description: 'Team the request belongs to',
|
|
complexity: 3,
|
|
})
|
|
async team(@Parent() req: TeamRequest) {
|
|
const team = await this.teamRequestService.getTeamOfRequest(req);
|
|
if (E.isLeft(team)) throwErr(team.left);
|
|
return team.right;
|
|
}
|
|
|
|
@ResolveField(() => TeamCollection, {
|
|
description: 'Collection the request belongs to',
|
|
complexity: 3,
|
|
})
|
|
async collection(@Parent() req: TeamRequest) {
|
|
const teamCollection = await this.teamRequestService.getCollectionOfRequest(
|
|
req,
|
|
);
|
|
if (E.isLeft(teamCollection)) throwErr(teamCollection.left);
|
|
return teamCollection.right;
|
|
}
|
|
|
|
// Query
|
|
@Query(() => [TeamRequest], {
|
|
description: 'Search the team for a specific request with title',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
async searchForRequest(@Args() args: SearchTeamRequestArgs) {
|
|
return this.teamRequestService.searchRequest(
|
|
args.teamID,
|
|
args.searchTerm,
|
|
args.cursor,
|
|
args.take,
|
|
);
|
|
}
|
|
|
|
@Query(() => TeamRequest, {
|
|
description: 'Gives a request with the given ID or null (if not exists)',
|
|
nullable: true,
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlRequestTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
async request(
|
|
@Args({
|
|
name: 'requestID',
|
|
description: 'ID of the request',
|
|
type: () => ID,
|
|
})
|
|
requestID: string,
|
|
) {
|
|
const teamRequest = await this.teamRequestService.getRequest(requestID);
|
|
if (O.isNone(teamRequest)) return null;
|
|
return teamRequest.value;
|
|
}
|
|
|
|
@Query(() => [TeamRequest], {
|
|
description: 'Gives a paginated list of requests in the collection',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
TeamMemberRole.VIEWER,
|
|
)
|
|
async requestsInCollection(@Args() input: GetTeamRequestInCollectionArgs) {
|
|
return this.teamRequestService.getRequestsInCollection(
|
|
input.collectionID,
|
|
input.cursor,
|
|
input.take,
|
|
);
|
|
}
|
|
|
|
// Mutation
|
|
@Mutation(() => TeamRequest, {
|
|
description: 'Create a team request in the given collection.',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.EDITOR, TeamMemberRole.OWNER)
|
|
async createRequestInCollection(
|
|
@Args({
|
|
name: 'collectionID',
|
|
description: 'ID of the collection',
|
|
type: () => ID,
|
|
})
|
|
collectionID: string,
|
|
@Args({
|
|
name: 'data',
|
|
type: () => CreateTeamRequestInput,
|
|
description:
|
|
'The request data (stringified JSON of Hoppscotch request object)',
|
|
})
|
|
data: CreateTeamRequestInput,
|
|
) {
|
|
const teamRequest = await this.teamRequestService.createTeamRequest(
|
|
collectionID,
|
|
data.teamID,
|
|
data.title,
|
|
data.request,
|
|
);
|
|
if (E.isLeft(teamRequest)) throwErr(teamRequest.left);
|
|
return teamRequest.right;
|
|
}
|
|
|
|
@Mutation(() => TeamRequest, {
|
|
description: 'Update a request with the given ID',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlRequestTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.EDITOR, TeamMemberRole.OWNER)
|
|
async updateRequest(
|
|
@Args({
|
|
name: 'requestID',
|
|
description: 'ID of the request',
|
|
type: () => ID,
|
|
})
|
|
requestID: string,
|
|
@Args({
|
|
name: 'data',
|
|
type: () => UpdateTeamRequestInput,
|
|
description:
|
|
'The updated request data (stringified JSON of Hoppscotch request object)',
|
|
})
|
|
data: UpdateTeamRequestInput,
|
|
) {
|
|
const teamRequest = await this.teamRequestService.updateTeamRequest(
|
|
requestID,
|
|
data.title,
|
|
data.request,
|
|
);
|
|
if (E.isLeft(teamRequest)) throwErr(teamRequest.left);
|
|
return teamRequest.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Delete a request with the given ID',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlRequestTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.EDITOR, TeamMemberRole.OWNER)
|
|
async deleteRequest(
|
|
@Args({
|
|
name: 'requestID',
|
|
description: 'ID of the request',
|
|
type: () => ID,
|
|
})
|
|
requestID: string,
|
|
) {
|
|
const isDeleted = await this.teamRequestService.deleteTeamRequest(
|
|
requestID,
|
|
);
|
|
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
|
|
return isDeleted.right;
|
|
}
|
|
|
|
@Mutation(() => Boolean, {
|
|
description: 'Update the order of requests in the lookup table',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlRequestTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.EDITOR, TeamMemberRole.OWNER)
|
|
async updateLookUpRequestOrder(
|
|
@Args()
|
|
args: UpdateLookUpRequestOrderArgs,
|
|
) {
|
|
const teamRequest = await this.teamRequestService.moveRequest(
|
|
args.collectionID,
|
|
args.requestID,
|
|
args.collectionID,
|
|
args.nextRequestID,
|
|
'updateLookUpRequestOrder',
|
|
);
|
|
if (E.isLeft(teamRequest)) throwErr(teamRequest.left);
|
|
return true;
|
|
}
|
|
|
|
@Mutation(() => TeamRequest, {
|
|
description: 'Move a request to the given collection',
|
|
})
|
|
@UseGuards(GqlAuthGuard, GqlRequestTeamMemberGuard)
|
|
@RequiresTeamRole(TeamMemberRole.EDITOR, TeamMemberRole.OWNER)
|
|
async moveRequest(@Args() args: MoveTeamRequestArgs) {
|
|
const teamRequest = await this.teamRequestService.moveRequest(
|
|
args.srcCollID,
|
|
args.requestID,
|
|
args.destCollID,
|
|
args.nextRequestID,
|
|
'moveRequest',
|
|
);
|
|
if (E.isLeft(teamRequest)) throwErr(teamRequest.left);
|
|
return teamRequest.right;
|
|
}
|
|
|
|
// Subscriptions
|
|
@Subscription(() => TeamRequest, {
|
|
description: 'Emits when a new request is added to a team',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
teamRequestAdded(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
) {
|
|
return this.pubsub.asyncIterator(`team_req/${teamID}/req_created`);
|
|
}
|
|
|
|
@Subscription(() => TeamRequest, {
|
|
description: 'Emitted when a request has been updated',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
teamRequestUpdated(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
) {
|
|
return this.pubsub.asyncIterator(`team_req/${teamID}/req_updated`);
|
|
}
|
|
|
|
@Subscription(() => ID, {
|
|
description:
|
|
'Emitted when a request has been deleted. Only the id of the request is emitted.',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
teamRequestDeleted(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
) {
|
|
return this.pubsub.asyncIterator(`team_req/${teamID}/req_deleted`);
|
|
}
|
|
|
|
@Subscription(() => RequestReorderData, {
|
|
description:
|
|
'Emitted when a requests position has been changed in its collection',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
requestOrderUpdated(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
) {
|
|
return this.pubsub.asyncIterator(`team_req/${teamID}/req_order_updated`);
|
|
}
|
|
|
|
@Subscription(() => TeamRequest, {
|
|
description:
|
|
'Emitted when a request has been moved from one collection into another',
|
|
resolve: (value) => value,
|
|
})
|
|
@SkipThrottle()
|
|
@UseGuards(GqlAuthGuard, GqlTeamMemberGuard)
|
|
@RequiresTeamRole(
|
|
TeamMemberRole.VIEWER,
|
|
TeamMemberRole.EDITOR,
|
|
TeamMemberRole.OWNER,
|
|
)
|
|
requestMoved(
|
|
@Args({
|
|
name: 'teamID',
|
|
description: 'ID of the team to listen to',
|
|
type: () => ID,
|
|
})
|
|
teamID: string,
|
|
) {
|
|
return this.pubsub.asyncIterator(`team_req/${teamID}/req_moved`);
|
|
}
|
|
}
|