Refactor global datasource part 3 (#16447)
## Context Following https://github.com/twentyhq/twenty/pull/16399 Now using the new global orm manager everywhere and returning a GlobalDatasource/WorkspaceDatasource based on a feature flag. This means we now need to wrap all our ORM calls within executeInWorkspaceContext callback (at least for now) so the global datasource can dynamically hydrate its context via the new store (the global datasource does not store anything related to workspaces as it is now a unique singleton). If feature flag is off it still uses local data stored in the workspace datasource.
This commit is contained in:
+20
-9
@@ -4,7 +4,8 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
export type UpdateWorkspaceMemberEmailJobData = {
|
||||
@@ -21,7 +22,7 @@ export class UpdateWorkspaceMemberEmailJob {
|
||||
|
||||
constructor(
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
@Process(UpdateWorkspaceMemberEmailJob.name)
|
||||
@@ -32,13 +33,23 @@ export class UpdateWorkspaceMemberEmailJob {
|
||||
const workspace =
|
||||
await this.userWorkspaceService.findFirstWorkspaceByUserId(userId);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspace.id);
|
||||
|
||||
await workspaceMemberRepository.update({ userId }, { userEmail: email });
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
await workspaceMemberRepository.update(
|
||||
{ userId },
|
||||
{ userEmail: email },
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ import {
|
||||
PermissionsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
describe('UserService', () => {
|
||||
let service: UserService;
|
||||
let userRepository: Repository<UserEntity>;
|
||||
let workspaceService: WorkspaceService;
|
||||
let twentyORMGlobalManager: TwentyORMGlobalManager;
|
||||
let globalWorkspaceOrmManager: GlobalWorkspaceOrmManager;
|
||||
let userRoleService: UserRoleService;
|
||||
|
||||
const mockWorkspaceMemberRepo = {
|
||||
@@ -70,9 +70,13 @@ describe('UserService', () => {
|
||||
useValue: { add: jest.fn() },
|
||||
},
|
||||
{
|
||||
provide: TwentyORMGlobalManager,
|
||||
provide: GlobalWorkspaceOrmManager,
|
||||
useValue: {
|
||||
getRepositoryForWorkspace: jest.fn(),
|
||||
getRepository: jest.fn(),
|
||||
executeInWorkspaceContext: jest
|
||||
.fn()
|
||||
|
||||
.mockImplementation((_authContext: any, fn: () => any) => fn()),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -99,8 +103,8 @@ describe('UserService', () => {
|
||||
getRepositoryToken(UserEntity),
|
||||
);
|
||||
userRoleService = module.get<UserRoleService>(UserRoleService);
|
||||
twentyORMGlobalManager = module.get<TwentyORMGlobalManager>(
|
||||
TwentyORMGlobalManager,
|
||||
globalWorkspaceOrmManager = module.get<GlobalWorkspaceOrmManager>(
|
||||
GlobalWorkspaceOrmManager,
|
||||
);
|
||||
workspaceService = module.get<WorkspaceService>(WorkspaceService);
|
||||
});
|
||||
@@ -115,9 +119,7 @@ describe('UserService', () => {
|
||||
);
|
||||
|
||||
expect(res).toBeNull();
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(globalWorkspaceOrmManager.getRepository).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fetches from workspace member repo when workspace active', async () => {
|
||||
@@ -127,7 +129,7 @@ describe('UserService', () => {
|
||||
} as WorkspaceMemberWorkspaceEntity);
|
||||
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.spyOn(globalWorkspaceOrmManager, 'getRepository')
|
||||
.mockResolvedValue(mockWorkspaceMemberRepo);
|
||||
|
||||
const res = await service.loadWorkspaceMember(
|
||||
@@ -138,11 +140,13 @@ describe('UserService', () => {
|
||||
} as WorkspaceEntity,
|
||||
);
|
||||
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).toHaveBeenCalledWith('w1', 'workspaceMember', {
|
||||
shouldBypassPermissionChecks: true,
|
||||
});
|
||||
expect(globalWorkspaceOrmManager.getRepository).toHaveBeenCalledWith(
|
||||
'w1',
|
||||
'workspaceMember',
|
||||
{
|
||||
shouldBypassPermissionChecks: true,
|
||||
},
|
||||
);
|
||||
expect(mockWorkspaceMemberRepo.findOne).toHaveBeenCalledWith({
|
||||
where: { userId: 'u1' },
|
||||
});
|
||||
@@ -158,9 +162,7 @@ describe('UserService', () => {
|
||||
} as WorkspaceEntity);
|
||||
|
||||
expect(res).toEqual([]);
|
||||
expect(
|
||||
twentyORMGlobalManager.getRepositoryForWorkspace,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(globalWorkspaceOrmManager.getRepository).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fetches members withDeleted flag', async () => {
|
||||
@@ -168,7 +170,7 @@ describe('UserService', () => {
|
||||
.spyOn(mockWorkspaceMemberRepo, 'find')
|
||||
.mockResolvedValue([{ id: 'wm1' } as WorkspaceMemberWorkspaceEntity]);
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.spyOn(globalWorkspaceOrmManager, 'getRepository')
|
||||
.mockResolvedValue(mockWorkspaceMemberRepo);
|
||||
|
||||
const res = await service.loadWorkspaceMembers(
|
||||
@@ -203,7 +205,7 @@ describe('UserService', () => {
|
||||
{ id: 'wm-del' } as WorkspaceMemberWorkspaceEntity,
|
||||
]);
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.spyOn(globalWorkspaceOrmManager, 'getRepository')
|
||||
.mockResolvedValue(mockWorkspaceMemberRepo);
|
||||
|
||||
await service.loadDeletedWorkspaceMembersOnly({
|
||||
@@ -323,7 +325,7 @@ describe('UserService', () => {
|
||||
{ id: 'wm-2', userId: 'uX' } as WorkspaceMemberWorkspaceEntity,
|
||||
]);
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.spyOn(globalWorkspaceOrmManager, 'getRepository')
|
||||
.mockResolvedValue(mockWorkspaceMemberRepo);
|
||||
|
||||
jest
|
||||
@@ -358,7 +360,7 @@ describe('UserService', () => {
|
||||
.spyOn(mockWorkspaceMemberRepo, 'find')
|
||||
.mockResolvedValue([wmForUser('u2')]);
|
||||
jest
|
||||
.spyOn(twentyORMGlobalManager, 'getRepositoryForWorkspace')
|
||||
.spyOn(globalWorkspaceOrmManager, 'getRepository')
|
||||
.mockResolvedValue(mockWorkspaceMemberRepo);
|
||||
|
||||
(userRepository.softDelete as jest.Mock).mockResolvedValue({
|
||||
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
|
||||
@@ -48,7 +49,7 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
private readonly workspaceDomainsService: WorkspaceDomainsService,
|
||||
private readonly emailVerificationService: EmailVerificationService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
@InjectMessageQueue(MessageQueue.workspaceQueue)
|
||||
@@ -62,18 +63,25 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspace.id);
|
||||
|
||||
return await workspaceMemberRepository.findOne({
|
||||
where: {
|
||||
userId: user.id,
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
return await workspaceMemberRepository.findOne({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
async loadWorkspaceMembers(workspace: WorkspaceEntity, withDeleted = false) {
|
||||
@@ -81,14 +89,23 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
return [];
|
||||
}
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspace.id);
|
||||
|
||||
return await workspaceMemberRepository.find({ withDeleted: withDeleted });
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
return await workspaceMemberRepository.find({
|
||||
withDeleted: withDeleted,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async loadDeletedWorkspaceMembersOnly(workspace: WorkspaceEntity) {
|
||||
@@ -96,17 +113,24 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
return [];
|
||||
}
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspace.id);
|
||||
|
||||
return await workspaceMemberRepository.find({
|
||||
where: { deletedAt: Not(IsNull()) },
|
||||
withDeleted: true,
|
||||
});
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
return await workspaceMemberRepository.find({
|
||||
where: { deletedAt: Not(IsNull()) },
|
||||
withDeleted: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async deleteUser(userId: string) {
|
||||
@@ -174,16 +198,23 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
userWorkspace: UserWorkspaceEntity,
|
||||
) {
|
||||
const workspaceId = userWorkspace.workspaceId;
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
const workspaceMembers =
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
return workspaceMemberRepository.find();
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceMembers = await workspaceMemberRepository.find();
|
||||
|
||||
const userWorkspaceId = userWorkspace.id;
|
||||
|
||||
if (workspaceMembers.length === 1) {
|
||||
@@ -224,7 +255,21 @@ export class UserService extends TypeOrmQueryService<UserEntity> {
|
||||
|
||||
assert(workspaceMember, 'WorkspaceMember not found');
|
||||
|
||||
await workspaceMemberRepository.delete({ userId: userWorkspace.userId });
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
await workspaceMemberRepository.delete({
|
||||
userId: userWorkspace.userId,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await this.userWorkspaceService.deleteUserWorkspace({
|
||||
userWorkspaceId,
|
||||
|
||||
@@ -14,10 +14,10 @@ import crypto from 'crypto';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { GraphQLJSONObject } from 'graphql-type-json';
|
||||
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/support.interface';
|
||||
@@ -73,7 +73,8 @@ import { type UserWorkspacePermissions } from 'src/engine/metadata-modules/permi
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
import { fromUserWorkspacePermissionsToUserWorkspacePermissionsDto } from 'src/engine/metadata-modules/role/utils/fromUserWorkspacePermissionsToUserWorkspacePermissionsDto';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
@@ -103,7 +104,7 @@ export class UserResolver {
|
||||
private readonly permissionsService: PermissionsService,
|
||||
private readonly workspaceMemberTranspiler: WorkspaceMemberTranspiler,
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
) {}
|
||||
|
||||
private async getUserWorkspacePermissions({
|
||||
@@ -425,18 +426,26 @@ export class UserResolver {
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceMemberRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
const authContext = buildSystemAuthContext(workspace.id);
|
||||
|
||||
const workspaceMemberToDelete = await workspaceMemberRepository.findOne({
|
||||
where: {
|
||||
id: workspaceMemberIdToDelete,
|
||||
},
|
||||
});
|
||||
const workspaceMemberToDelete =
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
authContext,
|
||||
async () => {
|
||||
const workspaceMemberRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkspaceMemberWorkspaceEntity>(
|
||||
workspace.id,
|
||||
'workspaceMember',
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
return workspaceMemberRepository.findOne({
|
||||
where: {
|
||||
id: workspaceMemberIdToDelete,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
if (!isDefined(workspaceMemberToDelete)) {
|
||||
throw new BadRequestException(
|
||||
|
||||
Reference in New Issue
Block a user