Refactor backend and add exception handlers (#189)
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
12
server/src/api/resolvers/guards/create-one.guard.ts
Normal file
12
server/src/api/resolvers/guards/create-one.guard.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
12
server/src/api/resolvers/guards/delete-many.guard.ts
Normal file
12
server/src/api/resolvers/guards/delete-many.guard.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
50
server/src/api/resolvers/guards/update-one.guard.ts
Normal file
50
server/src/api/resolvers/guards/update-one.guard.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(appController).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { HealthController } from './health.controller';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
@ -9,7 +8,7 @@ import { ConfigModule } from '@nestjs/config';
|
||||
import { ApiModule } from './api/api.module';
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot({}), TerminusModule, AuthModule, ApiModule],
|
||||
controllers: [AppController, HealthController],
|
||||
controllers: [HealthController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@ -5,10 +5,7 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { GoogleAuthController } from './google.auth.controller';
|
||||
import { GoogleStrategy } from './strategies/google.auth.strategy';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { UserRepository } from 'src/entities/user/user.repository';
|
||||
import { WorkspaceRepository } from 'src/entities/workspace/workspace.repository';
|
||||
import { RefreshTokenRepository } from 'src/entities/refresh-token/refresh-token.repository';
|
||||
import { TokenController } from './token.controller';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
const jwtModule = JwtModule.registerAsync({
|
||||
@ -26,16 +23,8 @@ const jwtModule = JwtModule.registerAsync({
|
||||
|
||||
@Module({
|
||||
imports: [jwtModule, ConfigModule.forRoot({})],
|
||||
controllers: [GoogleAuthController, AuthController],
|
||||
providers: [
|
||||
AuthService,
|
||||
JwtAuthStrategy,
|
||||
GoogleStrategy,
|
||||
UserRepository,
|
||||
WorkspaceRepository,
|
||||
RefreshTokenRepository,
|
||||
PrismaService,
|
||||
],
|
||||
controllers: [GoogleAuthController, TokenController],
|
||||
providers: [AuthService, JwtAuthStrategy, GoogleStrategy, PrismaService],
|
||||
exports: [jwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@ -26,7 +26,7 @@ export class GoogleAuthController {
|
||||
@Get('redirect')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
async googleAuthRedirect(@Req() req: GoogleRequest, @Res() res: Response) {
|
||||
const user = await this.authService.upsertUser(req.user);
|
||||
const user = await this.authService.createUser(req.user);
|
||||
|
||||
if (!user) {
|
||||
throw new HttpException(
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { Request } from 'express';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
type OperationEntity = {
|
||||
operation?: string;
|
||||
entity?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class CheckWorkspaceOwnership implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
|
||||
const { operation, entity } = this.fetchOperationAndEntity(request);
|
||||
const variables = request.body.variables;
|
||||
const workspace = await request.workspace;
|
||||
|
||||
if (!entity || !operation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operation === 'updateOne') {
|
||||
const object = await this.prismaService[entity].findUniqueOrThrow({
|
||||
where: { id: variables.id },
|
||||
});
|
||||
|
||||
if (!object) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Record not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
if (object.workspaceId !== workspace.id) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Record not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operation === 'deleteMany') {
|
||||
// TODO: write this logic
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operation === 'findMany') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operation === 'createOne') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private fetchOperationAndEntity(request: Request): OperationEntity {
|
||||
if (!request.body.operationName) {
|
||||
return { operation: undefined, entity: undefined };
|
||||
}
|
||||
|
||||
const regex =
|
||||
/(updateOne|deleteMany|createOne|findMany)(Person|Company|User)/i;
|
||||
const match = request.body.query.match(regex);
|
||||
if (match) {
|
||||
return {
|
||||
operation: match[1],
|
||||
entity: match[2].toLowerCase(),
|
||||
};
|
||||
}
|
||||
return { operation: undefined, entity: undefined };
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,9 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { JwtPayload } from '../strategies/jwt.auth.strategy';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { UserRepository } from 'src/entities/user/user.repository';
|
||||
import { WorkspaceRepository } from 'src/entities/workspace/workspace.repository';
|
||||
import { RefreshTokenRepository } from 'src/entities/refresh-token/refresh-token.repository';
|
||||
import { v4 } from 'uuid';
|
||||
import { RefreshToken, User } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
export type UserPayload = {
|
||||
firstName: string;
|
||||
@ -19,12 +17,10 @@ export class AuthService {
|
||||
constructor(
|
||||
private jwtService: JwtService,
|
||||
private configService: ConfigService,
|
||||
private userRepository: UserRepository,
|
||||
private workspaceRepository: WorkspaceRepository,
|
||||
private refreshTokenRepository: RefreshTokenRepository,
|
||||
private prismaService: PrismaService,
|
||||
) {}
|
||||
|
||||
async upsertUser(rawUser: UserPayload) {
|
||||
async createUser(rawUser: UserPayload) {
|
||||
if (!rawUser.email) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Email is missing' },
|
||||
@ -48,7 +44,7 @@ export class AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = await this.workspaceRepository.findUnique({
|
||||
const workspace = await this.prismaService.workspace.findUnique({
|
||||
where: { domainName: emailDomain },
|
||||
});
|
||||
|
||||
@ -59,50 +55,66 @@ export class AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
const user = await this.userRepository.upsertUser({
|
||||
data: {
|
||||
id: v4(),
|
||||
const user = await this.prismaService.user.upsert({
|
||||
where: {
|
||||
email: rawUser.email,
|
||||
},
|
||||
create: {
|
||||
id: v4(),
|
||||
displayName: rawUser.firstName + ' ' + rawUser.lastName,
|
||||
email: rawUser.email,
|
||||
locale: 'en',
|
||||
},
|
||||
workspaceId: workspace.id,
|
||||
update: {},
|
||||
});
|
||||
|
||||
await this.userRepository.upsertWorkspaceMember({
|
||||
data: {
|
||||
await this.prismaService.workspaceMember.upsert({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
create: {
|
||||
id: v4(),
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async generateAccessToken(refreshToken: string): Promise<string | undefined> {
|
||||
const refreshTokenObject = await this.refreshTokenRepository.findFirst({
|
||||
const refreshTokenObject = await this.prismaService.refreshToken.findFirst({
|
||||
where: { refreshToken: refreshToken },
|
||||
});
|
||||
|
||||
if (!refreshTokenObject) {
|
||||
return;
|
||||
throw new HttpException(
|
||||
{ reason: 'Invalid Refresh token' },
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const user = await this.userRepository.findUnique({
|
||||
const user = await this.prismaService.user.findUnique({
|
||||
where: { id: refreshTokenObject.userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
throw new HttpException(
|
||||
{ reason: 'Refresh token is not associated to a valid user' },
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = await this.workspaceRepository.findFirst({
|
||||
const workspace = await this.prismaService.workspace.findFirst({
|
||||
where: { workspaceMember: { some: { userId: user.id } } },
|
||||
});
|
||||
|
||||
if (!workspace) {
|
||||
return;
|
||||
throw new HttpException(
|
||||
{ reason: 'Refresh token is not associated to a valid workspace' },
|
||||
HttpStatus.FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
const payload: JwtPayload = {
|
||||
@ -113,12 +125,16 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async registerRefreshToken(user: User): Promise<RefreshToken> {
|
||||
const refreshToken = await this.refreshTokenRepository.upsertRefreshToken({
|
||||
data: {
|
||||
const refreshToken = await this.prismaService.refreshToken.upsert({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
create: {
|
||||
id: v4(),
|
||||
userId: user.id,
|
||||
refreshToken: v4(),
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
|
||||
return refreshToken;
|
||||
|
||||
@ -3,7 +3,7 @@ import { Request, Response } from 'express';
|
||||
import { AuthService } from './services/auth.service';
|
||||
|
||||
@Controller('auth/token')
|
||||
export class AuthController {
|
||||
export class TokenController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@Post()
|
||||
@ -12,35 +12,78 @@ generator nestgraphql {
|
||||
provider = "node node_modules/prisma-nestjs-graphql"
|
||||
output = "../../src/api/@generated"
|
||||
|
||||
decorate_1_type = "*CommentThreadTargetCreateNestedManyWithoutCommentThreadInput"
|
||||
decorate_1_field = "!(createMany)"
|
||||
decorate_1_name = "HideField"
|
||||
decorate_1_from = "@nestjs/graphql"
|
||||
decorate_1_arguments = "[]"
|
||||
// CommentThread create: Only Allow targets createMany and comments createMany
|
||||
decorate_createCommentThreadTargets_type = "*CommentThreadTargetCreateNestedManyWithoutCommentThreadInput"
|
||||
decorate_createCommentThreadTargets_field = "!(createMany)"
|
||||
decorate_createCommentThreadTargets_name = "HideField"
|
||||
decorate_createCommentThreadTargets_from = "@nestjs/graphql"
|
||||
decorate_createCommentThreadTargets_arguments = "[]"
|
||||
|
||||
decorate_2_type = "*CommentCreateNestedManyWithoutCommentThreadInput"
|
||||
decorate_2_field = "!(createMany)"
|
||||
decorate_2_name = "HideField"
|
||||
decorate_2_from = "@nestjs/graphql"
|
||||
decorate_2_arguments = "[]"
|
||||
decorate_createCommentThreadComments_type = "*CommentCreateNestedManyWithoutCommentThreadInput"
|
||||
decorate_createCommentThreadComments_field = "!(createMany)"
|
||||
decorate_createCommentThreadComments_name = "HideField"
|
||||
decorate_createCommentThreadComments_from = "@nestjs/graphql"
|
||||
decorate_createCommentThreadComments_arguments = "[]"
|
||||
|
||||
decorate_3_type = "*UserCreateNestedOneWithoutCommentsInput"
|
||||
decorate_3_field = "!(connect)"
|
||||
decorate_3_name = "HideField"
|
||||
decorate_3_from = "@nestjs/graphql"
|
||||
decorate_3_arguments = "[]"
|
||||
// Comment create: Only Allow author connect and commentThread connect
|
||||
decorate_createCommentUser_type = "*UserCreateNestedOneWithoutCommentsInput"
|
||||
decorate_createCommentUser_field = "!(connect)"
|
||||
decorate_createCommentUser_name = "HideField"
|
||||
decorate_createCommentUser_from = "@nestjs/graphql"
|
||||
decorate_createCommentUser_arguments = "[]"
|
||||
|
||||
decorate_4_type = "*CommentThreadCreateNestedOneWithoutCommentsInput"
|
||||
decorate_4_field = "!(connect)"
|
||||
decorate_4_name = "HideField"
|
||||
decorate_4_from = "@nestjs/graphql"
|
||||
decorate_4_arguments = "[]"
|
||||
decorate_createCommentCommentThread_type = "*CommentThreadCreateNestedOneWithoutCommentsInput"
|
||||
decorate_createCommentCommentThread_field = "!(connect)"
|
||||
decorate_createCommentCommentThread_name = "HideField"
|
||||
decorate_createCommentCommentThread_from = "@nestjs/graphql"
|
||||
decorate_createCommentCommentThread_arguments = "[]"
|
||||
|
||||
decorate_5_type = "!(*Aggregate*|*GroupBy*|*OrderBy*)"
|
||||
decorate_5_field = "_count"
|
||||
decorate_5_name = "HideField"
|
||||
decorate_5_from = "@nestjs/graphql"
|
||||
decorate_5_arguments = "[]"
|
||||
// Person create: Only Allow company connect
|
||||
decorate_createPersonCompany_type = "*CompanyCreateNestedOneWithoutPeopleInput"
|
||||
decorate_createPersonCompany_field = "!(connect)"
|
||||
decorate_createPersonCompany_name = "HideField"
|
||||
decorate_createPersonCompany_from = "@nestjs/graphql"
|
||||
decorate_createPersonCompany_arguments = "[]"
|
||||
|
||||
// Person update: Only Allow company connect
|
||||
decorate_updatePersonCompany_type = "*CompanyUpdateOneWithoutPeopleNestedInput"
|
||||
decorate_updatePersonCompany_field = "!(connect)"
|
||||
decorate_updatePersonCompany_name = "HideField"
|
||||
decorate_updatePersonCompany_from = "@nestjs/graphql"
|
||||
decorate_updatePersonCompany_arguments = "[]"
|
||||
|
||||
// Company create: Only Allow people and accountOwner connect
|
||||
decorate_createCompanyUser_type = "*UserCreateNestedOneWithoutCompaniesInput"
|
||||
decorate_createCompanyUser_field = "!(connect)"
|
||||
decorate_createCompanyUser_name = "HideField"
|
||||
decorate_createCompanyUser_from = "@nestjs/graphql"
|
||||
decorate_createCompanyUser_arguments = "[]"
|
||||
|
||||
decorate_createCompanyPerson_type = "*PersonCreateNestedManyWithoutCompanyInput"
|
||||
decorate_createCompanyPerson_field = "!(connect)"
|
||||
decorate_createCompanyPerson_name = "HideField"
|
||||
decorate_createCompanyPerson_from = "@nestjs/graphql"
|
||||
decorate_createCompanyPerson_arguments = "[]"
|
||||
|
||||
// Company update: Only Allow action on people and accountOwner
|
||||
decorate_updateCompanyUser_type = "*UserUpdateOneWithoutCompaniesNestedInput"
|
||||
decorate_updateCompanyUser_field = "!(connect)"
|
||||
decorate_updateCompanyUser_name = "HideField"
|
||||
decorate_updateCompanyUser_from = "@nestjs/graphql"
|
||||
decorate_updateCompanyUser_arguments = "[]"
|
||||
|
||||
decorate_updateCompanyPerson_type = "*PersonUpdateManyWithoutCompanyNestedInput"
|
||||
decorate_updateCompanyPerson_field = "!(connect)"
|
||||
decorate_updateCompanyPerson_name = "HideField"
|
||||
decorate_updateCompanyPerson_from = "@nestjs/graphql"
|
||||
decorate_updateCompanyPerson_arguments = "[]"
|
||||
|
||||
// Disable _count on all models except Aggregation use case
|
||||
decorate_count_type = "!(*Aggregate*|*GroupBy*|*OrderBy*)"
|
||||
decorate_count_field = "_count"
|
||||
decorate_count_name = "HideField"
|
||||
decorate_count_from = "@nestjs/graphql"
|
||||
decorate_count_arguments = "[]"
|
||||
}
|
||||
|
||||
model User {
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CompanyRepository } from './company.repository';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [CompanyRepository],
|
||||
exports: [CompanyRepository],
|
||||
})
|
||||
export class CompanyModule {}
|
||||
@ -1,25 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Company, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CompanyRepository {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findMany(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
cursor?: Prisma.CompanyWhereUniqueInput;
|
||||
where?: Prisma.CompanyWhereInput;
|
||||
orderBy?: Prisma.CompanyOrderByWithRelationInput;
|
||||
}): Promise<Company[]> {
|
||||
const { skip, take, cursor, where, orderBy } = params;
|
||||
return this.prisma.company.findMany({ skip, take, cursor, where, orderBy });
|
||||
}
|
||||
|
||||
async findOne(id: string | null) {
|
||||
if (id === null) return null;
|
||||
const company = await this.prisma.company.findUnique({ where: { id } });
|
||||
return company;
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PersonRepository } from './person.repository';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [PersonRepository],
|
||||
exports: [PersonRepository],
|
||||
})
|
||||
export class PersonModule {}
|
||||
@ -1,19 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Person, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class PersonRepository {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findMany(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
cursor?: Prisma.PersonWhereUniqueInput;
|
||||
where?: Prisma.PersonWhereInput;
|
||||
orderBy?: Prisma.PersonOrderByWithRelationInput;
|
||||
}): Promise<Person[]> {
|
||||
const { skip, take, cursor, where, orderBy } = params;
|
||||
return this.prisma.person.findMany({ skip, take, cursor, where, orderBy });
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, RefreshToken } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class RefreshTokenRepository {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async upsertRefreshToken(params: {
|
||||
data: Prisma.RefreshTokenUncheckedCreateInput;
|
||||
}): Promise<RefreshToken> {
|
||||
const { data } = params;
|
||||
|
||||
return await this.prisma.refreshToken.upsert({
|
||||
where: {
|
||||
id: data.id,
|
||||
},
|
||||
create: {
|
||||
id: data.id,
|
||||
userId: data.userId,
|
||||
refreshToken: data.refreshToken,
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
}
|
||||
|
||||
async findFirst(
|
||||
data: Prisma.RefreshTokenFindFirstArgs,
|
||||
): Promise<RefreshToken | null> {
|
||||
return await this.prisma.refreshToken.findFirst(data);
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserRepository } from './user.repository';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [UserRepository],
|
||||
exports: [UserRepository],
|
||||
})
|
||||
export class UserModule {}
|
||||
@ -1,67 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { User, Prisma, WorkspaceMember } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findMany(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
cursor?: Prisma.UserWhereUniqueInput;
|
||||
where?: Prisma.UserWhereInput;
|
||||
orderBy?: Prisma.UserOrderByWithRelationInput;
|
||||
}): Promise<User[]> {
|
||||
const { skip, take, cursor, where, orderBy } = params;
|
||||
return this.prisma.user.findMany({ skip, take, cursor, where, orderBy });
|
||||
}
|
||||
|
||||
async findUnique(params: {
|
||||
where?: Prisma.UserWhereInput;
|
||||
}): Promise<User | null> {
|
||||
const { where } = params;
|
||||
|
||||
return this.prisma.user.findFirst({
|
||||
where,
|
||||
});
|
||||
}
|
||||
|
||||
async upsertUser(params: {
|
||||
data: Prisma.UserCreateInput;
|
||||
workspaceId: string;
|
||||
}): Promise<User> {
|
||||
const { data } = params;
|
||||
|
||||
return await this.prisma.user.upsert({
|
||||
where: {
|
||||
email: data.email,
|
||||
},
|
||||
create: {
|
||||
id: data.id,
|
||||
displayName: data.displayName,
|
||||
email: data.email,
|
||||
locale: data.locale,
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
}
|
||||
|
||||
async upsertWorkspaceMember(params: {
|
||||
data: Prisma.WorkspaceMemberUncheckedCreateInput;
|
||||
}): Promise<WorkspaceMember> {
|
||||
const { data } = params;
|
||||
|
||||
return await this.prisma.workspaceMember.upsert({
|
||||
where: {
|
||||
userId: data.userId,
|
||||
},
|
||||
create: {
|
||||
id: data.id,
|
||||
userId: data.userId,
|
||||
workspaceId: data.workspaceId,
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { WorkspaceRepository } from './workspace.repository';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [WorkspaceRepository],
|
||||
exports: [WorkspaceRepository],
|
||||
})
|
||||
export class WorkspaceModule {}
|
||||
@ -1,37 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Workspace, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceRepository {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findMany(params: {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
cursor?: Prisma.WorkspaceWhereUniqueInput;
|
||||
where?: Prisma.WorkspaceWhereInput;
|
||||
orderBy?: Prisma.WorkspaceOrderByWithRelationInput;
|
||||
}): Promise<Workspace[]> {
|
||||
const { skip, take, cursor, where, orderBy } = params;
|
||||
return this.prisma.workspace.findMany({
|
||||
skip,
|
||||
take,
|
||||
cursor,
|
||||
where,
|
||||
orderBy,
|
||||
});
|
||||
}
|
||||
|
||||
async findUnique(
|
||||
params: Prisma.WorkspaceFindUniqueArgs,
|
||||
): Promise<Workspace | null> {
|
||||
return await this.prisma.workspace.findUnique(params);
|
||||
}
|
||||
|
||||
async findFirst(
|
||||
params: Prisma.WorkspaceFindFirstArgs,
|
||||
): Promise<Workspace | null> {
|
||||
return await this.prisma.workspace.findFirst(params);
|
||||
}
|
||||
}
|
||||
22
server/src/health.controller.spec.ts
Normal file
22
server/src/health.controller.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HealthController } from './health.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
|
||||
describe('HealthController', () => {
|
||||
let healthController: HealthController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [HealthController],
|
||||
imports: [TerminusModule],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
healthController = app.get<HealthController>(HealthController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(healthController).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -5,4 +5,5 @@ async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, { cors: true });
|
||||
await app.listen(3000);
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user