Refactor backend and add exception handlers (#189)

This commit is contained in:
Charles Bochet
2023-06-04 00:21:36 +02:00
committed by GitHub
parent a2fe159c2c
commit bbc80cd543
35 changed files with 459 additions and 899 deletions
@@ -1,18 +1,17 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { CompanyCreateWithoutPeopleInput } from './company-create-without-people.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { CompanyCreateOrConnectWithoutPeopleInput } from './company-create-or-connect-without-people.input';
import { CompanyWhereUniqueInput } from './company-where-unique.input';
import { Type } from 'class-transformer';
@InputType()
export class CompanyCreateNestedOneWithoutPeopleInput {
@Field(() => CompanyCreateWithoutPeopleInput, { nullable: true })
@Type(() => CompanyCreateWithoutPeopleInput)
@HideField()
create?: CompanyCreateWithoutPeopleInput;
@Field(() => CompanyCreateOrConnectWithoutPeopleInput, { nullable: true })
@Type(() => CompanyCreateOrConnectWithoutPeopleInput)
@HideField()
connectOrCreate?: CompanyCreateOrConnectWithoutPeopleInput;
@Field(() => CompanyWhereUniqueInput, { nullable: true })
@@ -1,37 +1,34 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { CompanyCreateWithoutPeopleInput } from './company-create-without-people.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { CompanyCreateOrConnectWithoutPeopleInput } from './company-create-or-connect-without-people.input';
import { CompanyUpsertWithoutPeopleInput } from './company-upsert-without-people.input';
import { CompanyWhereUniqueInput } from './company-where-unique.input';
import { Type } from 'class-transformer';
import { CompanyUpdateWithoutPeopleInput } from './company-update-without-people.input';
@InputType()
export class CompanyUpdateOneWithoutPeopleNestedInput {
@Field(() => CompanyCreateWithoutPeopleInput, { nullable: true })
@Type(() => CompanyCreateWithoutPeopleInput)
@HideField()
create?: CompanyCreateWithoutPeopleInput;
@Field(() => CompanyCreateOrConnectWithoutPeopleInput, { nullable: true })
@Type(() => CompanyCreateOrConnectWithoutPeopleInput)
@HideField()
connectOrCreate?: CompanyCreateOrConnectWithoutPeopleInput;
@Field(() => CompanyUpsertWithoutPeopleInput, { nullable: true })
@Type(() => CompanyUpsertWithoutPeopleInput)
@HideField()
upsert?: CompanyUpsertWithoutPeopleInput;
@Field(() => Boolean, { nullable: true })
@HideField()
disconnect?: boolean;
@Field(() => Boolean, { nullable: true })
@HideField()
delete?: boolean;
@Field(() => CompanyWhereUniqueInput, { nullable: true })
@Type(() => CompanyWhereUniqueInput)
connect?: CompanyWhereUniqueInput;
@Field(() => CompanyUpdateWithoutPeopleInput, { nullable: true })
@Type(() => CompanyUpdateWithoutPeopleInput)
@HideField()
update?: CompanyUpdateWithoutPeopleInput;
}
@@ -1,23 +1,21 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { PersonCreateWithoutCompanyInput } from './person-create-without-company.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { PersonCreateOrConnectWithoutCompanyInput } from './person-create-or-connect-without-company.input';
import { PersonCreateManyCompanyInputEnvelope } from './person-create-many-company-input-envelope.input';
import { PersonWhereUniqueInput } from './person-where-unique.input';
import { Type } from 'class-transformer';
@InputType()
export class PersonCreateNestedManyWithoutCompanyInput {
@Field(() => [PersonCreateWithoutCompanyInput], { nullable: true })
@Type(() => PersonCreateWithoutCompanyInput)
@HideField()
create?: Array<PersonCreateWithoutCompanyInput>;
@Field(() => [PersonCreateOrConnectWithoutCompanyInput], { nullable: true })
@Type(() => PersonCreateOrConnectWithoutCompanyInput)
@HideField()
connectOrCreate?: Array<PersonCreateOrConnectWithoutCompanyInput>;
@Field(() => PersonCreateManyCompanyInputEnvelope, { nullable: true })
@Type(() => PersonCreateManyCompanyInputEnvelope)
@HideField()
createMany?: PersonCreateManyCompanyInputEnvelope;
@Field(() => [PersonWhereUniqueInput], { nullable: true })
@@ -1,64 +1,49 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { PersonCreateWithoutCompanyInput } from './person-create-without-company.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { PersonCreateOrConnectWithoutCompanyInput } from './person-create-or-connect-without-company.input';
import { PersonUpsertWithWhereUniqueWithoutCompanyInput } from './person-upsert-with-where-unique-without-company.input';
import { PersonCreateManyCompanyInputEnvelope } from './person-create-many-company-input-envelope.input';
import { PersonWhereUniqueInput } from './person-where-unique.input';
import { Type } from 'class-transformer';
import { PersonUpdateWithWhereUniqueWithoutCompanyInput } from './person-update-with-where-unique-without-company.input';
import { PersonUpdateManyWithWhereWithoutCompanyInput } from './person-update-many-with-where-without-company.input';
import { PersonScalarWhereInput } from './person-scalar-where.input';
@InputType()
export class PersonUpdateManyWithoutCompanyNestedInput {
@Field(() => [PersonCreateWithoutCompanyInput], { nullable: true })
@Type(() => PersonCreateWithoutCompanyInput)
@HideField()
create?: Array<PersonCreateWithoutCompanyInput>;
@Field(() => [PersonCreateOrConnectWithoutCompanyInput], { nullable: true })
@Type(() => PersonCreateOrConnectWithoutCompanyInput)
@HideField()
connectOrCreate?: Array<PersonCreateOrConnectWithoutCompanyInput>;
@Field(() => [PersonUpsertWithWhereUniqueWithoutCompanyInput], {
nullable: true,
})
@Type(() => PersonUpsertWithWhereUniqueWithoutCompanyInput)
@HideField()
upsert?: Array<PersonUpsertWithWhereUniqueWithoutCompanyInput>;
@Field(() => PersonCreateManyCompanyInputEnvelope, { nullable: true })
@Type(() => PersonCreateManyCompanyInputEnvelope)
@HideField()
createMany?: PersonCreateManyCompanyInputEnvelope;
@Field(() => [PersonWhereUniqueInput], { nullable: true })
@Type(() => PersonWhereUniqueInput)
@HideField()
set?: Array<PersonWhereUniqueInput>;
@Field(() => [PersonWhereUniqueInput], { nullable: true })
@Type(() => PersonWhereUniqueInput)
@HideField()
disconnect?: Array<PersonWhereUniqueInput>;
@Field(() => [PersonWhereUniqueInput], { nullable: true })
@Type(() => PersonWhereUniqueInput)
@HideField()
delete?: Array<PersonWhereUniqueInput>;
@Field(() => [PersonWhereUniqueInput], { nullable: true })
@Type(() => PersonWhereUniqueInput)
connect?: Array<PersonWhereUniqueInput>;
@Field(() => [PersonUpdateWithWhereUniqueWithoutCompanyInput], {
nullable: true,
})
@Type(() => PersonUpdateWithWhereUniqueWithoutCompanyInput)
@HideField()
update?: Array<PersonUpdateWithWhereUniqueWithoutCompanyInput>;
@Field(() => [PersonUpdateManyWithWhereWithoutCompanyInput], {
nullable: true,
})
@Type(() => PersonUpdateManyWithWhereWithoutCompanyInput)
@HideField()
updateMany?: Array<PersonUpdateManyWithWhereWithoutCompanyInput>;
@Field(() => [PersonScalarWhereInput], { nullable: true })
@Type(() => PersonScalarWhereInput)
@HideField()
deleteMany?: Array<PersonScalarWhereInput>;
}
@@ -1,18 +1,17 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { UserCreateWithoutCompaniesInput } from './user-create-without-companies.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { UserCreateOrConnectWithoutCompaniesInput } from './user-create-or-connect-without-companies.input';
import { UserWhereUniqueInput } from './user-where-unique.input';
import { Type } from 'class-transformer';
@InputType()
export class UserCreateNestedOneWithoutCompaniesInput {
@Field(() => UserCreateWithoutCompaniesInput, { nullable: true })
@Type(() => UserCreateWithoutCompaniesInput)
@HideField()
create?: UserCreateWithoutCompaniesInput;
@Field(() => UserCreateOrConnectWithoutCompaniesInput, { nullable: true })
@Type(() => UserCreateOrConnectWithoutCompaniesInput)
@HideField()
connectOrCreate?: UserCreateOrConnectWithoutCompaniesInput;
@Field(() => UserWhereUniqueInput, { nullable: true })
@@ -1,37 +1,34 @@
import { Field } from '@nestjs/graphql';
import { InputType } from '@nestjs/graphql';
import { UserCreateWithoutCompaniesInput } from './user-create-without-companies.input';
import { Type } from 'class-transformer';
import { HideField } from '@nestjs/graphql';
import { UserCreateOrConnectWithoutCompaniesInput } from './user-create-or-connect-without-companies.input';
import { UserUpsertWithoutCompaniesInput } from './user-upsert-without-companies.input';
import { UserWhereUniqueInput } from './user-where-unique.input';
import { Type } from 'class-transformer';
import { UserUpdateWithoutCompaniesInput } from './user-update-without-companies.input';
@InputType()
export class UserUpdateOneWithoutCompaniesNestedInput {
@Field(() => UserCreateWithoutCompaniesInput, { nullable: true })
@Type(() => UserCreateWithoutCompaniesInput)
@HideField()
create?: UserCreateWithoutCompaniesInput;
@Field(() => UserCreateOrConnectWithoutCompaniesInput, { nullable: true })
@Type(() => UserCreateOrConnectWithoutCompaniesInput)
@HideField()
connectOrCreate?: UserCreateOrConnectWithoutCompaniesInput;
@Field(() => UserUpsertWithoutCompaniesInput, { nullable: true })
@Type(() => UserUpsertWithoutCompaniesInput)
@HideField()
upsert?: UserUpsertWithoutCompaniesInput;
@Field(() => Boolean, { nullable: true })
@HideField()
disconnect?: boolean;
@Field(() => Boolean, { nullable: true })
@HideField()
delete?: boolean;
@Field(() => UserWhereUniqueInput, { nullable: true })
@Type(() => UserWhereUniqueInput)
connect?: UserWhereUniqueInput;
@Field(() => UserUpdateWithoutCompaniesInput, { nullable: true })
@Type(() => UserUpdateWithoutCompaniesInput)
@HideField()
update?: UserUpdateWithoutCompaniesInput;
}
+5
View File
@@ -22,6 +22,7 @@ import { CompanyRelationsResolver } from './resolvers/relations/company-relation
import { CommentThreadRelationsResolver } from './resolvers/relations/comment-thread-relations.resolver';
import { PipelineRelationsResolver } from './resolvers/relations/pipeline-relations.resolver';
import { PipelineStageRelationsResolver } from './resolvers/relations/pipeline-stage-relations.resolver';
import { GraphQLError } from 'graphql';
@Module({
imports: [
@@ -29,6 +30,10 @@ import { PipelineStageRelationsResolver } from './resolvers/relations/pipeline-s
context: ({ req }) => ({ req }),
driver: ApolloDriver,
autoSchemaFile: true,
formatError: (error: GraphQLError) => {
error.extensions.stacktrace = undefined;
return error;
},
}),
AuthModule,
PrismaModule,
+7 -2
View File
@@ -11,10 +11,12 @@ import { AffectedRows } from '../@generated/prisma/affected-rows.output';
import { DeleteManyCompanyArgs } from '../@generated/company/delete-many-company.args';
import { Workspace } from '@prisma/client';
import { ArgsService } from './services/args.service';
import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard';
import { Prisma } from '@prisma/client';
import { UpdateOneGuard } from './guards/update-one.guard';
import { DeleteManyGuard } from './guards/delete-many.guard';
import { CreateOneGuard } from './guards/create-one.guard';
@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership)
@UseGuards(JwtAuthGuard)
@Resolver(() => Company)
export class CompanyResolver {
constructor(
@@ -35,6 +37,7 @@ export class CompanyResolver {
return this.prismaService.company.findMany(preparedArgs);
}
@UseGuards(UpdateOneGuard)
@Mutation(() => Company, {
nullable: true,
})
@@ -50,6 +53,7 @@ export class CompanyResolver {
} satisfies UpdateOneCompanyArgs as Prisma.CompanyUpdateArgs);
}
@UseGuards(DeleteManyGuard)
@Mutation(() => AffectedRows, {
nullable: false,
})
@@ -61,6 +65,7 @@ export class CompanyResolver {
});
}
@UseGuards(CreateOneGuard)
@Mutation(() => Company, {
nullable: false,
})
@@ -0,0 +1,18 @@
import { Catch, HttpException } from '@nestjs/common';
import { GqlExceptionFilter } from '@nestjs/graphql';
import { Prisma } from '@prisma/client';
import { GraphQLError } from 'graphql';
@Catch()
export class ExceptionFilter implements GqlExceptionFilter {
catch(exception: HttpException) {
if (exception instanceof Prisma.PrismaClientValidationError) {
throw new GraphQLError('Invalid request', {
extensions: {
code: 'INVALID_REQUEST',
},
});
}
return exception;
}
}
@@ -0,0 +1,12 @@
import { CanActivate, Injectable } from '@nestjs/common';
import { PrismaService } from 'src/database/prisma.service';
@Injectable()
export class CreateOneGuard implements CanActivate {
constructor(private prismaService: PrismaService) {}
async canActivate(): Promise<boolean> {
// TODO
return true;
}
}
@@ -0,0 +1,12 @@
import { CanActivate, Injectable } from '@nestjs/common';
import { PrismaService } from 'src/database/prisma.service';
@Injectable()
export class DeleteManyGuard implements CanActivate {
constructor(private prismaService: PrismaService) {}
async canActivate(): Promise<boolean> {
// TODO
return true;
}
}
@@ -0,0 +1,50 @@
import {
CanActivate,
ExecutionContext,
HttpException,
HttpStatus,
Injectable,
} from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { PrismaService } from 'src/database/prisma.service';
@Injectable()
export class UpdateOneGuard implements CanActivate {
constructor(private prismaService: PrismaService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const gqlContext = GqlExecutionContext.create(context);
const request = gqlContext.getContext().req;
const entity = gqlContext.getArgByIndex(3).returnType?.name;
const args = gqlContext.getArgs();
console.log(args.data);
if (!entity || !args.where?.id) {
throw new HttpException(
{ reason: 'Invalid Request' },
HttpStatus.BAD_REQUEST,
);
}
const object = await this.prismaService[entity].findUniqueOrThrow({
where: { id: args.where.id },
});
if (!object) {
throw new HttpException(
{ reason: 'Record not found' },
HttpStatus.NOT_FOUND,
);
}
const workspace = await request.workspace;
if (object.workspaceId !== workspace.id) {
throw new HttpException(
{ reason: 'Record not found' },
HttpStatus.NOT_FOUND,
);
}
return true;
}
}
+7 -2
View File
@@ -11,10 +11,12 @@ import { DeleteManyPersonArgs } from '../@generated/person/delete-many-person.ar
import { Workspace } from '../@generated/workspace/workspace.model';
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
import { ArgsService } from './services/args.service';
import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard';
import { Prisma } from '@prisma/client';
import { UpdateOneGuard } from './guards/update-one.guard';
import { DeleteManyGuard } from './guards/delete-many.guard';
import { CreateOneGuard } from './guards/create-one.guard';
@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership)
@UseGuards(JwtAuthGuard)
@Resolver(() => Person)
export class PersonResolver {
constructor(
@@ -39,6 +41,7 @@ export class PersonResolver {
});
}
@UseGuards(UpdateOneGuard)
@Mutation(() => Person, {
nullable: true,
})
@@ -54,6 +57,7 @@ export class PersonResolver {
} satisfies UpdateOnePersonArgs as Prisma.PersonUpdateArgs);
}
@UseGuards(DeleteManyGuard)
@Mutation(() => AffectedRows, {
nullable: false,
})
@@ -65,6 +69,7 @@ export class PersonResolver {
});
}
@UseGuards(CreateOneGuard)
@Mutation(() => Person, {
nullable: false,
})
+6 -9
View File
@@ -1,23 +1,20 @@
import { Resolver, Query, Args } from '@nestjs/graphql';
import { PrismaService } from 'src/database/prisma.service';
import { UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
import { UseFilters, UseGuards } from '@nestjs/common';
import { User } from '../@generated/user/user.model';
import { FindManyUserArgs } from '../@generated/user/find-many-user.args';
import { Workspace } from '@prisma/client';
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
import { ArgsService } from './services/args.service';
import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard';
import { ExceptionFilter } from './exception-filters/exception.filter';
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership)
@UseGuards(JwtAuthGuard)
@Resolver(() => User)
export class UserResolver {
constructor(
private readonly prismaService: PrismaService,
private readonly argsService: ArgsService,
) {}
constructor(private readonly prismaService: PrismaService) {}
@UseFilters(ExceptionFilter)
@Query(() => [User], {
nullable: false,
})