Decouple Send Email node from workflows (#13322)

- Renamed `WorkflowActionAdapter` to `ToolExecutorWorkflowAction`
- Renamed `settingPermission` table to `permissionFlag` and `setting`
column to `flag`
- Decoupled the send email logic from workflows to tools
- Add new `Tools Permission` section in FE

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Abdul Rahman
2025-07-24 16:01:33 +05:30
committed by GitHub
parent eb404478c3
commit e93adde4b8
98 changed files with 1076 additions and 705 deletions
@@ -28,6 +28,11 @@ export class CreateRoleInput {
@Field({ nullable: true })
canUpdateAllSettings?: boolean;
@IsBoolean()
@IsOptional()
@Field({ nullable: true })
canAccessAllTools?: boolean;
@IsBoolean()
@IsOptional()
@Field({ nullable: true })
@@ -4,8 +4,8 @@ import { Relation } from 'typeorm';
import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto';
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
import { PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { SettingPermissionDTO } from 'src/engine/metadata-modules/setting-permission/dtos/setting-permission.dto';
@ObjectType('Role')
export class RoleDTO {
@@ -33,6 +33,9 @@ export class RoleDTO {
@Field({ nullable: false })
canUpdateAllSettings: boolean;
@Field({ nullable: false })
canAccessAllTools: boolean;
@Field({ nullable: false })
canReadAllObjectRecords: boolean;
@@ -45,8 +48,8 @@ export class RoleDTO {
@Field({ nullable: false })
canDestroyAllObjectRecords: boolean;
@Field(() => [SettingPermissionDTO], { nullable: true })
settingPermissions?: SettingPermissionDTO[];
@Field(() => [PermissionFlagDTO], { nullable: true })
permissionFlags?: PermissionFlagDTO[];
@Field(() => [ObjectPermissionDTO], { nullable: true })
objectPermissions?: ObjectPermissionDTO[];
@@ -32,6 +32,11 @@ export class UpdateRolePayload {
@Field({ nullable: true })
canUpdateAllSettings?: boolean;
@IsBoolean()
@IsOptional()
@Field({ nullable: true })
canAccessAllTools?: boolean;
@IsBoolean()
@IsOptional()
@Field({ nullable: true })
@@ -11,8 +11,8 @@ import {
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
@Entity('role')
@Unique('IDX_ROLE_LABEL_WORKSPACE_ID_UNIQUE', ['label', 'workspaceId'])
@@ -26,6 +26,9 @@ export class RoleEntity {
@Column({ nullable: false, default: false })
canUpdateAllSettings: boolean;
@Column({ nullable: false, default: false })
canAccessAllTools: boolean;
@Column({ nullable: false, default: false })
canReadAllObjectRecords: boolean;
@@ -69,10 +72,10 @@ export class RoleEntity {
objectPermissions: Relation<ObjectPermissionEntity[]>;
@OneToMany(
() => SettingPermissionEntity,
(settingPermission: SettingPermissionEntity) => settingPermission.role,
() => PermissionFlagEntity,
(permissionFlag: PermissionFlagEntity) => permissionFlag.role,
)
settingPermissions: Relation<SettingPermissionEntity[]>;
permissionFlags: Relation<PermissionFlagEntity[]>;
@OneToMany(
() => FieldPermissionEntity,
@@ -7,12 +7,12 @@ import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AgentRoleModule } from 'src/engine/metadata-modules/agent-role/agent-role.module';
import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
import { PermissionFlagModule } from 'src/engine/metadata-modules/permission-flag/permission-flag.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleResolver } from 'src/engine/metadata-modules/role/role.resolver';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { SettingPermissionModule } from 'src/engine/metadata-modules/setting-permission/setting-permission.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
@@ -25,7 +25,7 @@ import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/wor
PermissionsModule,
UserWorkspaceModule,
ObjectPermissionModule,
SettingPermissionModule,
PermissionFlagModule,
WorkspacePermissionsCacheModule,
FileModule,
],
@@ -28,7 +28,10 @@ import { UpsertFieldPermissionsInput } from 'src/engine/metadata-modules/object-
import { UpsertObjectPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permissions.input';
import { FieldPermissionService } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.service';
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
import { UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import {
PermissionsException,
PermissionsExceptionCode,
@@ -39,9 +42,6 @@ import { CreateRoleInput } from 'src/engine/metadata-modules/role/dtos/create-ro
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
import { UpdateRoleInput } from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { SettingPermissionDTO } from 'src/engine/metadata-modules/setting-permission/dtos/setting-permission.dto';
import { UpsertSettingPermissionsInput } from 'src/engine/metadata-modules/setting-permission/dtos/upsert-setting-permission-input';
import { SettingPermissionService } from 'src/engine/metadata-modules/setting-permission/setting-permission.service';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
@@ -49,7 +49,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
@UsePipes(ResolverValidationPipe)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.ROLES),
SettingsPermissionsGuard(PermissionFlagType.ROLES),
)
@UseFilters(
PermissionsGraphqlApiExceptionFilter,
@@ -61,7 +61,7 @@ export class RoleResolver {
private readonly roleService: RoleService,
private readonly userWorkspaceService: UserWorkspaceService,
private readonly objectPermissionService: ObjectPermissionService,
private readonly settingPermissionService: SettingPermissionService,
private readonly settingPermissionService: PermissionFlagService,
private readonly agentRoleService: AgentRoleService,
private readonly fieldPermissionService: FieldPermissionService,
) {}
@@ -171,15 +171,15 @@ export class RoleResolver {
});
}
@Mutation(() => [SettingPermissionDTO])
async upsertSettingPermissions(
@Mutation(() => [PermissionFlagDTO])
async upsertPermissionFlags(
@AuthWorkspace() workspace: Workspace,
@Args('upsertSettingPermissionsInput')
upsertSettingPermissionsInput: UpsertSettingPermissionsInput,
): Promise<SettingPermissionDTO[]> {
return this.settingPermissionService.upsertSettingPermissions({
@Args('upsertPermissionFlagsInput')
upsertPermissionFlagsInput: UpsertPermissionFlagsInput,
): Promise<PermissionFlagDTO[]> {
return this.settingPermissionService.upsertPermissionFlags({
workspaceId: workspace.id,
input: upsertSettingPermissionsInput,
input: upsertPermissionFlagsInput,
});
}
@@ -40,7 +40,7 @@ export class RoleService {
where: {
workspaceId,
},
relations: ['roleTargets', 'settingPermissions', 'objectPermissions'],
relations: ['roleTargets', 'permissionFlags', 'objectPermissions'],
});
}
@@ -53,7 +53,7 @@ export class RoleService {
id,
workspaceId,
},
relations: ['roleTargets', 'settingPermissions'],
relations: ['roleTargets', 'permissionFlags'],
});
}
@@ -72,6 +72,7 @@ export class RoleService {
description: input.description,
icon: input.icon,
canUpdateAllSettings: input.canUpdateAllSettings,
canAccessAllTools: input.canAccessAllTools,
canReadAllObjectRecords: input.canReadAllObjectRecords,
canUpdateAllObjectRecords: input.canUpdateAllObjectRecords,
canSoftDeleteAllObjectRecords: input.canSoftDeleteAllObjectRecords,
@@ -143,6 +144,7 @@ export class RoleService {
description: 'Admin role',
icon: 'IconUserCog',
canUpdateAllSettings: true,
canAccessAllTools: true,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
@@ -209,6 +211,7 @@ export class RoleService {
description: 'Member role',
icon: 'IconUser',
canUpdateAllSettings: false,
canAccessAllTools: false,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
@@ -229,6 +232,7 @@ export class RoleService {
description: 'Guest role',
icon: 'IconUser',
canUpdateAllSettings: false,
canAccessAllTools: false,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
@@ -250,6 +254,7 @@ export class RoleService {
const keysToValidate = [
'label',
'canUpdateAllSettings',
'canAccessAllTools',
'canReadAllObjectRecords',
'canUpdateAllObjectRecords',
'canSoftDeleteAllObjectRecords',
@@ -6,6 +6,7 @@ export const fromRoleEntityToRoleDto = (role: RoleEntity): RoleDTO => {
id: role.id,
label: role.label,
canUpdateAllSettings: role.canUpdateAllSettings,
canAccessAllTools: role.canAccessAllTools,
description: role.description,
icon: role.icon,
isEditable: role.isEditable,
@@ -1,6 +1,6 @@
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { UserWorkspacePermissions } from 'src/engine/metadata-modules/permissions/types/user-workspace-permissions';
import { UserWorkspacePermissionsDto } from 'src/engine/metadata-modules/role/dtos/user-workspace-permissions.dto';
@@ -20,7 +20,7 @@ export const fromUserWorkspacePermissionsToUserWorkspacePermissionsDto = ({
);
const settingsPermissions = (
Object.keys(rawSettingsPermissions) as SettingPermissionType[]
Object.keys(rawSettingsPermissions) as PermissionFlagType[]
).filter((feature) => rawSettingsPermissions[feature] === true);
const objectRecordsPermissions = (