Add comments to Prisma Schema and GraphQL server (#162)
* Lowercase all relations in prisma/graphql schema * Add Comments data model and graphql schema * Make comments availalble on the api through resolvers and guard them * Update front graphql schema * Fix PR
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { Resolver, Args, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
||||
import { CommentThread } from '../@generated/comment-thread/comment-thread.model';
|
||||
import { CreateOneCommentThreadArgs } from '../@generated/comment-thread/create-one-comment-thread.args';
|
||||
import { CreateOneCommentThreadGuard } from './guards/create-one-comment-thread.guard';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => CommentThread)
|
||||
export class CommentThreadResolver {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
@UseGuards(CreateOneCommentThreadGuard)
|
||||
@Mutation(() => CommentThread, {
|
||||
nullable: false,
|
||||
})
|
||||
async createOneCommentThread(
|
||||
@Args() args: CreateOneCommentThreadArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<CommentThread> {
|
||||
const newCommentData = args.data.comments?.createMany?.data
|
||||
? args.data.comments?.createMany?.data?.map((comment) => ({
|
||||
...comment,
|
||||
...{ workspaceId: workspace.id },
|
||||
}))
|
||||
: [];
|
||||
return this.prismaService.commentThread.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ comments: { createMany: { data: newCommentData } } },
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Resolver, Args, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
||||
import { CreateOneCommentArgs } from '../@generated/comment/create-one-comment.args';
|
||||
import { Comment } from '../@generated/comment/comment.model';
|
||||
import { CreateOneCommentGuard } from './guards/create-one-comment.guard';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Comment)
|
||||
export class CommentResolver {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
@UseGuards(CreateOneCommentGuard)
|
||||
@Mutation(() => Comment, {
|
||||
nullable: false,
|
||||
})
|
||||
async createOneComment(
|
||||
@Args() args: CreateOneCommentArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<Comment> {
|
||||
return this.prismaService.comment.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneCommentThreadGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const targets = args.data?.commentThreadTargets?.createMany?.data;
|
||||
const comments = args.data?.comments?.createMany?.data;
|
||||
const workspaceId = await request.workspace;
|
||||
|
||||
if (!targets || targets.length === 0) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing commentThreadTargets' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
await targets.map(async (target) => {
|
||||
if (!target.commentableId || !target.commentableType) {
|
||||
throw new HttpException(
|
||||
{
|
||||
reason:
|
||||
'Missing commentThreadTarget.commentableId or commentThreadTarget.commentableType',
|
||||
},
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
if (!['Person', 'Company'].includes(target.commentableType)) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Invalid commentThreadTarget.commentableType' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const targetEntity = await this.prismaService[
|
||||
target.commentableType
|
||||
].findUnique({
|
||||
where: { id: target.commentableId },
|
||||
});
|
||||
|
||||
if (targetEntity.workspaceId !== workspaceId) {
|
||||
throw new HttpException(
|
||||
{ reason: 'CommentThreadTarget not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (!comments) {
|
||||
return true;
|
||||
}
|
||||
|
||||
await comments.map(async (comment) => {
|
||||
if (!comment.authorId) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing comment.authorId' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const author = await this.prismaService.user.findUnique({
|
||||
where: { id: comment.authorId },
|
||||
});
|
||||
|
||||
if (!author) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Comment.authorId not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const userWorkspaceMember =
|
||||
await this.prismaService.workspaceMember.findFirst({
|
||||
where: { userId: author.id },
|
||||
});
|
||||
|
||||
if (
|
||||
!userWorkspaceMember ||
|
||||
userWorkspaceMember.workspaceId !== workspaceId
|
||||
) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Comment.authorId not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneCommentGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const authorId = args.data?.author?.connect?.id;
|
||||
const commentThreadId = args.data?.commentThread?.connect?.id;
|
||||
|
||||
if (!authorId || !commentThreadId) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing author or commentThread' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const author = await this.prismaService.user.findUnique({
|
||||
where: { id: authorId },
|
||||
});
|
||||
|
||||
const commentThread = await this.prismaService.commentThread.findUnique({
|
||||
where: { id: commentThreadId },
|
||||
});
|
||||
|
||||
if (!author || !commentThread) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const userWorkspaceMember =
|
||||
await this.prismaService.workspaceMember.findFirst({
|
||||
where: { userId: author.id },
|
||||
});
|
||||
|
||||
if (!userWorkspaceMember) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = await request.workspace;
|
||||
|
||||
if (
|
||||
userWorkspaceMember.workspaceId !== workspace.id ||
|
||||
commentThread.workspaceId !== workspace.id
|
||||
) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as TypeGraphQL from '@nestjs/graphql';
|
||||
import { CommentThread } from 'src/api/@generated/comment-thread/comment-thread.model';
|
||||
import { Comment } from 'src/api/@generated/comment/comment.model';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@TypeGraphQL.Resolver(() => CommentThread)
|
||||
export class CommentThreadRelationsResolver {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
@TypeGraphQL.ResolveField(() => [Comment], {
|
||||
nullable: false,
|
||||
})
|
||||
async comments(
|
||||
@TypeGraphQL.Root() commentThread: CommentThread,
|
||||
): Promise<Comment[]> {
|
||||
return this.prismaService.comment.findMany({
|
||||
where: {
|
||||
commentThreadId: commentThread.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as TypeGraphQL from '@nestjs/graphql';
|
||||
import { CommentThread } from 'src/api/@generated/comment-thread/comment-thread.model';
|
||||
import { Company } from 'src/api/@generated/company/company.model';
|
||||
import { User } from 'src/api/@generated/user/user.model';
|
||||
import { Workspace } from 'src/api/@generated/workspace/workspace.model';
|
||||
@@ -35,4 +36,22 @@ export class CompanyRelationsResolver {
|
||||
})
|
||||
.workspace({});
|
||||
}
|
||||
|
||||
@TypeGraphQL.ResolveField(() => [CommentThread], {
|
||||
nullable: false,
|
||||
})
|
||||
async commentThreads(
|
||||
@TypeGraphQL.Root() company: Company,
|
||||
): Promise<CommentThread[]> {
|
||||
return this.prismaService.commentThread.findMany({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
some: {
|
||||
commentableId: company.id,
|
||||
commentableType: 'Company',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as TypeGraphQL from '@nestjs/graphql';
|
||||
import { CommentThread } from 'src/api/@generated/comment-thread/comment-thread.model';
|
||||
import { Company } from 'src/api/@generated/company/company.model';
|
||||
import { Person } from 'src/api/@generated/person/person.model';
|
||||
import { Workspace } from 'src/api/@generated/workspace/workspace.model';
|
||||
@@ -33,4 +34,22 @@ export class PersonRelationsResolver {
|
||||
})
|
||||
.workspace({});
|
||||
}
|
||||
|
||||
@TypeGraphQL.ResolveField(() => [CommentThread], {
|
||||
nullable: false,
|
||||
})
|
||||
async commentThreads(
|
||||
@TypeGraphQL.Root() person: Person,
|
||||
): Promise<CommentThread[]> {
|
||||
return await this.prismaService.commentThread.findMany({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
some: {
|
||||
commentableId: person.id,
|
||||
commentableType: 'Person',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class UserRelationsResolver {
|
||||
@TypeGraphQL.ResolveField(() => WorkspaceMember, {
|
||||
nullable: true,
|
||||
})
|
||||
async WorkspaceMember(
|
||||
async workspaceMember(
|
||||
@TypeGraphQL.Parent() user: User,
|
||||
): Promise<WorkspaceMember | null> {
|
||||
return await this.prismaService.user
|
||||
@@ -24,7 +24,7 @@ export class UserRelationsResolver {
|
||||
id: user.id,
|
||||
},
|
||||
})
|
||||
.WorkspaceMember({});
|
||||
.workspaceMember({});
|
||||
}
|
||||
|
||||
@TypeGraphQL.ResolveField(() => [Company], {
|
||||
@@ -49,7 +49,7 @@ export class UserRelationsResolver {
|
||||
@TypeGraphQL.ResolveField(() => [RefreshToken], {
|
||||
nullable: false,
|
||||
})
|
||||
async RefreshTokens(
|
||||
async refreshTokens(
|
||||
@TypeGraphQL.Parent() user: User,
|
||||
@TypeGraphQL.Info() info: GraphQLResolveInfo,
|
||||
@TypeGraphQL.Args() args: FindManyRefreshTokenArgs,
|
||||
@@ -60,7 +60,7 @@ export class UserRelationsResolver {
|
||||
id: user.id,
|
||||
},
|
||||
})
|
||||
.RefreshTokens({
|
||||
.refreshTokens({
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user