Support orderBy as array (#5681)
closes: #4301 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -10,9 +10,11 @@ export const fetchAllThreadMessagesOperationSignatureFactory: RecordGqlOperation
|
|||||||
eq: messageThreadId || '',
|
eq: messageThreadId || '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: [
|
||||||
receivedAt: 'AscNullsLast',
|
{
|
||||||
},
|
receivedAt: 'AscNullsLast',
|
||||||
|
},
|
||||||
|
],
|
||||||
limit: 10,
|
limit: 10,
|
||||||
},
|
},
|
||||||
fields: {
|
fields: {
|
||||||
|
|||||||
@ -17,9 +17,11 @@ export const useAttachments = (targetableObject: ActivityTargetableObject) => {
|
|||||||
eq: targetableObject.id,
|
eq: targetableObject.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: [
|
||||||
createdAt: 'DescNullsFirst',
|
{
|
||||||
},
|
createdAt: 'DescNullsFirst',
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -49,7 +49,7 @@ const mocks: MockedResponse[] = [
|
|||||||
query: gql`
|
query: gql`
|
||||||
query FindManyActivityTargets(
|
query FindManyActivityTargets(
|
||||||
$filter: ActivityTargetFilterInput
|
$filter: ActivityTargetFilterInput
|
||||||
$orderBy: ActivityTargetOrderByInput
|
$orderBy: [ActivityTargetOrderByInput]
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Int
|
$limit: Int
|
||||||
) {
|
) {
|
||||||
@ -103,7 +103,7 @@ const mocks: MockedResponse[] = [
|
|||||||
query: gql`
|
query: gql`
|
||||||
query FindManyActivities(
|
query FindManyActivities(
|
||||||
$filter: ActivityFilterInput
|
$filter: ActivityFilterInput
|
||||||
$orderBy: ActivityOrderByInput
|
$orderBy: [ActivityOrderByInput]
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Int
|
$limit: Int
|
||||||
) {
|
) {
|
||||||
@ -142,7 +142,7 @@ const mocks: MockedResponse[] = [
|
|||||||
variables: {
|
variables: {
|
||||||
filter: { id: { in: ['234'] } },
|
filter: { id: { in: ['234'] } },
|
||||||
limit: undefined,
|
limit: undefined,
|
||||||
orderBy: {},
|
orderBy: [{}],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: jest.fn(() => ({
|
result: jest.fn(() => ({
|
||||||
@ -178,7 +178,7 @@ describe('useActivities', () => {
|
|||||||
useActivities({
|
useActivities({
|
||||||
targetableObjects: [],
|
targetableObjects: [],
|
||||||
activitiesFilters: {},
|
activitiesFilters: {},
|
||||||
activitiesOrderByVariables: {},
|
activitiesOrderByVariables: [{}],
|
||||||
skip: false,
|
skip: false,
|
||||||
}),
|
}),
|
||||||
{ wrapper: Wrapper },
|
{ wrapper: Wrapper },
|
||||||
@ -202,7 +202,7 @@ describe('useActivities', () => {
|
|||||||
{ targetObjectNameSingular: 'company', id: '123' },
|
{ targetObjectNameSingular: 'company', id: '123' },
|
||||||
],
|
],
|
||||||
activitiesFilters: {},
|
activitiesFilters: {},
|
||||||
activitiesOrderByVariables: {},
|
activitiesOrderByVariables: [{}],
|
||||||
skip: false,
|
skip: false,
|
||||||
});
|
});
|
||||||
return { activities, setCurrentWorkspaceMember };
|
return { activities, setCurrentWorkspaceMember };
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const mocks: MockedResponse[] = [
|
|||||||
query: gql`
|
query: gql`
|
||||||
query FindManyActivityTargets(
|
query FindManyActivityTargets(
|
||||||
$filter: ActivityTargetFilterInput
|
$filter: ActivityTargetFilterInput
|
||||||
$orderBy: ActivityTargetOrderByInput
|
$orderBy: [ActivityTargetOrderByInput]
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Int
|
$limit: Int
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -109,7 +109,7 @@ export const usePrepareFindManyActivitiesQuery = () => {
|
|||||||
objectRecordsToOverwrite: filteredActivities,
|
objectRecordsToOverwrite: filteredActivities,
|
||||||
queryVariables: {
|
queryVariables: {
|
||||||
...nextFindManyActivitiesQueryFilter,
|
...nextFindManyActivitiesQueryFilter,
|
||||||
orderBy: { createdAt: 'DescNullsFirst' },
|
orderBy: [{ createdAt: 'DescNullsFirst' }],
|
||||||
},
|
},
|
||||||
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
|
recordGqlFields: FIND_ACTIVITIES_OPERATION_SIGNATURE.fields,
|
||||||
computeReferences: true,
|
computeReferences: true,
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export const useNotes = (targetableObject: ActivityTargetableObject) => {
|
|||||||
|
|
||||||
const { activities, loading } = useActivities({
|
const { activities, loading } = useActivities({
|
||||||
activitiesFilters: notesQueryVariables.filter ?? {},
|
activitiesFilters: notesQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: notesQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: notesQueryVariables.orderBy ?? [{}],
|
||||||
targetableObjects: [targetableObject],
|
targetableObjects: [targetableObject],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -110,13 +110,13 @@ export const useTasks = ({
|
|||||||
const { activities: completeTasksData } = useActivities({
|
const { activities: completeTasksData } = useActivities({
|
||||||
targetableObjects,
|
targetableObjects,
|
||||||
activitiesFilters: completedQueryVariables.filter ?? {},
|
activitiesFilters: completedQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: completedQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: completedQueryVariables.orderBy ?? [{}],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { activities: incompleteTaskData } = useActivities({
|
const { activities: incompleteTaskData } = useActivities({
|
||||||
targetableObjects,
|
targetableObjects,
|
||||||
activitiesFilters: incompleteQueryVariables.filter ?? {},
|
activitiesFilters: incompleteQueryVariables.filter ?? {},
|
||||||
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? {},
|
activitiesOrderByVariables: incompleteQueryVariables.orderBy ?? [{}],
|
||||||
});
|
});
|
||||||
|
|
||||||
const todayOrPreviousTasks = incompleteTaskData?.filter((task) => {
|
const todayOrPreviousTasks = incompleteTaskData?.filter((task) => {
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||||
|
|
||||||
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy =
|
export const FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY: RecordGqlOperationOrderBy =
|
||||||
{
|
[
|
||||||
createdAt: 'DescNullsFirst',
|
{
|
||||||
};
|
createdAt: 'DescNullsFirst',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@ -13,8 +13,10 @@ export const makeTimelineActivitiesQueryVariables = ({
|
|||||||
in: [...activityIds].sort(sortByAscString),
|
in: [...activityIds].sort(sortByAscString),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: [
|
||||||
createdAt: 'DescNullsFirst',
|
{
|
||||||
},
|
createdAt: 'DescNullsFirst',
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,9 +23,11 @@ export const useTimelineActivities = (
|
|||||||
eq: targetableObject.id,
|
eq: targetableObject.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: [
|
||||||
createdAt: 'DescNullsFirst',
|
{
|
||||||
},
|
createdAt: 'DescNullsFirst',
|
||||||
|
},
|
||||||
|
],
|
||||||
recordGqlFields: {
|
recordGqlFields: {
|
||||||
id: true,
|
id: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export const sortCachedObjectEdges = ({
|
|||||||
orderBy: RecordGqlOperationOrderBy;
|
orderBy: RecordGqlOperationOrderBy;
|
||||||
readCacheField: ReadFieldFunction;
|
readCacheField: ReadFieldFunction;
|
||||||
}) => {
|
}) => {
|
||||||
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy)[0];
|
const [orderByFieldName, orderByFieldValue] = Object.entries(orderBy[0])[0];
|
||||||
const [orderBySubFieldName, orderBySubFieldValue] =
|
const [orderBySubFieldName, orderBySubFieldValue] =
|
||||||
typeof orderByFieldValue === 'string'
|
typeof orderByFieldValue === 'string'
|
||||||
? []
|
? []
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export const query = gql`
|
|||||||
export const findManyViewsQuery = gql`
|
export const findManyViewsQuery = gql`
|
||||||
query FindManyViews(
|
query FindManyViews(
|
||||||
$filter: ViewFilterInput
|
$filter: ViewFilterInput
|
||||||
$orderBy: ViewOrderByInput
|
$orderBy: [ViewOrderByInput]
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Int
|
$limit: Int
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -26,8 +26,8 @@ describe('useGetObjectOrderByField', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.current).toEqual({
|
expect(result.current).toEqual([
|
||||||
name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' },
|
{ name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' } },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,8 +9,8 @@ describe('getObjectOrderByField', () => {
|
|||||||
(item) => item.nameSingular === 'person',
|
(item) => item.nameSingular === 'person',
|
||||||
)!;
|
)!;
|
||||||
const res = getOrderByFieldForObjectMetadataItem(objectMetadataItem);
|
const res = getOrderByFieldForObjectMetadataItem(objectMetadataItem);
|
||||||
expect(res).toEqual({
|
expect(res).toEqual([
|
||||||
name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' },
|
{ name: { firstName: 'AscNullsLast', lastName: 'AscNullsLast' } },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,20 +15,26 @@ export const getOrderByFieldForObjectMetadataItem = (
|
|||||||
if (isDefined(labelIdentifierFieldMetadata)) {
|
if (isDefined(labelIdentifierFieldMetadata)) {
|
||||||
switch (labelIdentifierFieldMetadata.type) {
|
switch (labelIdentifierFieldMetadata.type) {
|
||||||
case FieldMetadataType.FullName:
|
case FieldMetadataType.FullName:
|
||||||
return {
|
return [
|
||||||
[labelIdentifierFieldMetadata.name]: {
|
{
|
||||||
firstName: orderBy ?? 'AscNullsLast',
|
[labelIdentifierFieldMetadata.name]: {
|
||||||
lastName: orderBy ?? 'AscNullsLast',
|
firstName: orderBy ?? 'AscNullsLast',
|
||||||
|
lastName: orderBy ?? 'AscNullsLast',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
];
|
||||||
default:
|
default:
|
||||||
return {
|
return [
|
||||||
[labelIdentifierFieldMetadata.name]: orderBy ?? 'AscNullsLast',
|
{
|
||||||
};
|
[labelIdentifierFieldMetadata.name]: orderBy ?? 'AscNullsLast',
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return [
|
||||||
createdAt: orderBy ?? 'DescNullsLast',
|
{
|
||||||
};
|
createdAt: orderBy ?? 'DescNullsLast',
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
import { OrderBy } from '@/object-metadata/types/OrderBy';
|
||||||
|
|
||||||
export type RecordGqlOperationOrderBy = {
|
export type RecordGqlOperationOrderBy = Array<{
|
||||||
[fieldName: string]: OrderBy | { [subFieldName: string]: OrderBy };
|
[fieldName: string]: OrderBy | { [subFieldName: string]: OrderBy };
|
||||||
};
|
}>;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { RecoilRoot } from 'recoil';
|
|||||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||||
|
|
||||||
const expectedQueryTemplate = `
|
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) {
|
people(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
|||||||
@ -32,9 +32,9 @@ export const useGenerateCombinedFindManyRecordsQuery = ({
|
|||||||
const orderByPerMetadataItemArray = operationSignatures
|
const orderByPerMetadataItemArray = operationSignatures
|
||||||
.map(
|
.map(
|
||||||
({ objectNameSingular }) =>
|
({ objectNameSingular }) =>
|
||||||
`$orderBy${capitalize(objectNameSingular)}: ${capitalize(
|
`$orderBy${capitalize(objectNameSingular)}: [${capitalize(
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
)}OrderByInput`,
|
)}OrderByInput]`,
|
||||||
)
|
)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
|
|||||||
@ -30,9 +30,11 @@ describe('turnSortsIntoOrderBy', () => {
|
|||||||
it('should sort by recordPosition if no sorts', () => {
|
it('should sort by recordPosition if no sorts', () => {
|
||||||
const fields = [{ id: 'field1', name: 'createdAt' }] as FieldMetadataItem[];
|
const fields = [{ id: 'field1', name: 'createdAt' }] as FieldMetadataItem[];
|
||||||
expect(turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, [])).toEqual(
|
expect(turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, [])).toEqual(
|
||||||
{
|
[
|
||||||
position: 'AscNullsFirst',
|
{
|
||||||
},
|
position: 'AscNullsFirst',
|
||||||
|
},
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,10 +49,7 @@ describe('turnSortsIntoOrderBy', () => {
|
|||||||
const fields = [{ id: 'field1', name: 'field1' }] as FieldMetadataItem[];
|
const fields = [{ id: 'field1', name: 'field1' }] as FieldMetadataItem[];
|
||||||
expect(
|
expect(
|
||||||
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
|
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
|
||||||
).toEqual({
|
).toEqual([{ field1: 'AscNullsFirst' }, { position: 'AscNullsFirst' }]);
|
||||||
field1: 'AscNullsFirst',
|
|
||||||
position: 'AscNullsFirst',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create OrderByField with multiple sorts', () => {
|
it('should create OrderByField with multiple sorts', () => {
|
||||||
@ -72,11 +71,11 @@ describe('turnSortsIntoOrderBy', () => {
|
|||||||
] as FieldMetadataItem[];
|
] as FieldMetadataItem[];
|
||||||
expect(
|
expect(
|
||||||
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
|
turnSortsIntoOrderBy({ ...objectMetadataItem, fields }, sorts),
|
||||||
).toEqual({
|
).toEqual([
|
||||||
field1: 'AscNullsFirst',
|
{ field1: 'AscNullsFirst' },
|
||||||
field2: 'DescNullsLast',
|
{ field2: 'DescNullsLast' },
|
||||||
position: 'AscNullsFirst',
|
{ position: 'AscNullsFirst' },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore if field not found', () => {
|
it('should ignore if field not found', () => {
|
||||||
@ -87,9 +86,9 @@ describe('turnSortsIntoOrderBy', () => {
|
|||||||
definition: sortDefinition,
|
definition: sortDefinition,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(turnSortsIntoOrderBy(objectMetadataItem, sorts)).toEqual({
|
expect(turnSortsIntoOrderBy(objectMetadataItem, sorts)).toEqual([
|
||||||
position: 'AscNullsFirst',
|
{ position: 'AscNullsFirst' },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not return position for remotes', () => {
|
it('should not return position for remotes', () => {
|
||||||
@ -102,6 +101,6 @@ describe('turnSortsIntoOrderBy', () => {
|
|||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
turnSortsIntoOrderBy({ ...objectMetadataItem, isRemote: true }, sorts),
|
turnSortsIntoOrderBy({ ...objectMetadataItem, isRemote: true }, sorts),
|
||||||
).toEqual({});
|
).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,28 +15,23 @@ export const turnSortsIntoOrderBy = (
|
|||||||
): RecordGqlOperationOrderBy => {
|
): RecordGqlOperationOrderBy => {
|
||||||
const fields: Pick<Field, 'id' | 'name'>[] = objectMetadataItem?.fields ?? [];
|
const fields: Pick<Field, 'id' | 'name'>[] = objectMetadataItem?.fields ?? [];
|
||||||
const fieldsById = mapArrayToObject(fields, ({ id }) => id);
|
const fieldsById = mapArrayToObject(fields, ({ id }) => id);
|
||||||
const sortsOrderBy = Object.fromEntries(
|
const sortsOrderBy = sorts
|
||||||
sorts
|
.map((sort) => {
|
||||||
.map((sort) => {
|
const correspondingField = fieldsById[sort.fieldMetadataId];
|
||||||
const correspondingField = fieldsById[sort.fieldMetadataId];
|
|
||||||
|
|
||||||
if (isUndefinedOrNull(correspondingField)) {
|
if (isUndefinedOrNull(correspondingField)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const direction: OrderBy =
|
const direction: OrderBy =
|
||||||
sort.direction === 'asc' ? 'AscNullsFirst' : 'DescNullsLast';
|
sort.direction === 'asc' ? 'AscNullsFirst' : 'DescNullsLast';
|
||||||
|
|
||||||
return [correspondingField.name, direction];
|
return { [correspondingField.name]: direction };
|
||||||
})
|
})
|
||||||
.filter(isDefined),
|
.filter(isDefined);
|
||||||
);
|
|
||||||
|
|
||||||
if (hasPositionField(objectMetadataItem)) {
|
if (hasPositionField(objectMetadataItem)) {
|
||||||
return {
|
return [...sortsOrderBy, { position: 'AscNullsFirst' }];
|
||||||
...sortsOrderBy,
|
|
||||||
position: 'AscNullsFirst',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortsOrderBy;
|
return sortsOrderBy;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { FieldMetadataType } from '~/generated/graphql';
|
|||||||
const query = gql`
|
const query = gql`
|
||||||
query CombinedFindManyRecords(
|
query CombinedFindManyRecords(
|
||||||
$filterNameSingular: NameSingularFilterInput
|
$filterNameSingular: NameSingularFilterInput
|
||||||
$orderByNameSingular: NameSingularOrderByInput
|
$orderByNameSingular: [NameSingularOrderByInput]
|
||||||
$lastCursorNameSingular: String
|
$lastCursorNameSingular: String
|
||||||
$limitNameSingular: Int
|
$limitNameSingular: Int
|
||||||
) {
|
) {
|
||||||
@ -50,7 +50,7 @@ const mocks = [
|
|||||||
query,
|
query,
|
||||||
variables: {
|
variables: {
|
||||||
filterNameSingular: { id: { in: ['1'] } },
|
filterNameSingular: { id: { in: ['1'] } },
|
||||||
orderByNameSingular: { createdAt: 'DescNullsLast' },
|
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
|
||||||
limitNameSingular: 60,
|
limitNameSingular: 60,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -63,7 +63,7 @@ const mocks = [
|
|||||||
query,
|
query,
|
||||||
variables: {
|
variables: {
|
||||||
filterNameSingular: { and: [{}, { id: { in: ['1'] } }] },
|
filterNameSingular: { and: [{}, { id: { in: ['1'] } }] },
|
||||||
orderByNameSingular: { createdAt: 'DescNullsLast' },
|
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
|
||||||
limitNameSingular: 60,
|
limitNameSingular: 60,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -77,7 +77,7 @@ const mocks = [
|
|||||||
variables: {
|
variables: {
|
||||||
limitNameSingular: 60,
|
limitNameSingular: 60,
|
||||||
filterNameSingular: { not: { id: { in: ['1'] } } },
|
filterNameSingular: { not: { id: { in: ['1'] } } },
|
||||||
orderByNameSingular: { createdAt: 'DescNullsLast' },
|
orderByNameSingular: [{ createdAt: 'DescNullsLast' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: jest.fn(() => ({
|
result: jest.fn(() => ({
|
||||||
|
|||||||
@ -16,9 +16,7 @@ export const useOrderByFieldPerMetadataItem = ({
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
`orderBy${capitalize(objectMetadataItem.nameSingular)}`,
|
`orderBy${capitalize(objectMetadataItem.nameSingular)}`,
|
||||||
{
|
[...orderByField],
|
||||||
...orderByField,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
.filter(isDefined),
|
.filter(isDefined),
|
||||||
|
|||||||
@ -21,9 +21,9 @@ query FindMany${capitalize(
|
|||||||
objectMetadataItem.namePlural,
|
objectMetadataItem.namePlural,
|
||||||
)}($filter: ${capitalize(
|
)}($filter: ${capitalize(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
)}FilterInput, $orderBy: ${capitalize(
|
)}FilterInput, $orderBy: [${capitalize(
|
||||||
objectMetadataItem.nameSingular,
|
objectMetadataItem.nameSingular,
|
||||||
)}OrderByInput, $lastCursor: String, $limit: Int) {
|
)}OrderByInput], $lastCursor: String, $limit: Int) {
|
||||||
${
|
${
|
||||||
objectMetadataItem.namePlural
|
objectMetadataItem.namePlural
|
||||||
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
|
}(filter: $filter, orderBy: $orderBy, first: $limit, after: $lastCursor){
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
|
|||||||
export const query = gql`
|
export const query = gql`
|
||||||
query FindManyPeople(
|
query FindManyPeople(
|
||||||
$filter: PersonFilterInput
|
$filter: PersonFilterInput
|
||||||
$orderBy: PersonOrderByInput
|
$orderBy: [PersonOrderByInput]
|
||||||
$lastCursor: String
|
$lastCursor: String
|
||||||
$limit: Int = 60
|
$limit: Int = 60
|
||||||
) {
|
) {
|
||||||
@ -166,7 +166,7 @@ export const variables = {
|
|||||||
{ not: { id: { in: ['1', '2'] } } },
|
{ not: { id: { in: ['1', '2'] } } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
orderBy: { name: 'AscNullsLast' },
|
orderBy: [{ name: 'AscNullsLast' }],
|
||||||
},
|
},
|
||||||
filteredSelectedEntities: {
|
filteredSelectedEntities: {
|
||||||
limit: 60,
|
limit: 60,
|
||||||
@ -176,12 +176,12 @@ export const variables = {
|
|||||||
{ id: { in: ['1'] } },
|
{ id: { in: ['1'] } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
orderBy: { name: 'AscNullsLast' },
|
orderBy: [{ name: 'AscNullsLast' }],
|
||||||
},
|
},
|
||||||
selectedEntities: {
|
selectedEntities: {
|
||||||
limit: 60,
|
limit: 60,
|
||||||
filter: { id: { in: ['1'] } },
|
filter: { id: { in: ['1'] } },
|
||||||
orderBy: { name: 'AscNullsLast' },
|
orderBy: [{ name: 'AscNullsLast' }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
useFindManyRecords({
|
useFindManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter: selectedIdsFilter,
|
filter: selectedIdsFilter,
|
||||||
orderBy: { [orderByField]: sortOrder },
|
orderBy: [{ [orderByField]: sortOrder }],
|
||||||
skip: !selectedIds.length,
|
skip: !selectedIds.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
} = useFindManyRecords({
|
} = useFindManyRecords({
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter: makeAndFilterVariables([...searchFilters, selectedIdsFilter]),
|
filter: makeAndFilterVariables([...searchFilters, selectedIdsFilter]),
|
||||||
orderBy: { [orderByField]: sortOrder },
|
orderBy: [{ [orderByField]: sortOrder }],
|
||||||
skip: !selectedIds.length,
|
skip: !selectedIds.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ export const useFilteredSearchEntityQuery = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
filter: makeAndFilterVariables([...searchFilters, notFilter]),
|
filter: makeAndFilterVariables([...searchFilters, notFilter]),
|
||||||
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||||
orderBy: { [orderByField]: sortOrder },
|
orderBy: [{ [orderByField]: sortOrder }],
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -68,10 +68,7 @@ describe('ArgsStringFactory', () => {
|
|||||||
|
|
||||||
it('when orderBy is present, should return an array of objects', () => {
|
it('when orderBy is present, should return an array of objects', () => {
|
||||||
const args = {
|
const args = {
|
||||||
orderBy: {
|
orderBy: [{ id: 'AscNullsFirst' }, { name: 'AscNullsFirst' }],
|
||||||
id: 'AscNullsFirst',
|
|
||||||
name: 'AscNullsFirst',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
argsAliasCreate.mockReturnValue(args);
|
argsAliasCreate.mockReturnValue(args);
|
||||||
@ -85,11 +82,11 @@ describe('ArgsStringFactory', () => {
|
|||||||
|
|
||||||
it('when orderBy is present with position criteria, should return position at the end of the list', () => {
|
it('when orderBy is present with position criteria, should return position at the end of the list', () => {
|
||||||
const args = {
|
const args = {
|
||||||
orderBy: {
|
orderBy: [
|
||||||
position: 'AscNullsFirst',
|
{ position: 'AscNullsFirst' },
|
||||||
id: 'AscNullsFirst',
|
{ id: 'AscNullsFirst' },
|
||||||
name: 'AscNullsFirst',
|
{ name: 'AscNullsFirst' },
|
||||||
},
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
argsAliasCreate.mockReturnValue(args);
|
argsAliasCreate.mockReturnValue(args);
|
||||||
@ -103,11 +100,11 @@ describe('ArgsStringFactory', () => {
|
|||||||
|
|
||||||
it('when orderBy is present with position in the middle, should return position at the end of the list', () => {
|
it('when orderBy is present with position in the middle, should return position at the end of the list', () => {
|
||||||
const args = {
|
const args = {
|
||||||
orderBy: {
|
orderBy: [
|
||||||
id: 'AscNullsFirst',
|
{ id: 'AscNullsFirst' },
|
||||||
position: 'AscNullsFirst',
|
{ position: 'AscNullsFirst' },
|
||||||
name: 'AscNullsFirst',
|
{ name: 'AscNullsFirst' },
|
||||||
},
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
argsAliasCreate.mockReturnValue(args);
|
argsAliasCreate.mockReturnValue(args);
|
||||||
|
|||||||
@ -36,11 +36,16 @@ export class ArgsStringFactory {
|
|||||||
typeof computedArgs[key] === 'object' &&
|
typeof computedArgs[key] === 'object' &&
|
||||||
computedArgs[key] !== null
|
computedArgs[key] !== null
|
||||||
) {
|
) {
|
||||||
// If it's an object (and not null), stringify it
|
if (key === 'orderBy') {
|
||||||
argsString += `${key}: ${this.buildStringifiedObject(
|
argsString += `${key}: ${this.buildStringifiedOrderBy(
|
||||||
key,
|
computedArgs[key],
|
||||||
computedArgs[key],
|
)}, `;
|
||||||
)}, `;
|
} else {
|
||||||
|
// If it's an object (and not null), stringify it
|
||||||
|
argsString += `${key}: ${stringifyWithoutKeyQuote(
|
||||||
|
computedArgs[key],
|
||||||
|
)}, `;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// For other types (number, boolean), add as is
|
// For other types (number, boolean), add as is
|
||||||
argsString += `${key}: ${computedArgs[key]}, `;
|
argsString += `${key}: ${computedArgs[key]}, `;
|
||||||
@ -55,22 +60,30 @@ export class ArgsStringFactory {
|
|||||||
return argsString;
|
return argsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildStringifiedObject(
|
private buildStringifiedOrderBy(
|
||||||
key: string,
|
keyValuePairArray: Array<Record<string, any>>,
|
||||||
obj: Record<string, any>,
|
|
||||||
): string {
|
): string {
|
||||||
// PgGraphql is expecting the orderBy argument to be an array of objects
|
if (
|
||||||
if (key === 'orderBy') {
|
keyValuePairArray.length !== 0 &&
|
||||||
const orderByString = Object.keys(obj)
|
Object.keys(keyValuePairArray[0]).length === 0
|
||||||
.sort((_, b) => {
|
) {
|
||||||
return b === 'position' ? -1 : 0;
|
return `[]`;
|
||||||
})
|
}
|
||||||
.map((orderByKey) => `{${orderByKey}: ${obj[orderByKey]}}`)
|
// if position argument is present we want to put it at the very last
|
||||||
.join(', ');
|
let orderByString = keyValuePairArray
|
||||||
|
.sort((_, obj) => (Object.hasOwnProperty.call(obj, 'position') ? -1 : 0))
|
||||||
|
.map((obj) => {
|
||||||
|
const [key] = Object.keys(obj);
|
||||||
|
const value = obj[key];
|
||||||
|
|
||||||
return `[${orderByString}]`;
|
return `{${key}: ${value}}`;
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
if (orderByString.endsWith(', ')) {
|
||||||
|
orderByString = orderByString.slice(0, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringifyWithoutKeyQuote(obj);
|
return `[${orderByString}]`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,9 +16,9 @@ export enum OrderByDirection {
|
|||||||
DescNullsLast = 'DescNullsLast',
|
DescNullsLast = 'DescNullsLast',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RecordOrderBy = {
|
export type RecordOrderBy = Array<{
|
||||||
[Property in keyof Record]?: OrderByDirection;
|
[Property in keyof Record]?: OrderByDirection;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export interface RecordDuplicateCriteria {
|
export interface RecordDuplicateCriteria {
|
||||||
objectName: string;
|
objectName: string;
|
||||||
|
|||||||
@ -13,7 +13,11 @@ describe('getResolverArgs', () => {
|
|||||||
before: { type: GraphQLString, isNullable: true },
|
before: { type: GraphQLString, isNullable: true },
|
||||||
after: { type: GraphQLString, isNullable: true },
|
after: { type: GraphQLString, isNullable: true },
|
||||||
filter: { kind: InputTypeDefinitionKind.Filter, isNullable: true },
|
filter: { kind: InputTypeDefinitionKind.Filter, isNullable: true },
|
||||||
orderBy: { kind: InputTypeDefinitionKind.OrderBy, isNullable: true },
|
orderBy: {
|
||||||
|
kind: InputTypeDefinitionKind.OrderBy,
|
||||||
|
isNullable: true,
|
||||||
|
isArray: true,
|
||||||
|
},
|
||||||
limit: { type: GraphQLInt, isNullable: true },
|
limit: { type: GraphQLInt, isNullable: true },
|
||||||
},
|
},
|
||||||
findOne: {
|
findOne: {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export const getResolverArgs = (
|
|||||||
orderBy: {
|
orderBy: {
|
||||||
kind: InputTypeDefinitionKind.OrderBy,
|
kind: InputTypeDefinitionKind.OrderBy,
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
|
isArray: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case 'findOne':
|
case 'findOne':
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export class FindManyQueryFactory {
|
|||||||
return `
|
return `
|
||||||
query FindMany${capitalize(objectNamePlural)}(
|
query FindMany${capitalize(objectNamePlural)}(
|
||||||
$filter: ${objectNameSingular}FilterInput,
|
$filter: ${objectNameSingular}FilterInput,
|
||||||
$orderBy: ${objectNameSingular}OrderByInput,
|
$orderBy: [${objectNameSingular}OrderByInput],
|
||||||
$startingAfter: String,
|
$startingAfter: String,
|
||||||
$endingBefore: String,
|
$endingBefore: String,
|
||||||
$limit: Int = 60
|
$limit: Int = 60
|
||||||
|
|||||||
@ -26,7 +26,7 @@ describe('OrderByInputFactory', () => {
|
|||||||
it('should return default if order by missing', () => {
|
it('should return default if order by missing', () => {
|
||||||
const request: any = { query: {} };
|
const request: any = { query: {} };
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({});
|
expect(service.create(request, objectMetadata)).toEqual([{}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create order by parser properly', () => {
|
it('should create order by parser properly', () => {
|
||||||
@ -36,10 +36,10 @@ describe('OrderByInputFactory', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({
|
expect(service.create(request, objectMetadata)).toEqual([
|
||||||
fieldNumber: OrderByDirection.AscNullsFirst,
|
{ fieldNumber: OrderByDirection.AscNullsFirst },
|
||||||
fieldText: OrderByDirection.DescNullsLast,
|
{ fieldText: OrderByDirection.DescNullsLast },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should choose default direction if missing', () => {
|
it('should choose default direction if missing', () => {
|
||||||
@ -49,9 +49,9 @@ describe('OrderByInputFactory', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({
|
expect(service.create(request, objectMetadata)).toEqual([
|
||||||
fieldNumber: OrderByDirection.AscNullsFirst,
|
{ fieldNumber: OrderByDirection.AscNullsFirst },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handler complex fields', () => {
|
it('should handler complex fields', () => {
|
||||||
@ -61,9 +61,9 @@ describe('OrderByInputFactory', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({
|
expect(service.create(request, objectMetadata)).toEqual([
|
||||||
fieldCurrency: { amountMicros: OrderByDirection.AscNullsFirst },
|
{ fieldCurrency: { amountMicros: OrderByDirection.AscNullsFirst } },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handler complex fields with direction', () => {
|
it('should handler complex fields with direction', () => {
|
||||||
@ -73,9 +73,9 @@ describe('OrderByInputFactory', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({
|
expect(service.create(request, objectMetadata)).toEqual([
|
||||||
fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast },
|
{ fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast } },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handler multiple complex fields with direction', () => {
|
it('should handler multiple complex fields with direction', () => {
|
||||||
@ -86,10 +86,10 @@ describe('OrderByInputFactory', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(service.create(request, objectMetadata)).toEqual({
|
expect(service.create(request, objectMetadata)).toEqual([
|
||||||
fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast },
|
{ fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast } },
|
||||||
fieldLink: { label: OrderByDirection.AscNullsLast },
|
{ fieldLink: { label: OrderByDirection.AscNullsLast } },
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if direction invalid', () => {
|
it('should throw if direction invalid', () => {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
RecordOrderBy,
|
RecordOrderBy,
|
||||||
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||||
|
|
||||||
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
|
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
|
||||||
|
|
||||||
export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst;
|
export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst;
|
||||||
|
|
||||||
@ -17,12 +17,12 @@ export class OrderByInputFactory {
|
|||||||
const orderByQuery = request.query.order_by;
|
const orderByQuery = request.query.order_by;
|
||||||
|
|
||||||
if (typeof orderByQuery !== 'string') {
|
if (typeof orderByQuery !== 'string') {
|
||||||
return {};
|
return [{}];
|
||||||
}
|
}
|
||||||
|
|
||||||
//orderByQuery = field_1[AscNullsFirst],field_2[DescNullsLast],field_3
|
//orderByQuery = field_1[AscNullsFirst],field_2[DescNullsLast],field_3
|
||||||
const orderByItems = orderByQuery.split(',');
|
const orderByItems = orderByQuery.split(',');
|
||||||
let result = {};
|
let result: Array<Record<string, OrderByDirection>> = [];
|
||||||
let itemDirection = '';
|
let itemDirection = '';
|
||||||
let itemFields = '';
|
let itemFields = '';
|
||||||
|
|
||||||
@ -65,10 +65,14 @@ export class OrderByInputFactory {
|
|||||||
}
|
}
|
||||||
}, itemDirection);
|
}, itemDirection);
|
||||||
|
|
||||||
result = { ...result, ...fieldResult };
|
const resultFields = Object.keys(fieldResult).map((key) => ({
|
||||||
|
[key]: fieldResult[key],
|
||||||
|
}));
|
||||||
|
|
||||||
|
result = [...result, ...resultFields];
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFields(objectMetadata.objectMetadataItem, Object.keys(result));
|
checkArrayFields(objectMetadata.objectMetadataItem, result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
||||||
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
|
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
|
||||||
|
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
|
||||||
|
|
||||||
describe('checkFields', () => {
|
describe('checkFields', () => {
|
||||||
it('should check field types', () => {
|
it('should check field types', () => {
|
||||||
@ -13,4 +14,21 @@ describe('checkFields', () => {
|
|||||||
checkFields(objectMetadataItemMock, ['fieldNumber', 'wrongField']),
|
checkFields(objectMetadataItemMock, ['fieldNumber', 'wrongField']),
|
||||||
).toThrow();
|
).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check field types from array of fields', () => {
|
||||||
|
expect(() =>
|
||||||
|
checkArrayFields(objectMetadataItemMock, [{ fieldNumber: undefined }]),
|
||||||
|
).not.toThrow();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
checkArrayFields(objectMetadataItemMock, [{ wrongField: undefined }]),
|
||||||
|
).toThrow();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
checkArrayFields(objectMetadataItemMock, [
|
||||||
|
{ fieldNumber: undefined },
|
||||||
|
{ wrongField: undefined },
|
||||||
|
]),
|
||||||
|
).toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
|
import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||||
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
|
export const checkArrayFields = (
|
||||||
|
objectMetadata: ObjectMetadataInterface,
|
||||||
|
fields: Array<Record<string, any>>,
|
||||||
|
): void => {
|
||||||
|
const fieldMetadataNames = objectMetadata.fields
|
||||||
|
.map((field) => {
|
||||||
|
if (isCompositeFieldMetadataType(field.type)) {
|
||||||
|
const compositeType = compositeTypeDefintions.get(field.type);
|
||||||
|
|
||||||
|
if (!compositeType) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`Composite type '${field.type}' not found`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
field.name,
|
||||||
|
compositeType.properties.map(
|
||||||
|
(compositeProperty) => compositeProperty.name,
|
||||||
|
),
|
||||||
|
].flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.name;
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
for (const fieldObj of fields) {
|
||||||
|
for (const fieldName in fieldObj) {
|
||||||
|
if (!fieldMetadataNames.includes(fieldName)) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`field '${fieldName}' does not exist in '${computeObjectTargetTable(
|
||||||
|
objectMetadata,
|
||||||
|
)}' object`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user