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
@@ -11,11 +11,16 @@ import { ToolAdapterService } from 'src/engine/core-modules/ai/services/tool-ada
import { ToolService } from 'src/engine/core-modules/ai/services/tool.service';
import { TokenModule } from 'src/engine/core-modules/auth/token/token.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
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';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { MessagingModule } from 'src/modules/messaging/messaging.module';
@Global()
@Module({
@@ -27,6 +32,9 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
WorkspacePermissionsCacheModule,
WorkspaceCacheStorageModule,
UserRoleModule,
TwentyORMModule,
MessagingModule,
PermissionsModule,
],
controllers: [AiController, McpController],
providers: [
@@ -34,8 +42,10 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
AiModelRegistryService,
ToolService,
ToolAdapterService,
ToolRegistryService,
AIBillingService,
McpService,
SendEmailTool,
],
exports: [
AiService,
@@ -43,7 +53,9 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
AIBillingService,
ToolService,
ToolAdapterService,
ToolRegistryService,
McpService,
SendEmailTool,
],
})
export class AiModule {}
@@ -2,28 +2,49 @@ import { Injectable } from '@nestjs/common';
import { ToolSet } from 'ai';
import { TOOLS } from 'src/engine/core-modules/tool/constants/tools.const';
import { ToolRegistryService } from 'src/engine/core-modules/tool/services/tool-registry.service';
import { ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
import { Tool } from 'src/engine/core-modules/tool/types/tool.type';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
@Injectable()
export class ToolAdapterService {
getCoreTools(): ToolSet {
const tools = Array.from(TOOLS.entries()).reduce<ToolSet>(
(acc, [toolType, tool]) => {
acc[toolType.toLowerCase()] = {
description: tool.description,
parameters: tool.parameters,
execute: async (parameters) => {
const response = await tool.execute(parameters.input);
constructor(
private readonly toolRegistry: ToolRegistryService,
private readonly permissionsService: PermissionsService,
) {}
return response;
},
};
async getTools(roleId?: string, workspaceId?: string): Promise<ToolSet> {
const tools: ToolSet = {};
return acc;
},
{},
);
for (const toolType of this.toolRegistry.getAllToolTypes()) {
const tool = this.toolRegistry.getTool(toolType);
if (!tool.flag) {
tools[toolType.toLowerCase()] = this.createToolSet(tool);
} else if (roleId && workspaceId) {
const hasPermission = await this.permissionsService.hasToolPermission(
roleId,
workspaceId,
tool.flag as PermissionFlagType,
);
if (hasPermission) {
tools[toolType.toLowerCase()] = this.createToolSet(tool);
}
}
}
return tools;
}
private createToolSet(tool: Tool) {
return {
description: tool.description,
parameters: tool.parameters,
execute: async (parameters: { input: ToolInput }) =>
tool.execute(parameters.input),
};
}
}
@@ -65,7 +65,7 @@ import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard';
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { TwoFactorAuthenticationExceptionFilter } from 'src/engine/core-modules/two-factor-authentication/two-factor-authentication-exception.filter';
@@ -548,7 +548,7 @@ export class AuthResolver {
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.API_KEYS_AND_WEBHOOKS),
SettingsPermissionsGuard(PermissionFlagType.API_KEYS_AND_WEBHOOKS),
)
@Mutation(() => ApiKeyToken)
async generateApiKeyToken(
@@ -32,7 +32,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 {
PermissionsException,
PermissionsExceptionCode,
@@ -60,7 +60,7 @@ export class BillingResolver {
@Query(() => BillingSessionOutput)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async billingPortalSession(
@AuthWorkspace() workspace: Workspace,
@@ -124,7 +124,7 @@ export class BillingResolver {
@Mutation(() => BillingUpdateOutput)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async switchToYearlyInterval(@AuthWorkspace() workspace: Workspace) {
await this.billingSubscriptionService.switchToYearlyInterval(workspace);
@@ -135,7 +135,7 @@ export class BillingResolver {
@Mutation(() => BillingUpdateOutput)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async switchToEnterprisePlan(@AuthWorkspace() workspace: Workspace) {
await this.billingSubscriptionService.switchToEnterprisePlan(workspace);
@@ -154,7 +154,7 @@ export class BillingResolver {
@Mutation(() => BillingEndTrialPeriodOutput)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async endSubscriptionTrialPeriod(
@AuthWorkspace() workspace: Workspace,
@@ -165,7 +165,7 @@ export class BillingResolver {
@Query(() => [BillingMeteredProductUsageOutput])
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async getMeteredProductsUsage(
@AuthWorkspace() workspace: Workspace,
@@ -199,7 +199,7 @@ export class BillingResolver {
await this.permissionsService.userHasWorkspaceSettingPermission({
userWorkspaceId,
workspaceId,
setting: SettingPermissionType.WORKSPACE,
setting: PermissionFlagType.WORKSPACE,
isExecutedByApiKey,
});
@@ -23,7 +23,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ImapSmtpCalDavAPIService } from 'src/modules/connected-account/services/imap-smtp-caldav-apis.service';
@@ -32,7 +32,7 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s
@Resolver()
@UsePipes(ResolverValidationPipe)
@UseFilters(AuthGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter)
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.WORKSPACE))
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.WORKSPACE))
export class ImapSmtpCaldavResolver {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@@ -13,7 +13,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
@Resolver()
@@ -23,7 +23,7 @@ import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-module
PermissionsGraphqlApiExceptionFilter,
PreventNestToAutoLogGraphqlErrorsFilter,
)
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.WORKSPACE))
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.WORKSPACE))
export class LabResolver {
constructor(private featureFlagService: FeatureFlagService) {}
@@ -22,7 +22,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
@Resolver()
@@ -31,7 +31,7 @@ import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-module
PreventNestToAutoLogGraphqlErrorsFilter,
)
@UsePipes(ResolverValidationPipe)
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.SECURITY))
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.SECURITY))
export class SSOResolver {
constructor(private readonly sSOService: SSOService) {}
@@ -1,7 +0,0 @@
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
import { Tool } from 'src/engine/core-modules/tool/types/tool.type';
export const TOOLS: Map<ToolType, Tool> = new Map([
[ToolType.HTTP_REQUEST, new HttpTool()],
]);
@@ -1,3 +1,4 @@
export enum ToolType {
HTTP_REQUEST = 'HTTP_REQUEST',
SEND_EMAIL = 'SEND_EMAIL',
}
@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { ToolType } from 'src/engine/core-modules/tool/enums/tool-type.enum';
import { HttpTool } from 'src/engine/core-modules/tool/tools/http-tool/http-tool';
import { SendEmailTool } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool';
import { SendEmailInput } from 'src/engine/core-modules/tool/tools/send-email-tool/types/send-email-input.type';
import { Tool } from 'src/engine/core-modules/tool/types/tool.type';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
@Injectable()
export class ToolRegistryService {
private readonly toolFactories: Map<ToolType, () => Tool>;
constructor(private readonly sendEmailTool: SendEmailTool) {
this.toolFactories = new Map<ToolType, () => Tool>([
[ToolType.HTTP_REQUEST, () => new HttpTool()],
[
ToolType.SEND_EMAIL,
() => ({
description: this.sendEmailTool.description,
parameters: this.sendEmailTool.parameters,
execute: (params) =>
this.sendEmailTool.execute(params as SendEmailInput),
flag: PermissionFlagType.SEND_EMAIL_TOOL,
}),
],
]);
}
getTool(toolType: ToolType): Tool {
const factory = this.toolFactories.get(toolType);
if (!factory) {
throw new Error(`Unknown tool type: ${toolType}`);
}
return factory();
}
getAllToolTypes(): ToolType[] {
return Array.from(this.toolFactories.keys());
}
}
@@ -0,0 +1,14 @@
import { CustomException } from 'src/utils/custom-exception';
export class SendEmailToolException extends CustomException {
constructor(message: string, code: SendEmailToolExceptionCode) {
super(message, code);
}
}
export enum SendEmailToolExceptionCode {
INVALID_CONNECTED_ACCOUNT_ID = 'INVALID_CONNECTED_ACCOUNT_ID',
CONNECTED_ACCOUNT_NOT_FOUND = 'CONNECTED_ACCOUNT_NOT_FOUND',
INVALID_EMAIL = 'INVALID_EMAIL',
WORKSPACE_ID_NOT_FOUND = 'WORKSPACE_ID_NOT_FOUND',
}
@@ -0,0 +1,23 @@
import { z } from 'zod';
export const SendEmailInputZodSchema = z.object({
email: z.string().email().describe('The recipient email address'),
subject: z.string().describe('The email subject line'),
body: z.string().describe('The email body content (HTML or plain text)'),
connectedAccountId: z
.string()
.uuid()
.describe(
'The UUID of the connected account to send the email from. Provide this only if you have it; otherwise, leave blank.',
)
.optional(),
});
export const SendEmailToolParametersZodSchema = z.object({
toolDescription: z
.string()
.describe(
"A clear, human-readable status message describing the email being sent. This will be shown to the user while the tool is being called, so phrase it as a present-tense status update (e.g., 'Sending email to customer about order status'). Explain what email you are sending and to whom in natural language.",
),
input: SendEmailInputZodSchema,
});
@@ -0,0 +1,155 @@
import { Injectable, Logger } from '@nestjs/common';
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
import { isDefined, isValidUuid } from 'twenty-shared/utils';
import { z } from 'zod';
import {
SendEmailToolException,
SendEmailToolExceptionCode,
} from 'src/engine/core-modules/tool/tools/send-email-tool/exceptions/send-email-tool.exception';
import { SendEmailToolParametersZodSchema } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.schema';
import { SendEmailInput } from 'src/engine/core-modules/tool/tools/send-email-tool/types/send-email-input.type';
import { ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type';
import { Tool } from 'src/engine/core-modules/tool/types/tool.type';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MessagingSendMessageService } from 'src/modules/messaging/message-import-manager/services/messaging-send-message.service';
@Injectable()
export class SendEmailTool implements Tool {
private readonly logger = new Logger(SendEmailTool.name);
description =
'Send an email using a connected account. Requires SEND_EMAIL_TOOL permission.';
parameters = SendEmailToolParametersZodSchema;
constructor(
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly sendMessageService: MessagingSendMessageService,
) {}
private async getConnectedAccount(
connectedAccountId: string,
workspaceId: string,
) {
if (!isValidUuid(connectedAccountId)) {
throw new SendEmailToolException(
`Connected Account ID is not a valid UUID`,
SendEmailToolExceptionCode.INVALID_CONNECTED_ACCOUNT_ID,
);
}
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const connectedAccount = await connectedAccountRepository.findOneBy({
id: connectedAccountId,
});
if (!isDefined(connectedAccount)) {
throw new SendEmailToolException(
`Connected Account '${connectedAccountId}' not found`,
SendEmailToolExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
);
}
return connectedAccount;
}
private async getOrThrowFirstConnectedAccountId(
workspaceId: string,
): Promise<string> {
const connectedAccountRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ConnectedAccountWorkspaceEntity>(
workspaceId,
'connectedAccount',
);
const allAccounts = await connectedAccountRepository.find();
if (!allAccounts || allAccounts.length === 0) {
throw new SendEmailToolException(
'No connected accounts found for this workspace',
SendEmailToolExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
);
}
return allAccounts[0].id;
}
async execute(parameters: SendEmailInput): Promise<ToolOutput> {
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
const { email, subject, body } = parameters;
let { connectedAccountId } = parameters;
try {
const emailSchema = z.string().trim().email('Invalid email');
const emailValidation = emailSchema.safeParse(email);
if (!emailValidation.success) {
throw new SendEmailToolException(
`Email '${email}' is invalid`,
SendEmailToolExceptionCode.INVALID_EMAIL,
);
}
if (!workspaceId) {
throw new SendEmailToolException(
'Workspace ID not found',
SendEmailToolExceptionCode.WORKSPACE_ID_NOT_FOUND,
);
}
if (!connectedAccountId) {
connectedAccountId =
await this.getOrThrowFirstConnectedAccountId(workspaceId);
}
const connectedAccount = await this.getConnectedAccount(
connectedAccountId,
workspaceId,
);
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const safeBody = purify.sanitize(body || '');
const safeSubject = purify.sanitize(subject || '');
await this.sendMessageService.sendMessage(
{
to: email,
subject: safeSubject,
body: safeBody,
},
connectedAccount,
);
this.logger.log(`Email sent successfully to ${email}`);
return {
result: {
success: true,
message: `Email sent successfully to ${email}`,
},
};
} catch (error) {
if (error instanceof SendEmailToolException) {
return {
error: error.message,
};
}
this.logger.error(`Failed to send email: ${error}`);
return {
error: error instanceof Error ? error.message : 'Failed to send email',
};
}
}
}
@@ -0,0 +1,5 @@
import { z } from 'zod';
import { SendEmailInputZodSchema } from 'src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.schema';
export type SendEmailInput = z.infer<typeof SendEmailInputZodSchema>;
@@ -3,9 +3,11 @@ import { ZodType } from 'zod';
import { ToolInput } from 'src/engine/core-modules/tool/types/tool-input.type';
import { ToolOutput } from 'src/engine/core-modules/tool/types/tool-output.type';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
export type Tool = {
description: string;
parameters: JSONSchema7 | ZodType;
execute(input: ToolInput): Promise<ToolOutput>;
flag?: PermissionFlagType;
};
@@ -19,15 +19,15 @@ import {
} from 'typeorm';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { TwoFactorAuthenticationMethodSummaryDto } from 'src/engine/core-modules/two-factor-authentication/dto/two-factor-authentication-method.dto';
import { TwoFactorAuthenticationMethod } from 'src/engine/core-modules/two-factor-authentication/entities/two-factor-authentication-method.entity';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { TwoFactorAuthenticationMethod } from 'src/engine/core-modules/two-factor-authentication/entities/two-factor-authentication-method.entity';
import { TwoFactorAuthenticationMethodSummaryDto } from 'src/engine/core-modules/two-factor-authentication/dto/two-factor-authentication-method.dto';
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
registerEnumType(SettingPermissionType, {
name: 'SettingPermissionType',
registerEnumType(PermissionFlagType, {
name: 'PermissionFlagType',
});
registerEnumType(PermissionsOnAllObjectRecords, {
@@ -96,8 +96,8 @@ export class UserWorkspace {
)
twoFactorAuthenticationMethods: Relation<TwoFactorAuthenticationMethod[]>;
@Field(() => [SettingPermissionType], { nullable: true })
settingsPermissions?: SettingPermissionType[];
@Field(() => [PermissionFlagType], { nullable: true })
settingsPermissions?: PermissionFlagType[];
@Field(() => [PermissionsOnAllObjectRecords], {
nullable: true,
@@ -12,7 +12,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
@@ -21,7 +21,7 @@ import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-bu
@UseGuards(
WorkspaceAuthGuard,
UserAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS),
SettingsPermissionsGuard(PermissionFlagType.WORKFLOWS),
)
@UsePipes(ResolverValidationPipe)
@UseFilters(
@@ -16,7 +16,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
@@ -27,7 +27,7 @@ import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runne
@UseGuards(
WorkspaceAuthGuard,
UserAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS),
SettingsPermissionsGuard(PermissionFlagType.WORKFLOWS),
)
@UseFilters(
PermissionsGraphqlApiExceptionFilter,
@@ -14,7 +14,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
@@ -24,7 +24,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
@UseGuards(
WorkspaceAuthGuard,
UserAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS),
SettingsPermissionsGuard(PermissionFlagType.WORKFLOWS),
)
@UsePipes(ResolverValidationPipe)
@UseFilters(
@@ -10,7 +10,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service';
@@ -19,7 +19,7 @@ import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-b
@UseGuards(
WorkspaceAuthGuard,
UserAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS),
SettingsPermissionsGuard(PermissionFlagType.WORKFLOWS),
)
@UseFilters(
PermissionsGraphqlApiExceptionFilter,
@@ -14,7 +14,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard';
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
@@ -23,7 +23,7 @@ import { SendInvitationsInput } from './dtos/send-invitations.input';
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE_MEMBERS),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE_MEMBERS),
)
@UsePipes(ResolverValidationPipe)
@UseFilters(
@@ -36,7 +36,7 @@ import {
WorkspaceExceptionCode,
} from 'src/engine/core-modules/workspace/workspace.exception';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
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 {
PermissionsException,
PermissionsExceptionCode,
@@ -457,7 +457,7 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
const userHasPermission =
await this.permissionsService.userHasWorkspaceSettingPermission({
userWorkspaceId,
setting: SettingPermissionType.SECURITY,
setting: PermissionFlagType.SECURITY,
workspaceId: workspaceId,
isExecutedByApiKey: isDefined(apiKey),
});
@@ -504,7 +504,7 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
await this.permissionsService.userHasWorkspaceSettingPermission({
userWorkspaceId,
workspaceId,
setting: SettingPermissionType.WORKSPACE,
setting: PermissionFlagType.WORKSPACE,
isExecutedByApiKey: isDefined(apiKey),
});
@@ -58,7 +58,7 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { AgentService } from 'src/engine/metadata-modules/agent/agent.service';
import { AgentDTO } from 'src/engine/metadata-modules/agent/dtos/agent.dto';
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 { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
@@ -144,7 +144,7 @@ export class WorkspaceResolver {
@Mutation(() => SignedFileDTO)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async uploadWorkspaceLogo(
@AuthWorkspace() { id }: Workspace,
@@ -190,7 +190,7 @@ export class WorkspaceResolver {
@Mutation(() => Workspace)
@UseGuards(
WorkspaceAuthGuard,
SettingsPermissionsGuard(SettingPermissionType.WORKSPACE),
SettingsPermissionsGuard(PermissionFlagType.WORKSPACE),
)
async deleteCurrentWorkspace(@AuthWorkspace() { id }: Workspace) {
return this.workspaceService.deleteWorkspace(id);