Skip to content

Move getSchemaName to util #13357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata
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';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';

@Command({
name: 'upgrade:0-54:0-54-created-by-default-value',
Expand All @@ -27,7 +27,6 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(ObjectMetadataEntity, 'core')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
Expand All @@ -50,8 +49,7 @@ export class FixCreatedByDefaultValueCommand extends ActiveOrSuspendedWorkspaces
continue;
}

const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);

const tableName = computeTableName(
objectMetadataItem.nameSingular,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
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 { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { WorkflowRunStatus } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
Expand Down Expand Up @@ -86,8 +87,7 @@ export class AddEnqueuedStatusToWorkflowRunCommand extends ActiveOrSuspendedWork
);
}

const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);

const mainDataSource =
await this.workspaceDataSourceService.connectToMainDataSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service';

@Command({
Expand All @@ -27,7 +27,6 @@ export class FixSchemaArrayTypeCommand extends ActiveOrSuspendedWorkspacesMigrat
protected readonly workspaceRepository: Repository<Workspace>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly databaseStructureService: DatabaseStructureService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly typeORMService: TypeORMService,
@InjectRepository(FieldMetadataEntity, 'core')
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
Expand Down Expand Up @@ -61,8 +60,7 @@ export class FixSchemaArrayTypeCommand extends ActiveOrSuspendedWorkspacesMigrat
const object = field.object;

const tableName = computeObjectTargetTable(object);
const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);
const columns =
await this.databaseStructureService.getWorkspaceTableColumns(
schemaName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';

export const fetchTableColumns = async (
workspaceDataSourceService: WorkspaceDataSourceService,
workspaceId: string,
tableName: string,
): Promise<PostgresTableSchemaColumn[]> => {
const schemaName = workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);

// TODO: executeRawQuery is deprecated and will throw
const res = await workspaceDataSourceService.executeRawQuery(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { uuidToBase36 } from 'twenty-shared/utils';

export const getWorkspaceSchemaName = (workspaceId: string): string => {
return `workspace_${uuidToBase36(workspaceId)}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
PermissionsException,
PermissionsExceptionCode,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';

@Injectable()
export class WorkspaceDataSourceService {
Expand Down Expand Up @@ -50,7 +51,7 @@ export class WorkspaceDataSourceService {
* @returns
*/
public async createWorkspaceDBSchema(workspaceId: string): Promise<string> {
const schemaName = this.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Critical issue: The shared uuidToBase36 utility doesn't handle 'twenty-' prefixed dev UUIDs. The original method added 'twenty_' prefix for dev environments, but this logic is missing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this #13357 (comment)


return await this.typeormService.createSchema(schemaName);
}
Expand All @@ -63,45 +64,11 @@ export class WorkspaceDataSourceService {
* @returns
*/
public async deleteWorkspaceDBSchema(workspaceId: string): Promise<void> {
const schemaName = this.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);

return await this.typeormService.deleteSchema(schemaName);
}

/**
*
* Get the schema name for a workspace
* Note: This is assuming that the workspace only has one schema but we should prefer querying the metadata table instead.
*
* @param workspaceId
* @returns string
*/
public getSchemaName(workspaceId: string): string {
return `workspace_${this.uuidToBase36(workspaceId)}`;
}

/**
*
* Convert a uuid to base36
*
* @param uuid
* @returns string
*/
private uuidToBase36(uuid: string): string {
let devId = false;

if (uuid.startsWith('twenty-')) {
devId = true;
// Clean dev uuids (twenty-)
uuid = uuid.replace('twenty-', '');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic is legacy, we don't create uuid with "twenty-" prefix anymore (we use "20202020" instead)

}
const hexString = uuid.replace(/-/g, '');
const base10Number = BigInt('0x' + hexString);
const base36String = base10Number.toString(36);

return `${devId ? 'twenty_' : ''}${base36String}`;
}

public async executeRawQuery(
_query: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service';
import { FieldMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/field-metadata-health.service';
import { ObjectMetadataHealthService } from 'src/engine/workspace-manager/workspace-health/services/object-metadata-health.service';
Expand All @@ -31,7 +31,6 @@ export class WorkspaceHealthService {
private readonly dataSourceService: DataSourceService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly databaseStructureService: DatabaseStructureService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly objectMetadataHealthService: ObjectMetadataHealthService,
private readonly fieldMetadataHealthService: FieldMetadataHealthService,
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
Expand All @@ -42,8 +41,7 @@ export class WorkspaceHealthService {
workspaceId: string,
options: WorkspaceHealthOptions = { mode: WorkspaceHealthMode.All },
): Promise<WorkspaceHealthIssue[]> {
const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);
const issues: WorkspaceHealthIssue[] = [];

const dataSourceMetadata =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { WorkspaceMigrationColumnService } from 'src/engine/workspace-manager/workspace-migration-runner/services/workspace-migration-column.service';
import { PostgresQueryRunner } from 'src/engine/workspace-manager/workspace-migration-runner/types/postgres-query-runner.type';
Expand Down Expand Up @@ -54,8 +55,7 @@ export class WorkspaceMigrationRunnerService {
})),
);

const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const schemaName = getWorkspaceSchemaName(workspaceId);

await transactionQueryRunner.query(
`SET LOCAL search_path TO ${schemaName}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import {
CalendarEventListFetchJob,
Expand All @@ -30,7 +30,6 @@ export class CalendarEventListFetchCronJob {
private readonly workspaceRepository: Repository<Workspace>,
@InjectMessageQueue(MessageQueue.calendarQueue)
private readonly messageQueueService: MessageQueueService,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly exceptionHandlerService: ExceptionHandlerService,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {}
Expand All @@ -52,9 +51,7 @@ export class CalendarEventListFetchCronJob {

for (const activeWorkspace of activeWorkspaces) {
try {
const schemaName = this.workspaceDataSourceService.getSchemaName(
activeWorkspace.id,
);
const schemaName = getWorkspaceSchemaName(activeWorkspace.id);

const calendarChannels = await mainDataSource.query(
`SELECT * FROM ${schemaName}."calendarChannel" WHERE "isSyncEnabled" = true AND "syncStage" IN ('${CalendarChannelSyncStage.FULL_CALENDAR_EVENT_LIST_FETCH_PENDING}', '${CalendarChannelSyncStage.PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING}')`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { CalendarEventListFetchJobData } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
import { CalendarEventsImportJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job';
Expand Down Expand Up @@ -47,9 +48,7 @@ export class CalendarEventsImportCronJob {

for (const activeWorkspace of activeWorkspaces) {
try {
const schemaName = this.workspaceDataSourceService.getSchemaName(
activeWorkspace.id,
);
const schemaName = getWorkspaceSchemaName(activeWorkspace.id);

const calendarChannels = await mainDataSource.query(
`SELECT * FROM ${schemaName}."calendarChannel" WHERE "isSyncEnabled" = true AND "syncStage" = '${CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING}'`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Repository } from 'typeorm';
import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
import { TimelineActivityRepository } from 'src/modules/timeline/repositories/timeline-activity.repository';
Expand All @@ -17,7 +17,6 @@ export class CalendarEventParticipantListener {
constructor(
@InjectObjectMetadataRepository(TimelineActivityWorkspaceEntity)
private readonly timelineActivityRepository: TimelineActivityRepository,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@InjectRepository(ObjectMetadataEntity, 'core')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
) {}
Expand All @@ -38,8 +37,7 @@ export class CalendarEventParticipantListener {

// TODO: move to a job?

const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const dataSourceSchema = getWorkspaceSchemaName(workspaceId);

const calendarEventObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { MessageChannelSyncStage } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import {
Expand Down Expand Up @@ -48,9 +49,7 @@ export class MessagingMessageListFetchCronJob {

for (const activeWorkspace of activeWorkspaces) {
try {
const schemaName = this.workspaceDataSourceService.getSchemaName(
activeWorkspace.id,
);
const schemaName = getWorkspaceSchemaName(activeWorkspace.id);

// TODO: deprecate looking for FULL_MESSAGE_LIST_FETCH_PENDING as we introduce MESSAGE_LIST_FETCH_PENDING
const messageChannels = await mainDataSource.query(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { MessageChannelSyncStage } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import {
Expand Down Expand Up @@ -48,9 +49,7 @@ export class MessagingMessagesImportCronJob {

for (const activeWorkspace of activeWorkspaces) {
try {
const schemaName = this.workspaceDataSourceService.getSchemaName(
activeWorkspace.id,
);
const schemaName = getWorkspaceSchemaName(activeWorkspace.id);

const messageChannels = await mainDataSource.query(
`SELECT * FROM ${schemaName}."messageChannel" WHERE "isSyncEnabled" = true AND "syncStage" = '${MessageChannelSyncStage.MESSAGES_IMPORT_PENDING}'`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Repository } from 'typeorm';
import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
import { TimelineActivityRepository } from 'src/modules/timeline/repositories/timeline-activity.repository';
Expand All @@ -17,7 +17,6 @@ export class MessageParticipantListener {
constructor(
@InjectObjectMetadataRepository(TimelineActivityWorkspaceEntity)
private readonly timelineActivityRepository: TimelineActivityRepository,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@InjectRepository(ObjectMetadataEntity, 'core')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
) {}
Expand All @@ -35,9 +34,7 @@ export class MessageParticipantListener {

// TODO: move to a job?

const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(
batchEvent.workspaceId,
);
const dataSourceSchema = getWorkspaceSchemaName(batchEvent.workspaceId);

const messageObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
Expand Down
Loading