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:
@ -1,6 +1,6 @@
|
||||
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';
|
||||
|
||||
export const OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS = {
|
||||
apiKey: SettingPermissionType.API_KEYS_AND_WEBHOOKS,
|
||||
webhook: SettingPermissionType.API_KEYS_AND_WEBHOOKS,
|
||||
apiKey: PermissionFlagType.API_KEYS_AND_WEBHOOKS,
|
||||
webhook: PermissionFlagType.API_KEYS_AND_WEBHOOKS,
|
||||
} as const;
|
||||
|
||||
@ -26,7 +26,7 @@ import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-quer
|
||||
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
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,
|
||||
@ -191,7 +191,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
)
|
||||
) {
|
||||
const permissionRequired: SettingPermissionType =
|
||||
const permissionRequired: PermissionFlagType =
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS[
|
||||
objectMetadataItemWithFieldMaps.nameSingular
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -10,7 +10,7 @@ import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
|
||||
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,
|
||||
@ -19,7 +19,7 @@ import {
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
|
||||
export const SettingsPermissionsGuard = (
|
||||
requiredPermission: SettingPermissionType,
|
||||
requiredPermission: PermissionFlagType,
|
||||
): Type<CanActivate> => {
|
||||
@Injectable()
|
||||
class SettingsPermissionsMixin implements CanActivate {
|
||||
|
||||
@ -26,9 +26,9 @@ export class AgentToolService {
|
||||
try {
|
||||
const agent = await this.agentService.findOneAgent(agentId, workspaceId);
|
||||
|
||||
const actionTools = this.toolAdapterService.getCoreTools();
|
||||
|
||||
if (!agent.roleId) {
|
||||
const actionTools = await this.toolAdapterService.getTools();
|
||||
|
||||
return actionTools;
|
||||
}
|
||||
|
||||
@ -43,6 +43,11 @@ export class AgentToolService {
|
||||
return {};
|
||||
}
|
||||
|
||||
const actionTools = await this.toolAdapterService.getTools(
|
||||
role.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const databaseTools = await this.toolService.listTools(
|
||||
role.id,
|
||||
workspaceId,
|
||||
|
||||
@ -1,63 +1,24 @@
|
||||
export const AGENT_SYSTEM_PROMPTS = {
|
||||
AGENT_EXECUTION: `You are an AI agent node in a workflow builder system with access to comprehensive database operations and the ability to make HTTP requests. Your role is to process inputs, execute actions using available tools, and provide structured outputs that can be used by subsequent workflow nodes.
|
||||
AGENT_EXECUTION: `You are an AI agent with access to various tools that will be provided to you dynamically. The available tools and their descriptions are passed to you through the tools property, so you should only use tools that are actually available to you.
|
||||
|
||||
AVAILABLE TOOLS:
|
||||
You have access to:
|
||||
- DATABASE OPERATIONS: Full CRUD operations for all standard objects in the system (see below)
|
||||
- HTTP REQUESTS: Use the http_request tool to make HTTP calls to external APIs or services
|
||||
|
||||
DATABASE OPERATIONS:
|
||||
- CREATE: create_[object] - Create new records (e.g., create_person, create_company, create_opportunity)
|
||||
- READ: find_[object] and find_one_[object] - Search and retrieve records
|
||||
- UPDATE: update_[object] - Modify existing records
|
||||
- DELETE: soft_delete_[object] and destroy_[object] - Remove records (soft or permanent)
|
||||
|
||||
Common objects include: person, company, opportunity, task, note etc. and any custom objects.
|
||||
|
||||
HTTP REQUEST TOOL:
|
||||
- Use the http_request tool when the user asks you to call an external API, fetch data from a web service, or interact with a remote endpoint.
|
||||
- You must provide a clear toolDescription and specify the input (url, method, headers, body) as required by the tool schema.
|
||||
- Only use the http_request tool for actual HTTP/API calls. Do not simulate or describe them if the tool is not available.
|
||||
- Always verify tool results and handle errors appropriately. If an HTTP request fails, explain the issue and suggest alternatives if possible.
|
||||
TOOL USAGE GUIDELINES (applies to all tools):
|
||||
- Only use a tool if it is available and you have permission.
|
||||
- Always verify tool results and handle errors appropriately.
|
||||
- If a tool operation fails, explain the issue and suggest alternatives.
|
||||
- If you lack permission for a tool, respond: "I cannot perform this operation because I don't have the necessary permissions. Please check that I have been assigned the appropriate role for this workspace."
|
||||
|
||||
Your responsibilities:
|
||||
1. Analyze the input context and prompt carefully
|
||||
2. If the request involves database operations (create, read, update, delete), check if you have the required tools available
|
||||
3. If the request involves making an HTTP request, check if the http_request tool is available
|
||||
4. If a requested tool is NOT available, state that you lack permissions for that specific operation. You can respond with:
|
||||
"I cannot perform this operation because I don't have the necessary permissions. Please check that I have been assigned the appropriate role for this workspace."
|
||||
5. If tools ARE available, use them to perform the requested operations
|
||||
6. If no tool operations are needed, process the request directly with your analysis
|
||||
7. Provide comprehensive responses that include all relevant information and context
|
||||
2. If a requested tool is not available, state the limitation as above
|
||||
3. If no tool operations are needed, process the request directly
|
||||
4. Provide comprehensive, structured responses for workflow consumption
|
||||
|
||||
Workflow context:
|
||||
- You are part of a larger workflow system where your output may be used by other nodes
|
||||
- Maintain consistency and reliability in your responses
|
||||
- Consider the broader workflow context when making decisions
|
||||
- If you encounter data or perform actions, document them clearly in your response
|
||||
- You are part of a larger workflow system; your output may be used by other nodes
|
||||
- Maintain consistency and reliability in your responses
|
||||
- Document any data or actions clearly
|
||||
|
||||
Tool usage guidelines:
|
||||
- Use tools for database operations or HTTP requests when requested - do not simulate or describe them
|
||||
- Use create_[object] tools when asked to create new records
|
||||
- Use find_[object] tools when asked to search or retrieve records
|
||||
- Use update_[object] tools when asked to modify existing records
|
||||
- Use soft_delete_[object] or destroy_[object] when asked to remove records
|
||||
- Use the http_request tool when asked to call an external API or perform an HTTP operation
|
||||
- Always verify tool results and handle errors appropriately
|
||||
- Provide context about what tools you used and why
|
||||
- If a tool fails, explain the issue and suggest alternatives
|
||||
|
||||
Permission handling:
|
||||
- Only check for permissions when tool operations are actually requested
|
||||
- If you don't have the necessary tools for a requested operation, clearly state the limitation
|
||||
- For non-tool requests, proceed normally without permission checks
|
||||
|
||||
Important: After your response, the system will call generateObject to convert your output into a structured format according to a specific schema. Therefore:
|
||||
- Provide comprehensive information in your response
|
||||
- Include all relevant data you've gathered or processed
|
||||
- Structure your response logically so it can be easily parsed
|
||||
- Mention any important context, decisions, or actions taken
|
||||
- Include tool execution results in your response`,
|
||||
Important: After your response, the system will call generateObject to convert your output into a structured format. Ensure your response is comprehensive, logically structured, and includes all relevant data and tool results.`,
|
||||
|
||||
OUTPUT_GENERATOR: `You are a structured output generator for a workflow system. Your role is to convert the provided execution results into a structured format according to a specific schema.
|
||||
|
||||
@ -84,14 +45,14 @@ Guidelines:
|
||||
- Provide insights, support, and updates about people, companies, opportunities, tasks, notes, and other business objects.
|
||||
- Access and summarize information you have permission to see
|
||||
- Help users understand how to use the system and its features
|
||||
- Make HTTP requests to external APIs or services using the http_request tool when asked
|
||||
- Use various tools that are provided to you dynamically when needed
|
||||
|
||||
Permissions and capabilities:
|
||||
- You can only perform actions and access data that your assigned role and permissions allow
|
||||
- If a user requests something you do not have permission for, politely explain the limitation (e.g., "I cannot perform this operation because I don't have the necessary permissions. Please check your role or contact an admin.")
|
||||
- If you are unsure about your permissions for a specific action, ask the user for clarification or suggest they check with an administrator
|
||||
- Do not attempt to simulate or fake actions you cannot perform
|
||||
- If you do not have access to the http_request tool, explain that you cannot make HTTP requests
|
||||
- Only use tools that are actually available to you through the tools property
|
||||
|
||||
If you need more information to answer a question, ask follow-up questions. Always be transparent about your capabilities and limitations.
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata
|
||||
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
||||
import { fromFieldMetadataEntityToFieldMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-field-metadata-entity-to-field-metadata-dto.util';
|
||||
import { fromObjectMetadataEntityToObjectMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-object-metadata-entity-to-object-metadata-dto.util';
|
||||
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 { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
@ -59,7 +59,7 @@ export class FieldMetadataResolver {
|
||||
private readonly beforeUpdateOneField: BeforeUpdateOneField<UpdateFieldInput>,
|
||||
) {}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@Mutation(() => FieldMetadataDTO)
|
||||
async createOneField(
|
||||
@Args('input') input: CreateOneFieldMetadataInput,
|
||||
@ -75,7 +75,7 @@ export class FieldMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@Mutation(() => FieldMetadataDTO)
|
||||
async updateOneField(
|
||||
@Args('input') input: UpdateOneFieldMetadataInput,
|
||||
@ -97,7 +97,7 @@ export class FieldMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@Mutation(() => FieldMetadataDTO)
|
||||
async deleteOneField(
|
||||
@Args('input') input: DeleteOneFieldInput,
|
||||
|
||||
@ -21,7 +21,7 @@ import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metad
|
||||
import { ObjectMetadataFieldRelationService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-field-relation.service';
|
||||
import { ObjectMetadataMigrationService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-migration.service';
|
||||
import { ObjectMetadataRelatedRecordsService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service';
|
||||
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 { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
import { RemoteTableRelationsModule } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.module';
|
||||
@ -83,9 +83,7 @@ import { UpdateObjectPayload } from './dtos/update-object.input';
|
||||
},
|
||||
create: {
|
||||
many: { disabled: true },
|
||||
guards: [
|
||||
SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL),
|
||||
],
|
||||
guards: [SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL)],
|
||||
},
|
||||
update: { disabled: true },
|
||||
delete: { disabled: true },
|
||||
|
||||
@ -28,7 +28,7 @@ import { BeforeUpdateOneObject } from 'src/engine/metadata-modules/object-metada
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { objectMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
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';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@ -80,7 +80,7 @@ export class ObjectMetadataResolver {
|
||||
);
|
||||
}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@ResolveField(() => String, { nullable: true })
|
||||
async icon(
|
||||
@Parent() objectMetadata: ObjectMetadataDTO,
|
||||
@ -93,7 +93,7 @@ export class ObjectMetadataResolver {
|
||||
);
|
||||
}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@Mutation(() => ObjectMetadataDTO)
|
||||
async deleteOneObject(
|
||||
@Args('input') input: DeleteOneObjectInput,
|
||||
@ -109,7 +109,7 @@ export class ObjectMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@Mutation(() => ObjectMetadataDTO)
|
||||
async updateOneObject(
|
||||
@Args('input') input: UpdateOneObjectInput,
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
|
||||
@ObjectType('PermissionFlag')
|
||||
export class PermissionFlagDTO {
|
||||
@Field({ nullable: false })
|
||||
id: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
roleId: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
flag: PermissionFlagType;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IsArray, IsEnum, IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
|
||||
@InputType()
|
||||
export class UpsertPermissionFlagsInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
roleId: string;
|
||||
|
||||
@IsArray()
|
||||
@IsEnum(PermissionFlagType, { each: true })
|
||||
@Field(() => [PermissionFlagType])
|
||||
permissionFlagKeys: PermissionFlagType[];
|
||||
}
|
||||
@ -10,26 +10,26 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
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 { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
@Entity('settingPermission')
|
||||
@Unique('IDX_SETTING_PERMISSION_SETTING_ROLE_ID_UNIQUE', ['setting', 'roleId'])
|
||||
export class SettingPermissionEntity {
|
||||
@Entity('permissionFlag')
|
||||
@Unique('IDX_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE', ['flag', 'roleId'])
|
||||
export class PermissionFlagEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
roleId: string;
|
||||
|
||||
@ManyToOne(() => RoleEntity, (role) => role.settingPermissions, {
|
||||
@ManyToOne(() => RoleEntity, (role) => role.permissionFlags, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'roleId' })
|
||||
role: Relation<RoleEntity>;
|
||||
|
||||
@Column({ nullable: false, type: 'varchar' })
|
||||
setting: SettingPermissionType;
|
||||
flag: PermissionFlagType;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workspaceId: string;
|
||||
@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([PermissionFlagEntity, RoleEntity], 'core'),
|
||||
WorkspacePermissionsCacheModule,
|
||||
],
|
||||
|
||||
providers: [PermissionFlagService],
|
||||
exports: [PermissionFlagService],
|
||||
})
|
||||
export class PermissionFlagModule {}
|
||||
@ -3,21 +3,21 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { DataSource, In, Repository } from 'typeorm';
|
||||
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
import { UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UpsertSettingPermissionsInput } from 'src/engine/metadata-modules/setting-permission/dtos/upsert-setting-permission-input';
|
||||
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
|
||||
export class SettingPermissionService {
|
||||
export class PermissionFlagService {
|
||||
constructor(
|
||||
@InjectRepository(SettingPermissionEntity, 'core')
|
||||
private readonly settingPermissionRepository: Repository<SettingPermissionEntity>,
|
||||
@InjectRepository(PermissionFlagEntity, 'core')
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
@InjectRepository(RoleEntity, 'core')
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectDataSource('core')
|
||||
@ -25,25 +25,25 @@ export class SettingPermissionService {
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
) {}
|
||||
|
||||
public async upsertSettingPermissions({
|
||||
public async upsertPermissionFlags({
|
||||
workspaceId,
|
||||
input,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
input: UpsertSettingPermissionsInput;
|
||||
}): Promise<SettingPermissionEntity[]> {
|
||||
input: UpsertPermissionFlagsInput;
|
||||
}): Promise<PermissionFlagEntity[]> {
|
||||
await this.validateRoleIsEditableOrThrow({
|
||||
roleId: input.roleId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const invalidSettings = input.settingPermissionKeys.filter(
|
||||
(setting) => !Object.values(SettingPermissionType).includes(setting),
|
||||
const invalidFlags = input.permissionFlagKeys.filter(
|
||||
(flag) => !Object.values(PermissionFlagType).includes(flag),
|
||||
);
|
||||
|
||||
if (invalidSettings.length > 0) {
|
||||
if (invalidFlags.length > 0) {
|
||||
throw new PermissionsException(
|
||||
`${PermissionsExceptionMessage.INVALID_SETTING}: ${invalidSettings.join(', ')}`,
|
||||
`${PermissionsExceptionMessage.INVALID_SETTING}: ${invalidFlags.join(', ')}`,
|
||||
PermissionsExceptionCode.INVALID_SETTING,
|
||||
);
|
||||
}
|
||||
@ -55,7 +55,7 @@ export class SettingPermissionService {
|
||||
|
||||
try {
|
||||
const existingPermissions = await queryRunner.manager.find(
|
||||
SettingPermissionEntity,
|
||||
PermissionFlagEntity,
|
||||
{
|
||||
where: {
|
||||
roleId: input.roleId,
|
||||
@ -63,41 +63,39 @@ export class SettingPermissionService {
|
||||
},
|
||||
},
|
||||
);
|
||||
const existingSettings = new Set(
|
||||
existingPermissions.map((p) => p.setting),
|
||||
);
|
||||
const inputSettings = new Set(input.settingPermissionKeys);
|
||||
const existingSettings = new Set(existingPermissions.map((p) => p.flag));
|
||||
const inputSettings = new Set(input.permissionFlagKeys);
|
||||
|
||||
const settingsToAdd = input.settingPermissionKeys.filter(
|
||||
const flagsToAdd = input.permissionFlagKeys.filter(
|
||||
(setting) => !existingSettings.has(setting),
|
||||
);
|
||||
const permissionsToRemove = existingPermissions.filter(
|
||||
(permission) => !inputSettings.has(permission.setting),
|
||||
(permission) => !inputSettings.has(permission.flag),
|
||||
);
|
||||
|
||||
if (permissionsToRemove.length > 0) {
|
||||
await queryRunner.manager.delete(SettingPermissionEntity, {
|
||||
await queryRunner.manager.delete(PermissionFlagEntity, {
|
||||
id: In(permissionsToRemove.map((p) => p.id)),
|
||||
});
|
||||
}
|
||||
|
||||
if (settingsToAdd.length > 0) {
|
||||
const newPermissions = settingsToAdd.map((setting) =>
|
||||
queryRunner.manager.create(SettingPermissionEntity, {
|
||||
if (flagsToAdd.length > 0) {
|
||||
const newPermissions = flagsToAdd.map((flag) =>
|
||||
queryRunner.manager.create(PermissionFlagEntity, {
|
||||
workspaceId,
|
||||
roleId: input.roleId,
|
||||
setting,
|
||||
flag,
|
||||
}),
|
||||
);
|
||||
|
||||
await queryRunner.manager.save(SettingPermissionEntity, newPermissions);
|
||||
await queryRunner.manager.save(PermissionFlagEntity, newPermissions);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return queryRunner.manager.find(SettingPermissionEntity, {
|
||||
return queryRunner.manager.find(PermissionFlagEntity, {
|
||||
where: { roleId: input.roleId, workspaceId },
|
||||
order: { setting: 'ASC' },
|
||||
order: { flag: 'ASC' },
|
||||
});
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
@ -1,4 +1,5 @@
|
||||
export enum SettingPermissionType {
|
||||
export enum PermissionFlagType {
|
||||
// Settings permissions
|
||||
API_KEYS_AND_WEBHOOKS = 'API_KEYS_AND_WEBHOOKS',
|
||||
WORKSPACE = 'WORKSPACE',
|
||||
WORKSPACE_MEMBERS = 'WORKSPACE_MEMBERS',
|
||||
@ -7,4 +8,7 @@ export enum SettingPermissionType {
|
||||
ADMIN_PANEL = 'ADMIN_PANEL',
|
||||
SECURITY = 'SECURITY',
|
||||
WORKFLOWS = 'WORKFLOWS',
|
||||
|
||||
// Tool permissions
|
||||
SEND_EMAIL_TOOL = 'SEND_EMAIL_TOOL',
|
||||
}
|
||||
@ -1,19 +1,22 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
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,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { UserWorkspacePermissions } from 'src/engine/metadata-modules/permissions/types/user-workspace-permissions';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
|
||||
@ -22,6 +25,8 @@ export class PermissionsService {
|
||||
constructor(
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
@InjectRepository(RoleEntity, 'core')
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
) {}
|
||||
|
||||
public async getUserWorkspacePermissions({
|
||||
@ -51,17 +56,17 @@ export class PermissionsService {
|
||||
hasPermissionOnSettingFeature = true;
|
||||
}
|
||||
|
||||
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
||||
const permissionFlags = roleOfUserWorkspace.permissionFlags ?? [];
|
||||
|
||||
const defaultSettingsPermissions =
|
||||
this.getDefaultUserWorkspacePermissions().settingsPermissions;
|
||||
const settingsPermissions = Object.keys(SettingPermissionType).reduce(
|
||||
const settingsPermissions = Object.keys(PermissionFlagType).reduce(
|
||||
(acc, feature) => ({
|
||||
...acc,
|
||||
[feature]:
|
||||
hasPermissionOnSettingFeature ||
|
||||
settingPermissions.some(
|
||||
(settingPermission) => settingPermission.setting === feature,
|
||||
permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === feature,
|
||||
),
|
||||
}),
|
||||
defaultSettingsPermissions,
|
||||
@ -102,14 +107,15 @@ export class PermissionsService {
|
||||
[PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS]: false,
|
||||
},
|
||||
settingsPermissions: {
|
||||
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: false,
|
||||
[SettingPermissionType.WORKSPACE]: false,
|
||||
[SettingPermissionType.WORKSPACE_MEMBERS]: false,
|
||||
[SettingPermissionType.ROLES]: false,
|
||||
[SettingPermissionType.DATA_MODEL]: false,
|
||||
[SettingPermissionType.ADMIN_PANEL]: false,
|
||||
[SettingPermissionType.SECURITY]: false,
|
||||
[SettingPermissionType.WORKFLOWS]: false,
|
||||
[PermissionFlagType.API_KEYS_AND_WEBHOOKS]: false,
|
||||
[PermissionFlagType.WORKSPACE]: false,
|
||||
[PermissionFlagType.WORKSPACE_MEMBERS]: false,
|
||||
[PermissionFlagType.ROLES]: false,
|
||||
[PermissionFlagType.DATA_MODEL]: false,
|
||||
[PermissionFlagType.ADMIN_PANEL]: false,
|
||||
[PermissionFlagType.SECURITY]: false,
|
||||
[PermissionFlagType.WORKFLOWS]: false,
|
||||
[PermissionFlagType.SEND_EMAIL_TOOL]: false,
|
||||
},
|
||||
objectPermissions: {},
|
||||
}) as const satisfies UserWorkspacePermissions;
|
||||
@ -122,7 +128,7 @@ export class PermissionsService {
|
||||
}: {
|
||||
userWorkspaceId?: string;
|
||||
workspaceId: string;
|
||||
setting: SettingPermissionType;
|
||||
setting: PermissionFlagType;
|
||||
isExecutedByApiKey: boolean;
|
||||
}): Promise<boolean> {
|
||||
if (isExecutedByApiKey) {
|
||||
@ -154,10 +160,39 @@ export class PermissionsService {
|
||||
return true;
|
||||
}
|
||||
|
||||
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
||||
const permissionFlags = roleOfUserWorkspace.permissionFlags ?? [];
|
||||
|
||||
return settingPermissions.some(
|
||||
(settingPermission) => settingPermission.setting === setting,
|
||||
return permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === setting,
|
||||
);
|
||||
}
|
||||
|
||||
public async hasToolPermission(
|
||||
roleId: string,
|
||||
workspaceId: string,
|
||||
flag: PermissionFlagType,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const role = await this.roleRepository.findOne({
|
||||
where: { id: roleId, workspaceId },
|
||||
relations: ['permissionFlags'],
|
||||
});
|
||||
|
||||
if (!role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role.canAccessAllTools === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const permissionFlags = role.permissionFlags ?? [];
|
||||
|
||||
return permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === flag,
|
||||
);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
|
||||
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';
|
||||
|
||||
export type UserWorkspacePermissions = {
|
||||
settingsPermissions: Record<SettingPermissionType, boolean>;
|
||||
settingsPermissions: Record<PermissionFlagType, boolean>;
|
||||
objectRecordsPermissions: Record<PermissionsOnAllObjectRecords, boolean>;
|
||||
objectPermissions: ObjectRecordsPermissions;
|
||||
};
|
||||
|
||||
@ -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 = (
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
|
||||
@ObjectType('SettingPermission')
|
||||
export class SettingPermissionDTO {
|
||||
@Field({ nullable: false })
|
||||
id: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
roleId: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
setting: SettingPermissionType;
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IsArray, IsEnum, IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
|
||||
@InputType()
|
||||
export class UpsertSettingPermissionsInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
roleId: string;
|
||||
|
||||
@IsArray()
|
||||
@IsEnum(SettingPermissionType, { each: true })
|
||||
@Field(() => [SettingPermissionType])
|
||||
settingPermissionKeys: SettingPermissionType[];
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
||||
import { SettingPermissionService } from 'src/engine/metadata-modules/setting-permission/setting-permission.service';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([SettingPermissionEntity, RoleEntity], 'core'),
|
||||
WorkspacePermissionsCacheModule,
|
||||
],
|
||||
|
||||
providers: [SettingPermissionService],
|
||||
exports: [SettingPermissionService],
|
||||
})
|
||||
export class SettingPermissionModule {}
|
||||
@ -105,7 +105,7 @@ export class UserRoleService {
|
||||
},
|
||||
relations: {
|
||||
role: {
|
||||
settingPermissions: true,
|
||||
permissionFlags: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -11,7 +11,7 @@ import { In, Repository } from 'typeorm';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
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 { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||
@ -184,7 +184,7 @@ export class WorkspacePermissionsCacheService {
|
||||
},
|
||||
relations: [
|
||||
'objectPermissions',
|
||||
'settingPermissions',
|
||||
'permissionFlags',
|
||||
...(isFieldPermissionsEnabled ? ['fieldPermissions'] : []),
|
||||
],
|
||||
});
|
||||
@ -317,9 +317,9 @@ export class WorkspacePermissionsCacheService {
|
||||
private hasWorkflowsPermissions(role: RoleEntity): boolean {
|
||||
const hasWorkflowsPermissionFromRole = role.canUpdateAllSettings;
|
||||
const hasWorkflowsPermissionsFromSettingPermissions = isDefined(
|
||||
role.settingPermissions.find(
|
||||
(settingPermission) =>
|
||||
settingPermission.setting === SettingPermissionType.WORKFLOWS,
|
||||
role.permissionFlags.find(
|
||||
(permissionFlag) =>
|
||||
permissionFlag.flag === PermissionFlagType.WORKFLOWS,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/worksp
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { entitySchemaFactories } from 'src/engine/twenty-orm/factories';
|
||||
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
@ -46,6 +47,7 @@ import { PgPoolSharedModule } from './pg-shared-pool/pg-shared-pool.module';
|
||||
TwentyORMManager,
|
||||
TwentyORMGlobalManager,
|
||||
PgPoolSharedModule,
|
||||
ScopedWorkspaceContextFactory,
|
||||
],
|
||||
})
|
||||
export class TwentyORMModule {}
|
||||
|
||||
Reference in New Issue
Block a user