From 707043c3ba1e6828a45565939c469b97ececf649 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 21 Aug 2025 16:17:28 +0200 Subject: [PATCH] Rollout field permissions (#14024) Rolling out field permissions feature --- .../src/generated-metadata/graphql.ts | 1 - .../twenty-front/src/generated/graphql.ts | 1 - .../utils/mapFieldMetadataToGraphQLQuery.ts | 6 +- .../utils/mapObjectMetadataToGraphQLQuery.ts | 3 - .../cache/hooks/useCreateOneRecordInCache.ts | 6 - .../useReadFindManyRecordsQueryInCache.ts | 7 - .../useUpsertFindManyRecordsQueryInCache.ts | 7 - .../cache/utils/getRecordFromCache.ts | 5 +- .../cache/utils/updateRecordFromCache.ts | 3 - .../hooks/useCreateManyRecordsMutation.ts | 7 - .../hooks/useCreateOneRecordMutation.ts | 7 - .../hooks/useFindDuplicatesRecordsQuery.ts | 7 - .../hooks/useFindManyRecordsQuery.ts | 7 - .../hooks/useFindOneRecordQuery.ts | 7 - .../hooks/useUpdateOneRecordMutation.ts | 7 - ...useGenerateCombinedFindManyRecordsQuery.ts | 7 - .../usePerformCombinedFindManyRecords.ts | 7 - .../utils/generateFindManyRecordsQuery.ts | 3 - ...tingsRolePermissionsObjectLevelSection.tsx | 8 - ...sRolePermissionsObjectLevelTableHeader.tsx | 23 +- ...ingsRolePermissionsObjectLevelTableRow.tsx | 41 +- ...gsRolePermissionsObjectLevelObjectForm.tsx | 16 +- .../hooks/internal/useViewFromQueryParams.ts | 8 - .../core/interfaces/rest-api-base.handler.ts | 49 +- .../constants/public-feature-flag.const.ts | 9 - .../enums/feature-flag-key.enum.ts | 1 - .../services/feature-flag.service.ts | 8 - .../workspace-permissions-cache.service.ts | 43 +- .../workspace-entity-manager.spec.ts | 8 - .../workspace-entity-manager.ts | 7 +- .../repository/permissions.utils.ts | 102 ++-- .../workspace-delete-query-builder.ts | 3 - .../workspace-insert-query-builder.ts | 3 - .../workspace-select-query-builder.ts | 5 - .../workspace-soft-delete-query-builder.ts | 3 - .../workspace-update-query-builder.ts | 5 - .../core/utils/seed-feature-flags.util.ts | 5 - .../field-permissions.integration-spec.ts | 539 ++++++++---------- 38 files changed, 339 insertions(+), 645 deletions(-) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 3666bff861..9656c3a278 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -955,7 +955,6 @@ export enum FeatureFlagKey { IS_API_KEY_ROLES_ENABLED = 'IS_API_KEY_ROLES_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', - IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_MORPH_RELATION_ENABLED = 'IS_MORPH_RELATION_ENABLED', diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 47983b11ff..c01d87b2f4 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -919,7 +919,6 @@ export enum FeatureFlagKey { IS_API_KEY_ROLES_ENABLED = 'IS_API_KEY_ROLES_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', - IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_MORPH_RELATION_ENABLED = 'IS_MORPH_RELATION_ENABLED', diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index fe306f2308..433d05bcdc 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -6,9 +6,9 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; import { isNonCompositeField } from '@/object-record/object-filter-dropdown/utils/isNonCompositeField'; +import { type ObjectPermissions } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type FieldMetadataItem } from '../types/FieldMetadataItem'; -import { type ObjectPermissions } from 'twenty-shared/types'; type MapFieldMetadataToGraphQLQueryArgs = { objectMetadataItems: ObjectMetadataItem[]; @@ -23,7 +23,6 @@ type MapFieldMetadataToGraphQLQueryArgs = { string, ObjectPermissions & { objectMetadataId: string } >; - isFieldsPermissionsEnabled?: boolean; }; // TODO: change ObjectMetadataItems mock before refactoring with relation computed field export const mapFieldMetadataToGraphQLQuery = ({ @@ -33,7 +32,6 @@ export const mapFieldMetadataToGraphQLQuery = ({ relationRecordGqlFields, computeReferences = false, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }: MapFieldMetadataToGraphQLQueryArgs): string => { const fieldType = fieldMetadata.type; @@ -89,7 +87,6 @@ ${mapObjectMetadataToGraphQLQuery({ computeReferences: computeReferences, isRootLevel: false, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })}`; } @@ -137,7 +134,6 @@ ${mapObjectMetadataToGraphQLQuery({ computeReferences, isRootLevel: false, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} } }`; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts index 3268363747..9f91c38238 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts @@ -20,7 +20,6 @@ type MapObjectMetadataToGraphQLQueryArgs = { string, ObjectPermissions & { objectMetadataId: string } >; - isFieldsPermissionsEnabled?: boolean; }; export const mapObjectMetadataToGraphQLQuery = ({ @@ -30,7 +29,6 @@ export const mapObjectMetadataToGraphQLQuery = ({ computeReferences = false, isRootLevel = true, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled = false, }: MapObjectMetadataToGraphQLQueryArgs): string => { if ( !isRootLevel && @@ -103,7 +101,6 @@ export const mapObjectMetadataToGraphQLQuery = ({ relationRecordGqlFields, computeReferences, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }); }) .filter((field) => field !== '') diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts index 3adecfab7a..4208a25d29 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts @@ -11,9 +11,7 @@ import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphq import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useCreateOneRecordInCache = ({ objectMetadataItem, @@ -27,9 +25,6 @@ export const useCreateOneRecordInCache = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); const apolloCoreClient = useApolloCoreClient(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; return (record: ObjectRecord) => { const prefilledRecord = prefillRecord({ @@ -51,7 +46,6 @@ export const useCreateOneRecordInCache = ({ computeReferences: true, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} `; diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts index 865608c267..814e9342ed 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useReadFindManyRecordsQueryInCache.ts @@ -7,9 +7,7 @@ import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/ import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { isDefined } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useReadFindManyRecordsQueryInCache = ({ objectMetadataItem, @@ -22,10 +20,6 @@ export const useReadFindManyRecordsQueryInCache = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const readFindManyRecordsQueryInCache = < T extends ObjectRecord = ObjectRecord, >({ @@ -40,7 +34,6 @@ export const useReadFindManyRecordsQueryInCache = ({ objectMetadataItems, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }); const existingRecordsQueryResult = diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts index 15cb0f8a79..12c7f8d171 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useUpsertFindManyRecordsQueryInCache.ts @@ -8,8 +8,6 @@ import { type RecordGqlOperationVariables } from '@/object-record/graphql/types/ import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useUpsertFindManyRecordsQueryInCache = ({ objectMetadataItem, @@ -21,10 +19,6 @@ export const useUpsertFindManyRecordsQueryInCache = ({ const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const upsertFindManyRecordsQueryInCache = < T extends ObjectRecord = ObjectRecord, >({ @@ -44,7 +38,6 @@ export const useUpsertFindManyRecordsQueryInCache = ({ recordGqlFields, computeReferences, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }); const newObjectRecordConnection = getRecordConnectionFromRecords({ diff --git a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts index 7ce8ac502c..9e3ade0334 100644 --- a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts @@ -6,10 +6,10 @@ import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFr import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { type ObjectPermissions } from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; import { isEmptyObject } from '~/utils/isEmptyObject'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; -import { type ObjectPermissions } from 'twenty-shared/types'; export type GetRecordFromCacheArgs = { cache: ApolloCache; @@ -21,7 +21,6 @@ export type GetRecordFromCacheArgs = { string, ObjectPermissions & { objectMetadataId: string } >; - isFieldsPermissionsEnabled?: boolean; }; export const getRecordFromCache = ({ objectMetadataItem, @@ -30,7 +29,6 @@ export const getRecordFromCache = ({ recordId, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled = false, }: GetRecordFromCacheArgs) => { if (isUndefinedOrNull(objectMetadataItem)) { return null; @@ -48,7 +46,6 @@ export const getRecordFromCache = ({ objectMetadataItem, recordGqlFields: appliedRecordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }, )} `; diff --git a/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts b/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts index e0cfde0c7a..01ea8875b8 100644 --- a/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts @@ -17,7 +17,6 @@ export const updateRecordFromCache = ({ recordGqlFields, record, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }: { objectMetadataItems: ObjectMetadataItem[]; objectMetadataItem: ObjectMetadataItem; @@ -28,7 +27,6 @@ export const updateRecordFromCache = ({ string, ObjectPermissions & { objectMetadataId: string } >; - isFieldsPermissionsEnabled?: boolean; }) => { if (isUndefinedOrNull(objectMetadataItem)) { return null; @@ -44,7 +42,6 @@ export const updateRecordFromCache = ({ computeReferences: true, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }, )} `; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts index d0a2794277..546119885b 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecordsMutation.ts @@ -8,9 +8,7 @@ import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useCreateManyRecordsMutation = ({ @@ -28,10 +26,6 @@ export const useCreateManyRecordsMutation = ({ const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - if (isUndefinedOrNull(objectMetadataItem)) { return { createManyRecordsMutation: EMPTY_MUTATION }; } @@ -52,7 +46,6 @@ export const useCreateManyRecordsMutation = ({ objectMetadataItem, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }, )} }`; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts index 12d376014f..7e959d2bda 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts @@ -9,9 +9,7 @@ import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/ import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useCreateOneRecordMutation = ({ @@ -35,10 +33,6 @@ export const useCreateOneRecordMutation = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - if (isUndefinedOrNull(objectMetadataItem)) { return { createOneRecordMutation: EMPTY_MUTATION }; } @@ -56,7 +50,6 @@ export const useCreateOneRecordMutation = ({ objectMetadataItem, recordGqlFields: appliedRecordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} } `; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts index d847cb38bd..0d4294bad6 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts @@ -7,9 +7,7 @@ import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabl import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useFindDuplicateRecordsQuery = ({ objectNameSingular, @@ -24,10 +22,6 @@ export const useFindDuplicateRecordsQuery = ({ const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const findDuplicateRecordsQuery = gql` query FindDuplicate${capitalize( objectMetadataItem.nameSingular, @@ -40,7 +34,6 @@ export const useFindDuplicateRecordsQuery = ({ objectMetadataItems, objectMetadataItem, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} cursor } diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts index caf96d42dd..6abf13af38 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecordsQuery.ts @@ -8,8 +8,6 @@ import { generateFindManyRecordsQuery, type QueryCursorDirection, } from '@/object-record/utils/generateFindManyRecordsQuery'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useFindManyRecordsQuery = ({ objectNameSingular, @@ -30,10 +28,6 @@ export const useFindManyRecordsQuery = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const findManyRecordsQuery = generateFindManyRecordsQuery({ objectMetadataItem, objectMetadataItems, @@ -41,7 +35,6 @@ export const useFindManyRecordsQuery = ({ computeReferences, cursorDirection, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }); return { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts index d65774009f..784468ea07 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecordQuery.ts @@ -6,9 +6,7 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; export const useFindOneRecordQuery = ({ objectNameSingular, @@ -27,10 +25,6 @@ export const useFindOneRecordQuery = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const findOneRecordQuery = gql` query FindOne${capitalize( objectMetadataItem.nameSingular, @@ -54,7 +48,6 @@ export const useFindOneRecordQuery = ({ objectMetadataItem, recordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} }, `; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts index 80c03b0a13..1055e652a4 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordMutation.ts @@ -9,9 +9,7 @@ import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/ import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useUpdateOneRecordMutation = ({ @@ -31,10 +29,6 @@ export const useUpdateOneRecordMutation = ({ const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - if (isUndefinedOrNull(objectMetadataItem)) { return { updateOneRecordMutation: EMPTY_MUTATION }; } @@ -60,7 +54,6 @@ export const useUpdateOneRecordMutation = ({ computeReferences, recordGqlFields: appliedRecordGqlFields, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }, )} } diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts index edadaaced9..3019f9bd2f 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts @@ -9,10 +9,8 @@ import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/ import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import isEmpty from 'lodash.isempty'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; import { isNonEmptyArray } from '~/utils/isNonEmptyArray'; export const useGenerateCombinedFindManyRecordsQuery = ({ @@ -23,10 +21,6 @@ export const useGenerateCombinedFindManyRecordsQuery = ({ const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - if (!isNonEmptyArray(operationSignatures)) { return null; } @@ -118,7 +112,6 @@ export const useGenerateCombinedFindManyRecordsQuery = ({ objectMetadataItem, }), objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} cursor } diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts index aa105af441..6427baa411 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts @@ -13,10 +13,8 @@ import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions import { type CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { useRecoilValue } from 'recoil'; import { capitalize } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; export const usePerformCombinedFindManyRecords = () => { const apolloCoreClient = useApolloCoreClient(); @@ -24,10 +22,6 @@ export const usePerformCombinedFindManyRecords = () => { const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const generateCombinedFindManyRecordsQuery = ( operationSignatures: RecordGqlOperationSignature[], objectMetadataItemsValue: ObjectMetadataItem[], @@ -104,7 +98,6 @@ export const usePerformCombinedFindManyRecords = () => { objectMetadataItem, }), objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} cursor } diff --git a/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts index d44211c22e..ec5cc497ce 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateFindManyRecordsQuery.ts @@ -15,7 +15,6 @@ export const generateFindManyRecordsQuery = ({ computeReferences, cursorDirection, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }: { objectMetadataItem: ObjectMetadataItem; objectMetadataItems: ObjectMetadataItem[]; @@ -26,7 +25,6 @@ export const generateFindManyRecordsQuery = ({ string, ObjectPermissions & { objectMetadataId: string } >; - isFieldsPermissionsEnabled?: boolean; }) => gql` query FindMany${capitalize( objectMetadataItem.namePlural, @@ -47,7 +45,6 @@ query FindMany${capitalize( recordGqlFields, computeReferences, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, })} cursor } diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx index c3bdc4b270..65a6a08b6f 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection.tsx @@ -5,14 +5,12 @@ import { useObjectMetadataItemsThatCanHavePermission } from '@/settings/roles/ro import { SettingsPath } from '@/types/SettingsPath'; import { Table } from '@/ui/layout/table/components/Table'; import { TableCell } from '@/ui/layout/table/components/TableCell'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { isDefined } from 'twenty-shared/utils'; import { IconPlus } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { Section } from 'twenty-ui/layout'; -import { FeatureFlagKey } from '~/generated/graphql'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; const StyledCreateObjectOverrideSection = styled(Section)` @@ -70,16 +68,11 @@ export const SettingsRolePermissionsObjectLevelSection = ({ isDefined(objectMetadataItemsWithPermissionOverride) && objectMetadataItemsWithPermissionOverride?.length > 0; - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - return (
{hasObjectPermissions ? ( @@ -89,7 +82,6 @@ export const SettingsRolePermissionsObjectLevelSection = ({ key={objectMetadataItem.id} objectMetadataItem={objectMetadataItem} roleId={roleId} - isFieldsPermissionsEnabled={isFieldsPermissionsEnabled} /> ), ) diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx index 58a9355850..21a309f204 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableHeader.tsx @@ -1,35 +1,20 @@ -import { - OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS, - OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS_WITHOUT_FIELD_PERMISSIONS, -} from '@/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns'; +import { OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS } from '@/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableRow } from '@/ui/layout/table/components/TableRow'; import { t } from '@lingui/core/macro'; type SettingsRolePermissionsObjectLevelTableHeaderProps = { showPermissionsLabel?: boolean; - isFieldsPermissionsEnabled?: boolean; }; export const SettingsRolePermissionsObjectLevelTableHeader = ({ showPermissionsLabel = true, - isFieldsPermissionsEnabled = false, }: SettingsRolePermissionsObjectLevelTableHeaderProps) => ( - + {t`Object-Level`} {showPermissionsLabel ? t`Records` : ''} - {isFieldsPermissionsEnabled && ( - <> - {showPermissionsLabel ? t`See Fields` : ''} - {showPermissionsLabel ? t`Edit Fields` : ''} - - )} + {showPermissionsLabel ? t`See Fields` : ''} + {showPermissionsLabel ? t`Edit Fields` : ''} ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx index 33835aaf47..722f899380 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx @@ -2,10 +2,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { SettingsRolePermissionsObjectLevelOverrideCellContainer } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelOverrideCellContainer'; import { SettingsRolePermissionsObjectLevelSeeFieldsValueForObject } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSeeFieldsValueForObject'; import { SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject'; -import { - OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS, - OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS_WITHOUT_FIELD_PERMISSIONS, -} from '@/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns'; +import { OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS } from '@/settings/roles/role-permissions/object-level-permissions/constants/ObjectLevelPermissionTableGridAutoColumns'; import { SettingsPath } from '@/types/SettingsPath'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; @@ -32,13 +29,11 @@ const StyledNameLabel = styled.div` type SettingsRolePermissionsObjectLevelTableRowProps = { objectMetadataItem: ObjectMetadataItem; roleId: string; - isFieldsPermissionsEnabled: boolean; }; export const SettingsRolePermissionsObjectLevelTableRow = ({ objectMetadataItem, roleId, - isFieldsPermissionsEnabled, }: SettingsRolePermissionsObjectLevelTableRowProps) => { const { getIcon } = useIcons(); const theme = useTheme(); @@ -53,11 +48,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ roleId: roleId, objectMetadataId: objectMetadataItem.id, })} - gridAutoColumns={ - isFieldsPermissionsEnabled - ? OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS - : OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS_WITHOUT_FIELD_PERMISSIONS - } + gridAutoColumns={OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS} > {!!Icon && ( @@ -78,22 +69,18 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ objectLabel={objectLabelPlural} /> - {isFieldsPermissionsEnabled && ( - <> - - - - - - - - )} + + + + + + - {isFieldsPermissionsEnabled && ( - - )} + ); diff --git a/packages/twenty-front/src/modules/views/hooks/internal/useViewFromQueryParams.ts b/packages/twenty-front/src/modules/views/hooks/internal/useViewFromQueryParams.ts index 5adc658ab0..cd406f0387 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/useViewFromQueryParams.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/useViewFromQueryParams.ts @@ -16,10 +16,8 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery'; import { type ViewFilter } from '@/views/types/ViewFilter'; import { relationFilterValueSchemaObject } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema'; -import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { ViewFilterOperand } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { FeatureFlagKey } from '~/generated/graphql'; const filterQueryParamsSchema = z.object({ viewId: z.string().optional(), @@ -71,10 +69,6 @@ export const useViewFromQueryParams = () => { isDefined(filterQueryParams) && Object.entries(filterQueryParams).length > 0; - const featureFlags = useFeatureFlagsMap(); - const isFieldsPermissionsEnabled = - featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - const getFiltersFromQueryParams = useRecoilCallback( ({ snapshot }) => async () => { @@ -131,7 +125,6 @@ export const useViewFromQueryParams = () => { objectMetadataItem: relationObjectMetadataItem, objectMetadataItems, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, }), variables: { filter: { @@ -193,7 +186,6 @@ export const useViewFromQueryParams = () => { objectMetadataItem.fields, objectMetadataItems, objectPermissionsByObjectMetadataId, - isFieldsPermissionsEnabled, ], ); diff --git a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts index 66bd2b8653..05258a81ac 100644 --- a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts @@ -190,38 +190,29 @@ export abstract class RestApiBaseHandler { let restrictedFields: RestrictedFieldsPermissions = {}; - if ( - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED, - workspace.id, - ) - ) { - if (roleId) { - const objectMetadataPermissions = - await this.workspacePermissionsCacheService.getObjectRecordPermissionsForRoles( - { - workspaceId: workspace.id, - roleIds: roleId ? [roleId] : undefined, - }, - ); + if (roleId) { + const objectMetadataPermissions = + await this.workspacePermissionsCacheService.getObjectRecordPermissionsForRoles( + { + workspaceId: workspace.id, + roleIds: roleId ? [roleId] : undefined, + }, + ); - if ( - !isDefined( - objectMetadataPermissions?.[roleId]?.[ - objectMetadata.objectMetadataMapItem.id - ]?.restrictedFields, - ) - ) { - throw new InternalServerError( - 'Fields permissions not found for role', - ); - } - - restrictedFields = - objectMetadataPermissions[roleId][ + if ( + !isDefined( + objectMetadataPermissions?.[roleId]?.[ objectMetadata.objectMetadataMapItem.id - ].restrictedFields; + ]?.restrictedFields, + ) + ) { + throw new InternalServerError('Fields permissions not found for role'); } + + restrictedFields = + objectMetadataPermissions[roleId][ + objectMetadata.objectMetadataMapItem.id + ].restrictedFields; } return { diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts index 552a43dea1..3d69ca5c9b 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/constants/public-feature-flag.const.ts @@ -22,15 +22,6 @@ export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [ 'https://twenty.com/images/lab/is-imap-smtp-caldav-enabled.png', }, }, - { - key: FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED, - metadata: { - label: 'Fields Permissions', - description: 'Configure permissions at field-level for your workspace', - imagePath: - 'https://twenty.com/images/lab/is-fields-permissions-enabled.png', - }, - }, { key: FeatureFlagKey.IS_WORKFLOW_BRANCH_ENABLED, metadata: { diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index b5740a7ccc..640e63d08a 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -9,7 +9,6 @@ export enum FeatureFlagKey { IS_MORPH_RELATION_ENABLED = 'IS_MORPH_RELATION_ENABLED', IS_WORKFLOW_BRANCH_ENABLED = 'IS_WORKFLOW_BRANCH_ENABLED', IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED', - IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_WORKSPACE_MIGRATION_V2_ENABLED = 'IS_WORKSPACE_MIGRATION_V2_ENABLED', diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/services/feature-flag.service.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/services/feature-flag.service.ts index e973d033cd..bf7e7bea6b 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/services/feature-flag.service.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/services/feature-flag.service.ts @@ -132,14 +132,6 @@ export class FeatureFlagService { { workspaceId }, ); - if (featureFlag === FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED) { - await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache( - { - workspaceId, - }, - ); - } - return result; } } diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts index ce5fd3fe38..4f760c0595 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts @@ -9,7 +9,6 @@ import { import { isDefined } from 'twenty-shared/utils'; import { In, IsNull, Not, 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 { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants'; import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity'; @@ -179,24 +178,12 @@ export class WorkspacePermissionsCacheService { }): Promise { let roles: RoleEntity[] = []; - const workspaceFeatureFlagsMap = - await this.workspaceFeatureFlagsMapCacheService.getWorkspaceFeatureFlagsMap( - { workspaceId }, - ); - - const isFieldPermissionsEnabled = - workspaceFeatureFlagsMap[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - roles = await this.roleRepository.find({ where: { workspaceId, ...(roleIds ? { id: In(roleIds) } : {}), }, - relations: [ - 'objectPermissions', - 'permissionFlags', - ...(isFieldPermissionsEnabled ? ['fieldPermissions'] : []), - ], + relations: ['objectPermissions', 'permissionFlags', 'fieldPermissions'], }); const workspaceObjectMetadataCollection = @@ -256,22 +243,20 @@ export class WorkspacePermissionsCacheService { canDestroy, ); - if (isFieldPermissionsEnabled) { - const fieldPermissions = role.fieldPermissions.filter( - (fieldPermission) => - fieldPermission.objectMetadataId === objectMetadataId, - ); + const fieldPermissions = role.fieldPermissions.filter( + (fieldPermission) => + fieldPermission.objectMetadataId === objectMetadataId, + ); - for (const fieldPermission of fieldPermissions) { - if ( - isDefined(fieldPermission.canReadFieldValue) || - isDefined(fieldPermission.canUpdateFieldValue) - ) { - restrictedFields[fieldPermission.fieldMetadataId] = { - canRead: fieldPermission.canReadFieldValue, - canUpdate: fieldPermission.canUpdateFieldValue, - }; - } + for (const fieldPermission of fieldPermissions) { + if ( + isDefined(fieldPermission.canReadFieldValue) || + isDefined(fieldPermission.canUpdateFieldValue) + ) { + restrictedFields[fieldPermission.fieldMetadataId] = { + canRead: fieldPermission.canReadFieldValue, + canUpdate: fieldPermission.canUpdateFieldValue, + }; } } } diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 8e0e94fb95..911922c3cf 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -133,7 +133,6 @@ describe('WorkspaceEntityManager', () => { IS_WORKFLOW_BRANCH_ENABLED: false, IS_RELATION_CONNECT_ENABLED: false, - IS_FIELDS_PERMISSIONS_ENABLED: false, IS_CORE_VIEW_SYNCING_ENABLED: false, IS_CORE_VIEW_ENABLED: false, IS_WORKSPACE_MIGRATION_V2_ENABLED: false, @@ -158,7 +157,6 @@ describe('WorkspaceEntityManager', () => { IS_MORPH_RELATION_ENABLED: false, IS_RELATION_CONNECT_ENABLED: false, - IS_FIELDS_PERMISSIONS_ENABLED: true, IS_CORE_VIEW_SYNCING_ENABLED: false, }, permissionsPerRoleId: {}, @@ -235,10 +233,6 @@ describe('WorkspaceEntityManager', () => { ({ formattedResult }: { formattedResult: string[] }) => formattedResult, ); - jest.spyOn(entityManager as any, 'getFeatureFlagMap').mockReturnValue({ - IS_FIELDS_PERMISSIONS_ENABLED: true, - }); - jest .spyOn(entityManager as any, 'extractTargetNameSingularFromEntityTarget') .mockImplementation((entityName: string) => { @@ -327,7 +321,6 @@ describe('WorkspaceEntityManager', () => { }); expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({ entityName: 'test-entity', - isFieldPermissionsEnabled: true, operationType: 'update', objectMetadataMaps: mockInternalContext.objectMetadataMaps, objectsPermissions: mockPermissionOptions.objectRecordsPermissions, @@ -362,7 +355,6 @@ describe('WorkspaceEntityManager', () => { expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({ entityName: 'test-entity', operationType: 'delete', - isFieldPermissionsEnabled: true, objectMetadataMaps: mockInternalContext.objectMetadataMaps, objectsPermissions: mockPermissionOptions.objectRecordsPermissions, selectedColumns: [], diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts index e2c16e0ab0..a3e415016d 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts @@ -410,8 +410,6 @@ export class WorkspaceEntityManager extends EntityManager { selectedColumns, allFieldsSelected: false, updatedColumns, - isFieldPermissionsEnabled: - this.getFeatureFlagMap().IS_FIELDS_PERMISSIONS_ENABLED, }); } @@ -1214,14 +1212,11 @@ export class WorkspaceEntityManager extends EntityManager { entities: createdEntities, }); - const isFieldPermissionsEnabled = - this.getFeatureFlagMap().IS_FIELDS_PERMISSIONS_ENABLED; - const permissionCheckApplies = permissionOptionsFromArgs?.shouldBypassPermissionChecks !== true && objectMetadataItem.isSystem !== true; - if (isFieldPermissionsEnabled && permissionCheckApplies) { + if (permissionCheckApplies) { formattedResult = this.getFormattedResultWithoutNonReadableFields({ formattedResult, objectMetadataItem, diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts b/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts index 5dcc0606a4..fdaa99e604 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/permissions.utils.ts @@ -41,7 +41,6 @@ type ValidateOperationIsPermittedOrThrowArgs = { objectsPermissions: ObjectsPermissionsDeprecated; objectMetadataMaps: ObjectMetadataMaps; selectedColumns: string[] | '*'; - isFieldPermissionsEnabled?: boolean; allFieldsSelected: boolean; updatedColumns: string[]; }; @@ -52,7 +51,6 @@ export const validateOperationIsPermittedOrThrow = ({ objectsPermissions, objectMetadataMaps, selectedColumns, - isFieldPermissionsEnabled, allFieldsSelected, updatedColumns, }: ValidateOperationIsPermittedOrThrowArgs) => { @@ -81,9 +79,8 @@ export const validateOperationIsPermittedOrThrow = ({ return; } - const columnNameToFieldMetadataIdMap = isFieldPermissionsEnabled - ? getColumnNameToFieldMetadataIdMap(objectMetadata) - : {}; + const columnNameToFieldMetadataIdMap = + getColumnNameToFieldMetadataIdMap(objectMetadata); const permissionsForEntity = objectsPermissions[objectMetadataIdForEntity]; @@ -96,14 +93,12 @@ export const validateOperationIsPermittedOrThrow = ({ ); } - if (isFieldPermissionsEnabled) { - validateReadFieldPermissionOrThrow({ - restrictedFields: permissionsForEntity.restrictedFields, - selectedColumns, - columnNameToFieldMetadataIdMap, - allFieldsSelected, - }); - } + validateReadFieldPermissionOrThrow({ + restrictedFields: permissionsForEntity.restrictedFields, + selectedColumns, + columnNameToFieldMetadataIdMap, + allFieldsSelected, + }); break; case 'insert': case 'update': @@ -114,15 +109,13 @@ export const validateOperationIsPermittedOrThrow = ({ ); } - if (isFieldPermissionsEnabled) { - validateReadFieldPermissionOrThrow({ - restrictedFields: permissionsForEntity.restrictedFields, - selectedColumns, - columnNameToFieldMetadataIdMap, - }); - } + validateReadFieldPermissionOrThrow({ + restrictedFields: permissionsForEntity.restrictedFields, + selectedColumns, + columnNameToFieldMetadataIdMap, + }); - if (isFieldPermissionsEnabled && updatedColumns.length > 0) { + if (updatedColumns.length > 0) { validateUpdateFieldPermissionOrThrow({ restrictedFields: permissionsForEntity.restrictedFields, updatedColumns, @@ -138,13 +131,11 @@ export const validateOperationIsPermittedOrThrow = ({ ); } - if (isFieldPermissionsEnabled) { - validateReadFieldPermissionOrThrow({ - restrictedFields: permissionsForEntity.restrictedFields, - selectedColumns, - columnNameToFieldMetadataIdMap, - }); - } + validateReadFieldPermissionOrThrow({ + restrictedFields: permissionsForEntity.restrictedFields, + selectedColumns, + columnNameToFieldMetadataIdMap, + }); break; case 'restore': case 'soft-delete': @@ -155,13 +146,11 @@ export const validateOperationIsPermittedOrThrow = ({ ); } - if (isFieldPermissionsEnabled) { - validateReadFieldPermissionOrThrow({ - restrictedFields: permissionsForEntity.restrictedFields, - selectedColumns, - columnNameToFieldMetadataIdMap, - }); - } + validateReadFieldPermissionOrThrow({ + restrictedFields: permissionsForEntity.restrictedFields, + selectedColumns, + columnNameToFieldMetadataIdMap, + }); break; default: throw new PermissionsException( @@ -180,7 +169,6 @@ type ValidateQueryIsPermittedOrThrowArgs = { objectsPermissions: ObjectsPermissionsDeprecated; objectMetadataMaps: ObjectMetadataMaps; shouldBypassPermissionChecks: boolean; - isFieldPermissionsEnabled?: boolean; }; export const validateQueryIsPermittedOrThrow = ({ @@ -188,7 +176,6 @@ export const validateQueryIsPermittedOrThrow = ({ objectsPermissions, objectMetadataMaps, shouldBypassPermissionChecks, - isFieldPermissionsEnabled, }: ValidateQueryIsPermittedOrThrowArgs) => { if (shouldBypassPermissionChecks) { return; @@ -204,31 +191,29 @@ export const validateQueryIsPermittedOrThrow = ({ let selectedColumns: string[] | '*' = []; let updatedColumns: string[] = []; - if (isFieldPermissionsEnabled) { - selectedColumns = getSelectedColumnsFromExpressionMap({ - operationType, - expressionMap, - allFieldsSelected, - }); + selectedColumns = getSelectedColumnsFromExpressionMap({ + operationType, + expressionMap, + allFieldsSelected, + }); - if (operationType !== 'select') { - const valuesSet = expressionMap.valuesSet; + if (operationType !== 'select') { + const valuesSet = expressionMap.valuesSet; - if (Array.isArray(valuesSet)) { - updatedColumns = valuesSet.reduce((acc, value) => { - const keys = Object.keys(value); + if (Array.isArray(valuesSet)) { + updatedColumns = valuesSet.reduce((acc, value) => { + const keys = Object.keys(value); - keys.forEach((key) => { - if (!acc.includes(key)) { - acc.push(key); - } - }); + keys.forEach((key) => { + if (!acc.includes(key)) { + acc.push(key); + } + }); - return acc; - }, []); - } else { - updatedColumns = Object.keys(valuesSet ?? {}); - } + return acc; + }, []); + } else { + updatedColumns = Object.keys(valuesSet ?? {}); } } @@ -238,7 +223,6 @@ export const validateQueryIsPermittedOrThrow = ({ objectsPermissions, objectMetadataMaps, selectedColumns, - isFieldPermissionsEnabled, allFieldsSelected, updatedColumns, }); diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts index f603d099c7..cc77cbc8bf 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts @@ -14,7 +14,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception'; import { TwentyORMException, @@ -72,8 +71,6 @@ export class WorkspaceDeleteQueryBuilder< objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled: - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED], }); const mainAliasTarget = this.getMainAliasTarget(); diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts index d4c513c285..18dc54fc5e 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-insert-query-builder.ts @@ -12,7 +12,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type'; import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type'; import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type'; @@ -107,8 +106,6 @@ export class WorkspaceInsertQueryBuilder< objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled: - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED], }); const mainAliasTarget = this.getMainAliasTarget(); diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts index 4df7e77b03..ac343a8058 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts @@ -10,7 +10,6 @@ import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interf import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { PermissionsException, PermissionsExceptionCode, @@ -321,15 +320,11 @@ export class WorkspaceSelectQueryBuilder< } private validatePermissions(): void { - const isFieldPermissionsEnabled = - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED]; - validateQueryIsPermittedOrThrow({ expressionMap: this.expressionMap, objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled, }); } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts index 77a27cb34f..7a9b26aee4 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts @@ -12,7 +12,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception'; import { TwentyORMException, @@ -69,8 +68,6 @@ export class WorkspaceSoftDeleteQueryBuilder< objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled: - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED], }); const mainAliasTarget = this.getMainAliasTarget(); diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts index 656aa7551e..adec41c277 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts @@ -13,7 +13,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/ import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type'; import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type'; import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type'; @@ -91,8 +90,6 @@ export class WorkspaceUpdateQueryBuilder< objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled: - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED], }); const mainAliasTarget = this.getMainAliasTarget(); @@ -202,8 +199,6 @@ export class WorkspaceUpdateQueryBuilder< objectsPermissions: this.objectRecordsPermissions, objectMetadataMaps: this.internalContext.objectMetadataMaps, shouldBypassPermissionChecks: this.shouldBypassPermissionChecks, - isFieldPermissionsEnabled: - this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index d5a80a989b..4ce6a2eb50 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -70,11 +70,6 @@ export const seedFeatureFlags = async ( workspaceId: workspaceId, value: true, }, - { - key: FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED, - workspaceId: workspaceId, - value: true, - }, { key: FeatureFlagKey.IS_CORE_VIEW_SYNCING_ENABLED, workspaceId: workspaceId, diff --git a/packages/twenty-server/test/integration/rest/suites/field-permissions.integration-spec.ts b/packages/twenty-server/test/integration/rest/suites/field-permissions.integration-spec.ts index b6169d1d5d..89eb61a7b1 100644 --- a/packages/twenty-server/test/integration/rest/suites/field-permissions.integration-spec.ts +++ b/packages/twenty-server/test/integration/rest/suites/field-permissions.integration-spec.ts @@ -2,15 +2,11 @@ import gql from 'graphql-tag'; import { TEST_COMPANY_1_ID } from 'test/integration/constants/test-company-ids.constants'; import { TEST_PERSON_1_ID } from 'test/integration/constants/test-person-ids.constants'; import { TEST_PRIMARY_LINK_URL } from 'test/integration/constants/test-primary-link-url.constant'; -import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; -import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util'; import { upsertFieldPermissions } from 'test/integration/graphql/utils/upsert-field-permissions.util'; import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util'; import { generateRecordName } from 'test/integration/utils/generate-record-name'; -import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-workspaces.util'; - describe('Restricted fields', () => { let personCity: string; let memberRoleId: string; @@ -142,313 +138,44 @@ describe('Restricted fields', () => { }); }); - describe('With Feature flag enabled', () => { - beforeAll(async () => { - const enablePermissionsQuery = updateFeatureFlagFactory( - SEED_APPLE_WORKSPACE_ID, - 'IS_FIELDS_PERMISSIONS_ENABLED', - true, - ); + it('should hide fields when user has restricted read permissions - findOne', async () => { + await makeRestAPIRequest({ + method: 'get', + path: `/people/${TEST_PERSON_1_ID}`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + }) + .expect(200) + .expect((res) => { + const person = res.body.data.person; - await makeGraphqlAPIRequest(enablePermissionsQuery); - }); - - afterAll(async () => { - const disablePermissionsQuery = updateFeatureFlagFactory( - SEED_APPLE_WORKSPACE_ID, - 'IS_FIELDS_PERMISSIONS_ENABLED', - false, - ); - - await makeGraphqlAPIRequest(disablePermissionsQuery); - }); - - it('should hide fields when user has restricted read permissions - findOne', async () => { - await makeRestAPIRequest({ - method: 'get', - path: `/people/${TEST_PERSON_1_ID}`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - }) - .expect(200) - .expect((res) => { - const person = res.body.data.person; - - expect(person).toBeDefined(); - expect(person.id).toBeDefined(); - expect(person.emails).toBeUndefined(); - }); - }); - - describe('updateOne', () => { - it('should hide fields in the response when user has restricted read permissions', async () => { - // Create field permission restricting update access to phones field - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: false, - canUpdateFieldValue: null, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'patch', - path: `/people/${TEST_PERSON_1_ID}`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: { - name: { - firstName: 'John', - }, - }, - }) - .expect(200) - .expect((res) => { - const updatedPerson = res.body.data.updatePerson; - - expect(updatedPerson.name.firstName).toBe('John'); - expect(updatedPerson.phones).toBeUndefined(); - }); + expect(person).toBeDefined(); + expect(person.id).toBeDefined(); + expect(person.emails).toBeUndefined(); }); - it('should block update when user tries to update non-updatable field', async () => { - // Create field permission restricting update access to phones field - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: false, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'patch', - path: `/people/${TEST_PERSON_1_ID}`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: { - phones: { - primaryPhoneNumber: '987654321', - primaryPhoneCountryCode: 'FR', - primaryPhoneCallingCode: '+33', - }, - }, - }) - .expect(400) - .expect((res) => { - expect(res.body.messages[0]).toContain( - 'Entity performing the request does not have permission', - ); - }); - }); - - it('should allow update when user has no restricted update permissions', async () => { - // Remove field permission restrictions - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: null, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'patch', - path: `/people/${TEST_PERSON_1_ID}`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: { - city: 'Updated City', - }, - }) - .expect(200) - .expect((res) => { - const updatedPerson = res.body.data.updatePerson; - - expect(updatedPerson.city).toBe('Updated City'); - }); - }); - }); - - describe('createOne', () => { - it('should block create when user has restricted update permissions on phones field', async () => { - // Create field permission restricting update access to phones field - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: false, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'post', - path: `/people`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: { - phones: { - primaryPhoneNumber: '555123456', - primaryPhoneCountryCode: 'US', - primaryPhoneCallingCode: '+1', - }, - }, - }) - .expect(400) - .expect((res) => { - expect(res.body.messages[0]).toContain( - 'Entity performing the request does not have permission', - ); - }); - }); - - it('should allow create when user has no restricted update permissions', async () => { - // Remove field permission restrictions on phones - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: null, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'post', - path: `/people`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: { - city: 'New City', - }, - }) - .expect(201) - .expect((res) => { - const createdPerson = res.body.data.createPerson; - - expect(createdPerson.city).toBe('New City'); - expect(createdPerson.emails).toBeUndefined(); // No reading rights on emails - }); - }); - }); - - describe('createMany', () => { - it('should block createMany when user has restricted update permissions on phones field', async () => { - // Create field permission restricting update access to phones field - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: false, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'post', - path: `/batch/people`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: [ - { - phones: { - primaryPhoneNumber: '555123456', - primaryPhoneCountryCode: 'US', - primaryPhoneCallingCode: '+1', - }, - }, - ], - }) - .expect(400) - .expect((res) => { - expect(res.body.messages[0]).toContain( - 'Entity performing the request does not have permission', - ); - }); - }); - - it('should allow createMany when user has no restricted update permissions', async () => { - // Remove field permission restrictions - await upsertFieldPermissions({ - roleId: memberRoleId, - fieldPermissions: [ - { - objectMetadataId: personObjectId, - fieldMetadataId: phonesFieldId, - canReadFieldValue: null, - canUpdateFieldValue: null, - }, - ], - }); - - await makeRestAPIRequest({ - method: 'post', - path: `/batch/people`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - body: [ - { - city: 'Batch City 1', - }, - { - city: 'Batch City 2', - }, - ], - }) - .expect(201) - .expect((res) => { - const createdPeople = res.body.data.createPeople; - - expect(createdPeople).toHaveLength(2); - expect(createdPeople[0].city).toBe('Batch City 1'); - expect(createdPeople[0].emails).toBeUndefined(); // No reading rights on emails - expect(createdPeople[1].city).toBe('Batch City 2'); - expect(createdPeople[1].emails).toBeUndefined(); // No reading rights on emails - }); - }); - }); }); - describe('With feature flag disabled', () => { - it('should query all fields despite field permission restriction', async () => { - await makeRestAPIRequest({ - method: 'get', - path: `/people/${TEST_PERSON_1_ID}`, - bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, - }) - .expect(200) - .expect((res) => { - const person = res.body.data.person; + describe('updateOne', () => { + it('should hide fields in the response when user has restricted read permissions', async () => { + // Create field permission restricting update access to phones field + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: false, + canUpdateFieldValue: null, + }, + ], + }); - expect(person).toBeDefined(); - expect(person.id).toBeDefined(); - expect(person.emails).toBeDefined(); - }); - }); - - it('should allow updates despite field permission restriction', async () => { await makeRestAPIRequest({ method: 'patch', path: `/people/${TEST_PERSON_1_ID}`, bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, body: { - phones: { - primaryPhoneNumber: '111222333', - primaryPhoneCountryCode: 'US', - primaryPhoneCallingCode: '+1', + name: { + firstName: 'John', }, }, }) @@ -456,7 +183,215 @@ describe('Restricted fields', () => { .expect((res) => { const updatedPerson = res.body.data.updatePerson; - expect(updatedPerson.phones.primaryPhoneNumber).toBe('111222333'); + expect(updatedPerson.name.firstName).toBe('John'); + expect(updatedPerson.phones).toBeUndefined(); + }); + }); + it('should block update when user tries to update non-updatable field', async () => { + // Create field permission restricting update access to phones field + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: false, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'patch', + path: `/people/${TEST_PERSON_1_ID}`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: { + phones: { + primaryPhoneNumber: '987654321', + primaryPhoneCountryCode: 'FR', + primaryPhoneCallingCode: '+33', + }, + }, + }) + .expect(400) + .expect((res) => { + expect(res.body.messages[0]).toContain( + 'Entity performing the request does not have permission', + ); + }); + }); + + it('should allow update when user has no restricted update permissions', async () => { + // Remove field permission restrictions + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: null, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'patch', + path: `/people/${TEST_PERSON_1_ID}`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: { + city: 'Updated City', + }, + }) + .expect(200) + .expect((res) => { + const updatedPerson = res.body.data.updatePerson; + + expect(updatedPerson.city).toBe('Updated City'); + }); + }); + }); + + describe('createOne', () => { + it('should block create when user has restricted update permissions on phones field', async () => { + // Create field permission restricting update access to phones field + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: false, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'post', + path: `/people`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: { + phones: { + primaryPhoneNumber: '555123456', + primaryPhoneCountryCode: 'US', + primaryPhoneCallingCode: '+1', + }, + }, + }) + .expect(400) + .expect((res) => { + expect(res.body.messages[0]).toContain( + 'Entity performing the request does not have permission', + ); + }); + }); + + it('should allow create when user has no restricted update permissions', async () => { + // Remove field permission restrictions on phones + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: null, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'post', + path: `/people`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: { + city: 'New City', + }, + }) + .expect(201) + .expect((res) => { + const createdPerson = res.body.data.createPerson; + + expect(createdPerson.city).toBe('New City'); + expect(createdPerson.emails).toBeUndefined(); // No reading rights on emails + }); + }); + }); + + describe('createMany', () => { + it('should block createMany when user has restricted update permissions on phones field', async () => { + // Create field permission restricting update access to phones field + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: false, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'post', + path: `/batch/people`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: [ + { + phones: { + primaryPhoneNumber: '555123456', + primaryPhoneCountryCode: 'US', + primaryPhoneCallingCode: '+1', + }, + }, + ], + }) + .expect(400) + .expect((res) => { + expect(res.body.messages[0]).toContain( + 'Entity performing the request does not have permission', + ); + }); + }); + + it('should allow createMany when user has no restricted update permissions', async () => { + // Remove field permission restrictions + await upsertFieldPermissions({ + roleId: memberRoleId, + fieldPermissions: [ + { + objectMetadataId: personObjectId, + fieldMetadataId: phonesFieldId, + canReadFieldValue: null, + canUpdateFieldValue: null, + }, + ], + }); + + await makeRestAPIRequest({ + method: 'post', + path: `/batch/people`, + bearer: APPLE_JONY_MEMBER_ACCESS_TOKEN, + body: [ + { + city: 'Batch City 1', + }, + { + city: 'Batch City 2', + }, + ], + }) + .expect(201) + .expect((res) => { + const createdPeople = res.body.data.createPeople; + + expect(createdPeople).toHaveLength(2); + expect(createdPeople[0].city).toBe('Batch City 1'); + expect(createdPeople[0].emails).toBeUndefined(); // No reading rights on emails + expect(createdPeople[1].city).toBe('Batch City 2'); + expect(createdPeople[1].emails).toBeUndefined(); // No reading rights on emails }); }); });