mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 03:51:36 +03:00
Refetch aggregate queries on record creation/update/deletion of record (#8885)
Closes #8755. Refetching the aggregate queries on an object following creation, update, deletion of a record.
This commit is contained in:
parent
9ed9b4746a
commit
26ff344f56
@ -1,3 +1,3 @@
|
|||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
|
|
||||||
export type RecordGqlFieldsAggregate = Record<string, AGGREGATE_OPERATIONS>;
|
export type RecordGqlFieldsAggregate = Record<string, AGGREGATE_OPERATIONS[]>;
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const AGGREGATE_QUERY = gql`
|
||||||
|
query AggregateOpportunities($filter: OpportunityFilterInput) {
|
||||||
|
opportunities(filter: $filter) {
|
||||||
|
totalCount
|
||||||
|
sumAmount
|
||||||
|
avgAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const mockResponse = {
|
||||||
|
opportunities: {
|
||||||
|
totalCount: 42,
|
||||||
|
sumAmount: 1000000,
|
||||||
|
avgAmount: 23800
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,129 @@
|
|||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import {
|
||||||
|
AGGREGATE_QUERY,
|
||||||
|
mockResponse,
|
||||||
|
} from '@/object-record/hooks/__mocks__/useAggregateRecords';
|
||||||
|
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||||
|
import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateRecordsQuery';
|
||||||
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
|
// Mocks
|
||||||
|
jest.mock('@apollo/client');
|
||||||
|
jest.mock('@/object-metadata/hooks/useObjectMetadataItem');
|
||||||
|
jest.mock('@/object-record/hooks/useAggregateRecordsQuery');
|
||||||
|
|
||||||
|
const mockObjectMetadataItem = {
|
||||||
|
nameSingular: 'opportunity',
|
||||||
|
namePlural: 'opportunities',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockGqlFieldToFieldMap = {
|
||||||
|
sumAmount: ['amount', AGGREGATE_OPERATIONS.sum],
|
||||||
|
avgAmount: ['amount', AGGREGATE_OPERATIONS.avg],
|
||||||
|
totalCount: ['name', AGGREGATE_OPERATIONS.count],
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useAggregateRecords', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(useObjectMetadataItem as jest.Mock).mockReturnValue({
|
||||||
|
objectMetadataItem: mockObjectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
(useAggregateRecordsQuery as jest.Mock).mockReturnValue({
|
||||||
|
aggregateQuery: AGGREGATE_QUERY,
|
||||||
|
gqlFieldToFieldMap: mockGqlFieldToFieldMap,
|
||||||
|
});
|
||||||
|
|
||||||
|
(useQuery as jest.Mock).mockReturnValue({
|
||||||
|
data: mockResponse,
|
||||||
|
loading: false,
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format data correctly', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecords({
|
||||||
|
objectNameSingular: 'opportunity',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum, AGGREGATE_OPERATIONS.avg],
|
||||||
|
name: [AGGREGATE_OPERATIONS.count],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.data).toEqual({
|
||||||
|
amount: {
|
||||||
|
[AGGREGATE_OPERATIONS.sum]: 1000000,
|
||||||
|
[AGGREGATE_OPERATIONS.avg]: 23800,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
[AGGREGATE_OPERATIONS.count]: 42,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.current.loading).toBe(false);
|
||||||
|
expect(result.current.error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle loading state', () => {
|
||||||
|
(useQuery as jest.Mock).mockReturnValue({
|
||||||
|
data: undefined,
|
||||||
|
loading: true,
|
||||||
|
error: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecords({
|
||||||
|
objectNameSingular: 'opportunity',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.data).toEqual({});
|
||||||
|
expect(result.current.loading).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle error state', () => {
|
||||||
|
const mockError = new Error('Query failed');
|
||||||
|
(useQuery as jest.Mock).mockReturnValue({
|
||||||
|
data: undefined,
|
||||||
|
loading: false,
|
||||||
|
error: mockError,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecords({
|
||||||
|
objectNameSingular: 'opportunity',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.data).toEqual({});
|
||||||
|
expect(result.current.error).toBe(mockError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip query when specified', () => {
|
||||||
|
renderHook(() =>
|
||||||
|
useAggregateRecords({
|
||||||
|
objectNameSingular: 'opportunity',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
},
|
||||||
|
skip: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(useQuery).toHaveBeenCalledWith(
|
||||||
|
AGGREGATE_QUERY,
|
||||||
|
expect.objectContaining({
|
||||||
|
skip: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,144 @@
|
|||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateRecordsQuery';
|
||||||
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
|
import { generateAggregateQuery } from '@/object-record/utils/generateAggregateQuery';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
|
|
||||||
|
jest.mock('@/object-metadata/hooks/useObjectMetadataItem');
|
||||||
|
jest.mock('@/object-record/utils/generateAggregateQuery');
|
||||||
|
|
||||||
|
const mockObjectMetadataItem: ObjectMetadataItem = {
|
||||||
|
nameSingular: 'company',
|
||||||
|
namePlural: 'companies',
|
||||||
|
id: 'test-id',
|
||||||
|
labelSingular: 'Company',
|
||||||
|
labelPlural: 'Companies',
|
||||||
|
isCustom: false,
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: 'field-1',
|
||||||
|
name: 'amount',
|
||||||
|
label: 'Amount',
|
||||||
|
type: FieldMetadataType.Number,
|
||||||
|
isCustom: false,
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
} as FieldMetadataItem,
|
||||||
|
{
|
||||||
|
id: 'field-2',
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
type: FieldMetadataType.Text,
|
||||||
|
isCustom: false,
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
} as FieldMetadataItem,
|
||||||
|
],
|
||||||
|
indexMetadatas: [],
|
||||||
|
isLabelSyncedWithName: true,
|
||||||
|
isRemote: false,
|
||||||
|
isSystem: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useAggregateRecordsQuery', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
(useObjectMetadataItem as jest.Mock).mockReturnValue({
|
||||||
|
objectMetadataItem: mockObjectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
|
(generateAggregateQuery as jest.Mock).mockReturnValue({
|
||||||
|
loc: {
|
||||||
|
source: {
|
||||||
|
body: 'query AggregateCompanies($filter: CompanyFilterInput) { companies(filter: $filter) { totalCount } }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle simple count operation', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecordsQuery({
|
||||||
|
objectNameSingular: 'company',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
name: [AGGREGATE_OPERATIONS.count],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.gqlFieldToFieldMap).toEqual({
|
||||||
|
totalCount: ['name', 'COUNT'],
|
||||||
|
});
|
||||||
|
expect(generateAggregateQuery).toHaveBeenCalledWith({
|
||||||
|
objectMetadataItem: mockObjectMetadataItem,
|
||||||
|
recordGqlFields: {
|
||||||
|
totalCount: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle field aggregation', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecordsQuery({
|
||||||
|
objectNameSingular: 'company',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.gqlFieldToFieldMap).toEqual({
|
||||||
|
sumAmount: ['amount', 'SUM'],
|
||||||
|
});
|
||||||
|
expect(generateAggregateQuery).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
recordGqlFields: expect.objectContaining({
|
||||||
|
sumAmount: true,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for invalid aggregation operation', () => {
|
||||||
|
expect(() =>
|
||||||
|
renderHook(() =>
|
||||||
|
useAggregateRecordsQuery({
|
||||||
|
objectNameSingular: 'company',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
name: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple aggregations', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAggregateRecordsQuery({
|
||||||
|
objectNameSingular: 'company',
|
||||||
|
recordGqlFieldsAggregate: {
|
||||||
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
|
name: [AGGREGATE_OPERATIONS.count],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.current.gqlFieldToFieldMap).toHaveProperty('sumAmount');
|
||||||
|
expect(generateAggregateQuery).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
recordGqlFields: expect.objectContaining({
|
||||||
|
totalCount: true,
|
||||||
|
sumAmount: true,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -9,12 +9,19 @@ import {
|
|||||||
variables,
|
variables,
|
||||||
} from '@/object-record/hooks/__mocks__/useCreateManyRecords';
|
} from '@/object-record/hooks/__mocks__/useCreateManyRecords';
|
||||||
import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords';
|
import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
jest.mock('uuid', () => ({
|
jest.mock('uuid', () => ({
|
||||||
v4: jest.fn(),
|
v4: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useRefetchAggregateQueries');
|
||||||
|
const mockRefetchAggregateQueries = jest.fn();
|
||||||
|
(useRefetchAggregateQueries as jest.Mock).mockReturnValue({
|
||||||
|
refetchAggregateQueries: mockRefetchAggregateQueries,
|
||||||
|
});
|
||||||
|
|
||||||
mocked(v4)
|
mocked(v4)
|
||||||
.mockReturnValueOnce(variables.data[0].id)
|
.mockReturnValueOnce(variables.data[0].id)
|
||||||
.mockReturnValueOnce(variables.data[1].id);
|
.mockReturnValueOnce(variables.data[1].id);
|
||||||
@ -40,6 +47,9 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('useCreateManyRecords', () => {
|
describe('useCreateManyRecords', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('works as expected', async () => {
|
it('works as expected', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
@ -57,5 +67,6 @@ describe('useCreateManyRecords', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
expect(mockRefetchAggregateQueries).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
responseData,
|
responseData,
|
||||||
} from '@/object-record/hooks/__mocks__/useCreateOneRecord';
|
} from '@/object-record/hooks/__mocks__/useCreateOneRecord';
|
||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
const personId = 'a7286b9a-c039-4a89-9567-2dfa7953cda9';
|
const personId = 'a7286b9a-c039-4a89-9567-2dfa7953cda9';
|
||||||
@ -15,6 +16,12 @@ jest.mock('uuid', () => ({
|
|||||||
v4: jest.fn(() => personId),
|
v4: jest.fn(() => personId),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useRefetchAggregateQueries');
|
||||||
|
const mockRefetchAggregateQueries = jest.fn();
|
||||||
|
(useRefetchAggregateQueries as jest.Mock).mockReturnValue({
|
||||||
|
refetchAggregateQueries: mockRefetchAggregateQueries,
|
||||||
|
});
|
||||||
|
|
||||||
const mocks = [
|
const mocks = [
|
||||||
{
|
{
|
||||||
request: {
|
request: {
|
||||||
@ -34,6 +41,9 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('useCreateOneRecord', () => {
|
describe('useCreateOneRecord', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('works as expected', async () => {
|
it('works as expected', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() =>
|
() =>
|
||||||
@ -52,5 +62,6 @@ describe('useCreateOneRecord', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
expect(mockRefetchAggregateQueries).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
variables,
|
variables,
|
||||||
} from '@/object-record/hooks/__mocks__/useDeleteManyRecords';
|
} from '@/object-record/hooks/__mocks__/useDeleteManyRecords';
|
||||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { act } from 'react';
|
import { act } from 'react';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
@ -28,11 +29,20 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useRefetchAggregateQueries');
|
||||||
|
const mockRefetchAggregateQueries = jest.fn();
|
||||||
|
(useRefetchAggregateQueries as jest.Mock).mockReturnValue({
|
||||||
|
refetchAggregateQueries: mockRefetchAggregateQueries,
|
||||||
|
});
|
||||||
|
|
||||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
apolloMocks: mocks,
|
apolloMocks: mocks,
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useDeleteManyRecords', () => {
|
describe('useDeleteManyRecords', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('works as expected', async () => {
|
it('works as expected', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useDeleteManyRecords({ objectNameSingular: 'person' }),
|
() => useDeleteManyRecords({ objectNameSingular: 'person' }),
|
||||||
@ -48,5 +58,6 @@ describe('useDeleteManyRecords', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
expect(mockRefetchAggregateQueries).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
variables,
|
variables,
|
||||||
} from '@/object-record/hooks/__mocks__/useDeleteOneRecord';
|
} from '@/object-record/hooks/__mocks__/useDeleteOneRecord';
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
const personId = 'a7286b9a-c039-4a89-9567-2dfa7953cda9';
|
const personId = 'a7286b9a-c039-4a89-9567-2dfa7953cda9';
|
||||||
@ -25,11 +26,20 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useRefetchAggregateQueries');
|
||||||
|
const mockRefetchAggregateQueries = jest.fn();
|
||||||
|
(useRefetchAggregateQueries as jest.Mock).mockReturnValue({
|
||||||
|
refetchAggregateQueries: mockRefetchAggregateQueries,
|
||||||
|
});
|
||||||
|
|
||||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
apolloMocks: mocks,
|
apolloMocks: mocks,
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useDeleteOneRecord', () => {
|
describe('useDeleteOneRecord', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('works as expected', async () => {
|
it('works as expected', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useDeleteOneRecord({ objectNameSingular: 'person' }),
|
() => useDeleteOneRecord({ objectNameSingular: 'person' }),
|
||||||
@ -45,5 +55,6 @@ describe('useDeleteOneRecord', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
expect(mockRefetchAggregateQueries).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
|
import { getAggregateQueryName } from '@/object-record/utils/getAggregateQueryName';
|
||||||
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
|
jest.mock('@apollo/client', () => ({
|
||||||
|
useApolloClient: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@/workspace/hooks/useIsFeatureEnabled', () => ({
|
||||||
|
useIsFeatureEnabled: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useRefetchAggregateQueries', () => {
|
||||||
|
const mockRefetchQueries = jest.fn();
|
||||||
|
const mockApolloClient = {
|
||||||
|
refetchQueries: mockRefetchQueries,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
(useApolloClient as jest.Mock).mockReturnValue(mockApolloClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should refetch queries when feature flag is enabled', async () => {
|
||||||
|
// Arrange
|
||||||
|
(useIsFeatureEnabled as jest.Mock).mockReturnValue(true);
|
||||||
|
const objectMetadataNamePlural = 'opportunities';
|
||||||
|
const expectedQueryName = getAggregateQueryName(objectMetadataNamePlural);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useRefetchAggregateQueries({ objectMetadataNamePlural }),
|
||||||
|
);
|
||||||
|
await result.current.refetchAggregateQueries();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mockRefetchQueries).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockRefetchQueries).toHaveBeenCalledWith({
|
||||||
|
include: [expectedQueryName],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not refetch queries when feature flag is disabled', async () => {
|
||||||
|
// Arrange
|
||||||
|
(useIsFeatureEnabled as jest.Mock).mockReturnValue(false);
|
||||||
|
const objectMetadataNamePlural = 'opportunities';
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useRefetchAggregateQueries({ objectMetadataNamePlural }),
|
||||||
|
);
|
||||||
|
await result.current.refetchAggregateQueries();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mockRefetchQueries).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle errors during refetch', async () => {
|
||||||
|
// Arrange
|
||||||
|
(useIsFeatureEnabled as jest.Mock).mockReturnValue(true);
|
||||||
|
const error = new Error('Refetch failed');
|
||||||
|
mockRefetchQueries.mockRejectedValue(error);
|
||||||
|
const objectMetadataNamePlural = 'opportunities';
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useRefetchAggregateQueries({ objectMetadataNamePlural }),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
await expect(result.current.refetchAggregateQueries()).rejects.toThrow(
|
||||||
|
'Refetch failed',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -5,7 +5,9 @@ import {
|
|||||||
responseData,
|
responseData,
|
||||||
variables,
|
variables,
|
||||||
} from '@/object-record/hooks/__mocks__/useUpdateOneRecord';
|
} from '@/object-record/hooks/__mocks__/useUpdateOneRecord';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
|
import { expect } from '@storybook/test';
|
||||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||||
|
|
||||||
const person = { id: '36abbb63-34ed-4a16-89f5-f549ac55d0f9' };
|
const person = { id: '36abbb63-34ed-4a16-89f5-f549ac55d0f9' };
|
||||||
@ -35,6 +37,12 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
jest.mock('@/object-record/hooks/useRefetchAggregateQueries');
|
||||||
|
const mockRefetchAggregateQueries = jest.fn();
|
||||||
|
(useRefetchAggregateQueries as jest.Mock).mockReturnValue({
|
||||||
|
refetchAggregateQueries: mockRefetchAggregateQueries,
|
||||||
|
});
|
||||||
|
|
||||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
apolloMocks: mocks,
|
apolloMocks: mocks,
|
||||||
});
|
});
|
||||||
@ -42,6 +50,9 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
|||||||
const idToUpdate = '36abbb63-34ed-4a16-89f5-f549ac55d0f9';
|
const idToUpdate = '36abbb63-34ed-4a16-89f5-f549ac55d0f9';
|
||||||
|
|
||||||
describe('useUpdateOneRecord', () => {
|
describe('useUpdateOneRecord', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('works as expected', async () => {
|
it('works as expected', async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useUpdateOneRecord({ objectNameSingular: 'person' }),
|
() => useUpdateOneRecord({ objectNameSingular: 'person' }),
|
||||||
@ -61,5 +72,6 @@ describe('useUpdateOneRecord', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(mocks[0].result).toHaveBeenCalled();
|
expect(mocks[0].result).toHaveBeenCalled();
|
||||||
|
expect(mockRefetchAggregateQueries).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,18 +4,18 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
|||||||
import { RecordGqlFieldsAggregate } from '@/object-record/graphql/types/RecordGqlFieldsAggregate';
|
import { RecordGqlFieldsAggregate } from '@/object-record/graphql/types/RecordGqlFieldsAggregate';
|
||||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||||
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
||||||
import { useAggregateManyRecordsQuery } from '@/object-record/hooks/useAggregateManyRecordsQuery';
|
import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateRecordsQuery';
|
||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
import isEmpty from 'lodash.isempty';
|
import isEmpty from 'lodash.isempty';
|
||||||
import { isDefined } from 'twenty-ui';
|
import { isDefined } from 'twenty-ui';
|
||||||
|
|
||||||
export type AggregateManyRecordsData = {
|
export type AggregateRecordsData = {
|
||||||
[fieldName: string]: {
|
[fieldName: string]: {
|
||||||
[operation in AGGREGATE_OPERATIONS]?: string | number | undefined;
|
[operation in AGGREGATE_OPERATIONS]?: string | number | undefined;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAggregateManyRecords = ({
|
export const useAggregateRecords = ({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter,
|
filter,
|
||||||
recordGqlFieldsAggregate,
|
recordGqlFieldsAggregate,
|
||||||
@ -30,7 +30,7 @@ export const useAggregateManyRecords = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { aggregateQuery, gqlFieldToFieldMap } = useAggregateManyRecordsQuery({
|
const { aggregateQuery, gqlFieldToFieldMap } = useAggregateRecordsQuery({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
recordGqlFieldsAggregate,
|
recordGqlFieldsAggregate,
|
||||||
});
|
});
|
||||||
@ -45,7 +45,7 @@ export const useAggregateManyRecords = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const formattedData: AggregateManyRecordsData = {};
|
const formattedData: AggregateRecordsData = {};
|
||||||
|
|
||||||
if (!isEmpty(data)) {
|
if (!isEmpty(data)) {
|
||||||
Object.entries(data?.[objectMetadataItem.namePlural] ?? {})?.forEach(
|
Object.entries(data?.[objectMetadataItem.namePlural] ?? {})?.forEach(
|
@ -14,7 +14,7 @@ export type GqlFieldToFieldMap = {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAggregateManyRecordsQuery = ({
|
export const useAggregateRecordsQuery = ({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
recordGqlFieldsAggregate = {},
|
recordGqlFieldsAggregate = {},
|
||||||
}: {
|
}: {
|
||||||
@ -34,26 +34,20 @@ export const useAggregateManyRecordsQuery = ({
|
|||||||
const gqlFieldToFieldMap: GqlFieldToFieldMap = {};
|
const gqlFieldToFieldMap: GqlFieldToFieldMap = {};
|
||||||
|
|
||||||
Object.entries(recordGqlFieldsAggregate).forEach(
|
Object.entries(recordGqlFieldsAggregate).forEach(
|
||||||
([fieldName, aggregateOperation]) => {
|
([fieldName, aggregateOperations]) => {
|
||||||
if (
|
aggregateOperations.forEach((aggregateOperation) => {
|
||||||
!isDefined(fieldName) &&
|
const fieldToQuery =
|
||||||
aggregateOperation === AGGREGATE_OPERATIONS.count
|
availableAggregations[fieldName]?.[aggregateOperation];
|
||||||
) {
|
|
||||||
recordGqlFields.totalCount = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldToQuery =
|
if (!isDefined(fieldToQuery)) {
|
||||||
availableAggregations[fieldName]?.[aggregateOperation];
|
throw new Error(
|
||||||
|
`Cannot query operation ${aggregateOperation} on field ${fieldName}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
gqlFieldToFieldMap[fieldToQuery] = [fieldName, aggregateOperation];
|
||||||
|
|
||||||
if (!isDefined(fieldToQuery)) {
|
recordGqlFields[fieldToQuery] = true;
|
||||||
throw new Error(
|
});
|
||||||
`Cannot query operation ${aggregateOperation} on field ${fieldName}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
gqlFieldToFieldMap[fieldToQuery] = [fieldName, aggregateOperation];
|
|
||||||
|
|
||||||
recordGqlFields[fieldToQuery] = true;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -11,6 +11,7 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename
|
|||||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
|
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
||||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||||
@ -51,6 +52,10 @@ export const useCreateManyRecords = <
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const createManyRecords = async (
|
const createManyRecords = async (
|
||||||
recordsToCreate: Partial<CreatedObjectRecord>[],
|
recordsToCreate: Partial<CreatedObjectRecord>[],
|
||||||
upsert?: boolean,
|
upsert?: boolean,
|
||||||
@ -141,6 +146,7 @@ export const useCreateManyRecords = <
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refetchAggregateQueries();
|
||||||
return createdObjects.data?.[mutationResponseField] ?? [];
|
return createdObjects.data?.[mutationResponseField] ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename
|
|||||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
||||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||||
@ -55,6 +56,10 @@ export const useCreateOneRecord = <
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const createOneRecord = async (input: Partial<CreatedObjectRecord>) => {
|
const createOneRecord = async (input: Partial<CreatedObjectRecord>) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
@ -131,6 +136,7 @@ export const useCreateOneRecord = <
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refetchAggregateQueries();
|
||||||
return createdObject.data?.[mutationResponseField] ?? null;
|
return createdObject.data?.[mutationResponseField] ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
|||||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||||
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
import { useDeleteManyRecordsMutation } from '@/object-record/hooks/useDeleteManyRecordsMutation';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/utils/getDeleteManyRecordsMutationResponseField';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
@ -51,6 +52,10 @@ export const useDeleteManyRecords = ({
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const mutationResponseField = getDeleteManyRecordsMutationResponseField(
|
const mutationResponseField = getDeleteManyRecordsMutationResponseField(
|
||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
);
|
);
|
||||||
@ -194,7 +199,7 @@ export const useDeleteManyRecords = ({
|
|||||||
await sleep(options.delayInMsBetweenRequests);
|
await sleep(options.delayInMsBetweenRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await refetchAggregateQueries();
|
||||||
return deletedRecords;
|
return deletedRecords;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
|||||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/getDeleteOneRecordMutationResponseField';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
@ -35,6 +36,10 @@ export const useDeleteOneRecord = ({
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const mutationResponseField =
|
const mutationResponseField =
|
||||||
getDeleteOneRecordMutationResponseField(objectNameSingular);
|
getDeleteOneRecordMutationResponseField(objectNameSingular);
|
||||||
|
|
||||||
@ -126,6 +131,7 @@ export const useDeleteOneRecord = ({
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refetchAggregateQueries();
|
||||||
return deletedRecord.data?.[mutationResponseField] ?? null;
|
return deletedRecord.data?.[mutationResponseField] ?? null;
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -135,6 +141,7 @@ export const useDeleteOneRecord = ({
|
|||||||
mutationResponseField,
|
mutationResponseField,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectMetadataItems,
|
objectMetadataItems,
|
||||||
|
refetchAggregateQueries,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
|||||||
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
|
||||||
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
|
||||||
import { useDestroyManyRecordsMutation } from '@/object-record/hooks/useDestroyManyRecordsMutation';
|
import { useDestroyManyRecordsMutation } from '@/object-record/hooks/useDestroyManyRecordsMutation';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
import { getDestroyManyRecordsMutationResponseField } from '@/object-record/utils/getDestroyManyRecordsMutationResponseField';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
@ -48,6 +49,10 @@ export const useDestroyManyRecords = ({
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const mutationResponseField = getDestroyManyRecordsMutationResponseField(
|
const mutationResponseField = getDestroyManyRecordsMutationResponseField(
|
||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
);
|
);
|
||||||
@ -127,6 +132,7 @@ export const useDestroyManyRecords = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await refetchAggregateQueries();
|
||||||
return destroyedRecords;
|
return destroyedRecords;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { getAggregateQueryName } from '@/object-record/utils/getAggregateQueryName';
|
||||||
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
|
||||||
|
export const useRefetchAggregateQueries = ({
|
||||||
|
objectMetadataNamePlural,
|
||||||
|
}: {
|
||||||
|
objectMetadataNamePlural: string;
|
||||||
|
}) => {
|
||||||
|
const apolloClient = useApolloClient();
|
||||||
|
const isAggregateQueryEnabled = useIsFeatureEnabled(
|
||||||
|
'IS_AGGREGATE_QUERY_ENABLED',
|
||||||
|
);
|
||||||
|
const refetchAggregateQueries = async () => {
|
||||||
|
if (isAggregateQueryEnabled) {
|
||||||
|
const queryName = getAggregateQueryName(objectMetadataNamePlural);
|
||||||
|
|
||||||
|
await apolloClient.refetchQueries({
|
||||||
|
include: [queryName],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
refetchAggregateQueries,
|
||||||
|
};
|
||||||
|
};
|
@ -7,6 +7,7 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF
|
|||||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||||
|
import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries';
|
||||||
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||||
@ -45,6 +46,10 @@ export const useUpdateOneRecord = <
|
|||||||
|
|
||||||
const { objectMetadataItems } = useObjectMetadataItems();
|
const { objectMetadataItems } = useObjectMetadataItems();
|
||||||
|
|
||||||
|
const { refetchAggregateQueries } = useRefetchAggregateQueries({
|
||||||
|
objectMetadataNamePlural: objectMetadataItem.namePlural,
|
||||||
|
});
|
||||||
|
|
||||||
const updateOneRecord = async ({
|
const updateOneRecord = async ({
|
||||||
idToUpdate,
|
idToUpdate,
|
||||||
updateOneRecordInput,
|
updateOneRecordInput,
|
||||||
@ -152,6 +157,7 @@ export const useUpdateOneRecord = <
|
|||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refetchAggregateQueries();
|
||||||
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
return updatedRecord?.data?.[mutationResponseField] ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { RecordBoardContext } from '@/object-record/record-board/contexts/Record
|
|||||||
import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu';
|
import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu';
|
||||||
import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown';
|
import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown';
|
||||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||||
import { useAggregateManyRecordsForRecordBoardColumn } from '@/object-record/record-board/record-board-column/hooks/useAggregateManyRecordsForRecordBoardColumn';
|
import { useAggregateRecordsForRecordBoardColumn } from '@/object-record/record-board/record-board-column/hooks/useAggregateRecordsForRecordBoardColumn';
|
||||||
import { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions';
|
import { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions';
|
||||||
import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled';
|
import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled';
|
||||||
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
|
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
|
||||||
@ -96,7 +96,7 @@ export const RecordBoardColumnHeader = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { aggregateValue, aggregateLabel } =
|
const { aggregateValue, aggregateLabel } =
|
||||||
useAggregateManyRecordsForRecordBoardColumn();
|
useAggregateRecordsForRecordBoardColumn();
|
||||||
|
|
||||||
const { handleNewButtonClick } = useColumnNewCardActions(
|
const { handleNewButtonClick } = useColumnNewCardActions(
|
||||||
columnDefinition.id ?? '',
|
columnDefinition.id ?? '',
|
||||||
|
@ -11,7 +11,7 @@ import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types
|
|||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
|
|
||||||
type RecordBoardColumnHeaderAggregateDropdownProps = {
|
type RecordBoardColumnHeaderAggregateDropdownProps = {
|
||||||
aggregateValue: string | number;
|
aggregateValue?: string | number;
|
||||||
aggregateLabel?: string;
|
aggregateLabel?: string;
|
||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
dropdownId: string;
|
dropdownId: string;
|
||||||
|
@ -13,13 +13,16 @@ export const RecordBoardColumnHeaderAggregateDropdownButton = ({
|
|||||||
tooltip,
|
tooltip,
|
||||||
}: {
|
}: {
|
||||||
dropdownId: string;
|
dropdownId: string;
|
||||||
value: string | number;
|
value?: string | number;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div id={dropdownId}>
|
<div id={dropdownId}>
|
||||||
<StyledButton>
|
<StyledButton>
|
||||||
<Tag text={formatNumber(Number(value))} color={'transparent'} />
|
<Tag
|
||||||
|
text={value ? formatNumber(Number(value)) : '-'}
|
||||||
|
color={'transparent'}
|
||||||
|
/>
|
||||||
<AppTooltip
|
<AppTooltip
|
||||||
anchorSelect={`#${dropdownId}`}
|
anchorSelect={`#${dropdownId}`}
|
||||||
content={tooltip}
|
content={tooltip}
|
||||||
|
@ -34,14 +34,14 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
|
|||||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetContent}>
|
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetContent}>
|
||||||
{getAggregateOperationLabel(aggregateOperation)}
|
{getAggregateOperationLabel(aggregateOperation)}
|
||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
{availableFieldsIdsForAggregateOperation.map((fieldId) => {
|
<DropdownMenuItemsContainer>
|
||||||
const fieldMetadata = objectMetadataItem.fields.find(
|
{availableFieldsIdsForAggregateOperation.map((fieldId) => {
|
||||||
(field) => field.id === fieldId,
|
const fieldMetadata = objectMetadataItem.fields.find(
|
||||||
);
|
(field) => field.id === fieldId,
|
||||||
|
);
|
||||||
|
|
||||||
if (!fieldMetadata) return null;
|
if (!fieldMetadata) return null;
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItemsContainer>
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={fieldId}
|
key={fieldId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -54,9 +54,9 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
|
|||||||
LeftIcon={getIcon(fieldMetadata.icon) ?? Icon123}
|
LeftIcon={getIcon(fieldMetadata.icon) ?? Icon123}
|
||||||
text={fieldMetadata.label}
|
text={fieldMetadata.label}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuItemsContainer>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</DropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -66,19 +66,16 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
|||||||
}}
|
}}
|
||||||
text={getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}
|
text={getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuItemsContainer>
|
{Object.entries(availableAggregations).map(
|
||||||
{Object.entries(availableAggregations).map(
|
([
|
||||||
([
|
availableAggregationOperation,
|
||||||
availableAggregationOperation,
|
availableAggregationFieldsIdsForOperation,
|
||||||
availableAggregationFieldsIdsForOperation,
|
]) =>
|
||||||
]) =>
|
isEmpty(availableAggregationFieldsIdsForOperation) ? (
|
||||||
isEmpty(availableAggregationFieldsIdsForOperation) ? (
|
<></>
|
||||||
<></>
|
) : (
|
||||||
) : (
|
|
||||||
<DropdownMenuItemsContainer
|
|
||||||
key={`aggregate-dropdown-menu-content-${availableAggregationOperation}`}
|
|
||||||
>
|
|
||||||
<RecordBoardColumnHeaderAggregateDropdownMenuItem
|
<RecordBoardColumnHeaderAggregateDropdownMenuItem
|
||||||
|
key={`aggregate-dropdown-menu-content-${availableAggregationOperation}`}
|
||||||
onContentChange={() => {
|
onContentChange={() => {
|
||||||
setAggregateOperation(
|
setAggregateOperation(
|
||||||
availableAggregationOperation as AGGREGATE_OPERATIONS,
|
availableAggregationOperation as AGGREGATE_OPERATIONS,
|
||||||
@ -93,9 +90,9 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
|||||||
)}
|
)}
|
||||||
hasSubMenu
|
hasSubMenu
|
||||||
/>
|
/>
|
||||||
</DropdownMenuItemsContainer>
|
),
|
||||||
),
|
)}
|
||||||
)}
|
</DropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useAggregateManyRecords } from '@/object-record/hooks/useAggregateManyRecords';
|
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||||
import { buildRecordGqlFieldsAggregate } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregate';
|
import { buildRecordGqlFieldsAggregate } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregate';
|
||||||
@ -13,7 +13,7 @@ import { useContext } from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const useAggregateManyRecordsForRecordBoardColumn = () => {
|
export const useAggregateRecordsForRecordBoardColumn = () => {
|
||||||
const isAggregateQueryEnabled = useIsFeatureEnabled(
|
const isAggregateQueryEnabled = useIsFeatureEnabled(
|
||||||
'IS_AGGREGATE_QUERY_ENABLED',
|
'IS_AGGREGATE_QUERY_ENABLED',
|
||||||
);
|
);
|
||||||
@ -67,7 +67,7 @@ export const useAggregateManyRecordsForRecordBoardColumn = () => {
|
|||||||
: { eq: columnDefinition.value },
|
: { eq: columnDefinition.value },
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = useAggregateManyRecords({
|
const { data } = useAggregateRecords({
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
recordGqlFieldsAggregate,
|
recordGqlFieldsAggregate,
|
||||||
filter,
|
filter,
|
||||||
@ -82,7 +82,7 @@ export const useAggregateManyRecordsForRecordBoardColumn = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
aggregateValue: value ?? recordCount,
|
aggregateValue: isAggregateQueryEnabled ? value : recordCount,
|
||||||
aggregateLabel: isDefined(value) ? label : undefined,
|
aggregateLabel: isDefined(value) ? label : undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
@ -57,7 +57,7 @@ describe('buildRecordGqlFieldsAggregate', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
amount: AGGREGATE_OPERATIONS.sum,
|
amount: [AGGREGATE_OPERATIONS.sum],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ describe('buildRecordGqlFieldsAggregate', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
[MOCK_KANBAN_FIELD]: AGGREGATE_OPERATIONS.count,
|
[MOCK_KANBAN_FIELD]: [AGGREGATE_OPERATIONS.count],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
import { RecordGqlFieldsAggregate } from '@/object-record/graphql/types/RecordGqlFieldsAggregate';
|
||||||
import { KanbanAggregateOperation } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
import { KanbanAggregateOperation } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
@ -11,7 +12,7 @@ export const buildRecordGqlFieldsAggregate = ({
|
|||||||
objectMetadataItem: ObjectMetadataItem;
|
objectMetadataItem: ObjectMetadataItem;
|
||||||
recordIndexKanbanAggregateOperation: KanbanAggregateOperation;
|
recordIndexKanbanAggregateOperation: KanbanAggregateOperation;
|
||||||
kanbanFieldName: string;
|
kanbanFieldName: string;
|
||||||
}) => {
|
}): RecordGqlFieldsAggregate => {
|
||||||
let recordGqlFieldsAggregate = {};
|
let recordGqlFieldsAggregate = {};
|
||||||
|
|
||||||
const kanbanAggregateOperationFieldName = objectMetadataItem.fields?.find(
|
const kanbanAggregateOperationFieldName = objectMetadataItem.fields?.find(
|
||||||
@ -30,14 +31,15 @@ export const buildRecordGqlFieldsAggregate = ({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
recordGqlFieldsAggregate = {
|
recordGqlFieldsAggregate = {
|
||||||
[kanbanFieldName]: AGGREGATE_OPERATIONS.count,
|
[kanbanFieldName]: [AGGREGATE_OPERATIONS.count],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
recordGqlFieldsAggregate = {
|
recordGqlFieldsAggregate = {
|
||||||
[kanbanAggregateOperationFieldName]:
|
[kanbanAggregateOperationFieldName]: [
|
||||||
recordIndexKanbanAggregateOperation?.operation ??
|
recordIndexKanbanAggregateOperation?.operation ??
|
||||||
AGGREGATE_OPERATIONS.count,
|
AGGREGATE_OPERATIONS.count,
|
||||||
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { AggregateManyRecordsData } from '@/object-record/hooks/useAggregateManyRecords';
|
import { AggregateRecordsData } from '@/object-record/hooks/useAggregateRecords';
|
||||||
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
||||||
import { KanbanAggregateOperation } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
import { KanbanAggregateOperation } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
@ -8,7 +8,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const computeAggregateValueAndLabel = (
|
export const computeAggregateValueAndLabel = (
|
||||||
data: AggregateManyRecordsData,
|
data: AggregateRecordsData,
|
||||||
objectMetadataItem: ObjectMetadataItem,
|
objectMetadataItem: ObjectMetadataItem,
|
||||||
recordIndexKanbanAggregateOperation: KanbanAggregateOperation,
|
recordIndexKanbanAggregateOperation: KanbanAggregateOperation,
|
||||||
kanbanFieldName: string,
|
kanbanFieldName: string,
|
||||||
|
@ -35,7 +35,7 @@ describe('generateAggregateQuery', () => {
|
|||||||
const normalizedQuery = result.loc?.source.body.replace(/\s+/g, ' ').trim();
|
const normalizedQuery = result.loc?.source.body.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
expect(normalizedQuery).toBe(
|
expect(normalizedQuery).toBe(
|
||||||
'query AggregateManyCompanies($filter: CompanyFilterInput) { companies(filter: $filter) { id name createdAt } }',
|
'query AggregateCompanies($filter: CompanyFilterInput) { companies(filter: $filter) { id name createdAt } }',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ describe('generateAggregateQuery', () => {
|
|||||||
const normalizedQuery = result.loc?.source.body.replace(/\s+/g, ' ').trim();
|
const normalizedQuery = result.loc?.source.body.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
expect(normalizedQuery).toBe(
|
expect(normalizedQuery).toBe(
|
||||||
'query AggregateManyPeople($filter: PersonFilterInput) { people(filter: $filter) { id } }',
|
'query AggregatePeople($filter: PersonFilterInput) { people(filter: $filter) { id } }',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { getAggregateQueryName } from '@/object-record/utils/getAggregateQueryName';
|
||||||
|
|
||||||
|
describe('getAggregateQueryName', () => {
|
||||||
|
it('should return the correct aggregate query name for a valid plural name', () => {
|
||||||
|
expect(getAggregateQueryName('opportunities')).toBe(
|
||||||
|
'AggregateOpportunities',
|
||||||
|
);
|
||||||
|
expect(getAggregateQueryName('companies')).toBe('AggregateCompanies');
|
||||||
|
expect(getAggregateQueryName('people')).toBe('AggregatePeople');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when input is undefined', () => {
|
||||||
|
expect(() => getAggregateQueryName(undefined as any)).toThrow(
|
||||||
|
'objectMetadataNamePlural is required',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when input is null', () => {
|
||||||
|
expect(() => getAggregateQueryName(null as any)).toThrow(
|
||||||
|
'objectMetadataNamePlural is required',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -2,6 +2,7 @@ import gql from 'graphql-tag';
|
|||||||
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||||
|
import { getAggregateQueryName } from '@/object-record/utils/getAggregateQueryName';
|
||||||
import { capitalize } from '~/utils/string/capitalize';
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const generateAggregateQuery = ({
|
export const generateAggregateQuery = ({
|
||||||
@ -17,7 +18,7 @@ export const generateAggregateQuery = ({
|
|||||||
.join('\n ');
|
.join('\n ');
|
||||||
|
|
||||||
return gql`
|
return gql`
|
||||||
query AggregateMany${capitalize(objectMetadataItem.namePlural)}($filter: ${capitalize(
|
query ${getAggregateQueryName(objectMetadataItem.namePlural)}($filter: ${capitalize(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
)}FilterInput) {
|
)}FilterInput) {
|
||||||
${objectMetadataItem.namePlural}(filter: $filter) {
|
${objectMetadataItem.namePlural}(filter: $filter) {
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
|
export const getAggregateQueryName = (
|
||||||
|
objectMetadataNamePlural: string,
|
||||||
|
): string => {
|
||||||
|
if (!isDefined(objectMetadataNamePlural)) {
|
||||||
|
throw new Error('objectMetadataNamePlural is required');
|
||||||
|
}
|
||||||
|
return `Aggregate${capitalize(objectMetadataNamePlural)}`;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user