Files
twenty/packages/twenty-front/src/modules/settings/roles/role/hooks/useSaveDraftRoleToDB.ts
T
Abdul Rahman 79bcd90d8d Feat: role applicability controls (#14239)
Closes [#1404](https://github.com/twentyhq/core-team-issues/issues/1404)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-09-04 08:58:50 +05:30

333 lines
12 KiB
TypeScript

import { GET_ROLES } from '@/settings/roles/graphql/queries/getRolesQuery';
import { useUpdateAgentRole } from '@/settings/roles/hooks/useUpdateAgentRole';
import { useUpdateApiKeyRole } from '@/settings/roles/hooks/useUpdateApiKeyRole';
import { useUpdateWorkspaceMemberRole } from '@/settings/roles/hooks/useUpdateWorkspaceMemberRole';
import { useRemoveFieldPermissionInDraftRole } from '@/settings/roles/role-permissions/object-level-permissions/field-permissions/hooks/useRemoveFieldPermissionInDraftRole';
import { newFieldPermissionsFilter } from '@/settings/roles/role/hooks/utils/newFieldPermissionsFilter.util';
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
import { SettingsPath } from '@/types/SettingsPath';
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import {
useCreateOneRoleMutation,
useUpdateOneRoleMutation,
useUpsertFieldPermissionsMutation,
useUpsertObjectPermissionsMutation,
useUpsertPermissionFlagsMutation,
} from '~/generated-metadata/graphql';
import { type Role } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getDirtyFields } from '~/utils/getDirtyFields';
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
const ROLE_BASIC_KEYS: Array<keyof Role> = [
'label',
'description',
'icon',
'canUpdateAllSettings',
'canAccessAllTools',
'canReadAllObjectRecords',
'canUpdateAllObjectRecords',
'canSoftDeleteAllObjectRecords',
'canDestroyAllObjectRecords',
'canBeAssignedToUsers',
'canBeAssignedToAgents',
'canBeAssignedToApiKeys',
];
export const useSaveDraftRoleToDB = ({
roleId,
isCreateMode,
}: {
roleId: string;
isCreateMode: boolean;
}) => {
const [createRole] = useCreateOneRoleMutation();
const [updateRole] = useUpdateOneRoleMutation();
const [upsertPermissionFlags] = useUpsertPermissionFlagsMutation();
const [upsertObjectPermissions] = useUpsertObjectPermissionsMutation();
const [upsertFieldPermissions] = useUpsertFieldPermissionsMutation();
const { addWorkspaceMembersToRole } = useUpdateWorkspaceMemberRole(roleId);
const { addAgentsToRole } = useUpdateAgentRole(roleId);
const { addApiKeysToRole } = useUpdateApiKeyRole(roleId);
const navigateSettings = useNavigateSettings();
const settingsPersistedRole = useRecoilValue(
settingsPersistedRoleFamilyState(roleId),
);
const settingsDraftRole = useRecoilValue(
settingsDraftRoleFamilyState(roleId),
);
const dirtyFields = getDirtyFields(settingsDraftRole, settingsPersistedRole);
const fieldPermissionsThatShouldntBeCreatedBecauseTheyAreUseless =
settingsDraftRole.fieldPermissions?.filter((fieldPermissionToFilter) => {
const fieldPermissionDoesntExistYetInPersistedFieldPermissions =
!settingsPersistedRole?.fieldPermissions?.some(
(persistedFieldPermissionToFilter) =>
persistedFieldPermissionToFilter.fieldMetadataId ===
fieldPermissionToFilter.fieldMetadataId,
);
return (
fieldPermissionToFilter.canReadFieldValue !== false &&
fieldPermissionToFilter.canUpdateFieldValue !== false &&
fieldPermissionDoesntExistYetInPersistedFieldPermissions
);
});
const onlyMeaningfulFieldPermissions =
dirtyFields.fieldPermissions?.filter(
(dirtyFieldPermissionToFilter) =>
!fieldPermissionsThatShouldntBeCreatedBecauseTheyAreUseless?.some(
(fieldPermissionThatShouldntBeCreatedToFilter) =>
dirtyFieldPermissionToFilter.fieldMetadataId ===
fieldPermissionThatShouldntBeCreatedToFilter.fieldMetadataId,
),
) ?? [];
const fieldPermissionsToUpsert = onlyMeaningfulFieldPermissions.filter(
(dirtyFieldPermission) =>
newFieldPermissionsFilter(
dirtyFieldPermission,
settingsPersistedRole?.fieldPermissions,
),
);
const { removeFieldPermissionInDraftRole } =
useRemoveFieldPermissionInDraftRole();
const saveDraftRoleToDB = async () => {
if (
isNonEmptyArray(
fieldPermissionsThatShouldntBeCreatedBecauseTheyAreUseless,
)
) {
for (const fieldPermissionToRemoveInDraftRole of fieldPermissionsThatShouldntBeCreatedBecauseTheyAreUseless) {
removeFieldPermissionInDraftRole(
roleId,
fieldPermissionToRemoveInDraftRole.fieldMetadataId,
);
}
}
if (isCreateMode) {
const { data } = await createRole({
variables: {
createRoleInput: {
id: roleId,
label: settingsDraftRole.label,
description: settingsDraftRole.description,
icon: settingsDraftRole.icon,
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
canAccessAllTools: settingsDraftRole.canAccessAllTools,
canReadAllObjectRecords: settingsDraftRole.canReadAllObjectRecords,
canUpdateAllObjectRecords:
settingsDraftRole.canUpdateAllObjectRecords,
canSoftDeleteAllObjectRecords:
settingsDraftRole.canSoftDeleteAllObjectRecords,
canDestroyAllObjectRecords:
settingsDraftRole.canDestroyAllObjectRecords,
canBeAssignedToUsers: settingsDraftRole.canBeAssignedToUsers,
canBeAssignedToAgents: settingsDraftRole.canBeAssignedToAgents,
canBeAssignedToApiKeys: settingsDraftRole.canBeAssignedToApiKeys,
} satisfies Partial<Role>,
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
if (!data) {
return;
}
if (isDefined(dirtyFields.permissionFlags)) {
await upsertPermissionFlags({
variables: {
upsertPermissionFlagsInput: {
roleId: data.createOneRole.id,
permissionFlagKeys:
settingsDraftRole.permissionFlags?.map(
(permissionFlag) => permissionFlag.flag,
) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (isDefined(dirtyFields.objectPermissions)) {
await upsertObjectPermissions({
variables: {
upsertObjectPermissionsInput: {
roleId: data.createOneRole.id,
objectPermissions:
settingsDraftRole.objectPermissions?.map(
(objectPermission) => ({
objectMetadataId: objectPermission.objectMetadataId,
canReadObjectRecords: objectPermission.canReadObjectRecords,
canUpdateObjectRecords:
objectPermission.canUpdateObjectRecords,
canSoftDeleteObjectRecords:
objectPermission.canSoftDeleteObjectRecords,
canDestroyObjectRecords:
objectPermission.canDestroyObjectRecords,
}),
) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (isNonEmptyArray(fieldPermissionsToUpsert)) {
await upsertFieldPermissions({
variables: {
upsertFieldPermissionsInput: {
roleId: data.createOneRole.id,
fieldPermissions:
fieldPermissionsToUpsert.map((fieldPermission) => ({
objectMetadataId: fieldPermission.objectMetadataId,
fieldMetadataId: fieldPermission.fieldMetadataId,
canReadFieldValue: fieldPermission.canReadFieldValue,
canUpdateFieldValue: fieldPermission.canUpdateFieldValue,
})) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (
isDefined(dirtyFields.workspaceMembers) &&
settingsDraftRole.canBeAssignedToUsers
) {
await addWorkspaceMembersToRole({
roleId: data.createOneRole.id,
workspaceMemberIds: settingsDraftRole.workspaceMembers.map(
(member) => member.id,
),
});
}
if (
isDefined(dirtyFields.agents) &&
settingsDraftRole.canBeAssignedToAgents
) {
await addAgentsToRole({
roleId: data.createOneRole.id,
agentIds: settingsDraftRole.agents.map((agent) => agent.id),
});
}
if (
isDefined(dirtyFields.apiKeys) &&
settingsDraftRole.canBeAssignedToApiKeys
) {
await addApiKeysToRole({
roleId: data.createOneRole.id,
apiKeyIds: settingsDraftRole.apiKeys.map((apiKey) => apiKey.id),
});
}
navigateSettings(SettingsPath.RoleDetail, {
roleId: data.createOneRole.id,
});
} else {
if (isDefined(dirtyFields.permissionFlags)) {
await upsertPermissionFlags({
variables: {
upsertPermissionFlagsInput: {
roleId: roleId,
permissionFlagKeys:
settingsDraftRole.permissionFlags?.map(
(permissionFlag) => permissionFlag.flag,
) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (ROLE_BASIC_KEYS.some((key) => key in dirtyFields)) {
await updateRole({
variables: {
updateRoleInput: {
id: roleId,
update: {
label: settingsDraftRole.label,
description: settingsDraftRole.description,
icon: settingsDraftRole.icon,
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
canAccessAllTools: settingsDraftRole.canAccessAllTools,
canReadAllObjectRecords:
settingsDraftRole.canReadAllObjectRecords,
canUpdateAllObjectRecords:
settingsDraftRole.canUpdateAllObjectRecords,
canSoftDeleteAllObjectRecords:
settingsDraftRole.canSoftDeleteAllObjectRecords,
canDestroyAllObjectRecords:
settingsDraftRole.canDestroyAllObjectRecords,
canBeAssignedToUsers: settingsDraftRole.canBeAssignedToUsers,
canBeAssignedToAgents: settingsDraftRole.canBeAssignedToAgents,
canBeAssignedToApiKeys:
settingsDraftRole.canBeAssignedToApiKeys,
},
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (isDefined(dirtyFields.objectPermissions)) {
await upsertObjectPermissions({
variables: {
upsertObjectPermissionsInput: {
roleId: roleId,
objectPermissions:
settingsDraftRole.objectPermissions?.map(
(objectPermission) => ({
objectMetadataId: objectPermission.objectMetadataId,
canReadObjectRecords: objectPermission.canReadObjectRecords,
canUpdateObjectRecords:
objectPermission.canUpdateObjectRecords,
canSoftDeleteObjectRecords:
objectPermission.canSoftDeleteObjectRecords,
canDestroyObjectRecords:
objectPermission.canDestroyObjectRecords,
}),
) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
if (isNonEmptyArray(fieldPermissionsToUpsert)) {
await upsertFieldPermissions({
variables: {
upsertFieldPermissionsInput: {
roleId: roleId,
fieldPermissions:
fieldPermissionsToUpsert.map((fieldPermission) => ({
objectMetadataId: fieldPermission.objectMetadataId,
fieldMetadataId: fieldPermission.fieldMetadataId,
canReadFieldValue: fieldPermission.canReadFieldValue,
canUpdateFieldValue: fieldPermission.canUpdateFieldValue,
})) ?? [],
},
},
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
});
}
}
};
return {
saveDraftRoleToDB,
};
};