Rollout field permissions (#14024)
Rolling out field permissions feature
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
+1
-5
@@ -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,
|
||||
})}
|
||||
}
|
||||
}`;
|
||||
|
||||
-3
@@ -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 !== '')
|
||||
|
||||
-6
@@ -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 = <T extends ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
@@ -27,9 +25,6 @@ export const useCreateOneRecordInCache = <T extends ObjectRecord>({
|
||||
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 = <T extends ObjectRecord>({
|
||||
computeReferences: true,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
`;
|
||||
|
||||
|
||||
Vendored
-7
@@ -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 =
|
||||
|
||||
Vendored
-7
@@ -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({
|
||||
|
||||
+1
-4
@@ -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<object>;
|
||||
@@ -21,7 +21,6 @@ export type GetRecordFromCacheArgs = {
|
||||
string,
|
||||
ObjectPermissions & { objectMetadataId: string }
|
||||
>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
};
|
||||
export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
@@ -30,7 +29,6 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
recordId,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled = false,
|
||||
}: GetRecordFromCacheArgs) => {
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return null;
|
||||
@@ -48,7 +46,6 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
`;
|
||||
|
||||
-3
@@ -17,7 +17,6 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
recordGqlFields,
|
||||
record,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
}: {
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
@@ -28,7 +27,6 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
string,
|
||||
ObjectPermissions & { objectMetadataId: string }
|
||||
>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
}) => {
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return null;
|
||||
@@ -44,7 +42,6 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
computeReferences: true,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
`;
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)}
|
||||
}`;
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
},
|
||||
`;
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)}
|
||||
}
|
||||
|
||||
-7
@@ -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
|
||||
}
|
||||
|
||||
-7
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
-8
@@ -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 (
|
||||
<Section>
|
||||
<Table>
|
||||
<SettingsRolePermissionsObjectLevelTableHeader
|
||||
showPermissionsLabel={hasObjectPermissions}
|
||||
isFieldsPermissionsEnabled={isFieldsPermissionsEnabled}
|
||||
/>
|
||||
<StyledTableRows>
|
||||
{hasObjectPermissions ? (
|
||||
@@ -89,7 +82,6 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
||||
key={objectMetadataItem.id}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
roleId={roleId}
|
||||
isFieldsPermissionsEnabled={isFieldsPermissionsEnabled}
|
||||
/>
|
||||
),
|
||||
)
|
||||
|
||||
+4
-19
@@ -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) => (
|
||||
<TableRow
|
||||
gridAutoColumns={
|
||||
isFieldsPermissionsEnabled
|
||||
? OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS
|
||||
: OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS_WITHOUT_FIELD_PERMISSIONS
|
||||
}
|
||||
>
|
||||
<TableRow gridAutoColumns={OBJECT_LEVEL_PERMISSION_TABLE_GRID_AUTO_COLUMNS}>
|
||||
<TableHeader>{t`Object-Level`}</TableHeader>
|
||||
<TableHeader>{showPermissionsLabel ? t`Records` : ''}</TableHeader>
|
||||
{isFieldsPermissionsEnabled && (
|
||||
<>
|
||||
<TableHeader>{showPermissionsLabel ? t`See Fields` : ''}</TableHeader>
|
||||
<TableHeader>{showPermissionsLabel ? t`Edit Fields` : ''}</TableHeader>
|
||||
</>
|
||||
)}
|
||||
<TableHeader>{showPermissionsLabel ? t`See Fields` : ''}</TableHeader>
|
||||
<TableHeader>{showPermissionsLabel ? t`Edit Fields` : ''}</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
+14
-27
@@ -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}
|
||||
>
|
||||
<StyledNameTableCell>
|
||||
{!!Icon && (
|
||||
@@ -78,22 +69,18 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
|
||||
objectLabel={objectLabelPlural}
|
||||
/>
|
||||
</TableCell>
|
||||
{isFieldsPermissionsEnabled && (
|
||||
<>
|
||||
<TableCell>
|
||||
<SettingsRolePermissionsObjectLevelSeeFieldsValueForObject
|
||||
roleId={roleId}
|
||||
objectMetadataItemId={objectMetadataItem.id}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject
|
||||
roleId={roleId}
|
||||
objectMetadataItemId={objectMetadataItem.id}
|
||||
/>
|
||||
</TableCell>
|
||||
</>
|
||||
)}
|
||||
<TableCell>
|
||||
<SettingsRolePermissionsObjectLevelSeeFieldsValueForObject
|
||||
roleId={roleId}
|
||||
objectMetadataItemId={objectMetadataItem.id}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<SettingsRolePermissionsObjectLevelUpdateFieldsValueForObject
|
||||
roleId={roleId}
|
||||
objectMetadataItemId={objectMetadataItem.id}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align={'right'}>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
|
||||
+4
-12
@@ -5,11 +5,9 @@ import { SettingsRolePermissionsObjectLevelObjectFormObjectLevel } from '@/setti
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
type SettingsRolePermissionsObjectLevelObjectFormProps = {
|
||||
@@ -25,10 +23,6 @@ export const SettingsRolePermissionsObjectLevelObjectForm = ({
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const objectMetadata = useObjectMetadataItemById({
|
||||
objectId: objectMetadataId,
|
||||
});
|
||||
@@ -77,12 +71,10 @@ export const SettingsRolePermissionsObjectLevelObjectForm = ({
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
roleId={roleId}
|
||||
/>
|
||||
{isFieldsPermissionsEnabled && (
|
||||
<SettingsRolePermissionsObjectLevelObjectFieldPermissionTable
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
roleId={roleId}
|
||||
/>
|
||||
)}
|
||||
<SettingsRolePermissionsObjectLevelObjectFieldPermissionTable
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
roleId={roleId}
|
||||
/>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+20
-29
@@ -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 {
|
||||
|
||||
-9
@@ -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: {
|
||||
|
||||
-1
@@ -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',
|
||||
|
||||
-8
@@ -132,14 +132,6 @@ export class FeatureFlagService {
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
if (featureFlag === FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED) {
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-29
@@ -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<ObjectsPermissionsByRoleIdDeprecated> {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -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: [],
|
||||
|
||||
+1
-6
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
-3
@@ -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();
|
||||
|
||||
-3
@@ -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();
|
||||
|
||||
-5
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -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();
|
||||
|
||||
-5
@@ -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],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -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,
|
||||
|
||||
+237
-302
@@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user