Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)
* Some cleaning * Fix seeds * Fix all sign in, sign up flow and apiKey optimistic rendering * Fix
This commit is contained in:
33
server/src/core/user/dtos/workspace-member.dto.ts
Normal file
33
server/src/core/user/dtos/workspace-member.dto.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
@ObjectType('UserWorkspaceMemberName')
|
||||
export class UserWorkspaceMemberName {
|
||||
@Field({ nullable: false })
|
||||
firstName: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
lastName: string;
|
||||
}
|
||||
|
||||
@ObjectType('UserWorkspaceMember')
|
||||
export class UserWorkspaceMember {
|
||||
@IDField(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field(() => UserWorkspaceMemberName)
|
||||
name: UserWorkspaceMemberName;
|
||||
|
||||
@Field({ nullable: false })
|
||||
colorScheme: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
avatarUrl: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
locale: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
allowImpersonation: boolean;
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@ -14,11 +13,7 @@ describe('UserService', () => {
|
||||
providers: [
|
||||
UserService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
{
|
||||
provide: WorkspaceService,
|
||||
provide: getRepositoryToken(User),
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
85
server/src/core/user/services/user.service.ts
Normal file
85
server/src/core/user/services/user.service.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { UserWorkspaceMember } from 'src/core/user/dtos/workspace-member.dto';
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
|
||||
export class UserService extends TypeOrmQueryService<User> {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
) {
|
||||
super(userRepository);
|
||||
}
|
||||
|
||||
async loadWorkspaceMember(user: User) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
user.defaultWorkspace.id,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.typeORMService.connectToDataSource(
|
||||
dataSourceMetadata,
|
||||
);
|
||||
|
||||
const workspaceMembers = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "userId" = '${user.id}'`,
|
||||
);
|
||||
|
||||
assert(workspaceMembers.length === 1, 'WorkspaceMember not found');
|
||||
|
||||
const userWorkspaceMember = new UserWorkspaceMember();
|
||||
|
||||
userWorkspaceMember.id = workspaceMembers[0].id;
|
||||
userWorkspaceMember.colorScheme = workspaceMembers[0].colorScheme;
|
||||
userWorkspaceMember.locale = workspaceMembers[0].locale;
|
||||
userWorkspaceMember.allowImpersonation =
|
||||
workspaceMembers[0].allowImpersonation;
|
||||
userWorkspaceMember.avatarUrl = workspaceMembers[0].avatarUrl;
|
||||
userWorkspaceMember.name = {
|
||||
firstName: workspaceMembers[0].nameFirstName,
|
||||
lastName: workspaceMembers[0].nameLastName,
|
||||
};
|
||||
|
||||
return userWorkspaceMember;
|
||||
}
|
||||
|
||||
async createWorkspaceMember(user: User, avatarUrl?: string) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
user.defaultWorkspace.id,
|
||||
);
|
||||
|
||||
const workspaceDataSource = await this.typeORMService.connectToDataSource(
|
||||
dataSourceMetadata,
|
||||
);
|
||||
|
||||
await workspaceDataSource?.query(
|
||||
`INSERT INTO ${dataSourceMetadata.schema}."workspaceMember"
|
||||
("nameFirstName", "nameLastName", "colorScheme", "userId", "allowImpersonation", "avatarUrl")
|
||||
VALUES ('${user.firstName}', '${user.lastName}', 'Light', '${
|
||||
user.id
|
||||
}', true, '${avatarUrl ?? ''}')`,
|
||||
);
|
||||
}
|
||||
|
||||
async deleteUser({
|
||||
workspaceId: _workspaceId,
|
||||
userId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
}) {
|
||||
const user = await this.userRepository.findBy({ id: userId });
|
||||
assert(user, 'User not found');
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
38
server/src/core/user/user.auto-resolver-opts.ts
Normal file
38
server/src/core/user/user.auto-resolver-opts.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {
|
||||
AutoResolverOpts,
|
||||
ReadResolverOpts,
|
||||
PagingStrategies,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
|
||||
export const userAutoResolverOpts: AutoResolverOpts<
|
||||
any,
|
||||
any,
|
||||
unknown,
|
||||
unknown,
|
||||
ReadResolverOpts<any>,
|
||||
PagingStrategies
|
||||
>[] = [
|
||||
{
|
||||
EntityClass: User,
|
||||
DTOClass: User,
|
||||
enableTotalCount: true,
|
||||
pagingStrategy: PagingStrategies.CURSOR,
|
||||
read: {
|
||||
many: { disabled: true },
|
||||
one: { disabled: true },
|
||||
},
|
||||
create: {
|
||||
many: { disabled: true },
|
||||
one: { disabled: true },
|
||||
},
|
||||
update: {
|
||||
many: { disabled: true },
|
||||
one: { disabled: true },
|
||||
},
|
||||
delete: { many: { disabled: true }, one: { disabled: true } },
|
||||
guards: [JwtAuthGuard],
|
||||
},
|
||||
];
|
||||
74
server/src/core/user/user.entity.ts
Normal file
74
server/src/core/user/user.entity.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { ID, Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
} from 'typeorm';
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { UserWorkspaceMember } from 'src/core/user/dtos/workspace-member.dto';
|
||||
|
||||
@Entity({ name: 'user', schema: 'core' })
|
||||
@ObjectType('User')
|
||||
export class User {
|
||||
@IDField(() => ID)
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: true })
|
||||
firstName: string;
|
||||
|
||||
@Field()
|
||||
@Column({ nullable: true })
|
||||
lastName: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
email: string;
|
||||
|
||||
@Field()
|
||||
@Column({ default: false })
|
||||
emailVerified: boolean;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ default: false })
|
||||
disabled: boolean;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true })
|
||||
passwordHash: string;
|
||||
|
||||
@Field()
|
||||
@Column({ default: false })
|
||||
canImpersonate: boolean;
|
||||
|
||||
@Field()
|
||||
@CreateDateColumn({ type: 'timestamp with time zone' })
|
||||
createdAt: Date;
|
||||
|
||||
@Field()
|
||||
@UpdateDateColumn({ type: 'timestamp with time zone' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true })
|
||||
deletedAt: Date;
|
||||
|
||||
@Field(() => Workspace, { nullable: false })
|
||||
@ManyToOne(() => Workspace, (workspace) => workspace.users)
|
||||
defaultWorkspace: Workspace;
|
||||
|
||||
@OneToMany(() => RefreshToken, (refreshToken) => refreshToken.user)
|
||||
refreshTokens: RefreshToken[];
|
||||
|
||||
@Field(() => UserWorkspaceMember, { nullable: false })
|
||||
workspaceMember: UserWorkspaceMember;
|
||||
}
|
||||
@ -1,23 +1,34 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { FileModule } from 'src/core/file/file.module';
|
||||
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
|
||||
import { EnvironmentModule } from 'src/integrations/environment/environment.module';
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { UserResolver } from 'src/core/user/user.resolver';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
import { UserResolver } from './user.resolver';
|
||||
import config from '../../../ormconfig';
|
||||
|
||||
import { userAutoResolverOpts } from './user.auto-resolver-opts';
|
||||
|
||||
import { UserService } from './services/user.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot(config),
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [NestjsQueryTypeOrmModule.forFeature([User]), TypeORMModule],
|
||||
resolvers: userAutoResolverOpts,
|
||||
}),
|
||||
DataSourceModule,
|
||||
FileModule,
|
||||
WorkspaceModule,
|
||||
EnvironmentModule,
|
||||
AbilityModule,
|
||||
PrismaModule,
|
||||
],
|
||||
providers: [UserService, UserResolver],
|
||||
exports: [UserService],
|
||||
providers: [UserService, UserResolver, TypeORMService],
|
||||
})
|
||||
export class UserModule {}
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
import { UserResolver } from './user.resolver';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
describe('UserResolver', () => {
|
||||
let resolver: UserResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
UserResolver,
|
||||
{
|
||||
provide: UserService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: FileUploadService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<UserResolver>(UserResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,48 +1,32 @@
|
||||
import {
|
||||
Args,
|
||||
Resolver,
|
||||
Query,
|
||||
ResolveField,
|
||||
Args,
|
||||
Parent,
|
||||
ResolveField,
|
||||
Mutation,
|
||||
} from '@nestjs/graphql';
|
||||
import { UseFilters, UseGuards } from '@nestjs/common';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma, Workspace } from '@prisma/client';
|
||||
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
import { SupportDriver } from 'src/integrations/environment/interfaces/support.interface';
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
|
||||
import { FindManyUserArgs } from 'src/core/@generated/user/find-many-user.args';
|
||||
import { User } from 'src/core/@generated/user/user.model';
|
||||
import { ExceptionFilter } from 'src/filters/exception.filter';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
DeleteUserAbilityHandler,
|
||||
ReadUserAbilityHandler,
|
||||
UpdateUserAbilityHandler,
|
||||
} from 'src/ability/handlers/user.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { AuthUser } from 'src/decorators/auth-user.decorator';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { UpdateOneUserArgs } from 'src/core/@generated/user/update-one-user.args';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { UserWorkspaceMember } from 'src/core/user/dtos/workspace-member.dto';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
import { UserService } from './services/user.service';
|
||||
|
||||
const getHMACKey = (email?: string, key?: string | null) => {
|
||||
if (!email || !key) return null;
|
||||
@ -56,85 +40,24 @@ const getHMACKey = (email?: string, key?: string | null) => {
|
||||
export class UserResolver {
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
private environmentService: EnvironmentService,
|
||||
) {}
|
||||
|
||||
@Query(() => User)
|
||||
async currentUser(
|
||||
@AuthUser() { id }: User,
|
||||
@PrismaSelector({ modelName: 'User' })
|
||||
prismaSelect: PrismaSelect<'User'>,
|
||||
) {
|
||||
const select = prismaSelect.value;
|
||||
|
||||
const user = await this.userService.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
select,
|
||||
async currentUser(@AuthUser() { id }: User) {
|
||||
const user = await this.userService.findById(id, {
|
||||
relations: [{ name: 'defaultWorkspace', query: {} }],
|
||||
});
|
||||
assert(user, 'User not found');
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
@UseFilters(ExceptionFilter)
|
||||
@Query(() => [User], {
|
||||
@ResolveField(() => UserWorkspaceMember, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadUserAbilityHandler)
|
||||
async findManyUser(
|
||||
@Args() args: FindManyUserArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'User' })
|
||||
prismaSelect: PrismaSelect<'User'>,
|
||||
): Promise<Partial<User>[]> {
|
||||
return await this.userService.findMany({
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).User],
|
||||
}
|
||||
: accessibleBy(ability).User,
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => User)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateUserAbilityHandler)
|
||||
async updateUser(
|
||||
@Args() args: UpdateOneUserArgs,
|
||||
@AuthUser() { id }: User,
|
||||
@PrismaSelector({ modelName: 'User' })
|
||||
prismaSelect: PrismaSelect<'User'>,
|
||||
) {
|
||||
const user = await this.userService.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
assert(user, 'User not found');
|
||||
|
||||
return this.userService.update({
|
||||
where: args.where,
|
||||
data: args.data,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.UserUpdateArgs);
|
||||
}
|
||||
|
||||
@ResolveField(() => String, {
|
||||
nullable: false,
|
||||
})
|
||||
displayName(@Parent() parent: User): string {
|
||||
return `${parent.firstName ?? ''} ${parent.lastName ?? ''}`;
|
||||
async workspaceMember(@Parent() user: User): Promise<UserWorkspaceMember> {
|
||||
return this.userService.loadWorkspaceMember(user);
|
||||
}
|
||||
|
||||
@ResolveField(() => String, {
|
||||
@ -154,6 +77,10 @@ export class UserResolver {
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
{ createReadStream, filename, mimetype }: FileUpload,
|
||||
): Promise<string> {
|
||||
if (!id) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const stream = createReadStream();
|
||||
const buffer = await streamToBuffer(stream);
|
||||
const fileFolder = FileFolder.ProfilePicture;
|
||||
@ -165,20 +92,11 @@ export class UserResolver {
|
||||
fileFolder,
|
||||
});
|
||||
|
||||
await this.userService.update({
|
||||
where: { id },
|
||||
data: {
|
||||
avatarUrl: paths[0],
|
||||
},
|
||||
});
|
||||
|
||||
return paths[0];
|
||||
}
|
||||
|
||||
@Mutation(() => User)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteUserAbilityHandler)
|
||||
async deleteUserAccount(
|
||||
async deleteUser(
|
||||
@AuthUser() { id: userId }: User,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
) {
|
||||
|
||||
@ -1,140 +0,0 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
|
||||
export type UserPayload = {
|
||||
displayName: string | undefined | null;
|
||||
email: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.user.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.user.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.user.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.user.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.user.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.user.create;
|
||||
createMany = this.prismaService.client.user.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.user.update;
|
||||
upsert = this.prismaService.client.user.upsert;
|
||||
updateMany = this.prismaService.client.user.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.user.delete;
|
||||
deleteMany = this.prismaService.client.user.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.user.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.user.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.user.groupBy;
|
||||
|
||||
// Customs
|
||||
async createUser<T extends Prisma.UserCreateArgs>(
|
||||
args: Prisma.SelectSubset<T, Prisma.UserCreateArgs>,
|
||||
workspaceId?: string,
|
||||
): Promise<Prisma.UserGetPayload<T>> {
|
||||
assert(args.data.email, 'email is missing', BadRequestException);
|
||||
|
||||
// Create workspace if not exists
|
||||
const workspace = workspaceId
|
||||
? await this.workspaceService.findUnique({
|
||||
where: {
|
||||
id: workspaceId,
|
||||
},
|
||||
})
|
||||
: await this.workspaceService.createDefaultWorkspace();
|
||||
|
||||
assert(workspace, 'workspace is missing', BadRequestException);
|
||||
// Create user
|
||||
const user = await this.prismaService.client.user.upsert({
|
||||
where: {
|
||||
email: args.data.email,
|
||||
},
|
||||
create: {
|
||||
...(args.data as Prisma.UserCreateInput),
|
||||
defaultWorkspaceId: workspace.id,
|
||||
},
|
||||
update: {},
|
||||
...(args.select ? { select: args.select } : {}),
|
||||
...(args.include ? { include: args.include } : {}),
|
||||
} as Prisma.UserUpsertArgs);
|
||||
|
||||
return user as Prisma.UserGetPayload<T>;
|
||||
}
|
||||
|
||||
async deleteUser({
|
||||
workspaceId,
|
||||
userId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
}) {
|
||||
const { workspaceMember, refreshToken } = this.prismaService.client;
|
||||
const user = await this.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
assert(user, 'User not found');
|
||||
|
||||
const workspace = await this.workspaceService.findUnique({
|
||||
where: { id: workspaceId },
|
||||
select: { id: true },
|
||||
});
|
||||
assert(workspace, 'Workspace not found');
|
||||
|
||||
const workSpaceMembers = await workspaceMember.findMany({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
const isLastMember =
|
||||
workSpaceMembers.length === 1 && workSpaceMembers[0].userId === userId;
|
||||
|
||||
if (isLastMember) {
|
||||
// Delete entire workspace
|
||||
await this.workspaceService.deleteWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
} else {
|
||||
await this.prismaService.client.$transaction([
|
||||
workspaceMember.deleteMany({
|
||||
where: { userId },
|
||||
}),
|
||||
|
||||
refreshToken.deleteMany({
|
||||
where: { userId },
|
||||
}),
|
||||
|
||||
this.delete({ where: { id: userId } }),
|
||||
]);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user