Files
twenty/packages/twenty-server/src/engine/metadata-modules/data-source/data-source.service.ts
T
Charles BochetandGitHub 81fc960712 Deprecate dataSource table with dual-write to workspace.databaseSchema (#19059)
## Summary
- Starts deprecation of the `core.dataSource` table by introducing a
dual-write system: `DataSourceService.createDataSourceMetadata` now
writes to both `core.dataSource` and `core.workspace.databaseSchema`
- Migrates read sites (`WorkspaceDataSourceService.checkSchemaExists`,
`WorkspaceSchemaFactory`, `MiddlewareService`,
`WorkspacesMigrationCommandRunner`) to read from
`workspace.databaseSchema` instead of querying the `dataSource` table
- Removes the unused `databaseUrl` field from `WorkspaceEntity` and
drops the column via migration
- Adds a 1.20 upgrade command to backfill `workspace.databaseSchema`
from `dataSource.schema` for existing workspaces
2026-03-28 11:29:19 +01:00

107 lines
3.4 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { type FindManyOptions, Repository } from 'typeorm';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import {
DataSourceException,
DataSourceExceptionCode,
} from 'src/engine/metadata-modules/data-source/data-source.exception';
import { DataSourceEntity } from './data-source.entity';
// @deprecated - This service is being deprecated. During the transition,
// writes go to both the dataSource table and workspace table (dual-write).
// Reads should progressively migrate to use workspace.databaseSchema
// or the deterministic getWorkspaceSchemaName(workspaceId) utility.
@Injectable()
export class DataSourceService {
constructor(
@InjectRepository(DataSourceEntity)
private readonly dataSourceMetadataRepository: Repository<DataSourceEntity>,
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
) {}
async createDataSourceMetadata(
workspaceId: string,
workspaceSchema: string,
): Promise<DataSourceEntity> {
const dataSource = await this.dataSourceMetadataRepository.findOne({
where: { workspaceId },
});
// Dual-write: always keep workspace.databaseSchema in sync
await this.workspaceRepository.update(workspaceId, {
databaseSchema: workspaceSchema,
});
if (dataSource) {
return dataSource;
}
return this.dataSourceMetadataRepository.save({
workspaceId,
schema: workspaceSchema,
});
}
// @deprecated - Use workspace.activationStatus or workspace.databaseSchema
// to check if a workspace has been initialized instead.
async getManyDataSourceMetadata(
options: FindManyOptions<DataSourceEntity> = {},
): Promise<DataSourceEntity[]> {
return this.dataSourceMetadataRepository.find(options);
}
// @deprecated - Use workspace.databaseSchema or
// getWorkspaceSchemaName(workspaceId) instead.
async getDataSourcesMetadataFromWorkspaceId(
workspaceId: string,
): Promise<DataSourceEntity[]> {
return this.dataSourceMetadataRepository.find({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
// @deprecated - Use workspace.databaseSchema or
// getWorkspaceSchemaName(workspaceId) instead.
async getLastDataSourceMetadataFromWorkspaceId(
workspaceId: string,
): Promise<DataSourceEntity | null> {
return this.dataSourceMetadataRepository.findOne({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
// @deprecated - Use workspace.databaseSchema or
// getWorkspaceSchemaName(workspaceId) instead.
async getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId: string,
): Promise<DataSourceEntity> {
try {
return this.dataSourceMetadataRepository.findOneOrFail({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
} catch (error) {
throw new DataSourceException(
`Data source not found for workspace ${workspaceId}: ${error}`,
DataSourceExceptionCode.DATA_SOURCE_NOT_FOUND,
);
}
}
async delete(workspaceId: string): Promise<void> {
await this.dataSourceMetadataRepository.delete({ workspaceId });
// Dual-write: clear workspace.databaseSchema on delete
await this.workspaceRepository.update(workspaceId, {
databaseSchema: null,
});
}
}