[FE] handle restricted objects 2 (#12437)
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -8,6 +8,7 @@ import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateReco
|
||||
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
|
||||
// Mocks
|
||||
jest.mock('@apollo/client');
|
||||
@ -25,6 +26,10 @@ const mockGqlFieldToFieldMap = {
|
||||
totalCount: ['name', AggregateOperations.COUNT],
|
||||
};
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
describe('useAggregateRecords', () => {
|
||||
beforeEach(() => {
|
||||
(useObjectMetadataItem as jest.Mock).mockReturnValue({
|
||||
@ -44,14 +49,18 @@ describe('useAggregateRecords', () => {
|
||||
});
|
||||
|
||||
it('should format data correctly', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM, AggregateOperations.AVG],
|
||||
name: [AggregateOperations.COUNT],
|
||||
},
|
||||
}),
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM, AggregateOperations.AVG],
|
||||
name: [AggregateOperations.COUNT],
|
||||
},
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.data).toEqual({
|
||||
@ -74,13 +83,17 @@ describe('useAggregateRecords', () => {
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
}),
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.data).toEqual({});
|
||||
@ -95,13 +108,17 @@ describe('useAggregateRecords', () => {
|
||||
error: mockError,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
}),
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.data).toEqual({});
|
||||
@ -109,14 +126,18 @@ describe('useAggregateRecords', () => {
|
||||
});
|
||||
|
||||
it('should skip query when specified', () => {
|
||||
renderHook(() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
skip: true,
|
||||
}),
|
||||
renderHook(
|
||||
() =>
|
||||
useAggregateRecords({
|
||||
objectNameSingular: 'opportunity',
|
||||
recordGqlFieldsAggregate: {
|
||||
amount: [AggregateOperations.SUM],
|
||||
},
|
||||
skip: true,
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(useQuery).toHaveBeenCalledWith(
|
||||
|
||||
@ -57,6 +57,7 @@ describe('useDeleteManyRecords', () => {
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
recordId: expectedRecord.id,
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
});
|
||||
expect(cachedRecord).not.toBeNull();
|
||||
if (cachedRecord === null) throw new Error('Should never occur');
|
||||
@ -72,6 +73,7 @@ describe('useDeleteManyRecords', () => {
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
recordId,
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
}),
|
||||
).toBeNull(),
|
||||
);
|
||||
@ -119,6 +121,7 @@ describe('useDeleteManyRecords', () => {
|
||||
objectMetadataItem,
|
||||
record,
|
||||
}),
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@ -5,6 +5,7 @@ import { RecordGqlFieldsAggregate } from '@/object-record/graphql/types/RecordGq
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
||||
import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateRecordsQuery';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -35,10 +36,16 @@ export const useAggregateRecords = <T extends AggregateRecordsData>({
|
||||
recordGqlFieldsAggregate,
|
||||
});
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const hasReadPermission = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const { data, loading, error } = useQuery<RecordGqlOperationFindManyResult>(
|
||||
aggregateQuery,
|
||||
{
|
||||
skip: skip || !objectMetadataItem,
|
||||
skip: skip || !objectMetadataItem || !hasReadPermission,
|
||||
variables: {
|
||||
filter,
|
||||
},
|
||||
|
||||
@ -5,6 +5,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -61,7 +62,7 @@ export const useAttachRelatedRecordFromRecord = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const updateOneRecordAndAttachRelations = async ({
|
||||
recordId,
|
||||
relatedRecordId,
|
||||
@ -98,6 +99,7 @@ export const useAttachRelatedRecordFromRecord = ({
|
||||
[fieldOnRelatedObject]: previousRecord,
|
||||
},
|
||||
recordGqlFields: gqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { FieldActorForInputValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
@ -70,7 +71,7 @@ export const useCreateManyRecords = <
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
@ -118,6 +119,7 @@ export const useCreateManyRecords = <
|
||||
...baseOptimisticRecordInputCreatedBy,
|
||||
...recordToCreate,
|
||||
},
|
||||
objectPermissionsByObjectMetadataId,
|
||||
}),
|
||||
id: idForCreation as string,
|
||||
};
|
||||
@ -152,6 +154,7 @@ export const useCreateManyRecords = <
|
||||
recordsToCreate: recordNodeCreatedInCache,
|
||||
objectMetadataItems,
|
||||
shouldMatchRootQueryFilter,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
|
||||
@ -178,6 +181,7 @@ export const useCreateManyRecords = <
|
||||
objectMetadataItems,
|
||||
shouldMatchRootQueryFilter,
|
||||
checkForRecordInCache: true,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
},
|
||||
})
|
||||
|
||||
@ -6,9 +6,10 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateManyRecordsMutation = ({
|
||||
objectNameSingular,
|
||||
@ -21,6 +22,8 @@ export const useCreateManyRecordsMutation = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
@ -42,6 +45,7 @@ export const useCreateManyRecordsMutation = ({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
},
|
||||
)}
|
||||
}`;
|
||||
|
||||
@ -14,6 +14,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { computeOptimisticCreateRecordBaseRecordInput } from '@/object-record/utils/computeOptimisticCreateRecordBaseRecordInput';
|
||||
@ -62,6 +63,7 @@ export const useCreateOneRecord = <
|
||||
);
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
@ -90,6 +92,7 @@ export const useCreateOneRecord = <
|
||||
...recordInput,
|
||||
id: idForCreation,
|
||||
},
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
const recordCreatedInCache = createOneRecordInCache({
|
||||
...optimisticRecordInput,
|
||||
@ -113,6 +116,7 @@ export const useCreateOneRecord = <
|
||||
recordsToCreate: [optimisticRecordNode],
|
||||
objectMetadataItems,
|
||||
shouldMatchRootQueryFilter,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -136,6 +140,7 @@ export const useCreateOneRecord = <
|
||||
objectMetadataItems,
|
||||
shouldMatchRootQueryFilter,
|
||||
checkForRecordInCache: true,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@ import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObje
|
||||
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateOneRecordMutation = ({
|
||||
objectNameSingular,
|
||||
@ -30,6 +31,8 @@ export const useCreateOneRecordMutation = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return { createOneRecordMutation: EMPTY_MUTATION };
|
||||
}
|
||||
@ -46,6 +49,7 @@ export const useCreateOneRecordMutation = ({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
})}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -11,12 +11,13 @@ import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordF
|
||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
|
||||
type useDeleteManyRecordProps = {
|
||||
objectNameSingular: string;
|
||||
@ -52,7 +53,7 @@ export const useDeleteManyRecords = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
@ -115,6 +116,7 @@ export const useDeleteManyRecords = ({
|
||||
cache: apolloClient.cache,
|
||||
record: computedOptimisticRecord,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
computedOptimisticRecordsNode.push(optimisticRecordNode);
|
||||
@ -156,6 +158,7 @@ export const useDeleteManyRecords = ({
|
||||
cache: apolloClient.cache,
|
||||
record: { ...cachedRecord, deletedAt: null },
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
const cachedRecordWithConnection =
|
||||
|
||||
@ -9,6 +9,7 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
||||
@ -37,7 +38,7 @@ export const useDeleteOneRecord = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
@ -84,6 +85,7 @@ export const useDeleteOneRecord = ({
|
||||
cache: apolloClient.cache,
|
||||
record: computedOptimisticRecord,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
@ -133,6 +135,7 @@ export const useDeleteOneRecord = ({
|
||||
deletedAt: null,
|
||||
},
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
@ -156,6 +159,7 @@ export const useDeleteOneRecord = ({
|
||||
mutationResponseField,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
refetchAggregateQueries,
|
||||
],
|
||||
);
|
||||
|
||||
@ -8,12 +8,13 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||
import { useDestroyManyRecordsMutation } from '@/object-record/hooks/useDestroyManyRecordsMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
|
||||
type useDestroyManyRecordProps = {
|
||||
objectNameSingular: string;
|
||||
@ -47,7 +48,7 @@ export const useDestroyManyRecords = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
@ -120,6 +121,7 @@ export const useDestroyManyRecords = ({
|
||||
objectMetadataItem,
|
||||
recordsToCreate: cachedRecords,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
|
||||
@ -7,6 +7,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||
import { useDestroyOneRecordMutation } from '@/object-record/hooks/useDestroyOneRecordMutation';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getDestroyOneRecordMutationResponseField } from '@/object-record/utils/getDestroyOneRecordMutationResponseField';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@ -31,7 +32,7 @@ export const useDestroyOneRecord = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const mutationResponseField =
|
||||
getDestroyOneRecordMutationResponseField(objectNameSingular);
|
||||
|
||||
@ -73,6 +74,7 @@ export const useDestroyOneRecord = ({
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [originalRecord],
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
}
|
||||
|
||||
@ -89,6 +91,7 @@ export const useDestroyOneRecord = ({
|
||||
objectMetadataItem,
|
||||
objectNameSingular,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabled';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
@ -17,6 +18,8 @@ export const useFindDuplicateRecordsQuery = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const findDuplicateRecordsQuery = gql`
|
||||
@ -30,6 +33,7 @@ export const useFindDuplicateRecordsQuery = ({
|
||||
node ${mapObjectMetadataToGraphQLQuery({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { useFetchMoreRecordsWithPagination } from '@/object-record/hooks/useFetc
|
||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||
import { useHandleFindManyRecordsCompleted } from '@/object-record/hooks/useHandleFindManyRecordsCompleted';
|
||||
import { useHandleFindManyRecordsError } from '@/object-record/hooks/useHandleFindManyRecordsError';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { OnFindManyRecordsCompleted } from '@/object-record/types/OnFindManyRecordsCompleted';
|
||||
import { getQueryIdentifier } from '@/object-record/utils/getQueryIdentifier';
|
||||
@ -68,9 +69,15 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
or: [{ deletedAt: { is: 'NULL' } }, { deletedAt: { is: 'NOT_NULL' } }],
|
||||
};
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const hasReadPermission = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const { data, loading, error, fetchMore } =
|
||||
useQuery<RecordGqlOperationFindManyResult>(findManyRecordsQuery, {
|
||||
skip: skip || !objectMetadataItem,
|
||||
skip: skip || !objectMetadataItem || !hasReadPermission,
|
||||
variables: {
|
||||
filter: withSoftDeleted
|
||||
? {
|
||||
|
||||
@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import {
|
||||
generateFindManyRecordsQuery,
|
||||
QueryCursorDirection,
|
||||
@ -25,12 +26,15 @@ export const useFindManyRecordsQuery = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const findManyRecordsQuery = generateFindManyRecordsQuery({
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
cursorDirection,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -8,6 +8,7 @@ import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@ -38,10 +39,16 @@ export const useFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
||||
withSoftDeleted,
|
||||
});
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const hasReadPermission = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const { data, loading, error } = useQuery<{
|
||||
[nameSingular: string]: RecordGqlNode;
|
||||
}>(findOneRecordQuery, {
|
||||
skip: !objectMetadataItem || !objectRecordId || skip,
|
||||
skip: !objectMetadataItem || !objectRecordId || skip || !hasReadPermission,
|
||||
variables: { objectRecordId },
|
||||
onCompleted: (data) => {
|
||||
const recordWithoutConnection = getRecordFromRecordNode<T>({
|
||||
|
||||
@ -5,6 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
export const useFindOneRecordQuery = ({
|
||||
@ -22,6 +23,8 @@ export const useFindOneRecordQuery = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const findOneRecordQuery = gql`
|
||||
query FindOne${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
@ -44,6 +47,7 @@ export const useFindOneRecordQuery = ({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
})}
|
||||
},
|
||||
`;
|
||||
|
||||
@ -8,6 +8,7 @@ import { UseFindManyRecordsParams } from '@/object-record/hooks/useFindManyRecor
|
||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||
import { useHandleFindManyRecordsCompleted } from '@/object-record/hooks/useHandleFindManyRecordsCompleted';
|
||||
import { useHandleFindManyRecordsError } from '@/object-record/hooks/useHandleFindManyRecordsError';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { cursorFamilyState } from '@/object-record/states/cursorFamilyState';
|
||||
import { hasNextPageFamilyState } from '@/object-record/states/hasNextPageFamilyState';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
@ -55,6 +56,12 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
onCompleted,
|
||||
});
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const hasReadPermission = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const [findManyRecords, { data, loading, error, fetchMore }] =
|
||||
useLazyQuery<RecordGqlOperationFindManyResult>(findManyRecordsQuery, {
|
||||
variables: {
|
||||
@ -83,6 +90,20 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
const findManyRecordsLazy = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async () => {
|
||||
if (!hasReadPermission) {
|
||||
set(hasNextPageFamilyState(queryIdentifier), false);
|
||||
set(cursorFamilyState(queryIdentifier), '');
|
||||
|
||||
onCompleted?.([]);
|
||||
|
||||
return {
|
||||
data: null,
|
||||
loading: false,
|
||||
error: undefined,
|
||||
called: true,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await findManyRecords();
|
||||
|
||||
const hasNextPage =
|
||||
@ -98,19 +119,26 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
|
||||
return result;
|
||||
},
|
||||
[queryIdentifier, findManyRecords, objectMetadataItem],
|
||||
[
|
||||
hasReadPermission,
|
||||
findManyRecords,
|
||||
objectMetadataItem.namePlural,
|
||||
queryIdentifier,
|
||||
onCompleted,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
records,
|
||||
totalCount,
|
||||
loading,
|
||||
error,
|
||||
loading: hasReadPermission ? loading : false,
|
||||
error: hasReadPermission ? error : undefined,
|
||||
fetchMore,
|
||||
fetchMoreRecords,
|
||||
queryStateIdentifier: queryIdentifier,
|
||||
findManyRecords: findManyRecordsLazy,
|
||||
hasNextPage,
|
||||
hasReadPermission,
|
||||
};
|
||||
};
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
|
||||
type useObjectPermissionsReturnType = {
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
};
|
||||
|
||||
export const useObjectPermissions = (): useObjectPermissionsReturnType => {
|
||||
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
|
||||
const objectPermissions = currentUserWorkspace?.objectPermissions;
|
||||
|
||||
if (!isDefined(objectPermissions)) {
|
||||
return {
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
};
|
||||
}
|
||||
|
||||
const objectPermissionsByObjectMetadataId = objectPermissions?.reduce(
|
||||
(acc: Record<string, ObjectPermission>, objectPermission) => {
|
||||
acc[objectPermission.objectMetadataId] = objectPermission;
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
objectPermissionsByObjectMetadataId,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { getObjectPermissionsForObject } from '~/modules/object-metadata/utils/getObjectPermissionsForObject';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useObjectPermissions } from './useObjectPermissions';
|
||||
|
||||
export const useObjectPermissionsForObject = (objectMetadataId: string) => {
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
return useMemo(() => {
|
||||
return getObjectPermissionsForObject(
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId,
|
||||
);
|
||||
}, [objectPermissionsByObjectMetadataId, objectMetadataId]);
|
||||
};
|
||||
@ -9,12 +9,13 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRestoreManyRecordsMutation } from '@/object-record/hooks/useRestoreManyRecordsMutation';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getRestoreManyRecordsMutationResponseField } from '@/object-record/utils/getRestoreManyRecordsMutationResponseField';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
|
||||
type useRestoreManyRecordProps = {
|
||||
objectNameSingular: string;
|
||||
@ -50,7 +51,7 @@ export const useRestoreManyRecords = ({
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const mutationResponseField = getRestoreManyRecordsMutationResponseField(
|
||||
objectMetadataItem.namePlural,
|
||||
);
|
||||
@ -111,6 +112,7 @@ export const useRestoreManyRecords = ({
|
||||
cache: apolloClient.cache,
|
||||
record: computedOptimisticRecord,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache: apolloClient.cache,
|
||||
@ -169,6 +171,7 @@ export const useRestoreManyRecords = ({
|
||||
cache: apolloClient.cache,
|
||||
record: cachedRecord,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
|
||||
@ -10,6 +10,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
@ -57,6 +58,7 @@ export const useUpdateOneRecord = <
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||
@ -75,6 +77,7 @@ export const useUpdateOneRecord = <
|
||||
recordInput: updateOneRecordInput,
|
||||
cache: apolloClient.cache,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
const cachedRecord = getRecordFromCache<ObjectRecord>(idToUpdate);
|
||||
const cachedRecordWithConnection = getRecordNodeFromRecord<ObjectRecord>({
|
||||
@ -118,6 +121,7 @@ export const useUpdateOneRecord = <
|
||||
cache: apolloClient.cache,
|
||||
record: computedOptimisticRecord,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
@ -190,6 +194,7 @@ export const useUpdateOneRecord = <
|
||||
),
|
||||
},
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
|
||||
@ -7,6 +7,7 @@ import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObje
|
||||
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
@ -26,6 +27,8 @@ export const useUpdateOneRecordMutation = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return { updateOneRecordMutation: EMPTY_MUTATION };
|
||||
}
|
||||
@ -44,14 +47,15 @@ export const useUpdateOneRecordMutation = ({
|
||||
|
||||
const updateOneRecordMutation = gql`
|
||||
mutation UpdateOne${capitalizedObjectName}($idToUpdate: UUID!, $input: ${capitalizedObjectName}UpdateInput!) {
|
||||
${mutationResponseField}(id: $idToUpdate, data: $input) ${mapObjectMetadataToGraphQLQuery(
|
||||
{
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
computeReferences,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
},
|
||||
)}
|
||||
${mutationResponseField}(id: $idToUpdate, data: $input) ${mapObjectMetadataToGraphQLQuery(
|
||||
{
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
computeReferences,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
},
|
||||
)}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user