Support orderBy as array (#5681)

closes: #4301

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Aditya Pimpalkar
2024-06-14 10:23:37 +01:00
committed by GitHub
parent 85fd801480
commit 4603999d1c
35 changed files with 249 additions and 157 deletions

View File

@ -10,9 +10,11 @@ export const fetchAllThreadMessagesOperationSignatureFactory: RecordGqlOperation
eq: messageThreadId || '',
},
},
orderBy: {
receivedAt: 'AscNullsLast',
},
orderBy: [
{
receivedAt: 'AscNullsLast',
},
],
limit: 10,
},
fields: {

View File

@ -17,9 +17,11 @@ export const useAttachments = (targetableObject: ActivityTargetableObject) => {
eq: targetableObject.id,
},
},
orderBy: {
createdAt: 'DescNullsFirst',
},
orderBy: [
{
createdAt: 'DescNullsFirst',
},
],
});
return {

View File

@ -49,7 +49,7 @@ const mocks: MockedResponse[] = [
query: gql`
query FindManyActivityTargets(
$filter: ActivityTargetFilterInput
$orderBy: ActivityTargetOrderByInput
$orderBy: [ActivityTargetOrderByInput]
$lastCursor: String
$limit: Int
) {
@ -103,7 +103,7 @@ const mocks: MockedResponse[] = [
query: gql`
query FindManyActivities(
$filter: ActivityFilterInput
$orderBy: ActivityOrderByInput
$orderBy: [ActivityOrderByInput]
$lastCursor: String
$limit: Int
) {
@ -142,7 +142,7 @@ const mocks: MockedResponse[] = [
variables: {
filter: { id: { in: ['234'] } },
limit: undefined,
orderBy: {},
orderBy: [{}],
},
},
result: jest.fn(() => ({
@ -178,7 +178,7 @@ describe('useActivities', () => {
useActivities({
targetableObjects: [],
activitiesFilters: {},
activitiesOrderByVariables: {},
activitiesOrderByVariables: [{}],
skip: false,
}),
{ wrapper: Wrapper },
@ -202,7 +202,7 @@ describe('useActivities', () => {
{ targetObjectNameSingular: 'company', id: '123' },
],
activitiesFilters: {},
activitiesOrderByVariables: {},
activitiesOrderByVariables: [{}],
skip: false,
});
return { activities, setCurrentWorkspaceMember };

View File

@ -34,7 +34,7 @@ const mocks: MockedResponse[] = [
query: gql`
query FindManyActivityTargets(
$filter: ActivityTargetFilterInput
$orderBy: ActivityTargetOrderByInput
$orderBy: [ActivityTargetOrderByInput]
$lastCursor: String
$limit: Int
) {

View File

@ -109,7 +109,7 @@ export const usePrepareFindManyActivitiesQuery = () => {
objectRecordsToOverwrite: filteredActivities,
queryVariables: {
...nextFindManyActivitiesQueryFilter,
orderBy: { createdAt: 'DescNullsFirst' },
orderBy: [{ createdAt: 'DescNullsFirst' }],
},
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
computeReferences: true,

View File

@ -24,7 +24,7 @@ export const useNotes = (targetableObject: ActivityTargetableObject) => {
const { activities, loading } = useActivities({
activitiesFilters: notesQueryVariables.filter ?? {},
activitiesOrderByVariables: notesQueryVariables.orderBy ?? {},
activitiesOrderByVariables: notesQueryVariables.orderBy ?? [{}],
targetableObjects: [targetableObject],
});

View File

@ -110,13 +110,13 @@ export const useTasks = ({
const { activities: completeTasksData } = useActivities({
targetableObjects,
activitiesFilters: completedQueryVariables.filter ?? {},
activitiesOrderByVariables: completedQueryVariables.orderBy ?? {},
activitiesOrderByVariables: completedQueryVariables.orderBy ?? [{}],
});
const { activities: incompleteTaskData } = useActivities({
targetableObjects,
activitiesFilters: incompleteQueryVariables.filter ?? {},
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? {},
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? [{}],
});
const todayOrPreviousTasks = incompleteTaskData?.filter((task) => {

View File

@ -1,6 +1,8 @@
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy =
{
createdAt: 'DescNullsFirst',
};
[
{
createdAt: 'DescNullsFirst',
},
];

View File

@ -13,8 +13,10 @@ export const makeTimelineActivitiesQueryVariables = ({
in: [...activityIds].sort(sortByAscString),
},
},
orderBy: {
createdAt: 'DescNullsFirst',
},
orderBy: [
{
createdAt: 'DescNullsFirst',
},
],
};
};

View File

@ -23,9 +23,11 @@ export const useTimelineActivities = (
eq: targetableObject.id,
},
},
orderBy: {
createdAt: 'DescNullsFirst',
},
orderBy: [
{
createdAt: 'DescNullsFirst',
},
],
recordGqlFields: {
id: true,
createdAt: true,

View File

@ -16,7 +16,7 @@ export const sortCachedObjectEdges = ({
orderBy: RecordGqlOperationOrderBy;
readCacheField: ReadFieldFunction;
}) => {
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy)[0];
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy[0])[0];
const [orderBySubFieldName, orderBySubFieldValue] =
typeof orderByFieldValue === 'string'
? []

View File

@ -24,7 +24,7 @@ export const query = gql`
export const findManyViewsQuery = gql`
query FindManyViews(
$filter: ViewFilterInput
$orderBy: ViewOrderByInput
$orderBy: [ViewOrderByInput]
$lastCursor: String
$limit: Int
) {

View File

@ -26,8 +26,8 @@ describe('useGetObjectOrderByField', () => {
},
);
expect(result.current).toEqual({
name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' },
});
expect(result.current).toEqual([
{ name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' } },
]);
});
});

View File

@ -9,8 +9,8 @@ describe('getObjectOrderByField', () => {
(item) => item.nameSingular === 'person',
)!;
const res = getOrderByFieldForObjectMetadataItem(objectMetadataItem);
expect(res).toEqual({
name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' },
});
expect(res).toEqual([
{ name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' } },
]);
});
});

View File

@ -15,20 +15,26 @@ export const getOrderByFieldForObjectMetadataItem = (
if (isDefined(labelIdentifierFieldMetadata)) {
switch (labelIdentifierFieldMetadata.type) {
case FieldMetadataType.FullName:
return {
[labelIdentifierFieldMetadata.name]: {
firstName: orderBy ?? 'AscNullsLast',
lastName: orderBy ?? 'AscNullsLast',
return [
{
[labelIdentifierFieldMetadata.name]: {
firstName: orderBy ?? 'AscNullsLast',
lastName: orderBy ?? 'AscNullsLast',
},
},
};
];
default:
return {
[labelIdentifierFieldMetadata.name]: orderBy ?? 'AscNullsLast',
};
return [
{
[labelIdentifierFieldMetadata.name]: orderBy ?? 'AscNullsLast',
},
];
}
} else {
return {
createdAt: orderBy ?? 'DescNullsLast',
};
return [
{
createdAt: orderBy ?? 'DescNullsLast',
},
];
}
};

View File

@ -1,5 +1,5 @@
import { OrderBy } from '@/object-metadata/types/OrderBy';
export type RecordGqlOperationOrderBy = {
export type RecordGqlOperationOrderBy = Array<{
[fieldName: string]: OrderBy | { [subFieldName: string]: OrderBy };
};
}>;

View File

@ -5,7 +5,7 @@ import { RecoilRoot } from 'recoil';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
const expectedQueryTemplate = `
query FindManyPeople($filter: PersonFilterInput, $orderBy: PersonOrderByInput, $lastCursor: String, $limit: Int) {
query FindManyPeople($filter: PersonFilterInput, $orderBy: [PersonOrderByInput], $lastCursor: String, $limit: Int) {
people(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor) {
edges {
node {

View File

@ -32,9 +32,9 @@ export const useGenerateCombinedFindManyRecordsQuery = ({
const orderByPerMetadataItemArray = operationSignatures
.map(
({ objectNameSingular }) =>
`$orderBy${capitalize(objectNameSingular)}: ${capitalize(
`$orderBy${capitalize(objectNameSingular)}: [${capitalize(
objectNameSingular,
)}OrderByInput`,
)}OrderByInput]`,
)
.join(', ');

View File

@ -30,9 +30,11 @@ describe('turnSortsIntoOrderBy', () => {
it('should sort by recordPosition if no sorts', () => {
const fields = [{ id: 'field1', name: 'createdAt' }] as FieldMetadataItem[];
expect(turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, [])).toEqual(
{
position: 'AscNullsFirst',
},
[
{
position: 'AscNullsFirst',
},
],
);
});
@ -47,10 +49,7 @@ describe('turnSortsIntoOrderBy', () => {
const fields = [{ id: 'field1', name: 'field1' }] as FieldMetadataItem[];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
).toEqual({
field1: 'AscNullsFirst',
position: 'AscNullsFirst',
});
).toEqual([{ field1: 'AscNullsFirst' }, { position: 'AscNullsFirst' }]);
});
it('should create OrderByField with multiple sorts', () => {
@ -72,11 +71,11 @@ describe('turnSortsIntoOrderBy', () => {
] as FieldMetadataItem[];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
).toEqual({
field1: 'AscNullsFirst',
field2: 'DescNullsLast',
position: 'AscNullsFirst',
});
).toEqual([
{ field1: 'AscNullsFirst' },
{ field2: 'DescNullsLast' },
{ position: 'AscNullsFirst' },
]);
});
it('should ignore if field not found', () => {
@ -87,9 +86,9 @@ describe('turnSortsIntoOrderBy', () => {
definition: sortDefinition,
},
];
expect(turnSortsIntoOrderBy(objectMetadataItem, sorts)).toEqual({
position: 'AscNullsFirst',
});
expect(turnSortsIntoOrderBy(objectMetadataItem, sorts)).toEqual([
{ position: 'AscNullsFirst' },
]);
});
it('should not return position for remotes', () => {
@ -102,6 +101,6 @@ describe('turnSortsIntoOrderBy', () => {
];
expect(
turnSortsIntoOrderBy({ ...objectMetadataItem, isRemote: true }, sorts),
).toEqual({});
).toEqual([]);
});
});

View File

@ -15,28 +15,23 @@ export const turnSortsIntoOrderBy = (
): RecordGqlOperationOrderBy => {
const fields: Pick<Field, 'id' | 'name'>[] = objectMetadataItem?.fields ?? [];
const fieldsById = mapArrayToObject(fields, ({ id }) => id);
const sortsOrderBy = Object.fromEntries(
sorts
.map((sort) => {
const correspondingField = fieldsById[sort.fieldMetadataId];
const sortsOrderBy = sorts
.map((sort) => {
const correspondingField = fieldsById[sort.fieldMetadataId];
if (isUndefinedOrNull(correspondingField)) {
return undefined;
}
if (isUndefinedOrNull(correspondingField)) {
return undefined;
}
const direction: OrderBy =
sort.direction === 'asc' ? 'AscNullsFirst' : 'DescNullsLast';
const direction: OrderBy =
sort.direction === 'asc' ? 'AscNullsFirst' : 'DescNullsLast';
return [correspondingField.name, direction];
})
.filter(isDefined),
);
return { [correspondingField.name]: direction };
})
.filter(isDefined);
if (hasPositionField(objectMetadataItem)) {
return {
...sortsOrderBy,
position: 'AscNullsFirst',
};
return [...sortsOrderBy, { position: 'AscNullsFirst' }];
}
return sortsOrderBy;

View File

@ -11,7 +11,7 @@ import { FieldMetadataType } from '~/generated/graphql';
const query = gql`
query CombinedFindManyRecords(
$filterNameSingular: NameSingularFilterInput
$orderByNameSingular: NameSingularOrderByInput
$orderByNameSingular: [NameSingularOrderByInput]
$lastCursorNameSingular: String
$limitNameSingular: Int
) {
@ -50,7 +50,7 @@ const mocks = [
query,
variables: {
filterNameSingular: { id: { in: ['1'] } },
orderByNameSingular: { createdAt: 'DescNullsLast' },
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
limitNameSingular: 60,
},
},
@ -63,7 +63,7 @@ const mocks = [
query,
variables: {
filterNameSingular: { and: [{}, { id: { in: ['1'] } }] },
orderByNameSingular: { createdAt: 'DescNullsLast' },
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
limitNameSingular: 60,
},
},
@ -77,7 +77,7 @@ const mocks = [
variables: {
limitNameSingular: 60,
filterNameSingular: { not: { id: { in: ['1'] } } },
orderByNameSingular: { createdAt: 'DescNullsLast' },
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
},
},
result: jest.fn(() => ({

View File

@ -16,9 +16,7 @@ export const useOrderByFieldPerMetadataItem = ({
return [
`orderBy${capitalize(objectMetadataItem.nameSingular)}`,
{
...orderByField,
},
[...orderByField],
];
})
.filter(isDefined),

View File

@ -21,9 +21,9 @@ query FindMany${capitalize(
objectMetadataItem.namePlural,
)}($filter: ${capitalize(
objectMetadataItem.nameSingular,
)}FilterInput, $orderBy: ${capitalize(
)}FilterInput, $orderBy: [${capitalize(
objectMetadataItem.nameSingular,
)}OrderByInput, $lastCursor: String, $limit: Int) {
)}OrderByInput], $lastCursor: String, $limit: Int) {
${
objectMetadataItem.namePlural
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
export const query = gql`
query FindManyPeople(
$filter: PersonFilterInput
$orderBy: PersonOrderByInput
$orderBy: [PersonOrderByInput]
$lastCursor: String
$limit: Int = 60
) {
@ -166,7 +166,7 @@ export const variables = {
{ not: { id: { in: ['1', '2'] } } },
],
},
orderBy: { name: 'AscNullsLast' },
orderBy: [{ name: 'AscNullsLast' }],
},
filteredSelectedEntities: {
limit: 60,
@ -176,12 +176,12 @@ export const variables = {
{ id: { in: ['1'] } },
],
},
orderBy: { name: 'AscNullsLast' },
orderBy: [{ name: 'AscNullsLast' }],
},
selectedEntities: {
limit: 60,
filter: { id: { in: ['1'] } },
orderBy: { name: 'AscNullsLast' },
orderBy: [{ name: 'AscNullsLast' }],
},
};

View File

@ -49,7 +49,7 @@ export const useFilteredSearchEntityQuery = ({
useFindManyRecords({
objectNameSingular,
filter: selectedIdsFilter,
orderBy: { [orderByField]: sortOrder },
orderBy: [{ [orderByField]: sortOrder }],
skip: !selectedIds.length,
});
@ -93,7 +93,7 @@ export const useFilteredSearchEntityQuery = ({
} = useFindManyRecords({
objectNameSingular,
filter: makeAndFilterVariables([...searchFilters, selectedIdsFilter]),
orderBy: { [orderByField]: sortOrder },
orderBy: [{ [orderByField]: sortOrder }],
skip: !selectedIds.length,
});
@ -106,7 +106,7 @@ export const useFilteredSearchEntityQuery = ({
objectNameSingular,
filter: makeAndFilterVariables([...searchFilters, notFilter]),
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
orderBy: { [orderByField]: sortOrder },
orderBy: [{ [orderByField]: sortOrder }],
});
return {