Remove usages of connectToDataSource and use workspaceDataSource (#11873)
In this PR we are 1. cleaning typeORM service by removing connectToDataSource method 2. using workspaceDataSource instead of mainDataSource when possible, and replacing raw SQL with workspaceRepository methods to use
This commit is contained in:
@ -85,12 +85,13 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
|
||||
await this.validate(args, options);
|
||||
|
||||
const dataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(
|
||||
authContext.workspace.id,
|
||||
);
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: authContext.workspace.id,
|
||||
shouldFailIfMetadataNotFound: false,
|
||||
});
|
||||
|
||||
const featureFlagsMap = dataSource.featureFlagMap;
|
||||
const featureFlagsMap = workspaceDataSource.featureFlagMap;
|
||||
|
||||
const isPermissionsV2Enabled =
|
||||
featureFlagsMap[FeatureFlagKey.IsPermissionsV2Enabled];
|
||||
@ -127,7 +128,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
const executedByApiKey = isDefined(authContext.apiKey);
|
||||
const shouldBypassPermissionChecks = executedByApiKey;
|
||||
|
||||
const repository = dataSource.getRepository(
|
||||
const repository = workspaceDataSource.getRepository(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
@ -151,7 +152,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
const graphqlQueryResolverExecutionArgs = {
|
||||
args: computedArgs,
|
||||
options,
|
||||
dataSource,
|
||||
dataSource: workspaceDataSource,
|
||||
repository,
|
||||
graphqlQueryParser,
|
||||
graphqlQuerySelectedFieldsResult,
|
||||
|
||||
@ -156,7 +156,10 @@ export class RestApiCoreServiceV2 {
|
||||
}
|
||||
|
||||
const dataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(workspace.id);
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId: workspace.id,
|
||||
shouldFailIfMetadataNotFound: false,
|
||||
});
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
|
||||
@ -100,7 +100,9 @@ export class GoogleAPIsService {
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(workspaceId);
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const scopes = getGoogleApisOauthScopes();
|
||||
|
||||
|
||||
@ -104,7 +104,9 @@ export class MicrosoftAPIsService {
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(workspaceId);
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const scopes = getMicrosoftApisOauthScopes();
|
||||
|
||||
|
||||
@ -14,9 +14,7 @@ describe('JwtAuthStrategy', () => {
|
||||
let workspaceRepository: any;
|
||||
let userWorkspaceRepository: any;
|
||||
let userRepository: any;
|
||||
let dataSourceService: any;
|
||||
let typeORMService: any;
|
||||
|
||||
let twentyORMGlobalManager: any;
|
||||
const jwt = {
|
||||
sub: 'sub-default',
|
||||
jti: 'jti-default',
|
||||
@ -38,6 +36,12 @@ describe('JwtAuthStrategy', () => {
|
||||
extractJwtFromRequest: jest.fn(() => () => 'token'),
|
||||
};
|
||||
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest.fn(async () => ({
|
||||
findOne: jest.fn(async () => ({ id: 'api-key-id', revokedAt: null })),
|
||||
})),
|
||||
};
|
||||
|
||||
// first we test the API_KEY case
|
||||
it('should throw AuthException if type is API_KEY and workspace is not found', async () => {
|
||||
const payload = {
|
||||
@ -50,10 +54,8 @@ describe('JwtAuthStrategy', () => {
|
||||
};
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
{} as any,
|
||||
userWorkspaceRepository,
|
||||
@ -77,19 +79,15 @@ describe('JwtAuthStrategy', () => {
|
||||
findOneBy: jest.fn(async () => new Workspace()),
|
||||
};
|
||||
|
||||
dataSourceService = {
|
||||
getLastDataSourceMetadataFromWorkspaceIdOrFail: jest.fn(async () => ({})),
|
||||
};
|
||||
|
||||
typeORMService = {
|
||||
connectToDataSource: jest.fn(async () => {}),
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest.fn(async () => ({
|
||||
findOne: jest.fn(async () => null),
|
||||
})),
|
||||
};
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
{} as any,
|
||||
userWorkspaceRepository,
|
||||
@ -113,21 +111,15 @@ describe('JwtAuthStrategy', () => {
|
||||
findOneBy: jest.fn(async () => new Workspace()),
|
||||
};
|
||||
|
||||
const mockDataSource = {
|
||||
query: jest
|
||||
.fn()
|
||||
.mockResolvedValue([{ id: 'api-key-id', revokedAt: null }]),
|
||||
twentyORMGlobalManager = {
|
||||
getRepositoryForWorkspace: jest.fn(async () => ({
|
||||
findOne: jest.fn(async () => ({ id: 'api-key-id', revokedAt: null })),
|
||||
})),
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(typeORMService, 'connectToDataSource')
|
||||
.mockResolvedValue(mockDataSource as any);
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
{} as any,
|
||||
userWorkspaceRepository,
|
||||
@ -140,7 +132,6 @@ describe('JwtAuthStrategy', () => {
|
||||
});
|
||||
|
||||
// second we test the ACCESS cases
|
||||
|
||||
it('should throw AuthExceptionCode if type is ACCESS, no jti, and user not found', async () => {
|
||||
const payload = {
|
||||
sub: 'sub-default',
|
||||
@ -156,10 +147,8 @@ describe('JwtAuthStrategy', () => {
|
||||
};
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
userRepository,
|
||||
userWorkspaceRepository,
|
||||
@ -194,10 +183,8 @@ describe('JwtAuthStrategy', () => {
|
||||
};
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
userRepository,
|
||||
userWorkspaceRepository,
|
||||
@ -235,10 +222,8 @@ describe('JwtAuthStrategy', () => {
|
||||
};
|
||||
|
||||
strategy = new JwtAuthStrategy(
|
||||
{} as any,
|
||||
jwtWrapperService,
|
||||
typeORMService,
|
||||
dataSourceService,
|
||||
twentyORMGlobalManager,
|
||||
workspaceRepository,
|
||||
userRepository,
|
||||
userWorkspaceRepository,
|
||||
|
||||
@ -5,7 +5,6 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Strategy } from 'passport-jwt';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
@ -15,20 +14,17 @@ import {
|
||||
JwtPayload,
|
||||
} from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { ApiKeyWorkspaceEntity } from 'src/modules/api-key/standard-objects/api-key.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
private readonly jwtWrapperService: JwtWrapperService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectRepository(Workspace, 'core')
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(User, 'core')
|
||||
@ -37,7 +33,7 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
||||
) {
|
||||
const jwtFromRequestFunction = jwtWrapperService.extractJwtFromRequest();
|
||||
const secretOrKeyProviderFunction = async (request, rawJwtToken, done) => {
|
||||
const secretOrKeyProviderFunction = async (_request, rawJwtToken, done) => {
|
||||
try {
|
||||
const decodedToken = jwtWrapperService.decode(
|
||||
rawJwtToken,
|
||||
@ -75,20 +71,20 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
);
|
||||
}
|
||||
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
const apiKeyRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ApiKeyWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'apiKey',
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
const res = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."apiKey" WHERE id = $1`,
|
||||
[payload.jti],
|
||||
);
|
||||
|
||||
apiKey = res?.[0];
|
||||
apiKey = await apiKeyRepository.findOne({
|
||||
where: {
|
||||
id: payload.jti,
|
||||
},
|
||||
});
|
||||
|
||||
if (!apiKey || apiKey.revokedAt) {
|
||||
throw new AuthException(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
@ -13,7 +13,6 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { PermissionsException } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
@ -27,7 +26,6 @@ describe('UserWorkspaceService', () => {
|
||||
let userWorkspaceRepository: Repository<UserWorkspace>;
|
||||
let userRepository: Repository<User>;
|
||||
let objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
let dataSourceService: DataSourceService;
|
||||
let typeORMService: TypeORMService;
|
||||
let workspaceInvitationService: WorkspaceInvitationService;
|
||||
let workspaceEventEmitter: WorkspaceEventEmitter;
|
||||
@ -71,7 +69,7 @@ describe('UserWorkspaceService', () => {
|
||||
{
|
||||
provide: TypeORMService,
|
||||
useValue: {
|
||||
connectToDataSource: jest.fn(),
|
||||
getMainDataSource: jest.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -116,7 +114,6 @@ describe('UserWorkspaceService', () => {
|
||||
objectMetadataRepository = module.get(
|
||||
getRepositoryToken(ObjectMetadataEntity, 'metadata'),
|
||||
);
|
||||
dataSourceService = module.get<DataSourceService>(DataSourceService);
|
||||
typeORMService = module.get<TypeORMService>(TypeORMService);
|
||||
workspaceInvitationService = module.get<WorkspaceInvitationService>(
|
||||
WorkspaceInvitationService,
|
||||
@ -179,12 +176,9 @@ describe('UserWorkspaceService', () => {
|
||||
defaultAvatarUrl: 'avatar-url',
|
||||
locale: 'en',
|
||||
} as User;
|
||||
const dataSourceMetadata = {
|
||||
schema: 'public',
|
||||
} as DataSourceEntity;
|
||||
const workspaceDataSource = {
|
||||
const mainDataSource = {
|
||||
query: jest.fn(),
|
||||
};
|
||||
} as unknown as DataSource;
|
||||
const workspaceMember = [
|
||||
{
|
||||
id: 'workspace-member-id',
|
||||
@ -197,17 +191,16 @@ describe('UserWorkspaceService', () => {
|
||||
const objectMetadata = {
|
||||
nameSingular: 'workspaceMember',
|
||||
} as ObjectMetadataEntity;
|
||||
const workspaceMemberRepository = {
|
||||
insert: jest.fn(),
|
||||
find: jest.fn().mockResolvedValue(workspaceMember),
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(
|
||||
dataSourceService,
|
||||
'getLastDataSourceMetadataFromWorkspaceIdOrFail',
|
||||
)
|
||||
.mockResolvedValue(dataSourceMetadata);
|
||||
.spyOn(typeORMService, 'getMainDataSource')
|
||||
.mockReturnValue(mainDataSource);
|
||||
jest
|
||||
.spyOn(typeORMService, 'connectToDataSource')
|
||||
.mockResolvedValue(workspaceDataSource as any);
|
||||
workspaceDataSource.query
|
||||
.spyOn(mainDataSource, 'query')
|
||||
.mockResolvedValueOnce(undefined)
|
||||
.mockResolvedValueOnce(workspaceMember);
|
||||
jest
|
||||
@ -217,15 +210,23 @@ describe('UserWorkspaceService', () => {
|
||||
.spyOn(workspaceEventEmitter, 'emitDatabaseBatchEvent')
|
||||
.mockImplementation();
|
||||
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.mockResolvedValue(workspaceMemberRepository as any);
|
||||
|
||||
await service.createWorkspaceMember(workspaceId, user);
|
||||
|
||||
expect(
|
||||
dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail,
|
||||
).toHaveBeenCalledWith(workspaceId);
|
||||
expect(typeORMService.connectToDataSource).toHaveBeenCalledWith(
|
||||
dataSourceMetadata,
|
||||
);
|
||||
expect(workspaceDataSource.query).toHaveBeenCalledTimes(2);
|
||||
expect(workspaceMemberRepository.insert).toHaveBeenCalledWith({
|
||||
name: {
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
},
|
||||
colorScheme: 'System',
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
locale: 'en',
|
||||
avatarUrl: 'avatar-url',
|
||||
});
|
||||
expect(objectMetadataRepository.findOneOrFail).toHaveBeenCalledWith({
|
||||
where: {
|
||||
nameSingular: 'workspaceMember',
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@ -69,33 +69,35 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
||||
}
|
||||
|
||||
async createWorkspaceMember(workspaceId: string, user: User) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
await workspaceMemberRepository.insert({
|
||||
name: {
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
},
|
||||
colorScheme: 'System',
|
||||
userId: user.id,
|
||||
userEmail: user.email,
|
||||
avatarUrl: user.defaultAvatarUrl ?? '',
|
||||
locale: (user.locale ?? SOURCE_LOCALE) as keyof typeof APP_LOCALES,
|
||||
});
|
||||
|
||||
await workspaceDataSource?.query(
|
||||
`INSERT INTO ${dataSourceMetadata.schema}."workspaceMember"
|
||||
("nameFirstName", "nameLastName", "colorScheme", "userId", "userEmail", "avatarUrl", "locale")
|
||||
VALUES ($1, $2, 'System', $3, $4, $5, $6)`,
|
||||
[
|
||||
user.firstName,
|
||||
user.lastName,
|
||||
user.id,
|
||||
user.email,
|
||||
user.defaultAvatarUrl ?? '',
|
||||
user.locale ?? SOURCE_LOCALE,
|
||||
],
|
||||
);
|
||||
const workspaceMember = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "userId"='${user.id}'`,
|
||||
);
|
||||
const workspaceMember = await workspaceMemberRepository.find({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
assert(
|
||||
workspaceMember.length === 1,
|
||||
workspaceMember?.length === 1,
|
||||
`Error while creating workspace member ${user.email} on workspace ${workspaceId}`,
|
||||
);
|
||||
const objectMetadata = await this.objectMetadataRepository.findOneOrFail({
|
||||
|
||||
@ -6,13 +6,11 @@ import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { isWorkspaceActiveOrSuspended } from 'twenty-shared/workspace';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
||||
@ -38,13 +36,11 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {
|
||||
super(userRepository);
|
||||
}
|
||||
@ -88,17 +84,16 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
const workspaceMembers = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember"`,
|
||||
);
|
||||
const workspaceMembers = await workspaceMemberRepository.find();
|
||||
|
||||
if (workspaceMembers.length > 1) {
|
||||
const userWorkspace =
|
||||
@ -119,9 +114,7 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
|
||||
assert(workspaceMember, 'WorkspaceMember not found');
|
||||
|
||||
await workspaceDataSource?.query(
|
||||
`DELETE FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "userId" = '${userId}'`,
|
||||
);
|
||||
await workspaceMemberRepository.delete({ userId });
|
||||
|
||||
const objectMetadata = await this.objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
|
||||
@ -61,11 +61,11 @@ import {
|
||||
} 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 { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
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 { ViewService } from 'src/modules/view/services/view.service';
|
||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||
|
||||
import { FieldMetadataValidationService } from './field-metadata-validation.service';
|
||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||
@ -910,74 +910,70 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
createdFieldMetadatas: FieldMetadataEntity[],
|
||||
workspaceId: string,
|
||||
) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const workspaceQueryRunner = workspaceDataSource?.createQueryRunner();
|
||||
await workspaceDataSource.transaction(
|
||||
async (workspaceEntityManager: WorkspaceEntityManager) => {
|
||||
const viewsRepository = workspaceEntityManager.getRepository('view', {
|
||||
shouldBypassPermissionChecks: true,
|
||||
});
|
||||
|
||||
if (!workspaceQueryRunner) {
|
||||
throw new FieldMetadataException(
|
||||
'Could not create workspace query runner',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
await workspaceQueryRunner.connect();
|
||||
await workspaceQueryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
for (const createdFieldMetadata of createdFieldMetadatas) {
|
||||
const view = await workspaceQueryRunner?.query(
|
||||
`SELECT id FROM ${dataSourceMetadata.schema}."view"
|
||||
WHERE "objectMetadataId" = '${createdFieldMetadata.objectMetadataId}'`,
|
||||
const viewFieldsRepository = workspaceEntityManager.getRepository(
|
||||
'viewField',
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (!isEmpty(view)) {
|
||||
const existingViewFields = (await workspaceQueryRunner?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."viewField"
|
||||
WHERE "viewId" = '${view[0].id}'`,
|
||||
)) as ViewFieldWorkspaceEntity[];
|
||||
const isVisible =
|
||||
existingViewFields.length < settings.maxVisibleViewFields;
|
||||
for (const createdFieldMetadata of createdFieldMetadatas) {
|
||||
const views = await viewsRepository.find({
|
||||
where: {
|
||||
objectMetadataId: createdFieldMetadata.objectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
const createdFieldIsAlreadyInView = existingViewFields.some(
|
||||
(existingViewField) =>
|
||||
existingViewField.fieldMetadataId === createdFieldMetadata.id,
|
||||
);
|
||||
if (!isEmpty(views)) {
|
||||
const view = views[0];
|
||||
const existingViewFields = await viewFieldsRepository.find({
|
||||
where: {
|
||||
viewId: view.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!createdFieldIsAlreadyInView) {
|
||||
const lastPosition = existingViewFields
|
||||
.map((viewField) => viewField.position)
|
||||
.reduce((acc, position) => {
|
||||
if (position > acc) {
|
||||
return position;
|
||||
}
|
||||
const isVisible =
|
||||
existingViewFields.length < settings.maxVisibleViewFields;
|
||||
|
||||
return acc;
|
||||
}, -1);
|
||||
|
||||
await workspaceQueryRunner?.query(
|
||||
`INSERT INTO ${dataSourceMetadata.schema}."viewField"
|
||||
("fieldMetadataId", "position", "isVisible", "size", "viewId")
|
||||
VALUES ('${createdFieldMetadata.id}', '${
|
||||
lastPosition + 1
|
||||
}', ${isVisible}, 180, '${view[0].id}')`,
|
||||
const createdFieldIsAlreadyInView = existingViewFields.some(
|
||||
(existingViewField) =>
|
||||
existingViewField.fieldMetadataId === createdFieldMetadata.id,
|
||||
);
|
||||
|
||||
if (!createdFieldIsAlreadyInView) {
|
||||
const lastPosition = existingViewFields
|
||||
.map((viewField) => viewField.position)
|
||||
.reduce((acc, position) => {
|
||||
if (position > acc) {
|
||||
return position;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, -1);
|
||||
|
||||
await viewFieldsRepository.insert({
|
||||
fieldMetadataId: createdFieldMetadata.id,
|
||||
position: lastPosition + 1,
|
||||
isVisible,
|
||||
size: 180,
|
||||
viewId: view.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await workspaceQueryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await workspaceQueryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await workspaceQueryRunner.release();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async getFieldMetadataItemsByBatch(
|
||||
|
||||
@ -8,15 +8,15 @@ import {
|
||||
RemoteServerEntity,
|
||||
RemoteServerType,
|
||||
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
|
||||
import { STRIPE_DISTANT_TABLES } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/utils/stripe-distant-tables.util';
|
||||
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
|
||||
import { isQueryTimeoutError } from 'src/engine/utils/query-timeout.util';
|
||||
import {
|
||||
DistantTableException,
|
||||
DistantTableExceptionCode,
|
||||
} from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.exception';
|
||||
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
|
||||
import { STRIPE_DISTANT_TABLES } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/utils/stripe-distant-tables.util';
|
||||
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
|
||||
import { isQueryTimeoutError } from 'src/engine/utils/query-timeout.util';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
@Injectable()
|
||||
export class DistantTableService {
|
||||
@ -73,13 +73,11 @@ export class DistantTableService {
|
||||
const tmpSchemaId = v4();
|
||||
const tmpSchemaName = `${workspaceId}_${remoteServer.id}_${tmpSchemaId}`;
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
try {
|
||||
const distantTables = await workspaceDataSource.transaction(
|
||||
const distantTables = await mainDataSource.transaction(
|
||||
async (entityManager: EntityManager) => {
|
||||
await entityManager.query(`CREATE SCHEMA "${tmpSchemaName}"`);
|
||||
|
||||
|
||||
@ -37,13 +37,11 @@ export class ForeignTableService {
|
||||
workspaceId: string,
|
||||
foreignDataWrapperId: string,
|
||||
): Promise<string[]> {
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
return (
|
||||
await workspaceDataSource.query(
|
||||
await mainDataSource.query(
|
||||
`SELECT foreign_table_name, foreign_server_name FROM information_schema.foreign_tables WHERE foreign_server_name = $1`,
|
||||
[foreignDataWrapperId],
|
||||
)
|
||||
|
||||
@ -182,16 +182,14 @@ export class RemoteTableService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
const { baseName: localTableBaseName, suffix: localTableSuffix } =
|
||||
await getRemoteTableLocalName(
|
||||
input.name,
|
||||
dataSourceMetatada.schema,
|
||||
workspaceDataSource,
|
||||
mainDataSource,
|
||||
);
|
||||
|
||||
const localTableName = localTableSuffix
|
||||
|
||||
@ -61,13 +61,10 @@ export class SeederService {
|
||||
const schemaName =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const workspaceDataSource: DataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource: DataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
const entityManager: EntityManager =
|
||||
workspaceDataSource.createEntityManager();
|
||||
const entityManager: EntityManager = mainDataSource.createEntityManager();
|
||||
|
||||
const filteredFieldMetadataSeeds = objectMetadataSeed.fields.filter(
|
||||
(field) =>
|
||||
|
||||
@ -47,15 +47,15 @@ export class WorkspaceDataSource extends DataSource {
|
||||
roleId?: string,
|
||||
): WorkspaceRepository<Entity> {
|
||||
if (shouldBypassPermissionChecks === true) {
|
||||
return this.manager.getRepository(target, shouldBypassPermissionChecks);
|
||||
return this.manager.getRepository(target, {
|
||||
shouldBypassPermissionChecks: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (roleId) {
|
||||
return this.manager.getRepository(
|
||||
target,
|
||||
shouldBypassPermissionChecks,
|
||||
return this.manager.getRepository(target, {
|
||||
roleId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return this.manager.getRepository(target);
|
||||
|
||||
@ -2,6 +2,7 @@ import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import {
|
||||
EntityManager,
|
||||
EntityTarget,
|
||||
FindManyOptions,
|
||||
InsertResult,
|
||||
ObjectLiteral,
|
||||
QueryRunner,
|
||||
@ -44,16 +45,19 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
|
||||
override getRepository<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
shouldBypassPermissionChecks = false,
|
||||
roleId?: string,
|
||||
permissionOptions?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
roleId?: string;
|
||||
},
|
||||
): WorkspaceRepository<Entity> {
|
||||
const dataSource = this.connection;
|
||||
|
||||
const repositoryKey = this.getRepositoryKey({
|
||||
target,
|
||||
dataSource,
|
||||
roleId,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId: permissionOptions?.roleId,
|
||||
shouldBypassPermissionChecks:
|
||||
permissionOptions?.shouldBypassPermissionChecks ?? false,
|
||||
});
|
||||
const repoFromMap = this.repositories.get(repositoryKey);
|
||||
|
||||
@ -63,10 +67,11 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
|
||||
let objectPermissions = {};
|
||||
|
||||
if (roleId) {
|
||||
if (permissionOptions?.roleId) {
|
||||
const objectPermissionsByRoleId = dataSource.permissionsPerRoleId;
|
||||
|
||||
objectPermissions = objectPermissionsByRoleId?.[roleId] ?? {};
|
||||
objectPermissions =
|
||||
objectPermissionsByRoleId?.[permissionOptions?.roleId] ?? {};
|
||||
}
|
||||
|
||||
const newRepository = new WorkspaceRepository<Entity>(
|
||||
@ -76,7 +81,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
dataSource.featureFlagMap,
|
||||
this.queryRunner,
|
||||
objectPermissions,
|
||||
shouldBypassPermissionChecks,
|
||||
permissionOptions?.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
this.repositories.set(repositoryKey, newRepository);
|
||||
@ -135,17 +140,30 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
}
|
||||
}
|
||||
|
||||
override find<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
options?: FindManyOptions<Entity>,
|
||||
permissionOptions?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
},
|
||||
): Promise<Entity[]> {
|
||||
this.validatePermissions(target, 'select', permissionOptions);
|
||||
|
||||
return super.find(target, options);
|
||||
}
|
||||
|
||||
override insert<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
entityOrEntities:
|
||||
| QueryDeepPartialEntity<Entity>
|
||||
| QueryDeepPartialEntity<Entity>[],
|
||||
options?: {
|
||||
permissionOptions?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
},
|
||||
): Promise<InsertResult> {
|
||||
this.validatePermissions(target, 'insert', options);
|
||||
this.validatePermissions(target, 'insert', permissionOptions);
|
||||
|
||||
return super.insert(target, entityOrEntities);
|
||||
}
|
||||
@ -156,12 +174,12 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
| QueryDeepPartialEntity<Entity>
|
||||
| QueryDeepPartialEntity<Entity>[],
|
||||
conflictPathsOrOptions: string[] | UpsertOptions<Entity>,
|
||||
options?: {
|
||||
permissionOptions?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
},
|
||||
): Promise<InsertResult> {
|
||||
this.validatePermissions(target, 'update', options);
|
||||
this.validatePermissions(target, 'update', permissionOptions);
|
||||
|
||||
return super.upsert(target, entityOrEntities, conflictPathsOrOptions);
|
||||
}
|
||||
@ -194,7 +212,7 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
private validatePermissions<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
operationType: OperationType,
|
||||
options?: {
|
||||
permissionOptions?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
},
|
||||
@ -208,14 +226,15 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options?.shouldBypassPermissionChecks === true) {
|
||||
if (permissionOptions?.shouldBypassPermissionChecks === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateOperationIsPermittedOrThrow({
|
||||
entityName: this.extractTargetNameSingularFromEntityTarget(target),
|
||||
operationType,
|
||||
objectRecordsPermissions: options?.objectRecordsPermissions ?? {},
|
||||
objectRecordsPermissions:
|
||||
permissionOptions?.objectRecordsPermissions ?? {},
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,10 +65,13 @@ export class TwentyORMGlobalManager {
|
||||
return repository;
|
||||
}
|
||||
|
||||
async getDataSourceForWorkspace(
|
||||
workspaceId: string,
|
||||
async getDataSourceForWorkspace({
|
||||
workspaceId,
|
||||
shouldFailIfMetadataNotFound = true,
|
||||
) {
|
||||
}: {
|
||||
workspaceId: string;
|
||||
shouldFailIfMetadataNotFound?: boolean;
|
||||
}) {
|
||||
return await this.workspaceDataSourceFactory.create(
|
||||
workspaceId,
|
||||
null,
|
||||
|
||||
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { EntitySchemaOptions } from 'typeorm';
|
||||
|
||||
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
|
||||
@ -277,8 +278,13 @@ export class WorkspaceCacheStorageService {
|
||||
);
|
||||
}
|
||||
|
||||
async flush(workspaceId: string, metadataVersion: number): Promise<void> {
|
||||
await this.flushVersionedMetadata(workspaceId, metadataVersion);
|
||||
async flush(
|
||||
workspaceId: string,
|
||||
metadataVersion: number | undefined,
|
||||
): Promise<void> {
|
||||
if (isDefined(metadataVersion)) {
|
||||
await this.flushVersionedMetadata(workspaceId, metadataVersion);
|
||||
}
|
||||
|
||||
await this.cacheStorageService.del(
|
||||
`${WorkspaceCacheKeys.MetadataPermissionsRolesPermissions}:${workspaceId}`,
|
||||
|
||||
@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.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';
|
||||
|
||||
@Injectable()
|
||||
@ -20,11 +19,12 @@ export class WorkspaceDataSourceService {
|
||||
* @param workspaceId
|
||||
* @returns
|
||||
*/
|
||||
public async connectToWorkspaceDataSource(
|
||||
workspaceId: string,
|
||||
): Promise<DataSource> {
|
||||
const { dataSource } =
|
||||
await this.connectedToWorkspaceDataSourceAndReturnMetadata(workspaceId);
|
||||
public async connectToMainDataSource(): Promise<DataSource> {
|
||||
const dataSource = this.typeormService.getMainDataSource();
|
||||
|
||||
if (!dataSource) {
|
||||
throw new Error(`Could not connect to workspace data source`);
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
@ -38,26 +38,6 @@ export class WorkspaceDataSourceService {
|
||||
return dataSource.length > 0;
|
||||
}
|
||||
|
||||
public async connectedToWorkspaceDataSourceAndReturnMetadata(
|
||||
workspaceId: string,
|
||||
): Promise<{ dataSource: DataSource; dataSourceMetadata: DataSourceEntity }> {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const dataSource =
|
||||
await this.typeormService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
if (!dataSource) {
|
||||
throw new Error(
|
||||
`Could not connect to workspace data source for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { dataSource, dataSourceMetadata };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a new DB schema for a workspace
|
||||
@ -128,10 +108,9 @@ export class WorkspaceDataSourceService {
|
||||
if (transactionManager) {
|
||||
return await transactionManager.query(query, parameters);
|
||||
}
|
||||
const workspaceDataSource =
|
||||
await this.connectToWorkspaceDataSource(workspaceId);
|
||||
const dataSource = await this.connectToMainDataSource();
|
||||
|
||||
return await workspaceDataSource.query(query, parameters);
|
||||
return await dataSource.query(query, parameters);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Error executing raw query for workspace ${workspaceId}: ${error.message}`,
|
||||
|
||||
@ -9,7 +9,7 @@ import { personPrefillData } from 'src/engine/workspace-manager/standard-objects
|
||||
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
|
||||
|
||||
export const standardObjectsPrefillData = async (
|
||||
workspaceDataSource: DataSource,
|
||||
mainDataSource: DataSource,
|
||||
schemaName: string,
|
||||
objectMetadata: ObjectMetadataEntity[],
|
||||
) => {
|
||||
@ -34,30 +34,28 @@ export const standardObjectsPrefillData = async (
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
workspaceDataSource.transaction(
|
||||
async (entityManager: WorkspaceEntityManager) => {
|
||||
await companyPrefillData(entityManager, schemaName);
|
||||
await personPrefillData(entityManager, schemaName);
|
||||
const viewDefinitionsWithId = await seedViewWithDemoData(
|
||||
entityManager,
|
||||
schemaName,
|
||||
objectMetadataMap,
|
||||
);
|
||||
mainDataSource.transaction(async (entityManager: WorkspaceEntityManager) => {
|
||||
await companyPrefillData(entityManager, schemaName);
|
||||
await personPrefillData(entityManager, schemaName);
|
||||
const viewDefinitionsWithId = await seedViewWithDemoData(
|
||||
entityManager,
|
||||
schemaName,
|
||||
objectMetadataMap,
|
||||
);
|
||||
|
||||
await seedWorkspaceFavorites(
|
||||
viewDefinitionsWithId
|
||||
.filter(
|
||||
(view) =>
|
||||
view.key === 'INDEX' &&
|
||||
shouldSeedWorkspaceFavorite(
|
||||
view.objectMetadataId,
|
||||
objectMetadataMap,
|
||||
),
|
||||
)
|
||||
.map((view) => view.id),
|
||||
entityManager,
|
||||
schemaName,
|
||||
);
|
||||
},
|
||||
);
|
||||
await seedWorkspaceFavorites(
|
||||
viewDefinitionsWithId
|
||||
.filter(
|
||||
(view) =>
|
||||
view.key === 'INDEX' &&
|
||||
shouldSeedWorkspaceFavorite(
|
||||
view.objectMetadataId,
|
||||
objectMetadataMap,
|
||||
),
|
||||
)
|
||||
.map((view) => view.id),
|
||||
entityManager,
|
||||
schemaName,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
WorkspaceHealthOptions,
|
||||
} from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-options.interface';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
@ -31,7 +30,6 @@ export class WorkspaceHealthService {
|
||||
@InjectDataSource('metadata')
|
||||
private readonly metadataDataSource: DataSource,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly databaseStructureService: DatabaseStructureService,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
@ -62,9 +60,6 @@ export class WorkspaceHealthService {
|
||||
);
|
||||
}
|
||||
|
||||
// Try to connect to the data source
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
const objectMetadataCollection =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||
|
||||
|
||||
@ -196,20 +196,18 @@ export class WorkspaceManagerService {
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
if (!workspaceDataSource) {
|
||||
throw new Error('Could not connect to workspace data source');
|
||||
if (!mainDataSource) {
|
||||
throw new Error('Could not connect to main data source');
|
||||
}
|
||||
|
||||
const createdObjectMetadata =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||
|
||||
await standardObjectsPrefillData(
|
||||
workspaceDataSource,
|
||||
mainDataSource,
|
||||
dataSourceMetadata.schema,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
@ -226,20 +224,18 @@ export class WorkspaceManagerService {
|
||||
dataSourceMetadata: DataSourceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
if (!workspaceDataSource) {
|
||||
throw new Error('Could not connect to workspace data source');
|
||||
if (!mainDataSource) {
|
||||
throw new Error('Could not connect to main data source');
|
||||
}
|
||||
|
||||
const createdObjectMetadata =
|
||||
await this.objectMetadataService.findManyWithinWorkspace(workspaceId);
|
||||
|
||||
await seedWorkspaceWithDemoData(
|
||||
workspaceDataSource,
|
||||
mainDataSource,
|
||||
dataSourceMetadata.schema,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
@ -55,13 +55,11 @@ export class WorkspaceMigrationRunnerService {
|
||||
public async executeMigrationFromPendingMigrations(
|
||||
workspaceId: string,
|
||||
): Promise<WorkspaceMigrationTableAction[]> {
|
||||
const workspaceDataSource =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
const mainDataSource =
|
||||
await this.workspaceDataSourceService.connectToMainDataSource();
|
||||
|
||||
if (!workspaceDataSource) {
|
||||
throw new Error('Workspace data source not found');
|
||||
if (!mainDataSource) {
|
||||
throw new Error('Main data source not found');
|
||||
}
|
||||
|
||||
const pendingMigrations =
|
||||
@ -76,7 +74,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
return [...acc, ...pendingMigration.migrations];
|
||||
}, []);
|
||||
|
||||
const queryRunner = workspaceDataSource?.createQueryRunner();
|
||||
const queryRunner = mainDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
Reference in New Issue
Block a user