Chore: Duplicate certain user fields to workspaceMember (#1514)

* Move certain user fields to workspaceMember

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Merge main

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Update the generated GraphQL

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Update hooks

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>

* Rework typing

* Fix tests

* Remove console logs

---------

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: chiazokam <chiazokamecheta@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
gitstart-twenty
2023-09-16 01:32:58 +01:00
committed by GitHub
parent 0a7a0ac6cb
commit 549335054a
23 changed files with 890 additions and 115 deletions

View File

@ -78,6 +78,10 @@ export class AbilityFactory {
can(AbilityAction.Read, 'WorkspaceMember', { workspaceId: workspace.id });
can(AbilityAction.Delete, 'WorkspaceMember', { workspaceId: workspace.id });
cannot(AbilityAction.Delete, 'WorkspaceMember', { userId: user.id });
can(AbilityAction.Update, 'WorkspaceMember', {
userId: user.id,
workspaceId: workspace.id,
});
// Company
can(AbilityAction.Read, 'Company', { workspaceId: workspace.id });

View File

@ -89,11 +89,6 @@ export class AuthService {
email: signUpInput.email,
passwordHash,
locale: 'en',
settings: {
create: {
locale: 'en',
},
},
},
} as Prisma.UserCreateArgs,
workspace.id,
@ -105,11 +100,6 @@ export class AuthService {
email: signUpInput.email,
passwordHash,
locale: 'en',
settings: {
create: {
locale: 'en',
},
},
},
} as Prisma.UserCreateArgs);
}

View File

@ -67,6 +67,12 @@ export class UserService {
assert(workspace, 'workspace is missing', BadRequestException);
const userSettings = await this.prismaService.client.userSettings.create({
data: { locale: 'en' },
});
const settings = { connect: { id: userSettings.id } };
// Create user
const user = await this.prismaService.client.user.upsert({
where: {
@ -74,12 +80,14 @@ export class UserService {
},
create: {
...(args.data as Prisma.UserCreateInput),
settings,
workspaceMember: {
create: {
workspace: {
connect: { id: workspace.id },
},
settings,
},
},
locale: 'en',

View File

@ -2,6 +2,7 @@ import { Args, Query, Resolver, Mutation } from '@nestjs/graphql';
import { UseGuards } from '@nestjs/common';
import { accessibleBy } from '@casl/prisma';
import { Prisma } from '@prisma/client';
import { WorkspaceMember } from 'src/core/@generated/workspace-member/workspace-member.model';
import { AbilityGuard } from 'src/guards/ability.guard';
@ -9,6 +10,7 @@ import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
import {
DeleteWorkspaceMemberAbilityHandler,
ReadWorkspaceMemberAbilityHandler,
UpdateWorkspaceMemberAbilityHandler,
} from 'src/ability/handlers/workspace-member.ability-handler';
import { FindManyWorkspaceMemberArgs } from 'src/core/@generated/workspace-member/find-many-workspace-member.args';
import { UserAbility } from 'src/decorators/user-ability.decorator';
@ -22,6 +24,7 @@ import { DeleteOneWorkspaceMemberArgs } from 'src/core/@generated/workspace-memb
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
import { AuthUser } from 'src/decorators/auth-user.decorator';
import { User } from 'src/core/@generated/user/user.model';
import { UpdateOneWorkspaceMemberArgs } from 'src/core/@generated/workspace-member/update-one-workspace-member.args';
@UseGuards(JwtAuthGuard)
@Resolver(() => WorkspaceMember)
@ -81,4 +84,19 @@ export class WorkspaceMemberResolver {
select: prismaSelect.value,
});
}
@Mutation(() => WorkspaceMember)
@UseGuards(AbilityGuard)
@CheckAbilities(UpdateWorkspaceMemberAbilityHandler)
async UpdateOneWorkspaceMember(
@Args() args: UpdateOneWorkspaceMemberArgs,
@PrismaSelector({ modelName: 'WorkspaceMember' })
prismaSelect: PrismaSelect<'WorkspaceMember'>,
): Promise<Partial<WorkspaceMember>> {
return this.workspaceMemberService.update({
data: args.data,
where: args.where,
select: prismaSelect.value,
} as Prisma.WorkspaceMemberUpdateArgs);
}
}

View File

@ -0,0 +1,40 @@
/*
Warnings:
- Made the column `idealCustomerProfile` on table `companies` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE "activities" ADD COLUMN "workspaceMemberAssigneeId" TEXT,
ADD COLUMN "workspaceMemberAuthorId" TEXT;
-- AlterTable
ALTER TABLE "attachments" ADD COLUMN "workspaceMemberAuthorId" TEXT;
-- AlterTable
ALTER TABLE "comments" ADD COLUMN "workspaceMemberAuthorId" TEXT;
-- AlterTable
ALTER TABLE "companies" ADD COLUMN "workspaceMemberAccountOwnerId" TEXT,
ALTER COLUMN "idealCustomerProfile" SET NOT NULL;
-- AlterTable
ALTER TABLE "workspace_members" ADD COLUMN "settingsId" TEXT;
-- AddForeignKey
ALTER TABLE "workspace_members" ADD CONSTRAINT "workspace_members_settingsId_fkey" FOREIGN KEY ("settingsId") REFERENCES "user_settings"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "companies" ADD CONSTRAINT "companies_workspaceMemberAccountOwnerId_fkey" FOREIGN KEY ("workspaceMemberAccountOwnerId") REFERENCES "workspace_members"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "activities" ADD CONSTRAINT "activities_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "activities" ADD CONSTRAINT "activities_workspaceMemberAssigneeId_fkey" FOREIGN KEY ("workspaceMemberAssigneeId") REFERENCES "workspace_members"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "comments" ADD CONSTRAINT "comments_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "attachments" ADD CONSTRAINT "attachments_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -138,7 +138,8 @@ model UserSettings {
/// @Validator.IsString()
locale String
user User?
user User?
WorkspaceMember WorkspaceMember[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@ -210,6 +211,15 @@ model WorkspaceMember {
updatedAt DateTime @updatedAt
Favorite Favorite[]
authoredActivities Activity[] @relation(name: "authoredActivities")
assignedActivities Activity[] @relation(name: "assignedActivities")
authoredAttachments Attachment[] @relation(name: "authoredAttachments")
settings UserSettings? @relation(fields: [settingsId], references: [id])
settingsId String?
companies Company[]
comments Comment[]
@@map("workspace_members")
}
@ -242,13 +252,15 @@ model Company {
/// @Validator.IsOptional()
employees Int?
people Person[]
accountOwner User? @relation(fields: [accountOwnerId], references: [id], onDelete: SetNull)
accountOwnerId String?
people Person[]
accountOwner User? @relation(fields: [accountOwnerId], references: [id], onDelete: SetNull)
accountOwnerId String?
workspaceMemberAccountOwner WorkspaceMember? @relation(fields: [workspaceMemberAccountOwnerId], references: [id], onDelete: SetNull)
workspaceMemberAccountOwnerId String?
/// @TypeGraphQL.omit(input: true, output: true)
workspace Workspace @relation(fields: [workspaceId], references: [id])
workspace Workspace @relation(fields: [workspaceId], references: [id])
/// @TypeGraphQL.omit(input: true, output: true)
workspaceId String
workspaceId String
/// @TypeGraphQL.omit(input: true, output: true)
deletedAt DateTime?
@ -358,12 +370,20 @@ model Activity {
attachments Attachment[]
author User @relation(fields: [authorId], references: [id], name: "authoredActivities", onDelete: Cascade)
authorId String
assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedActivities", onDelete: SetNull)
assigneeId String?
workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], name: "authoredActivities", onDelete: Cascade)
workspaceMemberAuthorId String?
assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedActivities", onDelete: SetNull)
assigneeId String?
workspaceMemberAssignee WorkspaceMember? @relation(fields: [workspaceMemberAssigneeId], references: [id], name: "assignedActivities", onDelete: SetNull)
workspaceMemberAssigneeId String?
/// @TypeGraphQL.omit(input: true, output: true)
workspace Workspace @relation(fields: [workspaceId], references: [id])
workspace Workspace @relation(fields: [workspaceId], references: [id])
/// @TypeGraphQL.omit(input: true, output: true)
workspaceId String
workspaceId String
/// @TypeGraphQL.omit(input: true, output: true)
deletedAt DateTime?
@ -381,8 +401,12 @@ model Comment {
/// @Validator.IsString()
body String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], onDelete: Cascade)
workspaceMemberAuthorId String?
activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String?
commentThreadId String?
@ -550,8 +574,12 @@ model Attachment {
/// @TypeGraphQL.omit(input: true, output: true)
workspaceId String
author User @relation(fields: [authorId], references: [id], name: "authoredAttachments", onDelete: Cascade)
authorId String
author User @relation(fields: [authorId], references: [id], name: "authoredAttachments", onDelete: Cascade)
authorId String
workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], name: "authoredAttachments", onDelete: Cascade)
workspaceMemberAuthorId String?
activity Activity @relation(fields: [activityId], references: [id], onDelete: Cascade)
activityId String

View File

@ -1,5 +1,13 @@
import { PrismaClient } from '@prisma/client';
export const seedUsers = async (prisma: PrismaClient) => {
await prisma.userSettings.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109' },
update: {},
create: {
id: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109',
locale: 'en',
},
});
await prisma.user.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfc102' },
update: {},
@ -11,11 +19,7 @@ export const seedUsers = async (prisma: PrismaClient) => {
locale: 'en',
passwordHash:
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
settings: {
create: {
locale: 'en',
},
},
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109',
avatarUrl: null,
workspaceMember: {
connectOrCreate: {
@ -24,12 +28,21 @@ export const seedUsers = async (prisma: PrismaClient) => {
},
create: {
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109',
},
},
},
},
});
await prisma.userSettings.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215' },
update: {},
create: {
id: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215',
locale: 'en',
},
});
await prisma.user.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfa408' },
update: {},
@ -39,21 +52,26 @@ export const seedUsers = async (prisma: PrismaClient) => {
lastName: 'Ive',
email: 'jony.ive@apple.dev',
locale: 'en',
settings: {
create: {
locale: 'en',
},
},
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215',
avatarUrl: null,
workspaceMember: {
create: {
id: 'twenty-7ef9d213-1c25-4d02-bf35-6aeccf7ea419',
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215',
},
},
},
});
await prisma.userSettings.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328' },
update: {},
create: {
id: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328',
locale: 'en',
},
});
await prisma.user.upsert({
where: { id: 'twenty-gk256b39-3ec3-4fe3-8997-b76aa0bfa408' },
update: {},
@ -63,21 +81,26 @@ export const seedUsers = async (prisma: PrismaClient) => {
lastName: 'Schiler',
email: 'phil.schiler@apple.dev',
locale: 'en',
settings: {
create: {
locale: 'en',
},
},
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328',
avatarUrl: null,
workspaceMember: {
create: {
id: 'twenty-7ed9d213-1c25-4d02-bf35-6aeccf7ea419',
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328',
},
},
},
});
await prisma.userSettings.upsert({
where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430' },
update: {},
create: {
id: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430',
locale: 'en',
},
});
await prisma.user.upsert({
where: { id: 'twenty-dev-gk256b39-3ec3-4fe3-8997-b76aa0boa408' },
update: {},
@ -87,15 +110,12 @@ export const seedUsers = async (prisma: PrismaClient) => {
lastName: 'Bochet',
email: 'charles@twenty.dev',
locale: 'en',
settings: {
create: {
locale: 'en',
},
},
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430',
workspaceMember: {
create: {
id: 'twenty-dev-7ed9d213-1c25-4d02-bf35-6aeccf7oa419',
workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420',
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430',
},
},
},