mirror of
https://github.com/twentyhq/twenty.git
synced 2024-08-16 17:30:24 +03:00
4586 fix workspace member feature (#4680)
* Fix import * Handle delete workspace member consequences * Add a patch to request deleted workspace member's userId * Remove useless relations * Handle delete workspace + refactor * Add missing migration * Fix test * Code review returns * Add missing operation in migration file * Fix code review return update * Fix workspaceMember<>ConnectedAccount relation
This commit is contained in:
parent
00eee3158e
commit
27fdb00d07
@ -0,0 +1,102 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddMissingMigration1711557405330 implements MigrationInterface {
|
||||
name = 'AddMissingMigration1711557405330';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "FK_37fdc7357af701e595c5c3a9bd6"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "FK_cb488f32c6a0827b938edadf221"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE USING "createdAt"::TIMESTAMP WITH TIME ZONE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "createdAt" SET DEFAULT now()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "createdAt" SET NOT NULL;`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE USING "updatedAt"::TIMESTAMP WITH TIME ZONE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "updatedAt" SET DEFAULT now()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "updatedAt" SET NOT NULL;`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE USING "deletedAt"::TIMESTAMP WITH TIME ZONE`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" DROP CONSTRAINT "FK_2ec910029395fa7655621c88908"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" ALTER COLUMN "defaultWorkspaceId" SET NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "IndexOnUserIdAndWorkspaceIdUnique" UNIQUE ("userId", "workspaceId")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "FK_a2da2ea7d6cd1e5a4c5cb1791f8" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "FK_22f5e76f493c3fb20237cfc48b0" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" ADD CONSTRAINT "FK_2ec910029395fa7655621c88908" FOREIGN KEY ("defaultWorkspaceId") REFERENCES "core"."workspace"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" DROP CONSTRAINT "FK_2ec910029395fa7655621c88908"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "FK_22f5e76f493c3fb20237cfc48b0"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "FK_a2da2ea7d6cd1e5a4c5cb1791f8"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "IndexOnUserIdAndWorkspaceIdUnique"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" ALTER COLUMN "defaultWorkspaceId" DROP NOT NULL`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."user" ADD CONSTRAINT "FK_2ec910029395fa7655621c88908" FOREIGN KEY ("defaultWorkspaceId") REFERENCES "core"."workspace"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP COLUMN "deletedAt"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD "deletedAt" TIMESTAMP`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP COLUMN "updatedAt"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD "updatedAt" TIMESTAMP NOT NULL DEFAULT now()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" DROP COLUMN "createdAt"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT now()`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "FK_cb488f32c6a0827b938edadf221" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "FK_37fdc7357af701e595c5c3a9bd6" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
}
|
@ -19,9 +19,21 @@ export class UpdateRefreshTokenTable1711624086253
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" ADD CONSTRAINT "FK_d6ae19a7aa2bbd4919053257772" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" DROP CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" ADD CONSTRAINT "FK_8cd4819144baf069777b5729136" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" ADD CONSTRAINT "FK_7008a2b0fb083127f60b5f4448e" FOREIGN KEY ("userId") REFERENCES "core"."user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" DROP CONSTRAINT "FK_8cd4819144baf069777b5729136"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."appToken" DROP CONSTRAINT "FK_d6ae19a7aa2bbd4919053257772"`,
|
||||
);
|
||||
|
@ -407,6 +407,15 @@ export class WorkspaceQueryRunnerService {
|
||||
args,
|
||||
options,
|
||||
);
|
||||
|
||||
// TODO START: remove this awful patch and use our upcoming custom ORM is developed
|
||||
const deletedWorkspaceMember = await this.handleDeleteWorkspaceMember(
|
||||
args.id,
|
||||
workspaceId,
|
||||
objectMetadataItem,
|
||||
);
|
||||
// TODO END
|
||||
|
||||
const result = await this.execute(query, workspaceId);
|
||||
|
||||
const parsedResults = (
|
||||
@ -429,7 +438,10 @@ export class WorkspaceQueryRunnerService {
|
||||
recordId: args.id,
|
||||
objectMetadata: objectMetadataItem,
|
||||
details: {
|
||||
before: this.removeNestedProperties(parsedResults?.[0]),
|
||||
before: {
|
||||
...(deletedWorkspaceMember ?? {}),
|
||||
...this.removeNestedProperties(parsedResults?.[0]),
|
||||
},
|
||||
},
|
||||
} satisfies ObjectRecordDeleteEvent<any>);
|
||||
|
||||
@ -555,4 +567,33 @@ export class WorkspaceQueryRunnerService {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async handleDeleteWorkspaceMember(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
objectMetadataItem: ObjectMetadataInterface,
|
||||
) {
|
||||
if (objectMetadataItem.nameSingular !== 'workspaceMember') {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceMemberResult = await this.executeAndParse<IRecord>(
|
||||
`
|
||||
query {
|
||||
workspaceMemberCollection(filter: {id: {eq: "${id}"}}) {
|
||||
edges {
|
||||
node {
|
||||
userId: userId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
objectMetadataItem,
|
||||
'',
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return workspaceMemberResult.edges?.[0]?.node;
|
||||
}
|
||||
}
|
||||
|
@ -113,4 +113,46 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async handleRemoveWorkspaceMember(workspaceId: string, userId: string) {
|
||||
await this.userWorkspaceRepository.delete({
|
||||
userId,
|
||||
workspaceId,
|
||||
});
|
||||
await this.reassignOrRemoveUserDefaultWorkspace(workspaceId, userId);
|
||||
}
|
||||
|
||||
private async reassignOrRemoveUserDefaultWorkspace(
|
||||
workspaceId: string,
|
||||
userId: string,
|
||||
) {
|
||||
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||
where: { userId: userId },
|
||||
});
|
||||
|
||||
if (userWorkspaces.length === 0) {
|
||||
await this.userRepository.delete({ id: userId });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await this.userRepository.findOne({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User ${userId} not found in workspace ${workspaceId}`);
|
||||
}
|
||||
|
||||
if (user.defaultWorkspaceId === workspaceId) {
|
||||
await this.userRepository.update(
|
||||
{ id: userId },
|
||||
{
|
||||
defaultWorkspaceId: userWorkspaces[0].workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { MessageQueueJob } from 'src/engine/integrations/message-queue/interfaces/message-queue-job.interface';
|
||||
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
|
||||
export type HandleWorkspaceMemberDeletedJobData = {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
};
|
||||
@Injectable()
|
||||
export class HandleWorkspaceMemberDeletedJob
|
||||
implements MessageQueueJob<HandleWorkspaceMemberDeletedJobData>
|
||||
{
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
async handle(data: HandleWorkspaceMemberDeletedJobData): Promise<void> {
|
||||
const { workspaceId, userId } = data;
|
||||
|
||||
await this.userService.handleRemoveWorkspaceMember(workspaceId, userId);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { BillingService } from 'src/engine/core-modules/billing/billing.service';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
|
||||
import { WorkspaceService } from './workspace.service';
|
||||
|
||||
@ -37,6 +38,10 @@ describe('WorkspaceService', () => {
|
||||
provide: UserWorkspaceService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: UserService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: BillingService,
|
||||
useValue: {},
|
||||
|
@ -13,6 +13,7 @@ import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/a
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||
import { BillingService } from 'src/engine/core-modules/billing/billing.service';
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
|
||||
export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
constructor(
|
||||
@ -20,11 +21,10 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(UserWorkspace, 'core')
|
||||
private readonly userWorkspaceRepository: Repository<UserWorkspace>,
|
||||
@InjectRepository(User, 'core')
|
||||
private readonly userRepository: Repository<User>,
|
||||
private readonly workspaceManagerService: WorkspaceManagerService,
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
private readonly billingService: BillingService,
|
||||
private readonly userService: UserService,
|
||||
) {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
@ -49,7 +49,7 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
return await this.workspaceManagerService.doesDataSourceExist(id);
|
||||
}
|
||||
|
||||
async deleteWorkspace(id: string, shouldDeleteCoreWorkspace = true) {
|
||||
async solfDeleteWorkspace(id: string) {
|
||||
const workspace = await this.workspaceRepository.findOneBy({ id });
|
||||
|
||||
assert(workspace, 'Workspace not found');
|
||||
@ -58,9 +58,24 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
await this.billingService.deleteSubscription(workspace.id);
|
||||
|
||||
await this.workspaceManagerService.delete(id);
|
||||
if (shouldDeleteCoreWorkspace) {
|
||||
await this.workspaceRepository.delete(id);
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
async deleteWorkspace(id: string) {
|
||||
const userWorkspaces = await this.userWorkspaceRepository.findBy({
|
||||
workspaceId: id,
|
||||
});
|
||||
|
||||
const workspace = await this.solfDeleteWorkspace(id);
|
||||
|
||||
for (const userWorkspace of userWorkspaces) {
|
||||
await this.userService.handleRemoveWorkspaceMember(
|
||||
id,
|
||||
userWorkspace.userId,
|
||||
);
|
||||
}
|
||||
await this.workspaceRepository.delete(id);
|
||||
|
||||
return workspace;
|
||||
}
|
||||
@ -70,118 +85,4 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
.find()
|
||||
.then((workspaces) => workspaces.map((workspace) => workspace.id));
|
||||
}
|
||||
|
||||
private async reassignDefaultWorkspace(
|
||||
currentWorkspaceId: string,
|
||||
user: User,
|
||||
worskpaces: UserWorkspace[],
|
||||
) {
|
||||
// We'll filter all user workspaces without the one which its getting removed from
|
||||
const filteredUserWorkspaces = worskpaces.filter(
|
||||
(workspace) => workspace.workspaceId !== currentWorkspaceId,
|
||||
);
|
||||
|
||||
// Loop over each workspace in the filteredUserWorkspaces array and check if it currently exists in
|
||||
// the database
|
||||
for (let index = 0; index < filteredUserWorkspaces.length; index++) {
|
||||
const userWorkspace = filteredUserWorkspaces[index];
|
||||
|
||||
const nextWorkspace = await this.workspaceRepository.findOneBy({
|
||||
id: userWorkspace.workspaceId,
|
||||
});
|
||||
|
||||
if (nextWorkspace) {
|
||||
await this.userRepository.save({
|
||||
id: user.id,
|
||||
defaultWorkspace: nextWorkspace,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// if no workspaces are valid then we delete the user
|
||||
if (index === filteredUserWorkspaces.length - 1) {
|
||||
await this.userRepository.delete({ id: user.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
async removeWorkspaceMember(workspaceId: string, memberId: string) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
// using "SELECT *" here because we will need the corresponding members userId later
|
||||
const [workspaceMember] = await workspaceDataSource?.query(
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "id" = '${memberId}'`,
|
||||
);
|
||||
|
||||
if (!workspaceMember) {
|
||||
throw new NotFoundException('Member not found.');
|
||||
}
|
||||
|
||||
await workspaceDataSource?.query(
|
||||
`DELETE FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "id" = '${memberId}'`,
|
||||
);
|
||||
|
||||
const workspaceMemberUser = await this.userRepository.findOne({
|
||||
where: {
|
||||
id: workspaceMember.userId,
|
||||
},
|
||||
relations: ['defaultWorkspace'],
|
||||
});
|
||||
|
||||
if (!workspaceMemberUser) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||
where: { userId: workspaceMemberUser.id },
|
||||
relations: ['workspace'],
|
||||
});
|
||||
|
||||
// We want to check if we the user has signed up to more than one workspace
|
||||
if (userWorkspaces.length > 1) {
|
||||
// We neeed to check if the workspace that its getting removed from is its default workspace, if it is then
|
||||
// change the default workspace to point to the next workspace available.
|
||||
if (workspaceMemberUser.defaultWorkspace.id === workspaceId) {
|
||||
await this.reassignDefaultWorkspace(
|
||||
workspaceId,
|
||||
workspaceMemberUser,
|
||||
userWorkspaces,
|
||||
);
|
||||
}
|
||||
// if its not the default workspace then simply delete the user-workspace mapping
|
||||
await this.userWorkspaceRepository.delete({
|
||||
userId: workspaceMemberUser.id,
|
||||
workspaceId,
|
||||
});
|
||||
} else {
|
||||
await this.userWorkspaceRepository.delete({
|
||||
userId: workspaceMemberUser.id,
|
||||
});
|
||||
|
||||
// After deleting the user-workspace mapping, we have a condition where we have the users default workspace points to a
|
||||
// workspace which it doesnt have access to. So we delete the user.
|
||||
await this.userRepository.delete({ id: workspaceMemberUser.id });
|
||||
}
|
||||
|
||||
const payload =
|
||||
new ObjectRecordDeleteEvent<WorkspaceMemberObjectMetadata>();
|
||||
|
||||
payload.workspaceId = workspaceId;
|
||||
payload.details = {
|
||||
before: workspaceMember,
|
||||
};
|
||||
|
||||
this.eventEmitter.emit('workspaceMember.deleted', payload);
|
||||
|
||||
return memberId;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
|
||||
import { WorkspaceMemberObjectMetadata } from 'src/modules/workspace-member/standard-objects/workspace-member.object-metadata';
|
||||
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
|
||||
import {
|
||||
HandleWorkspaceMemberDeletedJob,
|
||||
HandleWorkspaceMemberDeletedJobData,
|
||||
} from 'src/engine/core-modules/workspace/handle-workspace-member-deleted.job';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceWorkspaceMemberListener {
|
||||
constructor(
|
||||
@Inject(MessageQueue.workspaceQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
|
||||
@OnEvent('workspaceMember.deleted')
|
||||
async handleDeleteEvent(
|
||||
payload: ObjectRecordDeleteEvent<WorkspaceMemberObjectMetadata>,
|
||||
) {
|
||||
const userId = payload.details.before.userId;
|
||||
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.messageQueueService.add<HandleWorkspaceMemberDeletedJobData>(
|
||||
HandleWorkspaceMemberDeletedJob.name,
|
||||
{ workspaceId: payload.workspaceId, userId },
|
||||
);
|
||||
}
|
||||
}
|
@ -8,11 +8,12 @@ import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.r
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { UserModule } from 'src/engine/core-modules/user/user.module';
|
||||
import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/workspace/workspace-workspace-member.listener';
|
||||
|
||||
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
|
||||
import { Workspace } from './workspace.entity';
|
||||
@ -27,19 +28,24 @@ import { WorkspaceService } from './services/workspace.service';
|
||||
BillingModule,
|
||||
FileUploadModule,
|
||||
NestjsQueryTypeOrmModule.forFeature(
|
||||
[User, Workspace, UserWorkspace, FeatureFlagEntity],
|
||||
[Workspace, UserWorkspace, FeatureFlagEntity],
|
||||
'core',
|
||||
),
|
||||
UserWorkspaceModule,
|
||||
WorkspaceManagerModule,
|
||||
DataSourceModule,
|
||||
TypeORMModule,
|
||||
UserModule,
|
||||
],
|
||||
services: [WorkspaceService],
|
||||
resolvers: workspaceAutoResolverOpts,
|
||||
}),
|
||||
],
|
||||
exports: [WorkspaceService],
|
||||
providers: [WorkspaceResolver, WorkspaceService],
|
||||
providers: [
|
||||
WorkspaceResolver,
|
||||
WorkspaceService,
|
||||
WorkspaceWorkspaceMemberListener,
|
||||
],
|
||||
})
|
||||
export class WorkspaceModule {}
|
||||
|
@ -47,6 +47,7 @@ import { MessageChannelObjectMetadata } from 'src/modules/messaging/standard-obj
|
||||
import { SaveEventToDbJob } from 'src/engine/api/graphql/workspace-query-runner/jobs/save-event-to-db.job';
|
||||
import { CreateCompanyAndContactJob } from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
|
||||
import { EventObjectMetadata } from 'src/modules/event/standard-objects/event.object-metadata';
|
||||
import { HandleWorkspaceMemberDeletedJob } from 'src/engine/core-modules/workspace/handle-workspace-member-deleted.job';
|
||||
import { GmailFullSynV2Module } from 'src/modules/messaging/services/gmail-full-sync-v2/gmail-full-sync.v2.module';
|
||||
import { GmailFetchMessageContentFromCacheModule } from 'src/modules/messaging/services/gmail-fetch-message-content-from-cache/gmail-fetch-message-content-from-cache.module';
|
||||
import { FetchAllMessagesFromCacheCronJob } from 'src/modules/messaging/commands/crons/fetch-all-messages-from-cache.cron-job';
|
||||
@ -139,6 +140,10 @@ import { GmailPartialSyncV2Module } from 'src/modules/messaging/services/gmail-p
|
||||
useClass: DeleteConnectedAccountAssociatedCalendarDataJob,
|
||||
},
|
||||
{ provide: UpdateSubscriptionJob.name, useClass: UpdateSubscriptionJob },
|
||||
{
|
||||
provide: HandleWorkspaceMemberDeletedJob.name,
|
||||
useClass: HandleWorkspaceMemberDeletedJob,
|
||||
},
|
||||
{
|
||||
provide: RecordPositionBackfillJob.name,
|
||||
useClass: RecordPositionBackfillJob,
|
||||
|
@ -9,6 +9,7 @@ export enum MessageQueue {
|
||||
calendarQueue = 'calendar-queue',
|
||||
contactCreationQueue = 'contact-creation-queue',
|
||||
billingQueue = 'billing-queue',
|
||||
workspaceQueue = 'workspace-queue',
|
||||
recordPositionBackfillQueue = 'record-position-backfill-queue',
|
||||
entityEventsToDbQueue = 'entity-events-to-db-queue',
|
||||
}
|
||||
|
@ -83,10 +83,7 @@ export class DeleteIncompleteWorkspacesCommand extends CommandRunner {
|
||||
} name: '${incompleteWorkspace.displayName}'`,
|
||||
);
|
||||
if (!options.dryRun) {
|
||||
await this.workspaceService.deleteWorkspace(
|
||||
incompleteWorkspace.id,
|
||||
false,
|
||||
);
|
||||
await this.workspaceService.solfDeleteWorkspace(incompleteWorkspace.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +186,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
inverseSideTarget: () => ConnectedAccountObjectMetadata,
|
||||
inverseSideFieldKey: 'accountOwner',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
connectedAccounts: ConnectedAccountObjectMetadata[];
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
"types": ["jest", "node"],
|
||||
"paths": {
|
||||
"src/*": ["packages/twenty-server/src/*"],
|
||||
"test/*": ["packages/twenty-server/test/*"],
|
||||
"twenty-emails": ["packages/twenty-emails/src/index.ts"]
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user