Deprecate old relations completely (#12482)

# What

Fully deprecate old relations because we have one bug tied to it and it
make the codebase complex

# How I've made this PR:
1. remove metadata datasource (we only keep 'core') => this was causing
extra complexity in the refactor + flaky reset
2. merge dev and demo datasets => as I needed to update the tests which
is very painful, I don't want to do it twice
3. remove all code tied to RELATION_METADATA /
relation-metadata.resolver, or anything tied to the old relation system
4. Remove ONE_TO_ONE and MANY_TO_MANY that are not supported
5. fix impacts on the different areas : see functional testing below 

# Functional testing

## Functional testing from the front-end:
1. Database Reset 
2. Sign In 
3. Workspace sign-up 
5. Browsing table / kanban / show 
6. Assigning a record in a one to many / in a many to one 
7. Deleting a record involved in a relation  => broken but not tied to
this PR
8. "Add new" from relation picker  => broken but not tied to this PR
9. Creating a Task / Note, Updating a Task / Note relations, Deleting a
Task / Note (from table, show page, right drawer)  => broken but not
tied to this PR
10. creating a relation from settings (custom / standard x oneToMany /
manyToOne) 
11. updating a relation from settings should not be possible 
12. deleting a relation from settings (custom / standard x oneToMany /
manyToOne) 
13. Make sure timeline activity still work (relation were involved
there), espacially with Task / Note => to be double checked  => Cannot
convert undefined or null to object
14. Workspace deletion / User deletion  
15. CSV Import should keep working  
16. Permissions: I have tested without permissions V2 as it's still hard
to test v2 work and it's not in prod yet 
17. Workflows global test  

## From the API:
1. Review open-api documentation (REST)  
2. Make sure REST Api are still able to fetch relations ==> won't do, we
have a coupling Get/Update/Create there, this requires refactoring
3. Make sure REST Api is still able to update / remove relation => won't
do same

## Automated tests
1. lint + typescript 
2. front unit tests: 
3. server unit tests 2 
4. front stories: 
5. server integration: 
6. chromatic check : expected 0
7. e2e check : expected no more that current failures

## Remove // Todos
1. All are captured by functional tests above, nothing additional to do

## (Un)related regressions
1. Table loading state is not working anymore, we see the empty state
before table content
2. Filtering by Creator Tim Ap return empty results
3. Not possible to add Tasks / Notes / Files from show page

# Result

## New seeds that can be easily extended
<img width="1920" alt="image"
src="https://github.com/user-attachments/assets/d290d130-2a5f-44e6-b419-7e42a89eec4b"
/>

## -5k lines of code
## No more 'metadata' dataSource (we only have 'core)
## No more relationMetadata (I haven't drop the table yet it's not
referenced in the code anymore)
## We are ready to fix the 6 months lag between current API results and
our mocked tests
## No more bug on relation creation / deletion

---------

Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Charles Bochet
2025-06-10 16:45:27 +02:00
committed by GitHub
parent 264861e020
commit a68895189c
426 changed files with 48870 additions and 54125 deletions

View File

@ -1,18 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DataSeedDemoWorkspaceService } from 'src/database/commands/data-seed-demo-workspace/services/data-seed-demo-workspace.service';
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
@Module({
imports: [
WorkspaceManagerModule,
TwentyConfigModule,
TypeOrmModule.forFeature([Workspace], 'core'),
],
providers: [DataSeedDemoWorkspaceService],
exports: [DataSeedDemoWorkspaceService],
})
export class DataSeedDemoWorkspaceModule {}

View File

@ -1,64 +0,0 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
import { deleteCoreSchema } from 'src/database/typeorm-seeds/core/demo';
import { rawDataSource } from 'src/database/typeorm/raw/raw.datasource';
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
@Injectable()
export class DataSeedDemoWorkspaceService {
constructor(
private readonly workspaceManagerService: WorkspaceManagerService,
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
private readonly workspaceSchemaCache: CacheStorageService,
private readonly twentyConfigService: TwentyConfigService,
) {}
async seedDemo(): Promise<void> {
try {
await rawDataSource.initialize();
// TODO: migrate demo seeds to dev seeds
const demoWorkspaceIds = ['', ''];
await this.workspaceSchemaCache.flush();
for (const workspaceId of demoWorkspaceIds) {
const existingWorkspaces = await this.workspaceRepository.findBy({
id: workspaceId,
});
if (existingWorkspaces.length > 0) {
await this.workspaceManagerService.delete(workspaceId);
await deleteCoreSchema(rawDataSource, workspaceId);
}
const appVersion = this.twentyConfigService.get('APP_VERSION');
await seedCoreSchema({
dataSource: rawDataSource,
workspaceId,
appVersion,
seedBilling: false,
seedFeatureFlags: false,
});
await this.workspaceManagerService.initDemo(workspaceId);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return;
}
}
}

View File

@ -1,54 +1,12 @@
import { Logger } from '@nestjs/common';
import { Command, CommandRunner } from 'nest-commander';
import { DataSource } from 'typeorm';
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/database/typeorm-seeds/core/workspaces';
import {
getDevSeedCompanyCustomFields,
getDevSeedPeopleCustomFields,
} from 'src/database/typeorm-seeds/metadata/fieldsMetadata';
import { seedApiKey } from 'src/database/typeorm-seeds/workspace/api-key';
import { seedCalendarChannels } from 'src/database/typeorm-seeds/workspace/calendar-channel';
import { seedCalendarChannelEventAssociations } from 'src/database/typeorm-seeds/workspace/calendar-channel-event-association';
import { seedCalendarEventParticipants } from 'src/database/typeorm-seeds/workspace/calendar-event-participants';
import { seedCalendarEvents } from 'src/database/typeorm-seeds/workspace/calendar-events';
import { seedCompanies } from 'src/database/typeorm-seeds/workspace/companies';
import { seedConnectedAccount } from 'src/database/typeorm-seeds/workspace/connected-account';
import { seedWorkspaceFavorites } from 'src/database/typeorm-seeds/workspace/favorites';
import { seedMessageChannelMessageAssociation } from 'src/database/typeorm-seeds/workspace/message-channel-message-associations';
import { seedMessageChannel } from 'src/database/typeorm-seeds/workspace/message-channels';
import { seedMessageParticipant } from 'src/database/typeorm-seeds/workspace/message-participants';
import { seedMessageThread } from 'src/database/typeorm-seeds/workspace/message-threads';
import { seedMessage } from 'src/database/typeorm-seeds/workspace/messages';
import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunities';
import { seedPeople } from 'src/database/typeorm-seeds/workspace/seedPeople';
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { rawDataSource } from 'src/database/typeorm/raw/raw.datasource';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { PETS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/pets-data-seeds';
import { SURVEY_RESULTS_DATA_SEEDS } from 'src/engine/seeder/data-seeds/survey-results-data-seeds';
import { PETS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/pets-metadata-seeds';
import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds';
import { SeederService } from 'src/engine/seeder/seeder.service';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views';
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
import { opportunitiesTableByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-table-by-stage.view';
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
// TODO: implement dry-run
} from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util';
import { DevSeederService } from 'src/engine/workspace-manager/dev-seeder/services/dev-seeder.service';
@Command({
name: 'workspace:seed:dev',
description:
@ -58,265 +16,18 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
workspaceIds = [SEED_APPLE_WORKSPACE_ID, SEED_ACME_WORKSPACE_ID];
private readonly logger = new Logger(DataSeedWorkspaceCommand.name);
constructor(
private readonly dataSourceService: DataSourceService,
private readonly typeORMService: TypeORMService,
private readonly fieldMetadataService: FieldMetadataService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly seederService: SeederService,
private readonly workspaceManagerService: WorkspaceManagerService,
private readonly twentyConfigService: TwentyConfigService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
) {
constructor(private readonly devSeederService: DevSeederService) {
super();
}
async run(): Promise<void> {
try {
for (const workspaceId of this.workspaceIds) {
await this.createWorkspaceSchema(workspaceId);
await this.devSeederService.seedDev(workspaceId);
}
} catch (error) {
this.logger.error(error);
return;
this.logger.error(error.stack);
}
for (const workspaceId of this.workspaceIds) {
await this.seedWorkspace(workspaceId);
}
}
async createWorkspaceSchema(workspaceId: string) {
const workspaceCachedMetadataVersion =
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
await this.workspaceCacheStorageService.flush(
workspaceId,
workspaceCachedMetadataVersion,
);
await rawDataSource.initialize();
const isBillingEnabled = this.twentyConfigService.get('IS_BILLING_ENABLED');
const appVersion = this.twentyConfigService.get('APP_VERSION');
await seedCoreSchema({
dataSource: rawDataSource,
workspaceId,
seedBilling: isBillingEnabled,
appVersion,
});
await rawDataSource.destroy();
await this.workspaceManagerService.initDev(workspaceId);
}
async seedWorkspace(workspaceId: string) {
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId,
);
const mainDataSource = this.typeORMService.getMainDataSource();
if (!mainDataSource) {
throw new Error('Could not connect to workspace data source');
}
try {
const { objectMetadataStandardIdToIdMap } =
await this.objectMetadataService.getObjectMetadataStandardIdToIdMap(
workspaceId,
);
await this.seedCompanyCustomFields(
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.company].id,
workspaceId,
);
await this.seedPeopleCustomFields(
objectMetadataStandardIdToIdMap[STANDARD_OBJECT_IDS.person].id,
workspaceId,
);
await this.seedCustomObjects({
dataSourceMetadata,
});
await this.seedRecords({
mainDataSource,
dataSourceMetadata,
});
} catch (error) {
this.logger.error(error);
}
}
async seedCustomObjects({
dataSourceMetadata,
}: {
dataSourceMetadata: DataSourceEntity;
}) {
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
dataSourceMetadata.workspaceId,
PETS_METADATA_SEEDS,
);
await this.seederService.seedCustomObjects(
dataSourceMetadata.id,
dataSourceMetadata.workspaceId,
SURVEY_RESULTS_METADATA_SEEDS,
);
}
async seedRecords({
mainDataSource,
dataSourceMetadata,
}: {
mainDataSource: DataSource;
dataSourceMetadata: DataSourceEntity;
}) {
await this.seedStandardObjectRecords(mainDataSource, dataSourceMetadata);
await this.seederService.seedCustomObjectRecords(
dataSourceMetadata.workspaceId,
PETS_METADATA_SEEDS,
PETS_DATA_SEEDS,
);
await this.seederService.seedCustomObjectRecords(
dataSourceMetadata.workspaceId,
SURVEY_RESULTS_METADATA_SEEDS,
SURVEY_RESULTS_DATA_SEEDS,
);
}
async seedStandardObjectRecords(
mainDataSource: DataSource,
dataSourceMetadata: DataSourceEntity,
) {
await mainDataSource.transaction(
async (entityManager: WorkspaceEntityManager) => {
const { objectMetadataStandardIdToIdMap } =
await this.objectMetadataService.getObjectMetadataStandardIdToIdMap(
dataSourceMetadata.workspaceId,
);
await seedCompanies(entityManager, dataSourceMetadata.schema);
await seedPeople(entityManager, dataSourceMetadata.schema);
await seedOpportunity(entityManager, dataSourceMetadata.schema);
await seedWorkspaceMember(
entityManager,
dataSourceMetadata.schema,
dataSourceMetadata.workspaceId,
);
if (dataSourceMetadata.workspaceId === SEED_APPLE_WORKSPACE_ID) {
await seedApiKey(entityManager, dataSourceMetadata.schema);
await seedMessageThread(entityManager, dataSourceMetadata.schema);
await seedConnectedAccount(entityManager, dataSourceMetadata.schema);
await seedMessage(entityManager, dataSourceMetadata.schema);
await seedMessageChannel(entityManager, dataSourceMetadata.schema);
await seedMessageChannelMessageAssociation(
entityManager,
dataSourceMetadata.schema,
);
await seedMessageParticipant(
entityManager,
dataSourceMetadata.schema,
);
await seedCalendarEvents(entityManager, dataSourceMetadata.schema);
await seedCalendarChannels(entityManager, dataSourceMetadata.schema);
await seedCalendarChannelEventAssociations(
entityManager,
dataSourceMetadata.schema,
);
await seedCalendarEventParticipants(
entityManager,
dataSourceMetadata.schema,
);
}
const viewDefinitionsWithId = await seedViewWithDemoData(
entityManager,
dataSourceMetadata.schema,
objectMetadataStandardIdToIdMap,
);
const devViewDefinitionsWithId = await createWorkspaceViews(
entityManager,
dataSourceMetadata.schema,
[opportunitiesTableByStageView(objectMetadataStandardIdToIdMap)],
);
viewDefinitionsWithId.push(...devViewDefinitionsWithId);
await seedWorkspaceFavorites(
viewDefinitionsWithId
.filter(
(view) =>
view.key === 'INDEX' &&
shouldSeedWorkspaceFavorite(
view.objectMetadataId,
objectMetadataStandardIdToIdMap,
),
)
.map((view) => view.id),
entityManager,
dataSourceMetadata.schema,
);
},
);
}
async seedCompanyCustomFields(
companyObjectMetadataId: string,
workspaceId: string,
) {
if (!companyObjectMetadataId) {
throw new Error(
`Company object metadata not found for workspace ${workspaceId}, can't seed custom fields`,
);
}
const DEV_SEED_COMPANY_CUSTOM_FIELDS = getDevSeedCompanyCustomFields(
companyObjectMetadataId,
workspaceId,
);
await this.fieldMetadataService.createMany(
DEV_SEED_COMPANY_CUSTOM_FIELDS.map((customField) => ({
...customField,
isCustom: true,
})),
);
}
async seedPeopleCustomFields(
personObjectMetadataId: string,
workspaceId: string,
) {
if (!personObjectMetadataId) {
throw new Error(
`Person object metadata not found for workspace ${workspaceId}, can't seed custom fields`,
);
}
const DEV_SEED_PERSON_CUSTOM_FIELDS = getDevSeedPeopleCustomFields(
personObjectMetadataId,
workspaceId,
);
await this.fieldMetadataService.createMany(
DEV_SEED_PERSON_CUSTOM_FIELDS.map((customField) => ({
...customField,
isCustom: true,
})),
);
}
}

View File

@ -7,8 +7,8 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { SeederModule } from 'src/engine/seeder/seeder.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { DevSeederModule } from 'src/engine/workspace-manager/dev-seeder/dev-seeder.module';
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
@Module({
@ -19,7 +19,7 @@ import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-m
TypeORMModule,
FieldMetadataModule,
ObjectMetadataModule,
SeederModule,
DevSeederModule,
WorkspaceManagerModule,
DataSourceModule,
WorkspaceCacheStorageModule,

View File

@ -1,232 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import {
ActiveOrSuspendedWorkspacesMigrationCommandOptions,
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
import { MEMBER_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/member-role-label.constants';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@Command({
name: 'upgrade:0-44:initialize-permissions',
description: 'Initialize permissions',
})
export class InitializePermissionsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(UserWorkspace, 'core')
protected readonly userWorkspaceRepository: Repository<UserWorkspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly roleService: RoleService,
private readonly userRoleService: UserRoleService,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
try {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
let adminRoleId: string | undefined;
const workspaceRoles =
await this.roleService.getWorkspaceRoles(workspaceId);
adminRoleId = workspaceRoles.find(
(role) => role.label === ADMIN_ROLE_LABEL,
)?.id;
if (!isDefined(adminRoleId)) {
adminRoleId = await this.createAdminRole({
workspaceId,
options,
});
}
await this.assignAdminRoleToMembers({
workspaceId,
adminRoleId,
options,
});
await this.setAdminRoleAsDefaultRole({
workspaceId,
adminRoleId,
options,
});
const memberRole = workspaceRoles.find(
(role) => role.label === MEMBER_ROLE_LABEL,
);
if (!isDefined(memberRole)) {
await this.createMemberRole({
workspaceId,
options,
});
}
} catch (error) {
this.logger.log(
chalk.red(`Error in workspace ${workspaceId} - ${error.message}`),
);
}
}
private async createAdminRole({
workspaceId,
options,
}: {
workspaceId: string;
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions;
}) {
this.logger.log(
chalk.green(`Creating admin role ${options.dryRun ? '(dry run)' : ''}`),
);
if (options.dryRun) {
return '';
}
const adminRole = await this.roleService.createAdminRole({
workspaceId,
});
return adminRole.id;
}
private async createMemberRole({
workspaceId,
options,
}: {
workspaceId: string;
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions;
}) {
this.logger.log(
chalk.green(`Creating member role ${options.dryRun ? '(dry run)' : ''}`),
);
if (options.dryRun) {
return '';
}
const memberRole = await this.roleService.createMemberRole({
workspaceId,
});
return memberRole.id;
}
private async setAdminRoleAsDefaultRole({
workspaceId,
adminRoleId,
options,
}: {
workspaceId: string;
adminRoleId: string;
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions;
}) {
const workspaceDefaultRole = await this.workspaceRepository.findOne({
where: {
id: workspaceId,
},
});
if (isDefined(workspaceDefaultRole?.defaultRoleId)) {
this.logger.log(
chalk.green(
'Workspace already has a default role. Skipping setting admin role as default role',
),
);
return;
}
this.logger.log(
chalk.green(
`Setting admin role as default role ${options.dryRun ? '(dry run)' : ''}`,
),
);
if (options.dryRun) {
return;
}
await this.workspaceRepository.update(workspaceId, {
defaultRoleId: adminRoleId,
});
}
private async assignAdminRoleToMembers({
workspaceId,
adminRoleId,
options,
}: {
workspaceId: string;
adminRoleId: string;
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions;
}) {
const userWorkspaces = await this.userWorkspaceRepository.find({
where: {
workspaceId,
},
});
const rolesByUserWorkspace =
await this.userRoleService.getRolesByUserWorkspaces({
userWorkspaceIds: userWorkspaces.map(
(userWorkspace) => userWorkspace.id,
),
workspaceId,
});
for (const userWorkspace of userWorkspaces) {
if (
rolesByUserWorkspace
.get(userWorkspace.id)
?.some((role) => isDefined(role))
) {
this.logger.log(
chalk.green(
`User workspace ${userWorkspace.id} already has a role. Skipping role assignation`,
),
);
continue;
}
this.logger.log(
chalk.green(
`Assigning admin role to workspace member ${userWorkspace.id} ${options.dryRun ? '(dry run)' : ''}`,
),
);
if (options.dryRun) {
continue;
}
await this.userRoleService.assignRoleToUserWorkspace({
roleId: adminRoleId,
userWorkspaceId: userWorkspace.id,
workspaceId,
});
}
}
}

View File

@ -1,272 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
const AGGREGATE_OPERATION_OPTIONS = [
{
value: AggregateOperations.AVG,
label: 'Average',
position: 0,
color: 'red',
},
{
value: AggregateOperations.COUNT,
label: 'Count',
position: 1,
color: 'purple',
},
{
value: AggregateOperations.MAX,
label: 'Maximum',
position: 2,
color: 'sky',
},
{
value: AggregateOperations.MIN,
label: 'Minimum',
position: 3,
color: 'turquoise',
},
{
value: AggregateOperations.SUM,
label: 'Sum',
position: 4,
color: 'yellow',
},
{
value: AggregateOperations.COUNT_EMPTY,
label: 'Count empty',
position: 5,
color: 'red',
},
{
value: AggregateOperations.COUNT_NOT_EMPTY,
label: 'Count not empty',
position: 6,
color: 'purple',
},
{
value: AggregateOperations.COUNT_UNIQUE_VALUES,
label: 'Count unique values',
position: 7,
color: 'sky',
},
{
value: AggregateOperations.PERCENTAGE_EMPTY,
label: 'Percent empty',
position: 8,
color: 'turquoise',
},
{
value: AggregateOperations.PERCENTAGE_NOT_EMPTY,
label: 'Percent not empty',
position: 9,
color: 'yellow',
},
{
value: AggregateOperations.COUNT_TRUE,
label: 'Count true',
position: 10,
color: 'red',
},
{
value: AggregateOperations.COUNT_FALSE,
label: 'Count false',
position: 11,
color: 'purple',
},
];
@Command({
name: 'upgrade:0-44:update-view-aggregate-operations',
description:
'Update View and ViewField entities with new aggregate operations (countTrue, countFalse)',
})
export class UpdateViewAggregateOperationsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
private readonly workspaceMigrationService: WorkspaceMigrationService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
await this.updateViewAggregateOperations(workspaceId);
await this.updateViewFieldAggregateOperations(workspaceId);
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
workspaceId,
);
await this.workspaceMetadataVersionService.incrementMetadataVersion(
workspaceId,
);
this.logger.log(
chalk.green(`Command completed for workspace ${workspaceId}.`),
);
}
private async updateViewAggregateOperations(
workspaceId: string,
): Promise<void> {
const viewObjectMetadata = await this.objectMetadataRepository.findOne({
where: {
workspaceId,
standardId: STANDARD_OBJECT_IDS.view,
},
relations: ['fields'],
});
if (!viewObjectMetadata) {
this.logger.warn(
`View object metadata not found for workspace ${workspaceId}`,
);
return;
}
const kanbanAggregateOperationField = viewObjectMetadata.fields.find(
(field) => field.name === 'kanbanAggregateOperation',
);
if (!kanbanAggregateOperationField) {
this.logger.warn(
`kanbanAggregateOperation field not found for workspace ${workspaceId}`,
);
return;
}
await this.fieldMetadataRepository.update(
{ id: kanbanAggregateOperationField.id },
{ options: AGGREGATE_OPERATION_OPTIONS },
);
this.logger.log(
`Updated kanbanAggregateOperation options for workspace ${workspaceId}`,
);
await this.workspaceMigrationService.createCustomMigration(
generateMigrationName(`update-view-operations`),
workspaceId,
[
{
name: computeObjectTargetTable(viewObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.ALTER,
{ ...kanbanAggregateOperationField, options: undefined },
{
...kanbanAggregateOperationField,
options: AGGREGATE_OPERATION_OPTIONS,
},
),
} satisfies WorkspaceMigrationTableAction,
],
);
}
private async updateViewFieldAggregateOperations(
workspaceId: string,
): Promise<void> {
const viewFieldObjectMetadata = await this.objectMetadataRepository.findOne(
{
where: {
workspaceId,
standardId: STANDARD_OBJECT_IDS.viewField,
},
relations: ['fields'],
},
);
if (!viewFieldObjectMetadata) {
this.logger.warn(
`ViewField object metadata not found for workspace ${workspaceId}`,
);
return;
}
const aggregateOperationField = viewFieldObjectMetadata.fields.find(
(field) => field.name === 'aggregateOperation',
);
if (!aggregateOperationField) {
this.logger.warn(
`aggregateOperation field not found for workspace ${workspaceId}`,
);
return;
}
await this.fieldMetadataRepository.update(
{ id: aggregateOperationField.id },
{ options: AGGREGATE_OPERATION_OPTIONS },
);
this.logger.log(
`Updated aggregateOperation options for workspace ${workspaceId}`,
);
await this.workspaceMigrationService.createCustomMigration(
generateMigrationName(`update-view-field-operations`),
workspaceId,
[
{
name: computeObjectTargetTable(viewFieldObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.ALTER,
{ ...aggregateOperationField, options: undefined },
{
...aggregateOperationField,
options: AGGREGATE_OPERATION_OPTIONS,
},
),
} satisfies WorkspaceMigrationTableAction,
],
);
}
}

View File

@ -1,37 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { InitializePermissionsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-initialize-permissions.command';
import { UpdateViewAggregateOperationsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-update-view-aggregate-operations.command';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
@Module({
imports: [
TypeOrmModule.forFeature([Workspace, UserWorkspace], 'core'),
TypeOrmModule.forFeature(
[FieldMetadataEntity, ObjectMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
RoleModule,
UserRoleModule,
WorkspaceMigrationModule,
WorkspaceMigrationRunnerModule,
WorkspaceMetadataVersionModule,
],
providers: [
InitializePermissionsCommand,
UpdateViewAggregateOperationsCommand,
],
exports: [InitializePermissionsCommand, UpdateViewAggregateOperationsCommand],
})
export class V0_44_UpgradeVersionCommandModule {}

View File

@ -1,32 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
@Module({
imports: [
TypeOrmModule.forFeature([Workspace, UserWorkspace], 'core'),
TypeOrmModule.forFeature(
[FieldMetadataEntity, ObjectMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
RoleModule,
UserRoleModule,
WorkspaceMigrationModule,
WorkspaceMigrationRunnerModule,
WorkspaceMetadataVersionModule,
],
providers: [],
exports: [],
})
export class V0_50_UpgradeVersionCommandModule {}

View File

@ -1,112 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { isDefined } from 'twenty-shared/utils';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Command({
name: 'upgrade:0-51:upgrade-created-by-enum',
description: 'Upgrade created by enum',
})
export class UpgradeCreatedByEnumCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const workspaceDataSource =
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
workspaceId,
});
const objectMetadatas = await this.objectMetadataRepository.find({
where: {
workspaceId,
},
relations: ['fields'],
});
const queryRunner = workspaceDataSource?.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
for (const objectMetadata of objectMetadatas) {
if (
!isDefined(
objectMetadata.fields.find((field) => field.name === 'createdBy'),
)
) {
continue;
}
const tableToUpdate = computeTableName(
objectMetadata.nameSingular,
objectMetadata.isCustom,
);
// Set current column as text
await queryRunner.query(
`ALTER TABLE "${schemaName}"."${tableToUpdate}"
ALTER COLUMN "createdBySource" SET DATA TYPE text USING "createdBySource"::text`,
);
// Drop default value
await queryRunner.query(
`ALTER TABLE "${schemaName}"."${tableToUpdate}"
ALTER COLUMN "createdBySource" DROP DEFAULT`,
);
// Drop the old enum type
await queryRunner.query(
`DROP TYPE "${schemaName}"."${tableToUpdate}_createdBySource_enum"`,
);
await queryRunner.query(
`CREATE TYPE "${schemaName}"."${tableToUpdate}_createdBySource_enum" AS ENUM ('EMAIL', 'CALENDAR', 'WORKFLOW', 'API', 'IMPORT', 'MANUAL', 'SYSTEM', 'WEBHOOK')`,
);
// Re-apply the enum type
await queryRunner.query(
`ALTER TABLE "${schemaName}"."${tableToUpdate}"
ALTER COLUMN "createdBySource" SET DATA TYPE "${schemaName}"."${tableToUpdate}_createdBySource_enum" USING "createdBySource"::"${schemaName}"."${tableToUpdate}_createdBySource_enum"`,
);
}
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
}

View File

@ -1,22 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { UpgradeCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/0-51/0-51-update-workflow-trigger-type-enum.command';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
@Module({
imports: [
TypeOrmModule.forFeature([Workspace], 'core'),
TypeOrmModule.forFeature(
[ObjectMetadataEntity, FieldMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
],
providers: [UpgradeCreatedByEnumCommand],
exports: [UpgradeCreatedByEnumCommand],
})
export class V0_51_UpgradeVersionCommandModule {}

View File

@ -1,145 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { Command } from 'nest-commander';
import { FieldMetadataType } from 'twenty-shared/types';
import { In, Repository } from 'typeorm';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import {
RelationDirection,
deduceRelationDirection,
} from 'src/engine/utils/deduce-relation-direction.util';
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
@Command({
name: 'upgrade:0-52:migrate-relations-to-field-metadata',
description: 'Migrate relations to field metadata',
})
export class MigrateRelationsToFieldMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const fieldMetadataCollection = await this.fieldMetadataRepository.find({
where: {
workspaceId,
type: In([FieldMetadataType.RELATION, FieldMetadataType.UUID]),
},
relations: ['fromRelationMetadata', 'toRelationMetadata'],
});
if (!fieldMetadataCollection.length) {
this.logger.log(
chalk.yellow(
`No relation field metadata found for workspace ${workspaceId}.`,
),
);
return;
}
const joinColumnFieldMetadataCollection = fieldMetadataCollection.filter(
(fieldMetadata) =>
isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.UUID),
// TODO: Fix this, it's working in other places but not here
) as FieldMetadataEntity<FieldMetadataType.UUID>[];
const fieldMetadataToUpdateCollection = fieldMetadataCollection
.filter((fieldMetadata) =>
isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION),
)
.map((fieldMetadata) =>
this.updateRelationFieldMetadata(
joinColumnFieldMetadataCollection,
// TODO: Fix this, it's working in other places but not here
fieldMetadata as FieldMetadataEntity<FieldMetadataType.RELATION>,
),
);
if (fieldMetadataToUpdateCollection.length > 0) {
await this.fieldMetadataRepository.save(fieldMetadataToUpdateCollection);
}
this.logger.log(
chalk.green(`Command completed for workspace ${workspaceId}.`),
);
}
private updateRelationFieldMetadata(
joinColumnFieldMetadataCollection: FieldMetadataEntity<FieldMetadataType.UUID>[],
fieldMetadata: FieldMetadataEntity<FieldMetadataType.RELATION>,
): FieldMetadataEntity<FieldMetadataType.RELATION> {
const relationMetadata =
fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata;
const joinColumnFieldMetadata = joinColumnFieldMetadataCollection.find(
(joinColumnFieldMetadata) =>
// We're deducing the field based on the name of the relation field
// This is not the best way to do this but we don't have a better way
joinColumnFieldMetadata.name === `${fieldMetadata.name}Id`,
);
const relationDirection = deduceRelationDirection(
fieldMetadata,
relationMetadata,
);
let relationType = relationMetadata.relationType as unknown as RelationType;
if (
relationDirection === RelationDirection.TO &&
relationType === RelationType.ONE_TO_MANY
) {
relationType = RelationType.MANY_TO_ONE;
}
const relationTargetFieldMetadataId =
relationDirection === RelationDirection.FROM
? relationMetadata.toFieldMetadataId
: relationMetadata.fromFieldMetadataId;
const relationTargetObjectMetadataId =
relationDirection === RelationDirection.FROM
? relationMetadata.toObjectMetadataId
: relationMetadata.fromObjectMetadataId;
return {
...fieldMetadata,
settings: {
relationType,
onDelete:
relationType === RelationType.MANY_TO_ONE
? relationMetadata.onDeleteAction
: undefined,
joinColumnName:
relationType === RelationType.MANY_TO_ONE
? joinColumnFieldMetadata?.name
: undefined,
},
relationTargetFieldMetadataId,
relationTargetObjectMetadataId,
};
}
}

View File

@ -1,90 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { Command } from 'nest-commander';
import { FieldMetadataType } from 'twenty-shared/types';
import { In, Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
import { DateDisplayFormat } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
type DeprecatedFieldMetadataDateSettings = {
displayAsRelativeDate?: boolean;
};
@Command({
name: 'upgrade:0-52:upgrade-date-and-date-time-field-settings',
description: 'Upgrade settings column on all date and date time fields',
})
export class UpgradeDateAndDateTimeFieldsSettingsJsonCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const fieldMetadataCollection = (await this.fieldMetadataRepository.find({
where: {
workspaceId,
type: In([FieldMetadataType.DATE, FieldMetadataType.DATE_TIME]),
},
})) as FieldMetadataEntity<FieldMetadataType.DATE>[];
const updatedFieldMetadataCollection = fieldMetadataCollection.map(
(field) => this.updateDateAndDateTimeFieldMetadata(field),
);
if (updatedFieldMetadataCollection.length > 0) {
await this.fieldMetadataRepository.save(updatedFieldMetadataCollection);
}
this.logger.log(
chalk.green(`Command completed for workspace ${workspaceId}.`),
);
}
private updateDateAndDateTimeFieldMetadata(
field: FieldMetadataEntity<FieldMetadataType.DATE>,
): FieldMetadataEntity<FieldMetadataType.DATE> {
const settings = field.settings as DeprecatedFieldMetadataDateSettings;
if (!isDefined(settings?.displayAsRelativeDate)) {
return field;
}
return {
...field,
settings: {
displayFormat: settings.displayAsRelativeDate
? DateDisplayFormat.RELATIVE
: DateDisplayFormat.USER_SETTINGS,
},
};
}
}

View File

@ -1,29 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-migrate-relations-to-field-metadata.command';
import { UpgradeDateAndDateTimeFieldsSettingsJsonCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [
TypeOrmModule.forFeature([Workspace], 'core'),
TypeOrmModule.forFeature(
[ObjectMetadataEntity, FieldMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
],
providers: [
UpgradeDateAndDateTimeFieldsSettingsJsonCommand,
MigrateRelationsToFieldMetadataCommand,
],
exports: [
UpgradeDateAndDateTimeFieldsSettingsJsonCommand,
MigrateRelationsToFieldMetadataCommand,
],
})
export class V0_52_UpgradeVersionCommandModule {}

View File

@ -1,151 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
@Command({
name: 'upgrade:0-53:backfill-workflow-next-step-ids',
description: 'Backfill workflow next step ids',
})
export class BackfillWorkflowNextStepIdsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const workflowVersionRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
workspaceId,
'workflowVersion',
);
const workflowRunRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowRunWorkspaceEntity>(
workspaceId,
'workflowRun',
);
const workflowVersions = await workflowVersionRepository.find();
this.logger.log(`Number of workflow versions: ${workflowVersions.length}`);
for (const workflowVersion of workflowVersions) {
this.logger.log(`Processing workflow version: ${workflowVersion.id}`);
const updatedSteps: WorkflowVersionWorkspaceEntity['steps'] = [];
const workflowSteps = workflowVersion.steps;
if (!workflowSteps) {
continue;
}
// for each step, add the next step id which is the next index
for (let stepIndex = 0; stepIndex < workflowSteps.length; stepIndex++) {
const updatedStep = {
...workflowSteps[stepIndex],
nextStepIds:
stepIndex < workflowSteps.length - 1 &&
workflowSteps[stepIndex + 1]?.id
? [workflowSteps[stepIndex + 1].id]
: undefined,
};
updatedSteps.push(updatedStep);
}
// update workflow run flows by batch of 500
const batchSize = 500;
const totalWorkflowRuns = await workflowRunRepository.count({
where: {
workflowVersionId: workflowVersion.id,
},
});
const totalBatches = Math.ceil(totalWorkflowRuns / batchSize);
this.logger.log(`Total batches: ${totalBatches}`);
for (let batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
const updatedWorkflowRuns: WorkflowRunWorkspaceEntity[] = [];
this.logger.log(`Processing batch ${batchIndex + 1}/${totalBatches}`);
const workflowRuns = await workflowRunRepository.find({
where: {
workflowVersionId: workflowVersion.id,
},
take: batchSize,
skip: batchIndex * batchSize,
order: {
id: 'ASC',
},
});
for (const workflowRun of workflowRuns) {
const flow = workflowRun.output?.flow;
if (!flow?.steps || flow.steps.length < 2) {
continue;
}
const updatedStepsMap = new Map(
updatedSteps.map((step) => [step.id, step]),
);
const updatedFlow = flow.steps.map((step) => {
const updatedStep = updatedStepsMap.get(step.id);
return {
...step,
nextStepIds: updatedStep?.nextStepIds,
};
});
const updatedWorkflowRun: WorkflowRunWorkspaceEntity = {
...workflowRun,
output: {
...workflowRun.output,
flow: {
trigger: workflowRun.output?.flow?.trigger as WorkflowTrigger,
steps: updatedFlow,
},
},
};
updatedWorkflowRuns.push(updatedWorkflowRun);
}
await workflowRunRepository.save(updatedWorkflowRuns);
}
await workflowVersionRepository.save({
...workflowVersion,
steps: updatedSteps,
});
this.logger.log(
`Updated workflow version ${workflowVersion.id} for workspace ${workspaceId}`,
);
}
}
}

View File

@ -1,108 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
MigrationCommandOptions,
MigrationCommandRunner,
} from 'src/database/commands/command-runners/migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@Command({
name: 'upgrade:0-53:copy-typeorm-migrations',
description: 'Copy _typeorm_migrations from metadata schema to core schema',
})
export class CopyTypeormMigrationsCommand extends MigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super();
}
override async runMigrationCommand(
_passedParams: string[],
options: MigrationCommandOptions,
): Promise<void> {
this.logger.log(
'Starting to copy _typeorm_migrations from metadata to core',
);
const queryRunner =
this.workspaceRepository.manager.connection.createQueryRunner();
try {
await queryRunner.connect();
await queryRunner.startTransaction();
// Check if metadata._typeorm_migrations table exists
const tableExists = await queryRunner.query(
`SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'metadata'
AND table_name = '_typeorm_migrations'
)`,
);
if (!tableExists[0].exists) {
this.logger.log(
'metadata._typeorm_migrations table does not exist, skipping migration',
);
await queryRunner.commitTransaction();
return;
}
const metadataMigrations = await queryRunner.query(
'SELECT * FROM metadata._typeorm_migrations ORDER BY id ASC',
);
this.logger.log(
`Found ${metadataMigrations.length} migrations in metadata schema`,
);
if (options?.dryRun) {
this.logger.log('Dry run mode - no changes will be applied');
return;
}
const existingCoreMigrations = await queryRunner.query(
'SELECT name FROM core._typeorm_migrations',
);
const existingMigrationNames = new Set(
// @ts-expect-error legacy noImplicitAny
existingCoreMigrations.map((migration) => migration.name),
);
for (const migration of metadataMigrations) {
if (!existingMigrationNames.has(migration.name)) {
await queryRunner.query(
'INSERT INTO core._typeorm_migrations ("timestamp", name) VALUES ($1, $2)',
[migration.timestamp, migration.name],
);
this.logger.log(`Copied migration: ${migration.name}`);
} else {
this.logger.log(
`Migration ${migration.name} already exists in core schema`,
);
}
}
await queryRunner.commitTransaction();
this.logger.log(
'Successfully copied all migrations from metadata to core schema',
);
} catch (error) {
await queryRunner.rollbackTransaction();
this.logger.error(`Failed to copy migrations: ${error.message}`);
throw error;
} finally {
await queryRunner.release();
}
}
}

View File

@ -1,71 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import {
AutomatedTriggerType,
WorkflowAutomatedTriggerWorkspaceEntity,
} from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity';
@Command({
name: 'upgrade:0-53:migrate-workflow-event-listeners-to-automated-triggers',
description: 'Migrate workflow event listeners to automated triggers',
})
export class MigrateWorkflowEventListenersToAutomatedTriggersCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const workflowEventListenerRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
workspaceId,
'workflowEventListener',
);
const workflowAutomatedTriggerRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowAutomatedTriggerWorkspaceEntity>(
workspaceId,
'workflowAutomatedTrigger',
);
const workflowEventListeners = await workflowEventListenerRepository.find();
await workflowAutomatedTriggerRepository.delete({
type: AutomatedTriggerType.DATABASE_EVENT,
});
for (const eventListener of workflowEventListeners) {
const { eventName, ...rest } = eventListener;
await workflowAutomatedTriggerRepository.save({
...rest,
type: AutomatedTriggerType.DATABASE_EVENT,
settings: { eventName },
});
}
this.logger.log(
`Migrated ${workflowEventListeners.length} workflow event listeners to automated triggers`,
);
}
}

View File

@ -1,84 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk';
import { Command } from 'nest-commander';
import { FieldMetadataType } from 'twenty-shared/types';
import { In, Like, Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
@Command({
name: 'upgrade:0-53:remove-relation-foreign-key-field-metadata',
description: 'Remove relation foreign key from field metadata',
})
export class RemoveRelationForeignKeyFieldMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly featureFlagService: FeatureFlagService,
@InjectRepository(FieldMetadataEntity, 'metadata')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const fieldMetadataCollection = await this.fieldMetadataRepository.find({
where: {
workspaceId,
type: In([FieldMetadataType.UUID]),
label: Like('%(foreign key)%'),
},
});
if (!fieldMetadataCollection.length) {
this.logger.log(
chalk.yellow(
`No relation field metadata found for workspace ${workspaceId}.`,
),
);
return;
}
if (options.dryRun) {
this.logger.log(
chalk.yellow(
`Dry run, would delete ${fieldMetadataCollection.length} relation field metadata for workspace ${workspaceId}.`,
),
);
} else {
await this.featureFlagService.enableFeatureFlags(
['IS_NEW_RELATION_ENABLED' as FeatureFlagKey],
workspaceId,
);
await this.fieldMetadataRepository.delete({
id: In(
fieldMetadataCollection.map((fieldMetadata) => fieldMetadata.id),
),
});
}
this.logger.log(
chalk.green(`Command completed for workspace ${workspaceId}.`),
);
}
}

View File

@ -1,76 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { SearchVectorService } from 'src/engine/metadata-modules/search-vector/search-vector.service';
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { SEARCH_FIELDS_FOR_PERSON } from 'src/modules/person/standard-objects/person.workspace-entity';
@Command({
name: 'upgrade:0-53:upgrade-search-vector-on-person-entity',
description: 'Upgrade search vector on person entity',
})
export class UpgradeSearchVectorOnPersonEntityCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(ObjectMetadataEntity, 'metadata')
protected readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly searchVectorService: SearchVectorService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
);
const personObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
select: ['id'],
where: {
workspaceId,
standardId: STANDARD_OBJECT_IDS.person,
},
});
if (!options.dryRun) {
await this.searchVectorService.updateSearchVector(
personObjectMetadata.id,
SEARCH_FIELDS_FOR_PERSON,
workspaceId,
);
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
workspaceId,
);
await this.workspaceMetadataVersionService.incrementMetadataVersion(
workspaceId,
);
}
this.logger.log(
`Migrated search vector on person entity for workspace ${workspaceId}`,
);
}
}

View File

@ -1,46 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BackfillWorkflowNextStepIdsCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-backfill-workflow-next-step-ids.command';
import { CopyTypeormMigrationsCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-copy-typeorm-migrations.command';
import { MigrateWorkflowEventListenersToAutomatedTriggersCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-migrate-workflow-event-listeners-to-automated-triggers.command';
import { RemoveRelationForeignKeyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-remove-relation-foreign-key-field-metadata.command';
import { UpgradeSearchVectorOnPersonEntityCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-upgrade-search-vector-on-person-entity.command';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { SearchVectorModule } from 'src/engine/metadata-modules/search-vector/search-vector.module';
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
@Module({
imports: [
TypeOrmModule.forFeature([Workspace], 'core'),
TypeOrmModule.forFeature(
[FieldMetadataEntity, ObjectMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
SearchVectorModule,
WorkspaceMigrationRunnerModule,
WorkspaceMetadataVersionModule,
FeatureFlagModule,
],
providers: [
MigrateWorkflowEventListenersToAutomatedTriggersCommand,
CopyTypeormMigrationsCommand,
BackfillWorkflowNextStepIdsCommand,
RemoveRelationForeignKeyFieldMetadataCommand,
UpgradeSearchVectorOnPersonEntityCommand,
],
exports: [
MigrateWorkflowEventListenersToAutomatedTriggersCommand,
RemoveRelationForeignKeyFieldMetadataCommand,
BackfillWorkflowNextStepIdsCommand,
CopyTypeormMigrationsCommand,
UpgradeSearchVectorOnPersonEntityCommand,
],
})
export class V0_53_UpgradeVersionCommandModule {}

View File

@ -25,7 +25,7 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(ObjectMetadataEntity, 'metadata')
@InjectRepository(ObjectMetadataEntity, 'core')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {

View File

@ -22,7 +22,7 @@ export class FixStandardSelectFieldsPositionCommand extends ActiveOrSuspendedWor
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(FieldMetadataEntity, 'metadata')
@InjectRepository(FieldMetadataEntity, 'core')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
) {

View File

@ -26,7 +26,7 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
),
TypeOrmModule.forFeature(
[FieldMetadataEntity, ObjectMetadataEntity],
'metadata',
'core',
),
WorkspaceDataSourceModule,
WorkspaceMigrationRunnerModule,

View File

@ -15,13 +15,16 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
@Module({
imports: [
TypeOrmModule.forFeature(
[Workspace, AppToken, User, UserWorkspace],
[
Workspace,
AppToken,
User,
UserWorkspace,
FieldMetadataEntity,
ObjectMetadataEntity,
],
'core',
),
TypeOrmModule.forFeature(
[FieldMetadataEntity, ObjectMetadataEntity],
'metadata',
),
WorkspaceDataSourceModule,
WorkspaceMigrationRunnerModule,
WorkspaceMetadataVersionModule,

View File

@ -1,11 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { V0_44_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-44/0-44-upgrade-version-command.module';
import { V0_50_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-50/0-50-upgrade-version-command.module';
import { V0_51_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-51/0-51-upgrade-version-command.module';
import { V0_52_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-version-command.module';
import { V0_53_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-53/0-53-upgrade-version-command.module';
import { V0_54_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-54/0-54-upgrade-version-command.module';
import { V0_55_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-55/0-55-upgrade-version-command.module';
import {
@ -18,11 +13,6 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
@Module({
imports: [
TypeOrmModule.forFeature([Workspace], 'core'),
V0_44_UpgradeVersionCommandModule,
V0_50_UpgradeVersionCommandModule,
V0_51_UpgradeVersionCommandModule,
V0_52_UpgradeVersionCommandModule,
V0_53_UpgradeVersionCommandModule,
V0_54_UpgradeVersionCommandModule,
V0_55_UpgradeVersionCommandModule,
WorkspaceSyncMetadataModule,

View File

@ -15,16 +15,6 @@ import {
UpgradeCommandRunner,
VersionCommands,
} from 'src/database/commands/command-runners/upgrade.command-runner';
import { InitializePermissionsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-initialize-permissions.command';
import { UpdateViewAggregateOperationsCommand } from 'src/database/commands/upgrade-version-command/0-44/0-44-update-view-aggregate-operations.command';
import { UpgradeCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/0-51/0-51-update-workflow-trigger-type-enum.command';
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-migrate-relations-to-field-metadata.command';
import { UpgradeDateAndDateTimeFieldsSettingsJsonCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field';
import { BackfillWorkflowNextStepIdsCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-backfill-workflow-next-step-ids.command';
import { CopyTypeormMigrationsCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-copy-typeorm-migrations.command';
import { MigrateWorkflowEventListenersToAutomatedTriggersCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-migrate-workflow-event-listeners-to-automated-triggers.command';
import { RemoveRelationForeignKeyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-remove-relation-foreign-key-field-metadata.command';
import { UpgradeSearchVectorOnPersonEntityCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-upgrade-search-vector-on-person-entity.command';
import { CleanNotFoundFilesCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-clean-not-found-files.command';
import { FixCreatedByDefaultValueCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-created-by-default-value.command';
import { FixStandardSelectFieldsPositionCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-fix-standard-select-fields-position.command';
@ -45,7 +35,6 @@ export class DatabaseMigrationService {
constructor(
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
protected readonly copyTypeormMigrationsCommand: CopyTypeormMigrationsCommand,
) {}
// TODO centralize with ActiveOrSuspendedRunner method
@ -75,17 +64,6 @@ export class DatabaseMigrationService {
this.logger.log('Running global database migrations');
try {
this.logger.log('Running metadata datasource migrations...');
await this.copyTypeormMigrationsCommand.runMigrationCommand([], {
dryRun: false,
verbose: false,
});
const metadataResult = await execPromise(
'npx -y typeorm migration:run -d dist/src/database/typeorm/metadata/metadata.datasource',
);
this.logger.log(metadataResult.stdout);
this.logger.log('Running core datasource migrations...');
const coreResult = await execPromise(
'npx -y typeorm migration:run -d dist/src/database/typeorm/core/core.datasource',
@ -147,24 +125,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
private readonly databaseMigrationService: DatabaseMigrationService,
// 0.44 Commands
protected readonly initializePermissionsCommand: InitializePermissionsCommand,
protected readonly updateViewAggregateOperationsCommand: UpdateViewAggregateOperationsCommand,
// 0.51 Commands
protected readonly upgradeCreatedByEnumCommand: UpgradeCreatedByEnumCommand,
// 0.52 Commands
protected readonly upgradeDateAndDateTimeFieldsSettingsJsonCommand: UpgradeDateAndDateTimeFieldsSettingsJsonCommand,
protected readonly migrateRelationsToFieldMetadataCommand: MigrateRelationsToFieldMetadataCommand,
// 0.53 Commands
protected readonly migrateWorkflowEventListenersToAutomatedTriggersCommand: MigrateWorkflowEventListenersToAutomatedTriggersCommand,
protected readonly backfillWorkflowNextStepIdsCommand: BackfillWorkflowNextStepIdsCommand,
protected readonly copyTypeormMigrationsCommand: CopyTypeormMigrationsCommand,
protected readonly upgradeSearchVectorOnPersonEntityCommand: UpgradeSearchVectorOnPersonEntityCommand,
protected readonly removeRelationForeignKeyFieldMetadataCommand: RemoveRelationForeignKeyFieldMetadataCommand,
// 0.54 Commands
protected readonly fixStandardSelectFieldsPositionCommand: FixStandardSelectFieldsPositionCommand,
protected readonly fixCreatedByDefaultValueCommand: FixCreatedByDefaultValueCommand,
@ -179,41 +139,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
syncWorkspaceMetadataCommand,
);
const commands_044: VersionCommands = {
beforeSyncMetadata: [
this.initializePermissionsCommand,
this.updateViewAggregateOperationsCommand,
],
afterSyncMetadata: [],
};
const commands_050: VersionCommands = {
beforeSyncMetadata: [],
afterSyncMetadata: [],
};
const commands_051: VersionCommands = {
beforeSyncMetadata: [this.upgradeCreatedByEnumCommand],
afterSyncMetadata: [],
};
const commands_052: VersionCommands = {
beforeSyncMetadata: [
this.upgradeDateAndDateTimeFieldsSettingsJsonCommand,
this.migrateRelationsToFieldMetadataCommand,
],
afterSyncMetadata: [],
};
const commands_053: VersionCommands = {
beforeSyncMetadata: [this.removeRelationForeignKeyFieldMetadataCommand],
afterSyncMetadata: [
this.migrateWorkflowEventListenersToAutomatedTriggersCommand,
this.backfillWorkflowNextStepIdsCommand,
this.upgradeSearchVectorOnPersonEntityCommand,
],
};
const commands_054: VersionCommands = {
beforeSyncMetadata: [
this.fixStandardSelectFieldsPositionCommand,
@ -227,11 +152,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
};
this.allCommands = {
'0.44.0': commands_044,
'0.50.0': commands_050,
'0.51.0': commands_051,
'0.52.0': commands_052,
'0.53.0': commands_053,
'0.54.0': commands_054,
};
}

View File

@ -1,33 +0,0 @@
import { DataSource } from 'typeorm';
const tableName = 'billingSubscription';
export const seedBillingSubscriptions = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'workspaceId',
'stripeCustomerId',
'stripeSubscriptionId',
'status',
'metadata',
])
.orIgnore()
.values([
{
workspaceId,
stripeCustomerId: 'cus_default0',
stripeSubscriptionId: 'sub_default0',
status: 'active',
metadata: {
workspaceId,
},
},
])
.execute();
};

View File

@ -1,16 +0,0 @@
import { DataSource } from 'typeorm';
const tableName = 'featureFlag';
export const deleteFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, { workspaceId })
.execute();
};

View File

@ -1,19 +0,0 @@
import { DataSource } from 'typeorm';
import { deleteFeatureFlags } from 'src/database/typeorm-seeds/core/demo/feature-flags';
import { deleteUserWorkspaces } from 'src/database/typeorm-seeds/core/demo/user-workspaces';
import { deleteUsersByWorkspace } from 'src/database/typeorm-seeds/core/demo/users';
import { deleteWorkspaces } from 'src/database/typeorm-seeds/core/demo/workspaces';
export const deleteCoreSchema = async (
workspaceDataSource: DataSource,
workspaceId: string,
) => {
const schemaName = 'core';
await deleteUserWorkspaces(workspaceDataSource, schemaName, workspaceId);
await deleteUsersByWorkspace(workspaceDataSource, schemaName, workspaceId);
await deleteFeatureFlags(workspaceDataSource, schemaName, workspaceId);
// deleteWorkspaces should be last
await deleteWorkspaces(workspaceDataSource, schemaName, workspaceId);
};

View File

@ -1,56 +0,0 @@
import { DataSource } from 'typeorm';
import { DEMO_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/demo/users';
const tableName = 'userWorkspace';
export const DEV_SEED_USER_WORKSPACE_IDS = {
NOAH: '20202020-9e3b-46d4-a556-88b9ddc2b534',
HUGO: '20202020-3957-4908-9c36-2929a23f8457',
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b015',
};
export const seedUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
.orIgnore()
.values([
{
id: DEV_SEED_USER_WORKSPACE_IDS.NOAH,
userId: DEMO_SEED_USER_IDS.NOAH,
workspaceId: workspaceId,
},
{
id: DEV_SEED_USER_WORKSPACE_IDS.HUGO,
userId: DEMO_SEED_USER_IDS.HUGO,
workspaceId: workspaceId,
},
{
id: DEV_SEED_USER_WORKSPACE_IDS.TIM,
userId: DEMO_SEED_USER_IDS.TIM,
workspaceId: workspaceId,
},
])
.execute();
};
export const deleteUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, {
workspaceId,
})
.execute();
};

View File

@ -1,77 +0,0 @@
import { DataSource } from 'typeorm';
// import { SeedWorkspaceId } from 'src/database/typeorm-seeds/core/workspaces';
const tableName = 'user';
export const DEMO_SEED_USER_IDS = {
NOAH: '20202020-9e3b-46d4-a556-88b9ddc2b035',
HUGO: '20202020-3957-4908-9c36-2929a23f8358',
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034',
};
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'firstName',
'lastName',
'email',
'passwordHash',
'isEmailVerified',
])
.orIgnore()
.values([
{
id: DEMO_SEED_USER_IDS.NOAH,
firstName: 'Noah',
lastName: 'A',
email: 'noah@demo.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
isEmailVerified: true,
},
{
id: DEMO_SEED_USER_IDS.HUGO,
firstName: 'Hugo',
lastName: 'I',
email: 'hugo@demo.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
isEmailVerified: true,
},
{
id: DEMO_SEED_USER_IDS.TIM,
firstName: 'Tim',
lastName: 'Apple',
email: 'tim@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
isEmailVerified: true,
},
])
.execute();
};
export const deleteUsersByWorkspace = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
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}"."id" IN (:...ids)`, { ids: user.map((u) => u.id) })
.execute();
};

View File

@ -1,49 +0,0 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { DataSource } from 'typeorm';
const tableName = 'workspace';
export const seedWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'displayName',
'domainName',
'inviteHash',
'logo',
'subdomain',
'activationStatus',
])
.orIgnore()
.values([
{
id: workspaceId,
displayName: 'Demo',
domainName: 'demo.dev',
inviteHash: 'demo.dev-invite-hash',
logo: 'https://twentyhq.github.io/placeholder-images/workspaces/apple-logo.png',
subdomain: 'demo',
activationStatus: WorkspaceActivationStatus.ACTIVE,
},
])
.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();
};

View File

@ -1,63 +0,0 @@
import { DataSource } from 'typeorm';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
const tableName = 'featureFlag';
export const seedFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value'])
.orIgnore()
.values([
{
key: FeatureFlagKey.IS_AIRTABLE_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_POSTGRESQL_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_STRIPE_INTEGRATION_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKey.IS_UNIQUE_INDEXES_ENABLED,
workspaceId: workspaceId,
value: false,
},
{
key: FeatureFlagKey.IS_AI_ENABLED,
workspaceId: workspaceId,
value: true,
},
])
.execute();
};
export const deleteFeatureFlags = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, { workspaceId })
.execute();
};

View File

@ -1,42 +0,0 @@
import { DataSource } from 'typeorm';
import { seedBillingSubscriptions } from 'src/database/typeorm-seeds/core/billing/billing-subscription';
import { seedFeatureFlags } from 'src/database/typeorm-seeds/core/feature-flags';
import { seedUserWorkspaces } from 'src/database/typeorm-seeds/core/user-workspaces';
import { seedUsers } from 'src/database/typeorm-seeds/core/users';
import { seedWorkspaces } from 'src/database/typeorm-seeds/core/workspaces';
type SeedCoreSchemaArgs = {
dataSource: DataSource;
workspaceId: string;
appVersion: string | undefined;
seedBilling?: boolean;
seedFeatureFlags?: boolean;
};
export const seedCoreSchema = async ({
appVersion,
dataSource,
workspaceId,
seedBilling = true,
seedFeatureFlags: shouldSeedFeatureFlags = true,
}: SeedCoreSchemaArgs) => {
const schemaName = 'core';
await seedWorkspaces({
dataSource,
schemaName,
workspaceId,
appVersion,
});
await seedUsers(dataSource, schemaName);
await seedUserWorkspaces(dataSource, schemaName, workspaceId);
if (shouldSeedFeatureFlags) {
await seedFeatureFlags(dataSource, schemaName, workspaceId);
}
if (seedBilling) {
await seedBillingSubscriptions(dataSource, schemaName, workspaceId);
}
};

View File

@ -1,78 +0,0 @@
import { DataSource } from 'typeorm';
import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/database/typeorm-seeds/core/workspaces';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
const tableName = 'userWorkspace';
export const DEV_SEED_USER_WORKSPACE_IDS = {
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b035',
JONY: '20202020-3957-4908-9c36-2929a23f8353',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b1',
TIM_ACME: '20202020-e10a-4c27-a90b-b08c57b02d44',
};
export const seedUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
let userWorkspaces: Pick<UserWorkspace, 'id' | 'userId' | 'workspaceId'>[] =
[];
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
userWorkspaces = [
{
id: DEV_SEED_USER_WORKSPACE_IDS.TIM,
userId: DEV_SEED_USER_IDS.TIM,
workspaceId,
},
{
id: DEV_SEED_USER_WORKSPACE_IDS.JONY,
userId: DEV_SEED_USER_IDS.JONY,
workspaceId,
},
{
id: DEV_SEED_USER_WORKSPACE_IDS.PHIL,
userId: DEV_SEED_USER_IDS.PHIL,
workspaceId,
},
];
}
if (workspaceId === SEED_ACME_WORKSPACE_ID) {
userWorkspaces = [
{
id: DEV_SEED_USER_WORKSPACE_IDS.TIM_ACME,
userId: DEV_SEED_USER_IDS.TIM,
workspaceId,
},
];
}
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
.orIgnore()
.values(userWorkspaces)
.execute();
};
export const deleteUserWorkspaces = async (
dataSource: DataSource,
schemaName: string,
workspaceId: string,
) => {
await dataSource
.createQueryBuilder()
.delete()
.from(`${schemaName}.${tableName}`)
.where(`"${tableName}"."workspaceId" = :workspaceId`, {
workspaceId,
})
.execute();
};

View File

@ -1,62 +0,0 @@
import { DataSource } from 'typeorm';
const tableName = 'user';
export const DEV_SEED_USER_IDS = {
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b034',
JONY: '20202020-3957-4908-9c36-2929a23f8357',
PHIL: '20202020-7169-42cf-bc47-1cfef15264b8',
};
export const seedUsers = async (dataSource: DataSource, schemaName: string) => {
await dataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'firstName',
'lastName',
'email',
'passwordHash',
'canImpersonate',
'canAccessFullAdminPanel',
'isEmailVerified',
])
.orIgnore()
.values([
{
id: DEV_SEED_USER_IDS.TIM,
firstName: 'Tim',
lastName: 'Apple',
email: 'tim@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
{
id: DEV_SEED_USER_IDS.JONY,
firstName: 'Jony',
lastName: 'Ive',
email: 'jony.ive@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
{
id: DEV_SEED_USER_IDS.PHIL,
firstName: 'Phil',
lastName: 'Schiler',
email: 'phil.schiler@apple.dev',
passwordHash:
'$2b$10$3LwXjJRtLsfx4hLuuXhxt.3mWgismTiZFCZSG3z9kDrSfsrBl0fT6', // tim@apple.dev
canImpersonate: true,
canAccessFullAdminPanel: true,
isEmailVerified: true,
},
])
.execute();
};

View File

@ -1,83 +0,0 @@
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { DataSource } from 'typeorm';
import { Workspace } 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_ACME_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',
] as const satisfies (keyof Workspace)[];
type WorkspaceSeederFields = Pick<
Workspace,
(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,
},
[SEED_ACME_WORKSPACE_ID]: {
id: SEED_ACME_WORKSPACE_ID,
displayName: 'Acme',
subdomain: 'acme',
inviteHash: 'acme.dev-invite-hash',
logo: 'https://logos-world.net/wp-content/uploads/2022/05/Acme-Logo-700x394.png',
activationStatus: WorkspaceActivationStatus.PENDING_CREATION, // will be set to active after default role creation
version: version,
},
};
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();
};

View File

@ -1,190 +0,0 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
export const getDevSeedCompanyCustomFields = (
objectMetadataId: string,
workspaceId: string,
): CreateFieldInput[] => {
return [
{
workspaceId,
type: FieldMetadataType.TEXT,
name: 'tagline',
label: 'Tagline',
description: "Company's Tagline",
icon: 'IconAdCircle',
isActive: true,
isNullable: false,
isUnique: false,
defaultValue: "''",
objectMetadataId,
},
{
workspaceId,
type: FieldMetadataType.LINKS,
name: 'introVideo',
label: 'Intro Video',
description: "Company's Intro Video",
icon: 'IconVideo',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
},
{
workspaceId,
type: FieldMetadataType.MULTI_SELECT,
name: 'workPolicy',
label: 'Work Policy',
description: "Company's Work Policy",
icon: 'IconHome',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
options: [
{
color: 'green',
label: 'On-Site',
position: 0,
value: 'ON_SITE',
},
{
color: 'turquoise',
label: 'Hybrid',
position: 1,
value: 'HYBRID',
},
{
color: 'sky',
label: 'Remote Work',
position: 2,
value: 'REMOTE_WORK',
},
],
},
{
workspaceId,
type: FieldMetadataType.BOOLEAN,
name: 'visaSponsorship',
label: 'Visa Sponsorship',
description: "Company's Visa Sponsorship Policy",
icon: 'IconBrandVisa',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
defaultValue: false,
},
];
};
export const getDevSeedPeopleCustomFields = (
objectMetadataId: string,
workspaceId: string,
): CreateFieldInput[] => {
return [
{
workspaceId,
type: FieldMetadataType.TEXT,
name: 'intro',
label: 'Intro',
description: "Contact's Intro",
icon: 'IconNote',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
},
{
workspaceId,
type: FieldMetadataType.PHONES,
name: 'whatsapp',
label: 'Whatsapp',
description: "Contact's Whatsapp Number",
icon: 'IconBrandWhatsapp',
isActive: true,
isNullable: false,
isUnique: false,
defaultValue: {
primaryPhoneNumber: "''",
primaryPhoneCountryCode: "'FR'",
primaryPhoneCallingCode: "'+33'",
additionalPhones: null,
},
objectMetadataId,
},
{
workspaceId,
type: FieldMetadataType.MULTI_SELECT,
name: 'workPreference',
label: 'Work Preference',
description: "Person's Work Preference",
icon: 'IconHome',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
options: [
{
color: 'green',
label: 'On-Site',
position: 0,
value: 'ON_SITE',
},
{
color: 'turquoise',
label: 'Hybrid',
position: 1,
value: 'HYBRID',
},
{
color: 'sky',
label: 'Remote Work',
position: 2,
value: 'REMOTE_WORK',
},
],
},
{
workspaceId,
type: FieldMetadataType.RATING,
name: 'performanceRating',
label: 'Performance Rating',
description: "Person's Performance Rating",
icon: 'IconStars',
isActive: true,
isNullable: true,
isUnique: false,
objectMetadataId,
options: [
{
label: '1',
value: 'RATING_1',
position: 0,
},
{
label: '2',
value: 'RATING_2',
position: 1,
},
{
label: '3',
value: 'RATING_3',
position: 2,
},
{
label: '4',
value: 'RATING_4',
position: 3,
},
{
label: '5',
value: 'RATING_5',
position: 4,
},
],
},
];
};

View File

@ -1,20 +0,0 @@
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
export const getDevSeedCustomObjects = (
workspaceId: string,
dataSourceId: string,
): CreateObjectInput[] => {
return [
{
workspaceId,
dataSourceId,
labelPlural: 'Rockets',
labelSingular: 'Rocket',
namePlural: 'rockets',
nameSingular: 'rocket',
description: 'A rocket',
icon: 'IconRocket',
isRemote: false,
},
];
};

View File

@ -1,28 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'apiKey';
const API_KEY_ID = '20202020-f401-4d8a-a731-64d007c27bad';
export const seedApiKey = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'name', 'expiresAt'])
.orIgnore()
.values([
{
id: API_KEY_ID,
name: 'My api key',
expiresAt: new Date(
new Date().getTime() + 1000 * 60 * 60 * 24 * 365 * 100, // In 100 years
),
},
])
.execute();
};

View File

@ -1,32 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'calendarChannelEventAssociation';
export const seedCalendarChannelEventAssociations = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'calendarChannelId',
'calendarEventId',
'eventExternalId',
'recurringEventExternalId',
])
.orIgnore()
.values([
{
id: 'e1ab9e1b-df6e-438e-a788-11c96dcecdd3',
calendarChannelId: '59efdefe-a40f-4faf-bb9f-c6f9945b8203',
calendarEventId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
eventExternalId: 'exampleExternalId',
recurringEventExternalId: 'exampleRecurringExternalId',
},
])
.execute();
};

View File

@ -1,36 +0,0 @@
import { DEV_SEED_CONNECTED_ACCOUNT_IDS } from 'src/database/typeorm-seeds/workspace/connected-account';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
const tableName = 'calendarChannel';
export const seedCalendarChannels = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'connectedAccountId',
'handle',
'visibility',
'isContactAutoCreationEnabled',
'isSyncEnabled',
])
.orIgnore()
.values([
{
id: '59efdefe-a40f-4faf-bb9f-c6f9945b8203',
connectedAccountId: DEV_SEED_CONNECTED_ACCOUNT_IDS.TIM,
handle: 'tim@apple.com',
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
isContactAutoCreationEnabled: true,
isSyncEnabled: true,
},
])
.execute();
};

View File

@ -1,51 +0,0 @@
import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople';
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
const tableName = 'calendarEventParticipant';
export const seedCalendarEventParticipants = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'calendarEventId',
'handle',
'displayName',
'isOrganizer',
'responseStatus',
'personId',
'workspaceMemberId',
])
.orIgnore()
.values([
{
id: 'da8f47c3-8055-49ad-b7e4-9c9d5bbc1ecc',
calendarEventId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
handle: 'christoph.calisto@linkedin.com',
displayName: 'Christoph Calisto',
isOrganizer: true,
responseStatus: CalendarEventParticipantResponseStatus.ACCEPTED,
personId: DEV_SEED_PERSON_IDS.CHRISTOPH,
workspaceMemberId: null,
},
{
id: 'e1ab9e1b-df6e-438e-a788-11c96dcecdd3',
calendarEventId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
handle: 'tim@apple.com',
displayName: 'Tim Apple',
isOrganizer: false,
responseStatus: CalendarEventParticipantResponseStatus.ACCEPTED,
personId: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
},
])
.execute();
};

View File

@ -1,54 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'calendarEvent';
export const DEV_SEED_CALENDAR_EVENT_IDS = {
CALENDAR_EVENT_1: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
};
export const seedCalendarEvents = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'title',
'isCanceled',
'isFullDay',
'startsAt',
'endsAt',
'externalCreatedAt',
'externalUpdatedAt',
'description',
'location',
'iCalUID',
'conferenceSolution',
'conferenceLinkPrimaryLinkLabel',
'conferenceLinkPrimaryLinkUrl',
])
.orIgnore()
.values([
{
id: DEV_SEED_CALENDAR_EVENT_IDS.CALENDAR_EVENT_1,
title: 'Meeting with Christoph',
isCanceled: false,
isFullDay: false,
startsAt: new Date(new Date().setHours(10, 0)).toISOString(),
endsAt: new Date(new Date().setHours(11, 0)).toISOString(),
externalCreatedAt: new Date().toISOString(),
externalUpdatedAt: new Date().toISOString(),
description: 'Discuss project progress',
location: 'Seattle',
iCalUID: 'event1@calendar.com',
conferenceSolution: 'Zoom',
conferenceLinkPrimaryLinkLabel: 'https://zoom.us/j/1234567890',
conferenceLinkPrimaryLinkUrl: 'https://zoom.us/j/1234567890',
},
])
.execute();
};

View File

@ -1,273 +0,0 @@
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'company';
export const DEV_SEED_COMPANY_IDS = {
LINKEDIN: '20202020-3ec3-4fe3-8997-b76aa0bfa408',
FACEBOOK: '20202020-5d81-46d6-bf83-f7fd33ea6102',
ANTHROPIC: '20202020-0713-40a5-8216-82802401d33e',
MICROSOFT: '20202020-ed89-413a-b31a-962986e67bb4',
AIRBNB: '20202020-171e-4bcc-9cf7-43448d6fb278',
GOOGLE: '20202020-c21e-4ec2-873b-de4264d89025',
NETFLIX: '20202020-707e-44dc-a1d2-30030bf1a944',
LIBEO: '20202020-3f74-492d-a101-2a70f50a1645',
CLAAP: '20202020-cfbf-4156-a790-e39854dcd4eb',
HASURA: '20202020-f86b-419f-b794-02319abe8637',
WEWORK: '20202020-5518-4553-9433-42d8eb82834b',
SAMSUNG: '20202020-f79e-40dd-bd06-c36e6abb4678',
ALGOLIA: '20202020-1455-4c57-afaf-dd5dc086361d',
};
export const seedCompanies = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'name',
'domainNamePrimaryLinkUrl',
'addressAddressStreet1',
'addressAddressStreet2',
'addressAddressCity',
'addressAddressState',
'addressAddressPostcode',
'addressAddressCountry',
'position',
'tagline',
'workPolicy',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
])
.orIgnore()
.values([
{
id: DEV_SEED_COMPANY_IDS.LINKEDIN,
name: 'Linkedin',
domainNamePrimaryLinkUrl: 'https://linkedin.com',
addressAddressStreet1: 'Eutaw Street',
addressAddressStreet2: null,
addressAddressCity: 'Dublin',
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: 'Ireland',
position: 1,
tagline: "Linkedin's tagline",
workPolicy: ['ON_SITE'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.FACEBOOK,
name: 'Facebook',
domainNamePrimaryLinkUrl: 'https://facebook.com',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 2,
tagline: "Facebook's tagline",
workPolicy: ['REMOTE_WORK'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.ANTHROPIC,
name: 'Anthropic',
domainNamePrimaryLinkUrl: 'https://anthropic.com',
addressAddressStreet1: '548 Market Street',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: null,
addressAddressPostcode: '94104',
addressAddressCountry: 'United States',
position: 3,
tagline: "Anthropic's tagline",
workPolicy: ['ON_SITE'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.MICROSOFT,
name: 'Microsoft',
domainNamePrimaryLinkUrl: 'https://microsoft.com',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 4,
tagline: "Microsoft's tagline",
workPolicy: ['ON_SITE', 'HYBRID'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.AIRBNB,
name: 'Airbnb',
domainNamePrimaryLinkUrl: 'https://airbnb.com',
addressAddressStreet1: '888 Brannan St',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: 'CA',
addressAddressPostcode: '94103',
addressAddressCountry: 'United States',
position: 5,
tagline: "Airbnb's tagline",
workPolicy: ['ON_SITE'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.GOOGLE,
name: 'Google',
domainNamePrimaryLinkUrl: 'https://google.com',
addressAddressStreet1: '760 Market St',
addressAddressStreet2: 'Floor 10',
addressAddressCity: 'San Francisco',
addressAddressState: null,
addressAddressPostcode: '94102',
addressAddressCountry: 'United States',
position: 6,
tagline: "Google's tagline",
workPolicy: ['HYBRID', 'REMOTE_WORK'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.NETFLIX,
name: 'Netflix',
domainNamePrimaryLinkUrl: 'https://netflix.com',
addressAddressStreet1: '2300 Harrison St',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: 'CA',
addressAddressPostcode: '94110',
addressAddressCountry: 'United States',
position: 7,
tagline: "Netflix's tagline",
workPolicy: ['REMOTE_WORK'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.LIBEO,
name: 'Libeo',
domainNamePrimaryLinkUrl: 'https://libeo.io',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 8,
tagline: "Libeo's tagline",
workPolicy: ['HYBRID'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.CLAAP,
name: 'Claap',
domainNamePrimaryLinkUrl: 'https://claap.io',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 9,
tagline: "Claap's tagline",
workPolicy: ['ON_SITE', 'REMOTE_WORK'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.HASURA,
name: 'Hasura',
domainNamePrimaryLinkUrl: 'https://hasura.io',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 10,
tagline: "Hasura's tagline",
workPolicy: ['HYBRID'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.WEWORK,
name: 'Wework',
domainNamePrimaryLinkUrl: 'https://wework.com',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 11,
tagline: "Wework's tagline",
workPolicy: ['ON_SITE'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.SAMSUNG,
name: 'Samsung',
domainNamePrimaryLinkUrl: 'https://samsung.com',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 12,
tagline: "Samsung's tagline",
workPolicy: ['HYBRID'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_COMPANY_IDS.ALGOLIA,
name: 'Algolia',
domainNamePrimaryLinkUrl: 'https://algolia.com',
addressAddressStreet1: null,
addressAddressStreet2: null,
addressAddressCity: null,
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: null,
position: 13,
tagline: "Algolia's tagline",
workPolicy: ['ON_SITE'],
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
])
.execute();
};

View File

@ -1,73 +0,0 @@
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'connectedAccount';
export const DEV_SEED_CONNECTED_ACCOUNT_IDS = {
TIM: '20202020-9ac0-4390-9a1a-ab4d2c4e1bb7',
JONY: '20202020-0cc8-4d60-a3a4-803245698908',
PHIL: '20202020-cafc-4323-908d-e5b42ad69fdf',
};
export const seedConnectedAccount = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'lastSyncHistoryId',
'accountOwnerId',
'refreshToken',
'accessToken',
'provider',
'handle',
])
.orIgnore()
.values([
{
id: DEV_SEED_CONNECTED_ACCOUNT_IDS.TIM,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'tim@apple.dev',
},
{
id: DEV_SEED_CONNECTED_ACCOUNT_IDS.JONY,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: DEV_SEED_WORKSPACE_MEMBER_IDS.JONY,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'jony.ive@apple.dev',
},
{
id: DEV_SEED_CONNECTED_ACCOUNT_IDS.PHIL,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
lastSyncHistoryId: 'exampleLastSyncHistory',
accountOwnerId: DEV_SEED_WORKSPACE_MEMBER_IDS.PHIL,
refreshToken: 'exampleRefreshToken',
accessToken: 'exampleAccessToken',
provider: 'google',
handle: 'phil.schiler@apple.dev',
},
])
.execute();
};

View File

@ -1,26 +0,0 @@
import { v4 } from 'uuid';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'favorite';
export const seedWorkspaceFavorites = async (
viewIds: string[],
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'viewId', 'position'])
.values(
viewIds.map((viewId, index) => ({
id: v4(),
viewId,
position: index,
})),
)
.execute();
};

View File

@ -1,71 +0,0 @@
import { DEV_SEED_MESSAGE_CHANNEL_IDS } from 'src/database/typeorm-seeds/workspace/message-channels';
import { DEV_SEED_MESSAGE_IDS } from 'src/database/typeorm-seeds/workspace/messages';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
const tableName = 'messageChannelMessageAssociation';
export const DEV_SEED_MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_IDS = {
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1: '20202020-cc69-44ef-a82c-600c0dbf39ba',
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2: '20202020-d80e-4a13-b10b-72ba09082668',
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3: '20202020-e6ec-4c8a-b431-0901eaf395a9',
};
export const seedMessageChannelMessageAssociation = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'messageThreadExternalId',
'messageExternalId',
'messageId',
'messageChannelId',
'direction',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_IDS.MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_1,
messageChannelId: DEV_SEED_MESSAGE_CHANNEL_IDS.TIM,
direction: MessageDirection.OUTGOING,
},
{
id: DEV_SEED_MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_IDS.MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_2,
messageChannelId: DEV_SEED_MESSAGE_CHANNEL_IDS.TIM,
direction: MessageDirection.OUTGOING,
},
{
id: DEV_SEED_MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_IDS.MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadExternalId: null,
messageExternalId: null,
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_3,
messageChannelId: DEV_SEED_MESSAGE_CHANNEL_IDS.TIM,
direction: MessageDirection.INCOMING,
},
])
.execute();
};

View File

@ -1,81 +0,0 @@
import { DEV_SEED_CONNECTED_ACCOUNT_IDS } from 'src/database/typeorm-seeds/workspace/connected-account';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
import {
MessageChannelSyncStage,
MessageChannelVisibility,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
const tableName = 'messageChannel';
export const DEV_SEED_MESSAGE_CHANNEL_IDS = {
TIM: '20202020-9b80-4c2c-a597-383db48de1d6',
JONY: '20202020-5ffe-4b32-814a-983d5e4911cd',
PHIL: '20202020-e2f1-49b5-85d2-5d3a3386990c',
};
export const seedMessageChannel = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'isContactAutoCreationEnabled',
'type',
'connectedAccountId',
'handle',
'isSyncEnabled',
'visibility',
'syncStage',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_CHANNEL_IDS.TIM,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: DEV_SEED_CONNECTED_ACCOUNT_IDS.TIM,
handle: 'tim@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
{
id: DEV_SEED_MESSAGE_CHANNEL_IDS.JONY,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: DEV_SEED_CONNECTED_ACCOUNT_IDS.JONY,
handle: 'jony.ive@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
{
id: DEV_SEED_MESSAGE_CHANNEL_IDS.PHIL,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
type: 'email',
connectedAccountId: DEV_SEED_CONNECTED_ACCOUNT_IDS.PHIL,
handle: 'phil.schiler@apple.dev',
isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
},
])
.execute();
};

View File

@ -1,114 +0,0 @@
import { DEV_SEED_MESSAGE_IDS } from 'src/database/typeorm-seeds/workspace/messages';
import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople';
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'messageParticipant';
export const DEV_SEED_MESSAGE_PARTICIPANT_IDS = {
MESSAGE_PARTICIPANT_1: '20202020-0f2a-49d8-8aa2-ec8786153a0b',
MESSAGE_PARTICIPANT_2: '20202020-4e83-41ec-93e2-fd70ff09f68c',
MESSAGE_PARTICIPANT_3: '20202020-e716-4dd5-ac61-3315bc559e2d',
MESSAGE_PARTICIPANT_4: '20202020-fc7d-4ad8-9aea-b78bcbf79cdd',
MESSAGE_PARTICIPANT_5: '20202020-564c-4a3c-abbf-e942e8c3f9c9',
MESSAGE_PARTICIPANT_6: '20202020-7e4a-489a-ba6b-1ae6b7d721ac',
};
export const seedMessageParticipant = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'workspaceMemberId',
'personId',
'displayName',
'handle',
'role',
'messageId',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.CHRISTOPH,
displayName: 'Christoph',
handle: 'outgoing',
role: 'from',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_1,
},
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.SYLVIE,
displayName: 'Sylvie',
handle: 'incoming',
role: 'to',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_1,
},
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.CHRISTOPHER_G,
displayName: 'Christopher',
handle: 'incoming',
role: 'to',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_1,
},
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_4,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.CHRISTOPH,
displayName: 'Christoph',
handle: 'outgoing',
role: 'from',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_2,
},
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_5,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.SYLVIE,
displayName: 'Sylvie',
handle: 'incoming',
role: 'to',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_2,
},
{
id: DEV_SEED_MESSAGE_PARTICIPANT_IDS.MESSAGE_PARTICIPANT_6,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
workspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
personId: DEV_SEED_PERSON_IDS.CHRISTOPHER_G,
displayName: 'Christopher',
handle: 'incoming',
role: 'to',
messageId: DEV_SEED_MESSAGE_IDS.MESSAGE_2,
},
])
.execute();
};

View File

@ -1,105 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'messageThreadSubscriber';
export const DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS = {
MESSAGE_THREAD_SUBSCRIBER_1: '20202020-cc69-44ef-a82c-600c0dbf39ba',
MESSAGE_THREAD_SUBSCRIBER_2: '20202020-d80e-4a13-b10b-72ba09082668',
MESSAGE_THREAD_SUBSCRIBER_3: '20202020-e6ec-4c8a-b431-0901eaf395a9',
MESSAGE_THREAD_SUBSCRIBER_4: '20202020-1455-4c57-afaf-dd5dc086361d',
MESSAGE_THREAD_SUBSCRIBER_5: '20202020-f79e-40dd-bd06-c36e6abb4678',
MESSAGE_THREAD_SUBSCRIBER_6: '20202020-3ec3-4fe3-8997-b76aa0bfa408',
MESSAGE_THREAD_SUBSCRIBER_7: '20202020-c21e-4ec2-873b-de4264d89025',
};
export const DEV_SEED_MESSAGE_THREAD_IDS = {
MESSAGE_THREAD_1: '20202020-8bfa-453b-b99b-bc435a7d4da8',
MESSAGE_THREAD_2: '20202020-634a-4fde-aa7c-28a0eaf203ca',
MESSAGE_THREAD_3: '20202020-1b56-4f10-a2fa-2ccaddf81f6c',
MESSAGE_THREAD_4: '20202020-d51c-485a-b1b6-ed7c63e05d72',
};
export const DEV_SEED_USER_IDS = {
TIM: '20202020-0687-4c41-b707-ed1bfca972a7',
PHIL: '20202020-1553-45c6-a028-5a9064cce07f',
JONY: '20202020-77d5-4cb6-b60a-f4a835a85d61',
};
export const seedMessageThreadSubscribers = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'messageThreadId',
'workspaceMemberId',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_1,
workspaceMemberId: DEV_SEED_USER_IDS.PHIL,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_1,
workspaceMemberId: DEV_SEED_USER_IDS.JONY,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_2,
workspaceMemberId: DEV_SEED_USER_IDS.TIM,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_4,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_3,
workspaceMemberId: DEV_SEED_USER_IDS.JONY,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_5,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_4,
workspaceMemberId: DEV_SEED_USER_IDS.TIM,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_6,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_4,
workspaceMemberId: DEV_SEED_USER_IDS.PHIL,
},
{
id: DEV_SEED_MESSAGE_THREAD_SUBSCRIBERS_IDS.MESSAGE_THREAD_SUBSCRIBER_7,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_4,
workspaceMemberId: DEV_SEED_USER_IDS.JONY,
},
])
.execute();
};

View File

@ -1,56 +0,0 @@
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'messageThread';
export const DEV_SEED_MESSAGE_THREAD_IDS = {
MESSAGE_THREAD_1: '20202020-8bfa-453b-b99b-bc435a7d4da8',
MESSAGE_THREAD_2: '20202020-634a-4fde-aa7c-28a0eaf203ca',
MESSAGE_THREAD_3: '20202020-1b56-4f10-a2fa-2ccaddf81f6c',
MESSAGE_THREAD_4: '20202020-d51c-485a-b1b6-ed7c63e05d72',
MESSAGE_THREAD_5: '20202020-3f74-492d-a101-2a70f50a1645',
};
export const seedMessageThread = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
{
id: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_4,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
},
])
.execute();
};

View File

@ -1,69 +0,0 @@
import { DEV_SEED_MESSAGE_THREAD_IDS } from 'src/database/typeorm-seeds/workspace/message-threads';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'message';
export const DEV_SEED_MESSAGE_IDS = {
MESSAGE_1: '20202020-2b8a-405d-8f42-e820ca921421',
MESSAGE_2: '20202020-04c8-4f24-93f2-764948e95014',
MESSAGE_3: '20202020-ac6b-4f86-87a2-5f5f9d1b6481',
};
export const seedMessage = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'createdAt',
'updatedAt',
'deletedAt',
'receivedAt',
'text',
'subject',
'messageThreadId',
'headerMessageId',
])
.orIgnore()
.values([
{
id: DEV_SEED_MESSAGE_IDS.MESSAGE_1,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Hello, \n I hope this email finds you well. I am writing to request a meeting. I believe it would be beneficial for both parties to collaborate and explore potential opportunities. Would you be available for a meeting sometime next week? Please let me know your availability, and I will arrange a suitable time. \n Looking forward to your response.\n Best regards',
subject: 'Meeting Request',
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_1,
headerMessageId: '99ef24a8-2b8a-405d-8f42-e820ca921421',
},
{
id: DEV_SEED_MESSAGE_IDS.MESSAGE_2,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Good Morning,\n I am writing to inquire about information. Could you please provide me with details regarding this topic? \n Your assistance in this matter would be greatly appreciated. Thank you in advance for your prompt response. \n Best regards,Tim',
subject: 'Inquiry Regarding Topic',
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_2,
headerMessageId: '8f804a9a-04c8-4f24-93f2-764948e95014',
},
{
id: DEV_SEED_MESSAGE_IDS.MESSAGE_3,
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: null,
receivedAt: new Date(),
text: 'Good Evening,\nI wanted to extend my sincere gratitude for taking the time to meet with me earlier today. It was a pleasure discussing with you, and I am excited about the potential opportunities for collaboration. \n Please feel free to reach out if you have any further questions or require additional information. I look forward to our continued communication. Best regards.',
subject: 'Thank You for the Meeting',
messageThreadId: DEV_SEED_MESSAGE_THREAD_IDS.MESSAGE_THREAD_1,
headerMessageId: '3939d68a-ac6b-4f86-87a2-5f5f9d1b6481',
},
])
.execute();
};

View File

@ -1,98 +0,0 @@
import { DEV_SEED_COMPANY_IDS } from 'src/database/typeorm-seeds/workspace/companies';
import { DEV_SEED_PERSON_IDS } from 'src/database/typeorm-seeds/workspace/seedPeople';
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'opportunity';
export const DEV_SEED_OPPORTUNITY_IDS = {
OPPORTUNITY_1: '20202020-be10-422b-a663-16bd3c2228e1',
OPPORTUNITY_2: '20202020-0543-4cc2-9f96-95cc699960f2',
OPPORTUNITY_3: '20202020-2f89-406f-90ea-180f433b2445',
OPPORTUNITY_4: '20202020-35b1-4045-9cde-42f715148954',
};
export const seedOpportunity = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'name',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'stage',
'position',
'pointOfContactId',
'companyId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
])
.orIgnore()
.values([
{
id: DEV_SEED_OPPORTUNITY_IDS.OPPORTUNITY_1,
name: 'Opportunity 1',
amountAmountMicros: 100000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'NEW',
position: 1,
pointOfContactId: DEV_SEED_PERSON_IDS.CHRISTOPH,
companyId: DEV_SEED_COMPANY_IDS.LINKEDIN,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: DEV_SEED_OPPORTUNITY_IDS.OPPORTUNITY_2,
name: 'Opportunity 2',
amountAmountMicros: 2000000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'MEETING',
position: 2,
pointOfContactId: DEV_SEED_PERSON_IDS.CHRISTOPHER_G,
companyId: DEV_SEED_COMPANY_IDS.FACEBOOK,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: DEV_SEED_OPPORTUNITY_IDS.OPPORTUNITY_3,
name: 'Opportunity 3',
amountAmountMicros: 300000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'PROPOSAL',
position: 3,
pointOfContactId: DEV_SEED_PERSON_IDS.NICHOLAS,
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Cook',
},
{
id: DEV_SEED_OPPORTUNITY_IDS.OPPORTUNITY_4,
name: 'Opportunity 4',
amountAmountMicros: 4000000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
stage: 'PROPOSAL',
position: 4,
pointOfContactId: DEV_SEED_PERSON_IDS.MATTHEW,
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: null,
createdByName: '',
},
])
.execute();
};

View File

@ -1,326 +0,0 @@
import { DEV_SEED_COMPANY_IDS } from 'src/database/typeorm-seeds/workspace/companies';
import { DEV_SEED_WORKSPACE_MEMBER_IDS } from 'src/database/typeorm-seeds/workspace/workspace-members';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'person';
export const DEV_SEED_PERSON_IDS = {
CHRISTOPH: '20202020-1c0e-494c-a1b6-85b1c6fefaa5',
SYLVIE: '20202020-ac73-4797-824e-87a1f5aea9e0',
CHRISTOPHER_G: '20202020-f517-42fd-80ae-14173b3b70ae',
ASHLEY: '20202020-eee1-4690-ad2c-8619e5b56a2e',
NICHOLAS: '20202020-6784-4449-afdf-dc62cb8702f2',
ISABELLA: '20202020-490f-4466-8391-733cfd66a0c8',
MATTHEW: '20202020-80f1-4dff-b570-a74942528de3',
ELIZABETH: '20202020-338b-46df-8811-fa08c7d19d35',
CHRISTOPHER_N: '20202020-64ad-4b0e-bbfd-e9fd795b7016',
AVERY: '20202020-5d54-41b7-ba36-f0d20e1417ae',
ETHAN: '20202020-623d-41fe-92e7-dd45b7c568e1',
MADISON: '20202020-2d40-4e49-8df4-9c6a049190ef',
BERTRAND: '20202020-2d40-4e49-8df4-9c6a049190df',
LOUIS: '20202020-2d40-4e49-8df4-9c6a049191de',
LORIE: '20202020-2d40-4e49-8df4-9c6a049191df',
};
export const seedPeople = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'nameFirstName',
'nameLastName',
'phonesPrimaryPhoneCountryCode',
'phonesPrimaryPhoneCallingCode',
'phonesPrimaryPhoneNumber',
'city',
'companyId',
'emailsPrimaryEmail',
'position',
'whatsappPrimaryPhoneCountryCode',
'whatsappPrimaryPhoneCallingCode',
'whatsappPrimaryPhoneNumber',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
])
.orIgnore()
.values([
{
id: DEV_SEED_PERSON_IDS.CHRISTOPH,
nameFirstName: 'Christoph',
nameLastName: 'Callisto',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '789012345',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.LINKEDIN,
emailsPrimaryEmail: 'christoph.calisto@linkedin.com',
position: 1,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '789012345',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.SYLVIE,
nameFirstName: 'Sylvie',
nameLastName: 'Palmer',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '780123456',
city: 'Los Angeles',
companyId: DEV_SEED_COMPANY_IDS.LINKEDIN,
emailsPrimaryEmail: 'sylvie.palmer@linkedin.com',
position: 2,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '780123456',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.CHRISTOPHER_G,
nameFirstName: 'Christopher',
nameLastName: 'Gonzalez',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '789012345',
city: 'San Francisco',
companyId: DEV_SEED_COMPANY_IDS.ANTHROPIC,
emailsPrimaryEmail: 'christopher.gonzalez@anthropic.com',
position: 3,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '789012345',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.ASHLEY,
nameFirstName: 'Ashley',
nameLastName: 'Parker',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '780123456',
city: 'Los Angeles',
companyId: DEV_SEED_COMPANY_IDS.ANTHROPIC,
emailsPrimaryEmail: 'ashley.parker@anthropic.com',
position: 4,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '780123456',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.NICHOLAS,
nameFirstName: 'Nicholas',
nameLastName: 'Wright',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '781234567',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
emailsPrimaryEmail: 'nicholas.wright@microsoft.com',
position: 5,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '781234567',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.ISABELLA,
nameFirstName: 'Isabella',
nameLastName: 'Scott',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '782345678',
city: 'New York',
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
emailsPrimaryEmail: 'isabella.scott@microsoft.com',
position: 6,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '782345678',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.MATTHEW,
nameFirstName: 'Matthew',
nameLastName: 'Green',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '783456789',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
emailsPrimaryEmail: 'matthew.green@microsoft.com',
position: 7,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '783456789',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.ELIZABETH,
nameFirstName: 'Elizabeth',
nameLastName: 'Baker',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '784567890',
city: 'New York',
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
emailsPrimaryEmail: 'elizabeth.baker@airbnb.com',
position: 8,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '784567890',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.CHRISTOPHER_N,
nameFirstName: 'Christopher',
nameLastName: 'Nelson',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '785678901',
city: 'San Francisco',
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
emailsPrimaryEmail: 'christopher.nelson@airbnb.com',
position: 9,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '785678901',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.AVERY,
nameFirstName: 'Avery',
nameLastName: 'Carter',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '786789012',
city: 'New York',
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
emailsPrimaryEmail: 'avery.carter@airbnb.com',
position: 10,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '786789012',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.ETHAN,
nameFirstName: 'Ethan',
nameLastName: 'Mitchell',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '787890123',
city: 'Los Angeles',
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
emailsPrimaryEmail: 'ethan.mitchell@google.com',
position: 11,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '787890123',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.MADISON,
nameFirstName: 'Madison',
nameLastName: 'Perez',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '788901234',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
emailsPrimaryEmail: 'madison.perez@google.com',
position: 12,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '788901234',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.BERTRAND,
nameFirstName: 'Bertrand',
nameLastName: 'Voulzy',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '788901234',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
emailsPrimaryEmail: 'bertrand.voulzy@google.com',
position: 13,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '788901234',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.LOUIS,
nameFirstName: 'Louis',
nameLastName: 'Duss',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '789012345',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
emailsPrimaryEmail: 'louis.duss@google.com',
position: 14,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '789012345',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
{
id: DEV_SEED_PERSON_IDS.LORIE,
nameFirstName: 'Lorie',
nameLastName: 'Vladim',
phonesPrimaryPhoneCountryCode: 'FR',
phonesPrimaryPhoneCallingCode: '+33',
phonesPrimaryPhoneNumber: '788901235',
city: 'Seattle',
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
emailsPrimaryEmail: 'lorie.vladim@google.com',
position: 15,
whatsappPrimaryPhoneCountryCode: 'FR',
whatsappPrimaryPhoneCallingCode: '+33',
whatsappPrimaryPhoneNumber: '788901235',
createdBySource: 'MANUAL',
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
createdByName: 'Tim Apple',
},
])
.execute();
};

View File

@ -1,96 +0,0 @@
import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users';
import {
SEED_ACME_WORKSPACE_ID,
SEED_APPLE_WORKSPACE_ID,
} from 'src/database/typeorm-seeds/core/workspaces';
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
const tableName = 'workspaceMember';
export const DEV_SEED_WORKSPACE_MEMBER_IDS = {
TIM: '20202020-0687-4c41-b707-ed1bfca972a7',
JONY: '20202020-77d5-4cb6-b60a-f4a835a85d61',
PHIL: '20202020-1553-45c6-a028-5a9064cce07f',
};
type WorkspaceMembers = Pick<
WorkspaceMember,
'id' | 'locale' | 'colorScheme'
> & {
nameFirstName: string;
nameLastName: string;
userEmail: string;
userId: string;
};
export const seedWorkspaceMember = async (
entityManager: WorkspaceEntityManager,
schemaName: string,
workspaceId: string,
) => {
let workspaceMembers: WorkspaceMembers[] = [];
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
workspaceMembers = [
{
id: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
nameFirstName: 'Tim',
nameLastName: 'Apple',
locale: 'en',
colorScheme: 'Light',
userEmail: 'tim@apple.dev',
userId: DEV_SEED_USER_IDS.TIM,
},
{
id: DEV_SEED_WORKSPACE_MEMBER_IDS.JONY,
nameFirstName: 'Jony',
nameLastName: 'Ive',
locale: 'en',
colorScheme: 'Light',
userEmail: 'jony.ive@apple.dev',
userId: DEV_SEED_USER_IDS.JONY,
},
{
id: DEV_SEED_WORKSPACE_MEMBER_IDS.PHIL,
nameFirstName: 'Phil',
nameLastName: 'Schiler',
locale: 'en',
colorScheme: 'Light',
userEmail: 'phil.schiler@apple.dev',
userId: DEV_SEED_USER_IDS.PHIL,
},
];
}
if (workspaceId === SEED_ACME_WORKSPACE_ID) {
workspaceMembers = [
{
id: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
nameFirstName: 'Tim',
nameLastName: 'Apple',
locale: 'en',
colorScheme: 'Light',
userEmail: 'tim@apple.dev',
userId: DEV_SEED_USER_IDS.TIM,
},
];
}
await entityManager
.createQueryBuilder(undefined, undefined, undefined, {
shouldBypassPermissionChecks: true,
})
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'nameFirstName',
'nameLastName',
'locale',
'colorScheme',
'userEmail',
'userId',
])
.orIgnore()
.values(workspaceMembers)
.execute();
};

View File

@ -16,9 +16,13 @@ export const typeORMCoreModuleOptions: TypeOrmModuleOptions = {
schema: 'core',
entities:
process.env.IS_BILLING_ENABLED === 'true'
? [`${isJest ? '' : 'dist/'}src/engine/core-modules/**/*.entity{.ts,.js}`]
? [
`${isJest ? '' : 'dist/'}src/engine/core-modules/**/*.entity{.ts,.js}`,
`${isJest ? '' : 'dist/'}src/engine/metadata-modules/**/*.entity{.ts,.js}`,
]
: [
`${isJest ? '' : 'dist/'}src/engine/core-modules/**/!(billing-*).entity{.ts,.js}`,
`${isJest ? '' : 'dist/'}src/engine/core-modules/**/!(billing-*).entity.{ts,js}`,
`${isJest ? '' : 'dist/'}src/engine/metadata-modules/**/*.entity{.ts,.js}`,
],
synchronize: false,
migrationsRun: false,

View File

@ -1,39 +0,0 @@
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { config } from 'dotenv';
import { DataSource, DataSourceOptions } from 'typeorm';
config({
path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
override: true,
});
const isJest = process.argv.some((arg) => arg.includes('jest'));
export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
url: process.env.PG_DATABASE_URL,
type: 'postgres',
logging: ['error'],
schema: 'core',
entities: [
`${isJest ? '' : 'dist/'}src/engine/metadata-modules/**/*.entity{.ts,.js}`,
],
synchronize: false,
migrationsRun: false,
migrationsTableName: '_typeorm_migrations',
migrations: [
`${isJest ? '' : 'dist/'}src/database/typeorm/metadata/migrations/*{.ts,.js}`,
],
ssl:
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
? {
rejectUnauthorized: false,
}
: undefined,
extra: {
query_timeout: 10000,
},
};
export const connectionSource = new DataSource(
typeORMMetadataModuleOptions as DataSourceOptions,
);

View File

@ -6,13 +6,6 @@ import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty
import { TypeORMService } from './typeorm.service';
import { typeORMMetadataModuleOptions } from './metadata/metadata.datasource';
const metadataTypeORMFactory = async (): Promise<TypeOrmModuleOptions> => ({
...typeORMMetadataModuleOptions,
name: 'metadata',
});
const coreTypeORMFactory = async (): Promise<TypeOrmModuleOptions> => ({
...typeORMCoreModuleOptions,
name: 'core',
@ -21,10 +14,6 @@ const coreTypeORMFactory = async (): Promise<TypeOrmModuleOptions> => ({
@Module({
imports: [
TwentyConfigModule,
TypeOrmModule.forRootAsync({
useFactory: metadataTypeORMFactory,
name: 'metadata',
}),
TypeOrmModule.forRootAsync({
useFactory: coreTypeORMFactory,
name: 'core',