refactor(workspace, users, billing): remove default workspace + rename (#9360)

Replaced user-based parameterization with workspace-focused logic across
seed scripts, mocks, and billing services. Removed redundant `user`
references and standardized to `workspace` to align with updated
business rules. Adjusted mock data and tests to reflect these changes.

Fix https://github.com/twentyhq/twenty/issues/9295
This commit is contained in:
Antoine Moreaux
2025-01-06 12:33:57 +01:00
committed by GitHub
parent 25083e5405
commit 3eb7ec909e
12 changed files with 35 additions and 51 deletions

View File

@ -10,7 +10,7 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { getCompaniesMock } from '~/testing/mock-data/companies';
import {
mockDefaultWorkspace,
mockCurrentWorkspace,
mockedWorkspaceMemberData,
} from '~/testing/mock-data/users';
import { sleep } from '~/utils/sleep';
@ -54,7 +54,7 @@ const meta: Meta<typeof CommandMenu> = {
isCommandMenuOpenedState,
);
setCurrentWorkspace(mockDefaultWorkspace);
setCurrentWorkspace(mockCurrentWorkspace);
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
setIsCommandMenuOpened(true);

View File

@ -13,7 +13,7 @@ import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadat
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import {
mockDefaultWorkspace,
mockCurrentWorkspace,
mockedWorkspaceMemberData,
} from '~/testing/mock-data/users';
@ -26,7 +26,7 @@ const RelationWorkspaceSetterEffect = () => {
);
useEffect(() => {
setCurrentWorkspace(mockDefaultWorkspace);
setCurrentWorkspace(mockCurrentWorkspace);
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
}, [setCurrentWorkspace, setCurrentWorkspaceMember]);

View File

@ -13,7 +13,7 @@ import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadat
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import {
mockDefaultWorkspace,
mockCurrentWorkspace,
mockedWorkspaceMemberData,
} from '~/testing/mock-data/users';
@ -32,7 +32,7 @@ const RelationWorkspaceSetterEffect = () => {
);
useEffect(() => {
setCurrentWorkspace(mockDefaultWorkspace);
setCurrentWorkspace(mockCurrentWorkspace);
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
}, [setCurrentWorkspace, setCurrentWorkspaceMember]);

View File

@ -7,7 +7,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus';
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
import {
mockDefaultWorkspace,
mockCurrentWorkspace,
mockedUserData,
} from '~/testing/mock-data/users';
@ -35,7 +35,7 @@ const renderHooks = (
act(() => {
result.current.setCurrentUser({ ...mockedUserData, onboardingStatus });
result.current.setCurrentWorkspace({
...mockDefaultWorkspace,
...mockCurrentWorkspace,
currentBillingSubscription: withCurrentBillingSubscription
? { id: v4(), status: SubscriptionStatus.Active }
: undefined,

View File

@ -9,7 +9,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { supportChatState } from '@/client-config/states/supportChatState';
import { graphqlMocks } from '~/testing/graphqlMocks';
import {
mockDefaultWorkspace,
mockCurrentWorkspace,
mockedUserData,
mockedWorkspaceMemberData,
} from '~/testing/mock-data/users';
@ -29,7 +29,7 @@ const meta: Meta<typeof SupportDropdown> = {
currentWorkspaceMemberState,
);
setCurrentWorkspace(mockDefaultWorkspace);
setCurrentWorkspace(mockCurrentWorkspace);
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
setCurrentUser(mockedUserData);
setSupportChat({ supportDriver: 'front', supportFrontChatId: '1234' });

View File

@ -36,7 +36,7 @@ export const avatarUrl =
export const workspaceLogoUrl =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
export const mockDefaultWorkspace: Workspace = {
export const mockCurrentWorkspace: Workspace = {
subdomain: 'acme.twenty.com',
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6w',
displayName: 'Twenty',
@ -107,9 +107,9 @@ export const mockedUserData: MockedUser = {
supportUserHash:
'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d',
workspaceMember: mockedWorkspaceMemberData,
currentWorkspace: mockDefaultWorkspace,
currentWorkspace: mockCurrentWorkspace,
locale: 'en',
workspaces: [{ workspace: mockDefaultWorkspace }],
workspaces: [{ workspace: mockCurrentWorkspace }],
workspaceMembers: [mockedWorkspaceMemberData],
onboardingStatus: OnboardingStatus.Completed,
userVars: {},
@ -129,9 +129,9 @@ export const mockedOnboardingUserData = (
supportUserHash:
'4fb61d34ed3a4aeda2476d4b308b5162db9e1809b2b8277e6fdc6efc4a609254',
workspaceMember: null,
currentWorkspace: mockDefaultWorkspace,
currentWorkspace: mockCurrentWorkspace,
locale: 'en',
workspaces: [{ workspace: mockDefaultWorkspace }],
workspaces: [{ workspace: mockCurrentWorkspace }],
onboardingStatus: onboardingStatus || null,
};
};

View File

@ -21,7 +21,7 @@ export const seedCoreSchema = async (
const schemaName = 'core';
await seedWorkspaces(workspaceDataSource, schemaName, workspaceId);
await seedUsers(workspaceDataSource, schemaName, workspaceId);
await seedUsers(workspaceDataSource, schemaName);
await seedUserWorkspaces(workspaceDataSource, schemaName, workspaceId);
};

View File

@ -13,7 +13,6 @@ export const DEMO_SEED_USER_IDS = {
export const seedUsers = async (
workspaceDataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
.createQueryBuilder()
@ -56,16 +55,22 @@ export const seedUsers = async (
};
export const deleteUsersByWorkspace = async (
workspaceDataSource: DataSource,
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
const user = await dataSource
.createQueryBuilder(`${schemaName}.${tableName}`, 'user')
.leftJoinAndSelect('user.workspaces', 'userWorkspace')
.where(`userWorkspace."workspaceId" = :workspaceId`, {
workspaceId,
})
.getMany();
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."defaultWorkspaceId" = :workspaceId`, {
workspaceId,
})
.where(`"${tableName}"."id" IN (:...ids)`, { ids: user.map((u) => u.id) })
.execute();
};

View File

@ -12,7 +12,7 @@ export const seedCoreSchema = async (
const schemaName = 'core';
await seedWorkspaces(workspaceDataSource, schemaName, workspaceId);
await seedUsers(workspaceDataSource, schemaName, workspaceId);
await seedUsers(workspaceDataSource, schemaName);
await seedUserWorkspaces(workspaceDataSource, schemaName, workspaceId);
await seedFeatureFlags(workspaceDataSource, schemaName, workspaceId);
};

View File

@ -1,7 +1,5 @@
import { DataSource } from 'typeorm';
// import { SeedWorkspaceId } from 'src/database/typeorm-seeds/core/workspaces';
const tableName = 'user';
export const DEV_SEED_USER_IDS = {
@ -13,7 +11,6 @@ export const DEV_SEED_USER_IDS = {
export const seedUsers = async (
workspaceDataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
.createQueryBuilder()
@ -54,18 +51,3 @@ export const seedUsers = async (
])
.execute();
};
export const deleteUsersByWorkspace = async (
workspaceDataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await workspaceDataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."defaultWorkspaceId" = :workspaceId`, {
workspaceId,
})
.execute();
};

View File

@ -37,9 +37,8 @@ export class BillingResolver {
}
@Query(() => SessionEntity)
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
@UseGuards(WorkspaceAuthGuard)
async billingPortalSession(
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
@Args() { returnUrlPath }: BillingSessionInput,
) {
@ -89,8 +88,8 @@ export class BillingResolver {
@Mutation(() => UpdateBillingEntity)
@UseGuards(WorkspaceAuthGuard)
async updateBillingSubscription(@AuthUser() user: User) {
await this.billingSubscriptionService.applyBillingSubscription(user);
async updateBillingSubscription(@AuthWorkspace() workspace: Workspace) {
await this.billingSubscriptionService.applyBillingSubscription(workspace);
return { success: true };
}

View File

@ -3,7 +3,6 @@ import { InjectRepository } from '@nestjs/typeorm';
import assert from 'assert';
import { User } from '@sentry/node';
import Stripe from 'stripe';
import { Not, Repository } from 'typeorm';
@ -15,6 +14,7 @@ import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/bill
import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum';
import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@Injectable()
export class BillingSubscriptionService {
@ -114,9 +114,9 @@ export class BillingSubscriptionService {
return entitlement.value;
}
async applyBillingSubscription(user: User) {
async applyBillingSubscription(workspace: Workspace) {
const billingSubscription = await this.getCurrentBillingSubscriptionOrThrow(
{ workspaceId: user.defaultWorkspaceId },
{ workspaceId: workspace.id },
);
const newInterval =
@ -125,9 +125,7 @@ export class BillingSubscriptionService {
: SubscriptionInterval.Year;
const billingSubscriptionItem =
await this.getCurrentBillingSubscriptionItemOrThrow(
user.defaultWorkspaceId,
);
await this.getCurrentBillingSubscriptionItemOrThrow(workspace.id);
const productPrice = await this.stripeService.getStripePrice(
AvailableProduct.BasePlan,