Twenty standard and workspace custom applications 1/3 (#15625)

# Introduction
related to https://github.com/twentyhq/core-team-issues/issues/1833
In this PR we're starting the sync-metadata and standardIds deprecation
by introducing `twenty-standard` application that will regroup every
standard object such as company and opportunities. But also the
`custom-workspace-application` which is an app created at the same time
as a workspace and that will regroup everything configure within the
workspace ( custom objects fields etc )

## What's done
On both new workspace and seeded workspace creation:
- Creating a custom workspace app
- Creating a twenty standard app
- Refactored the seed core schema and workspace creation to be run
within a transaction in order to handle circular dependency foreignkey
requirements ( which is deferred for app toward workspace )
- Updated workspace entity to have a custom workspace relation (
nullable for the moment until we implem an upgrade command to handle
retro comp )
- Integration testing on user, workspace creation deletion and expected
default apps creation
- ~~Soft deleted user on `deleteUser`~~ Done by marie and rebased on it

## What's next
- Update seeder to propagate the `twenty-standard` workspace
`applicationId` to every standard synchronized entities ( cheap and fast
iteration through the about to be deprecated sync-metadata as an easy
way to synchronize standards metadata entities ).
- Update seeder to propagate the `custom-workspace-application`
workspace `applicationId` to anything custom ( `pets` and `rockets` )
- Prepend `custom-workspace-application` `applicationId` to every
metadata API operations ( create a specific cache etc )
- Upgrade command on all existing workspace to create a custom app and
associate its applicationId to any existing custom entities
- Make `universalIdentifier` and `applicationId` required for any
syncable entity
This commit is contained in:
Paul Rastoin
2025-11-10 16:13:12 +01:00
committed by GitHub
parent 9a80164cf3
commit 7a1e699fc8
106 changed files with 2167 additions and 985 deletions
@@ -1,13 +1,19 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
const tableName = 'billingCustomer';
export const seedBillingCustomers = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type SeedBillingCustomersArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedBillingCustomers = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedBillingCustomersArgs) => {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['workspaceId', 'stripeCustomerId'])
@@ -1,13 +1,19 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
const tableName = 'billingSubscription';
export const seedBillingSubscriptions = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type SeedBillingSubscriptionsArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedBillingSubscriptions = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedBillingSubscriptionsArgs) => {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
@@ -0,0 +1,52 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
export const WORKSPACE_FIELDS_TO_SEED = [
'id',
'displayName',
'subdomain',
'inviteHash',
'logo',
'activationStatus',
'isTwoFactorAuthenticationEnforced',
'version',
'workspaceCustomApplicationId',
] as const satisfies (keyof WorkspaceEntity)[];
export type CreateWorkspaceInput = Pick<
WorkspaceEntity,
(typeof WORKSPACE_FIELDS_TO_SEED)[number]
>;
export const SEED_APPLE_WORKSPACE_ID = '20202020-1c25-4d02-bf25-6aeccf7ea419';
export const SEED_YCOMBINATOR_WORKSPACE_ID =
'3b8e6458-5fc1-4e63-8563-008ccddaa6db';
export type SeededWorkspacesIds =
| typeof SEED_APPLE_WORKSPACE_ID
| typeof SEED_YCOMBINATOR_WORKSPACE_ID;
export const SEEDER_CREATE_WORKSPACE_INPUT = {
[SEED_APPLE_WORKSPACE_ID]: {
id: SEED_APPLE_WORKSPACE_ID,
displayName: 'Apple',
subdomain: 'apple',
inviteHash: 'apple.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/apple-logo.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
isTwoFactorAuthenticationEnforced: false,
},
[SEED_YCOMBINATOR_WORKSPACE_ID]: {
id: SEED_YCOMBINATOR_WORKSPACE_ID,
displayName: 'YCombinator',
subdomain: 'yc',
inviteHash: 'yc.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/ycombinator-logo.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
isTwoFactorAuthenticationEnforced: false,
},
} as const satisfies Record<
SeededWorkspacesIds,
Omit<CreateWorkspaceInput, 'version' | 'workspaceCustomApplicationId'>
>;
@@ -12,14 +12,14 @@ import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import {
RANDOM_USER_WORKSPACE_IDS,
USER_WORKSPACE_DATA_SEED_IDS,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
import { API_KEY_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/api-key-data-seeds.constant';
import { ADMIN_ROLE } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-roles/roles/admin-role';
@@ -1,11 +1,11 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
import { AgentChatMessageRole } from 'src/engine/metadata-modules/agent/agent-chat-message.entity';
import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { USER_WORKSPACE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
const agentChatThreadTableName = 'agentChatThread';
const agentChatMessageTableName = 'agentChatMessage';
@@ -43,11 +43,17 @@ export const AGENT_CHAT_MESSAGE_PART_DATA_SEED_IDS = {
YCOMBINATOR_MESSAGE_4_PART_1: '20202020-0000-4000-8000-000000000054',
};
const seedChatThreads = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
type SeedChatThreadsArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
const seedChatThreads = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedChatThreadsArgs) => {
let threadId: string;
let userWorkspaceId: string;
@@ -65,7 +71,7 @@ const seedChatThreads = async (
const now = new Date();
await dataSource
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentChatThreadTableName}`, [
@@ -88,12 +94,19 @@ const seedChatThreads = async (
return threadId;
};
const seedChatMessages = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
threadId: string,
) => {
type SeedChatMessagesArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
threadId: string;
};
const seedChatMessages = async ({
queryRunner,
schemaName,
workspaceId,
threadId,
}: SeedChatMessagesArgs) => {
let messageIds: string[];
let partIds: string[];
let messages: Array<{
@@ -274,7 +287,7 @@ const seedChatMessages = async (
);
}
await dataSource
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentChatMessageTableName}`, [
@@ -287,7 +300,7 @@ const seedChatMessages = async (
.values(messages)
.execute();
await dataSource
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${agentChatMessagePartTableName}`, [
@@ -303,12 +316,27 @@ const seedChatMessages = async (
.execute();
};
export const seedAgents = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
const threadId = await seedChatThreads(dataSource, schemaName, workspaceId);
await seedChatMessages(dataSource, schemaName, workspaceId, threadId);
type SeedAgentsArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedAgents = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedAgentsArgs) => {
const threadId = await seedChatThreads({
queryRunner,
schemaName,
workspaceId,
});
await seedChatMessages({
queryRunner,
schemaName,
workspaceId,
threadId,
});
};
@@ -1,15 +1,21 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
import { API_KEY_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/api-key-data-seeds.constant';
const tableName = 'apiKey';
export const seedApiKeys = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type SeedApiKeysArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedApiKeys = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedApiKeysArgs) => {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
@@ -1,18 +1,26 @@
import { type DataSource } from 'typeorm';
import { type ApplicationService } from 'src/engine/core-modules/application/application.service';
import { seedBillingCustomers } from 'src/engine/workspace-manager/dev-seeder/core/billing/utils/seed-billing-customers.util';
import { seedBillingSubscriptions } from 'src/engine/workspace-manager/dev-seeder/core/billing/utils/seed-billing-subscriptions.util';
import {
type SeededWorkspacesIds,
SEEDER_CREATE_WORKSPACE_INPUT,
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { seedAgents } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-agents.util';
import { seedApiKeys } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-api-keys.util';
import { seedFeatureFlags } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util';
import { seedUserWorkspaces } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-user-workspaces.util';
import { seedUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
import { seedWorkspaces } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
import { createWorkspace } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspace.util';
import { computeWorkspaceCustomCreateApplicationInput } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/compute-workspace-custom-create-application-input';
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
type SeedCoreSchemaArgs = {
dataSource: DataSource;
workspaceId: string;
workspaceId: SeededWorkspacesIds;
appVersion: string | undefined;
applicationService: ApplicationService;
seedBilling?: boolean;
seedFeatureFlags?: boolean;
};
@@ -21,30 +29,75 @@ export const seedCoreSchema = async ({
appVersion,
dataSource,
workspaceId,
applicationService,
seedBilling = true,
seedFeatureFlags: shouldSeedFeatureFlags = true,
}: SeedCoreSchemaArgs) => {
const schemaName = 'core';
await seedWorkspaces({
dataSource,
schemaName,
workspaceId,
appVersion,
});
await seedUsers(dataSource, schemaName);
await seedUserWorkspaces(dataSource, schemaName, workspaceId);
const createWorkspaceStaticInput = SEEDER_CREATE_WORKSPACE_INPUT[workspaceId];
const workspaceCustomApplicationCreateInput =
computeWorkspaceCustomCreateApplicationInput({
workspace: {
id: workspaceId,
displayName: createWorkspaceStaticInput.displayName,
},
});
await seedAgents(dataSource, schemaName, workspaceId);
const version = extractVersionMajorMinorPatch(appVersion);
const queryRunner = dataSource.createQueryRunner();
await seedApiKeys(dataSource, schemaName, workspaceId);
await queryRunner.connect();
await queryRunner.startTransaction();
if (shouldSeedFeatureFlags) {
await seedFeatureFlags(dataSource, schemaName, workspaceId);
}
try {
const customWorkspaceApplication = await applicationService.create(
{
...workspaceCustomApplicationCreateInput,
serverlessFunctionLayerId: null,
},
queryRunner,
);
if (seedBilling) {
await seedBillingCustomers(dataSource, schemaName, workspaceId);
await seedBillingSubscriptions(dataSource, schemaName, workspaceId);
await createWorkspace({
queryRunner,
schemaName,
createWorkspaceInput: {
...createWorkspaceStaticInput,
version,
workspaceCustomApplicationId: customWorkspaceApplication.id,
},
});
await seedUsers({ queryRunner, schemaName });
await seedUserWorkspaces({ queryRunner, schemaName, workspaceId });
await applicationService.createTwentyStandardApplication(
{
workspaceId,
},
queryRunner,
);
await seedAgents({ queryRunner, schemaName, workspaceId });
await seedApiKeys({ queryRunner, schemaName, workspaceId });
if (shouldSeedFeatureFlags) {
await seedFeatureFlags({ queryRunner, schemaName, workspaceId });
}
if (seedBilling) {
await seedBillingCustomers({ queryRunner, schemaName, workspaceId });
await seedBillingSubscriptions({ queryRunner, schemaName, workspaceId });
}
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
};
@@ -1,16 +1,22 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
const tableName = 'featureFlag';
export const seedFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type SeedFeatureFlagsArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedFeatureFlags = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedFeatureFlagsArgs) => {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value'])
@@ -90,12 +96,18 @@ export const seedFeatureFlags = async (
.execute();
};
export const deleteFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type DeleteFeatureFlagsArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const deleteFeatureFlags = async ({
queryRunner,
schemaName,
workspaceId,
}: DeleteFeatureFlagsArgs) => {
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
@@ -1,12 +1,12 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
import { type UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { generateRandomUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-random-users.util';
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { generateRandomUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-random-users.util';
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
const tableName = 'userWorkspace';
@@ -28,11 +28,17 @@ const {
export const RANDOM_USER_WORKSPACE_IDS = randomUserWorkspaceIds;
export const seedUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
type SeedUserWorkspacesArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const seedUserWorkspaces = async ({
queryRunner,
schemaName,
workspaceId,
}: SeedUserWorkspacesArgs) => {
let userWorkspaces: Pick<
UserWorkspaceEntity,
'id' | 'userId' | 'workspaceId'
@@ -89,7 +95,7 @@ export const seedUserWorkspaces = async (
},
];
}
await dataSource
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
@@ -98,12 +104,18 @@ export const seedUserWorkspaces = async (
.execute();
};
export const deleteUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
type DeleteUserWorkspacesArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const deleteUserWorkspaces = async ({
queryRunner,
schemaName,
workspaceId,
}: DeleteUserWorkspacesArgs) => {
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
@@ -1,4 +1,4 @@
import { type DataSource } from 'typeorm';
import { type QueryRunner } from 'typeorm';
import { generateRandomUsers } from './generate-random-users.util';
@@ -15,7 +15,12 @@ const { users: randomUsers, userIds: randomUserIds } = generateRandomUsers();
export const RANDOM_USER_IDS = randomUserIds;
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
type SeedUsersArgs = {
queryRunner: QueryRunner;
schemaName: string;
};
export const seedUsers = async ({ queryRunner, schemaName }: SeedUsersArgs) => {
const originalUsers = [
{
id: USER_DATA_SEED_IDS.TIM,
@@ -65,7 +70,7 @@ export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
const allUsers = [...originalUsers, ...randomUsers];
await dataSource
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
@@ -0,0 +1,47 @@
import { type QueryRunner } from 'typeorm';
import {
type CreateWorkspaceInput,
WORKSPACE_FIELDS_TO_SEED,
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
const tableName = 'workspace';
export type SeedWorkspaceArgs = {
queryRunner: QueryRunner;
schemaName: string;
createWorkspaceInput: CreateWorkspaceInput;
};
export const createWorkspace = async ({
schemaName,
queryRunner,
createWorkspaceInput,
}: SeedWorkspaceArgs) => {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, WORKSPACE_FIELDS_TO_SEED)
.orIgnore()
.values(createWorkspaceInput)
.execute();
};
type DeleteWorkspacesArgs = {
queryRunner: QueryRunner;
schemaName: string;
workspaceId: string;
};
export const deleteWorkspaces = async ({
queryRunner,
schemaName,
workspaceId,
}: DeleteWorkspacesArgs) => {
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`${tableName}."id" = :id`, { id: workspaceId })
.execute();
};
@@ -1,87 +0,0 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { type DataSource } from 'typeorm';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
const tableName = 'workspace';
export const SEED_APPLE_WORKSPACE_ID = '20202020-1c25-4d02-bf25-6aeccf7ea419';
export const SEED_YCOMBINATOR_WORKSPACE_ID =
'3b8e6458-5fc1-4e63-8563-008ccddaa6db';
export type SeedWorkspaceArgs = {
dataSource: DataSource;
schemaName: string;
workspaceId: string;
appVersion: string | undefined;
};
const workspaceSeederFields = [
'id',
'displayName',
'subdomain',
'inviteHash',
'logo',
'activationStatus',
'version',
'isTwoFactorAuthenticationEnforced',
] as const satisfies (keyof WorkspaceEntity)[];
type WorkspaceSeederFields = Pick<
WorkspaceEntity,
(typeof workspaceSeederFields)[number]
>;
export const seedWorkspaces = async ({
schemaName,
dataSource,
workspaceId,
appVersion,
}: SeedWorkspaceArgs) => {
const version = extractVersionMajorMinorPatch(appVersion);
const workspaces: Record<string, WorkspaceSeederFields> = {
[SEED_APPLE_WORKSPACE_ID]: {
id: SEED_APPLE_WORKSPACE_ID,
displayName: 'Apple',
subdomain: 'apple',
inviteHash: 'apple.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/apple-logo.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
version: version,
isTwoFactorAuthenticationEnforced: false,
},
[SEED_YCOMBINATOR_WORKSPACE_ID]: {
id: SEED_YCOMBINATOR_WORKSPACE_ID,
displayName: 'YCombinator',
subdomain: 'yc',
inviteHash: 'yc.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/ycombinator-logo.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
version: version,
isTwoFactorAuthenticationEnforced: false,
},
};
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, workspaceSeederFields)
.orIgnore()
.values(workspaces[workspaceId])
.execute();
};
export const deleteWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`${tableName}."id" = :id`, { id: workspaceId })
.execute();
};
@@ -1,9 +1,9 @@
import { generateRandomUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-random-users.util';
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { generateRandomUsers } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-random-users.util';
import { USER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-users.util';
type WorkspaceMemberDataSeed = {
id: string;
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -36,6 +37,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
RoleModule,
UserRoleModule,
ApiKeyModule,
ApplicationModule,
FeatureFlagModule,
FileStorageModule,
WorkspaceSyncMetadataModule,
@@ -14,7 +14,7 @@ import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/works
import {
SEED_APPLE_WORKSPACE_ID,
SEED_YCOMBINATOR_WORKSPACE_ID,
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
} from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { COMPANY_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/company-custom-field-seeds.constant';
import { PERSON_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/person-custom-field-seeds.constant';
import { PET_CUSTOM_FIELD_SEEDS } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-fields/constants/pet-custom-field-seeds.constant';
@@ -1,8 +1,10 @@
import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { isDefined } from 'twenty-shared/utils';
import { DataSource } from 'typeorm';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
@@ -11,6 +13,7 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadat
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { SeededWorkspacesIds } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant';
import { DevSeederPermissionsService } from 'src/engine/workspace-manager/dev-seeder/core/services/dev-seeder-permissions.service';
import { seedCoreSchema } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-core-schema.util';
import { seedPageLayoutTabs } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-page-layout-tabs.util';
@@ -18,6 +21,7 @@ import { seedPageLayoutWidgets } from 'src/engine/workspace-manager/dev-seeder/c
import { seedPageLayouts } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-page-layouts.util';
import { DevSeederDataService } from 'src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service';
import { DevSeederMetadataService } from 'src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service';
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/twenty-standard-applications';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@Injectable()
@@ -33,17 +37,19 @@ export class DevSeederService {
private readonly devSeederPermissionsService: DevSeederPermissionsService,
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly devSeederDataService: DevSeederDataService,
private readonly applicationService: ApplicationService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {}
public async seedDev(workspaceId: string): Promise<void> {
public async seedDev(workspaceId: SeededWorkspacesIds): Promise<void> {
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
const appVersion = this.twentyConfigService.get('APP_VERSION');
await seedCoreSchema({
dataSource: this.coreDataSource,
workspaceId,
applicationService: this.applicationService,
seedBilling: isBillingEnabled,
appVersion,
});
@@ -62,6 +68,18 @@ export class DevSeederService {
const featureFlags =
await this.featureFlagService.getWorkspaceFeatureFlagsMap(workspaceId);
const twentyStandardApplication =
await this.applicationService.findByUniversalIdentifier({
workspaceId,
universalIdentifier: TWENTY_STANDARD_APPLICATION.universalIdentifier,
});
if (!isDefined(twentyStandardApplication)) {
throw new Error(
'Seeder failed to find twenty standard application, should never occur',
);
}
await this.workspaceSyncMetadataService.synchronize({
workspaceId: workspaceId,
dataSourceId: dataSourceMetadata.id,