## Query depth deprecation

I'm deprecating depth parameter in our graphql query / cache tooling.
They were obsolete since we introduce the possibility to provide
RecordGqlFields

## Refactor combinedFindManyRecordHook

The hook can now take an array of operationSignatures

## Fix tasks issues

Fix optimistic rendering issue. Note that we still haven't handle
optimisticEffect on creation properly
This commit is contained in:
Charles Bochet
2024-04-29 23:33:23 +02:00
committed by GitHub
parent c946572fde
commit 6a14b1c6d6
187 changed files with 958 additions and 1482 deletions

View File

@ -1 +0,0 @@
export const MAX_QUERY_DEPTH_FOR_CACHE_INJECTION = 1;

View File

@ -7,6 +7,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { prefillRecord } from '@/object-record/utils/prefillRecord';
import { capitalize } from '~/utils/string/capitalize';
@ -32,13 +33,15 @@ export const useCreateOneRecordInCache = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem,
computeReferences: true,
recordGqlFields: generateDepthOneRecordGqlFields({
objectMetadataItem,
}),
})}
`;
const prefilledRecord = prefillRecord({
objectMetadataItem,
input: record,
depth: 1,
});
const recordToCreateWithNestedConnections = getRecordNodeFromRecord({

View File

@ -5,33 +5,46 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export const useGetRecordFromCache = ({
objectNameSingular,
recordGqlFields,
}: {
objectNameSingular: string;
recordGqlFields?: RecordGqlFields;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const appliedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const apolloClient = useApolloClient();
return useCallback(
<CachedObjectRecord extends ObjectRecord = ObjectRecord>(
<T extends ObjectRecord = ObjectRecord>(
recordId: string,
cache = apolloClient.cache,
) => {
return getRecordFromCache<CachedObjectRecord>({
return getRecordFromCache<T>({
cache,
recordId,
objectMetadataItems,
objectMetadataItem,
recordGqlFields: appliedRecordGqlFields,
});
},
[objectMetadataItem, objectMetadataItems, apolloClient],
[
apolloClient.cache,
objectMetadataItems,
objectMetadataItem,
appliedRecordGqlFields,
],
);
};

View File

@ -3,9 +3,9 @@ import { useApolloClient } from '@apollo/client';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordQueryResult } from '@/object-record/types/ObjectRecordQueryResult';
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
import { isDefined } from '~/utils/isDefined';
@ -22,32 +22,28 @@ export const useReadFindManyRecordsQueryInCache = ({
T extends ObjectRecord = ObjectRecord,
>({
queryVariables,
queryFields,
depth,
recordGqlFields,
}: {
queryVariables: ObjectRecordQueryVariables;
queryFields?: Record<string, any>;
depth?: number;
queryVariables: RecordGqlOperationVariables;
recordGqlFields?: Record<string, any>;
}) => {
const findManyRecordsQueryForCacheRead = generateFindManyRecordsQuery({
objectMetadataItem,
objectMetadataItems,
queryFields,
depth,
recordGqlFields,
});
const existingRecordsQueryResult = apolloClient.readQuery<
ObjectRecordQueryResult<T>
>({
query: findManyRecordsQueryForCacheRead,
variables: queryVariables,
});
const existingRecordsQueryResult =
apolloClient.readQuery<RecordGqlOperationFindManyResult>({
query: findManyRecordsQueryForCacheRead,
variables: queryVariables,
});
const existingRecordConnection =
existingRecordsQueryResult?.[objectMetadataItem.namePlural];
const existingObjectRecords = isDefined(existingRecordConnection)
? getRecordsFromRecordConnection({
? getRecordsFromRecordConnection<T>({
recordConnection: existingRecordConnection,
})
: [];

View File

@ -3,10 +3,9 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { MAX_QUERY_DEPTH_FOR_CACHE_INJECTION } from '@/object-record/cache/constants/MaxQueryDepthForCacheInjection';
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
export const useUpsertFindManyRecordsQueryInCache = ({
@ -22,22 +21,19 @@ export const useUpsertFindManyRecordsQueryInCache = ({
T extends ObjectRecord = ObjectRecord,
>({
queryVariables,
depth = MAX_QUERY_DEPTH_FOR_CACHE_INJECTION,
objectRecordsToOverwrite,
queryFields,
recordGqlFields,
computeReferences = false,
}: {
queryVariables: ObjectRecordQueryVariables;
depth?: number;
queryVariables: RecordGqlOperationVariables;
objectRecordsToOverwrite: T[];
queryFields?: Record<string, any>;
recordGqlFields?: Record<string, any>;
computeReferences?: boolean;
}) => {
const findManyRecordsQueryForCacheOverwrite = generateFindManyRecordsQuery({
objectMetadataItem,
objectMetadataItems,
depth,
queryFields,
recordGqlFields,
computeReferences,
});
@ -45,7 +41,7 @@ export const useUpsertFindManyRecordsQueryInCache = ({
objectMetadataItems: objectMetadataItems,
objectMetadataItem: objectMetadataItem,
records: objectRecordsToOverwrite,
queryFields,
recordGqlFields,
computeReferences,
});

View File

@ -0,0 +1,6 @@
import { RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
export type RecordGqlRefConnection = Omit<RecordGqlConnection, 'edges'> & {
edges: RecordGqlRefEdge[];
};

View File

@ -0,0 +1,6 @@
import { RecordGqlRefNode } from '@/object-record/cache/types/RecordGqlRefNode';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
export type RecordGqlRefEdge = Omit<RecordGqlEdge, 'node'> & {
node: RecordGqlRefNode;
};

View File

@ -0,0 +1,3 @@
import { Reference } from '@apollo/client';
export type RecordGqlRefNode = Reference;

View File

@ -2,18 +2,18 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getConnectionTypename } from '@/object-record/cache/utils/getConnectionTypename';
import { getEmptyPageInfo } from '@/object-record/cache/utils/getEmptyPageInfo';
import { getRecordEdgeFromRecord } from '@/object-record/cache/utils/getRecordEdgeFromRecord';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem,
records,
queryFields,
recordGqlFields,
withPageInfo = true,
computeReferences = false,
isRootLevel = true,
depth = 1,
}: {
objectMetadataItems: ObjectMetadataItem[];
objectMetadataItem: Pick<
@ -21,11 +21,10 @@ export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
'fields' | 'namePlural' | 'nameSingular'
>;
records: T[];
queryFields?: Record<string, any>;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
withPageInfo?: boolean;
isRootLevel?: boolean;
computeReferences?: boolean;
depth?: number;
}) => {
return {
__typename: getConnectionTypename(objectMetadataItem.nameSingular),
@ -33,14 +32,13 @@ export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
return getRecordEdgeFromRecord({
objectMetadataItems,
objectMetadataItem,
queryFields,
recordGqlFields,
record,
isRootLevel,
computeReferences,
depth,
});
}),
...(withPageInfo && { pageInfo: getEmptyPageInfo() }),
...(withPageInfo && { totalCount: records.length }),
} as ObjectRecordConnection<T>;
} as RecordGqlConnection;
};

View File

@ -1,13 +1,13 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem,
queryFields,
recordGqlFields,
record,
computeReferences = false,
isRootLevel = false,
@ -17,10 +17,9 @@ export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
ObjectMetadataItem,
'fields' | 'namePlural' | 'nameSingular'
>;
queryFields?: Record<string, any>;
recordGqlFields?: Record<string, any>;
computeReferences?: boolean;
isRootLevel?: boolean;
depth?: number;
record: T;
}) => {
return {
@ -29,13 +28,12 @@ export const getRecordEdgeFromRecord = <T extends ObjectRecord>({
...getRecordNodeFromRecord({
objectMetadataItems,
objectMetadataItem,
queryFields,
recordGqlFields,
record,
computeReferences,
isRootLevel,
depth: 1,
}),
},
cursor: '',
} as ObjectRecordEdge<T>;
} as RecordGqlEdge;
};

View File

@ -1,9 +1,10 @@
import { ApolloCache, gql } from '@apollo/client';
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { capitalize } from '~/utils/string/capitalize';
@ -13,16 +14,21 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
objectMetadataItems,
cache,
recordId,
recordGqlFields,
}: {
cache: ApolloCache<object>;
recordId: string;
objectMetadataItems: ObjectMetadataItem[];
objectMetadataItem: ObjectMetadataItem;
recordGqlFields?: RecordGqlFields;
}) => {
if (isUndefinedOrNull(objectMetadataItem)) {
return null;
}
const appliedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
const cacheReadFragment = gql`
@ -30,6 +36,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
{
objectMetadataItems,
objectMetadataItem,
recordGqlFields: appliedRecordGqlFields,
},
)}
`;
@ -49,7 +56,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
return null;
}
return getRecordFromRecordNode({
return getRecordFromRecordNode<T>({
recordNode: record,
}) as CachedObjectRecord<T>;
});
};

View File

@ -1,4 +1,5 @@
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
@ -6,7 +7,7 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const getRecordFromRecordNode = <T extends ObjectRecord>({
recordNode,
}: {
recordNode: T;
recordNode: RecordGqlNode;
}): T => {
return {
...Object.fromEntries(
@ -32,5 +33,6 @@ export const getRecordFromRecordNode = <T extends ObjectRecord>({
}),
),
id: recordNode.id,
__typename: recordNode.__typename,
} as T;
};

View File

@ -1,10 +1,10 @@
import { isNull, isUndefined } from '@sniptt/guards';
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import {
FieldMetadataType,
@ -16,22 +16,20 @@ import { lowerAndCapitalize } from '~/utils/string/lowerAndCapitalize';
export const getRecordNodeFromRecord = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem,
queryFields,
recordGqlFields,
record,
computeReferences = true,
isRootLevel = true,
depth = 1,
}: {
objectMetadataItems: ObjectMetadataItem[];
objectMetadataItem: Pick<
ObjectMetadataItem,
'fields' | 'namePlural' | 'nameSingular'
>;
queryFields?: Record<string, any>;
recordGqlFields?: Record<string, any>;
computeReferences?: boolean;
isRootLevel?: boolean;
record: T | null;
depth?: number;
}) => {
if (isNull(record)) {
return null;
@ -42,13 +40,13 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
if (!isRootLevel && computeReferences) {
return {
__ref: `${nodeTypeName}:${record.id}`,
} as unknown as CachedObjectRecord<T>; // Todo Fix typing
} as unknown as RecordGqlNode; // Fix typing: we want a Reference in computeReferences mode
}
const nestedRecord = Object.fromEntries(
Object.entries(record)
.map(([fieldName, value]) => {
if (isDefined(queryFields) && !queryFields[fieldName]) {
if (isDefined(recordGqlFields) && !recordGqlFields[fieldName]) {
return undefined;
}
@ -60,14 +58,6 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
return undefined;
}
if (
!isUndefined(depth) &&
depth < 1 &&
field.type === FieldMetadataType.Relation
) {
return undefined;
}
if (
field.type === FieldMetadataType.Relation &&
field.relationDefinition?.direction ===
@ -87,15 +77,14 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem: oneToManyObjectMetadataItem,
records: value as ObjectRecord[],
queryFields:
queryFields?.[fieldName] === true ||
isUndefined(queryFields?.[fieldName])
recordGqlFields:
recordGqlFields?.[fieldName] === true ||
isUndefined(recordGqlFields?.[fieldName])
? undefined
: queryFields?.[fieldName],
: recordGqlFields?.[fieldName],
withPageInfo: false,
isRootLevel: false,
computeReferences,
depth: depth - 1,
}),
];
}
@ -159,8 +148,5 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
.filter(isDefined),
) as T; // Todo fix typing once we have investigated apollo edges / nodes removal
return {
__typename: getNodeTypename(objectMetadataItem.nameSingular),
...nestedRecord,
};
return { ...nestedRecord, __typename: nodeTypeName };
};

View File

@ -1,11 +1,11 @@
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
export const getRecordsFromRecordConnection = <T extends ObjectRecord>({
recordConnection,
}: {
recordConnection: ObjectRecordConnection<T>;
recordConnection: RecordGqlConnection;
}): T[] => {
return recordConnection.edges.map((edge) =>
getRecordFromRecordNode<T>({ recordNode: edge.node }),

View File

@ -1,12 +1,12 @@
import { z } from 'zod';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { capitalize } from '~/utils/string/capitalize';
export const isObjectRecordConnection = (
objectNameSingular: string,
value: unknown,
): value is ObjectRecordConnection => {
): value is RecordGqlConnection => {
const objectConnectionTypeName = `${capitalize(
objectNameSingular,
)}Connection`;

View File

@ -1,13 +1,13 @@
import { StoreValue } from '@apollo/client';
import { z } from 'zod';
import { CachedObjectRecordConnection } from '@/apollo/types/CachedObjectRecordConnection';
import { RecordGqlRefConnection } from '@/object-record/cache/types/RecordGqlRefConnection';
import { capitalize } from '~/utils/string/capitalize';
export const isObjectRecordConnectionWithRefs = (
objectNameSingular: string,
storeValue: StoreValue,
): storeValue is CachedObjectRecordConnection => {
): storeValue is RecordGqlRefConnection => {
const objectConnectionTypeName = `${capitalize(
objectNameSingular,
)}Connection`;

View File

@ -4,6 +4,7 @@ import gql from 'graphql-tag';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { capitalize } from '~/utils/string/capitalize';
@ -44,14 +45,13 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
objectMetadataItems,
objectMetadataItem,
record,
depth: 1,
});
if (isUndefinedOrNull(recordWithConnection)) {
return;
}
cache.writeFragment<T & { __typename: string }>({
cache.writeFragment<RecordGqlNode>({
id: cachedRecordId,
fragment: cacheWriteFragment,
data: recordWithConnection,

View File

@ -1,11 +1,10 @@
import { Nullable } from 'twenty-ui';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
export type ObjectRecordConnection<T extends ObjectRecord = ObjectRecord> = {
export type RecordGqlConnection = {
__typename?: string;
edges: ObjectRecordEdge<T>[];
edges: RecordGqlEdge[];
pageInfo: {
hasNextPage?: boolean;
hasPreviousPage?: boolean;

View File

@ -0,0 +1,7 @@
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
export type RecordGqlEdge = {
__typename: string;
node: RecordGqlNode;
cursor: string;
};

View File

@ -0,0 +1 @@
export type RecordGqlFields = Record<string, any>;

View File

@ -0,0 +1,5 @@
export type RecordGqlNode = {
id: string;
[key: string]: any;
__typename: string;
};

View File

@ -93,22 +93,22 @@ export type LeafFilter =
| undefined;
export type AndObjectRecordFilter = {
and?: ObjectRecordQueryFilter[];
and?: RecordGqlOperationFilter[];
};
export type OrObjectRecordFilter = {
or?: ObjectRecordQueryFilter[] | ObjectRecordQueryFilter;
or?: RecordGqlOperationFilter[] | RecordGqlOperationFilter;
};
export type NotObjectRecordFilter = {
not?: ObjectRecordQueryFilter;
not?: RecordGqlOperationFilter;
};
export type LeafObjectRecordFilter = {
[fieldName: string]: LeafFilter;
};
export type ObjectRecordQueryFilter =
export type RecordGqlOperationFilter =
| LeafObjectRecordFilter
| AndObjectRecordFilter
| OrObjectRecordFilter

View File

@ -0,0 +1,5 @@
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
export type RecordGqlOperationFindManyResult = {
[objectNamePlural: string]: RecordGqlConnection;
};

View File

@ -0,0 +1,5 @@
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
export type RecordGqlOperationFindOneResult = {
[objectNameSingular: string]: RecordGqlNode;
};

View File

@ -0,0 +1,3 @@
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
export type RecordGqlOperationGqlRecordFields = RecordGqlFields;

View File

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

View File

@ -0,0 +1,8 @@
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
export type RecordGqlOperationSignature = {
objectNameSingular: string;
variables: RecordGqlOperationVariables;
fields?: RecordGqlOperationGqlRecordFields;
};

View File

@ -0,0 +1,5 @@
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
export type RecordGqlOperationSignatureFactory = (
factoryParams: any,
) => RecordGqlOperationSignature;

View File

@ -0,0 +1,8 @@
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
export type RecordGqlOperationVariables = {
filter?: RecordGqlOperationFilter;
orderBy?: RecordGqlOperationOrderBy;
limit?: number;
};

View File

@ -0,0 +1,14 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const generateDepthOneRecordGqlFields = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
return objectMetadataItem.fields.reduce((acc, field) => {
return {
...acc,
[field.name]: true,
};
}, {});
};

View File

@ -1,22 +1,29 @@
import { ReactNode } from 'react';
import { expect } from '@storybook/test';
import { renderHook } from '@testing-library/react';
import { RecoilRoot } from 'recoil';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/multiple-objects/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter';
const Wrapper = ({ children }: { children: ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
<RecoilRoot>
<JestObjectMetadataItemSetter>{children}</JestObjectMetadataItemSetter>
</RecoilRoot>
);
describe('useGenerateFindManyRecordsForMultipleMetadataItemsQuery', () => {
it('should work as expected', async () => {
const { result } = renderHook(
() => {
const mockObjectMetadataItems = getObjectMetadataItemsMock();
return useGenerateFindManyRecordsForMultipleMetadataItemsQuery({
targetObjectMetadataItems: mockObjectMetadataItems.slice(0, 2),
return useGenerateCombinedFindManyRecordsQuery({
operationSignatures: getObjectMetadataItemsMock()
.slice(0, 2)
.map((item) => ({
objectNameSingular: item.nameSingular,
variables: {},
})),
});
},
{

View File

@ -2,10 +2,12 @@ import { useApolloClient } from '@apollo/client';
import { v4 } from 'uuid';
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
@ -14,8 +16,7 @@ import { isDefined } from '~/utils/isDefined';
type useCreateManyRecordsProps = {
objectNameSingular: string;
queryFields?: Record<string, any>;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
skipPostOptmisticEffect?: boolean;
};
@ -23,8 +24,7 @@ export const useCreateManyRecords = <
CreatedObjectRecord extends ObjectRecord = ObjectRecord,
>({
objectNameSingular,
queryFields,
depth = 1,
recordGqlFields,
skipPostOptmisticEffect = false,
}: useCreateManyRecordsProps) => {
const apolloClient = useApolloClient();
@ -33,13 +33,15 @@ export const useCreateManyRecords = <
objectNameSingular,
});
const computedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const { createManyRecordsMutation } = useCreateManyRecordsMutation({
objectNameSingular,
queryFields,
depth,
recordGqlFields: computedRecordGqlFields,
});
const createOneRecordInCache = useCreateOneRecordInCache<CachedObjectRecord>({
const createOneRecordInCache = useCreateOneRecordInCache<ObjectRecord>({
objectMetadataItem,
});
@ -65,7 +67,10 @@ export const useCreateManyRecords = <
const recordsCreatedInCache = [];
for (const recordToCreate of sanitizedCreateManyRecordsInput) {
const recordCreatedInCache = createOneRecordInCache(recordToCreate);
const recordCreatedInCache = createOneRecordInCache({
...recordToCreate,
__typename: getObjectTypename(objectMetadataItem.nameSingular),
});
if (isDefined(recordCreatedInCache)) {
recordsCreatedInCache.push(recordCreatedInCache);

View File

@ -5,18 +5,17 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { capitalize } from '~/utils/string/capitalize';
export const useCreateManyRecordsMutation = ({
objectNameSingular,
queryFields,
depth,
recordGqlFields,
}: {
objectNameSingular: string;
queryFields?: Record<string, any>;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
@ -39,8 +38,7 @@ export const useCreateManyRecordsMutation = ({
${mutationResponseField}(data: $data) ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem,
queryFields,
depth,
recordGqlFields,
})}
}`;

View File

@ -2,10 +2,12 @@ import { useApolloClient } from '@apollo/client';
import { v4 } from 'uuid';
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
import { CachedObjectRecord } from '@/apollo/types/CachedObjectRecord';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreateOneRecordInCache';
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
@ -14,8 +16,7 @@ import { isDefined } from '~/utils/isDefined';
type useCreateOneRecordProps = {
objectNameSingular: string;
queryFields?: Record<string, any>;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
skipPostOptmisticEffect?: boolean;
};
@ -23,7 +24,7 @@ export const useCreateOneRecord = <
CreatedObjectRecord extends ObjectRecord = ObjectRecord,
>({
objectNameSingular,
queryFields,
recordGqlFields,
skipPostOptmisticEffect = false,
}: useCreateOneRecordProps) => {
const apolloClient = useApolloClient();
@ -32,14 +33,19 @@ export const useCreateOneRecord = <
objectNameSingular,
});
const computedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const { createOneRecordMutation } = useCreateOneRecordMutation({
objectNameSingular,
queryFields,
recordGqlFields: computedRecordGqlFields,
});
const createOneRecordInCache = useCreateOneRecordInCache<CachedObjectRecord>({
objectMetadataItem,
});
const createOneRecordInCache = useCreateOneRecordInCache<CreatedObjectRecord>(
{
objectMetadataItem,
},
);
const { objectMetadataItems } = useObjectMetadataItems();
@ -57,6 +63,7 @@ export const useCreateOneRecord = <
const recordCreatedInCache = createOneRecordInCache({
...input,
id: idForCreation,
__typename: getObjectTypename(objectMetadataItem.nameSingular),
});
if (isDefined(recordCreatedInCache)) {

View File

@ -5,23 +5,29 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { capitalize } from '~/utils/string/capitalize';
export const useCreateOneRecordMutation = ({
objectNameSingular,
queryFields,
depth,
recordGqlFields,
}: {
objectNameSingular: string;
queryFields?: Record<string, any>;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const appliedRecordGqlFields =
recordGqlFields ??
generateDepthOneRecordGqlFields({
objectMetadataItem,
});
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
if (isUndefinedOrNull(objectMetadataItem)) {
@ -39,8 +45,7 @@ export const useCreateOneRecordMutation = ({
${mutationResponseField}(data: $input) ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem,
queryFields,
depth,
recordGqlFields: appliedRecordGqlFields,
})}
}
`;

View File

@ -4,25 +4,22 @@ import { useQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { logError } from '~/utils/logError';
import { ObjectRecordQueryResult } from '../types/ObjectRecordQueryResult';
export const useFindDuplicateRecords = <T extends ObjectRecord = ObjectRecord>({
objectRecordId = '',
objectNameSingular,
onCompleted,
depth,
}: ObjectMetadataItemIdentifier & {
objectRecordId: string | undefined;
onCompleted?: (data: ObjectRecordConnection<T>) => void;
onCompleted?: (data: RecordGqlConnection) => void;
skip?: boolean;
depth?: number;
}) => {
const findDuplicateQueryStateIdentifier = objectNameSingular;
@ -32,7 +29,6 @@ export const useFindDuplicateRecords = <T extends ObjectRecord = ObjectRecord>({
const { findDuplicateRecordsQuery } = useFindDuplicateRecordsQuery({
objectNameSingular,
depth,
});
const { enqueueSnackBar } = useSnackBar();
@ -41,7 +37,7 @@ export const useFindDuplicateRecords = <T extends ObjectRecord = ObjectRecord>({
objectMetadataItem.nameSingular,
);
const { data, loading, error } = useQuery<ObjectRecordQueryResult<T>>(
const { data, loading, error } = useQuery<RecordGqlOperationFindManyResult>(
findDuplicateRecordsQuery,
{
variables: {

View File

@ -9,10 +9,8 @@ import { capitalize } from '~/utils/string/capitalize';
export const useFindDuplicateRecordsQuery = ({
objectNameSingular,
depth,
}: {
objectNameSingular: string;
depth?: number;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
@ -31,7 +29,6 @@ export const useFindDuplicateRecordsQuery = ({
node ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem,
depth,
})}
cursor
}

View File

@ -8,11 +8,13 @@ import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMembe
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
import { RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
import { filterUniqueRecordEdgesByCursor } from '@/object-record/utils/filterUniqueRecordEdgesByCursor';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { isDefined } from '~/utils/isDefined';
@ -22,7 +24,6 @@ import { capitalize } from '~/utils/string/capitalize';
import { cursorFamilyState } from '../states/cursorFamilyState';
import { hasNextPageFamilyState } from '../states/hasNextPageFamilyState';
import { isFetchingMoreRecordsFamilyState } from '../states/isFetchingMoreRecordsFamilyState';
import { ObjectRecordQueryResult } from '../types/ObjectRecordQueryResult';
export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
@ -31,20 +32,19 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
limit,
onCompleted,
skip,
queryFields,
recordGqlFields,
fetchPolicy,
}: ObjectMetadataItemIdentifier &
ObjectRecordQueryVariables & {
RecordGqlOperationVariables & {
onCompleted?: (
records: T[],
options?: {
pageInfo?: ObjectRecordConnection['pageInfo'];
pageInfo?: RecordGqlConnection['pageInfo'];
totalCount?: number;
},
) => void;
skip?: boolean;
depth?: number;
queryFields?: Record<string, any>;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
fetchPolicy?: WatchQueryFetchPolicy;
}) => {
const findManyQueryStateIdentifier =
@ -71,56 +71,55 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const { findManyRecordsQuery } = useFindManyRecordsQuery({
objectNameSingular,
queryFields,
recordGqlFields,
});
const { enqueueSnackBar } = useSnackBar();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { data, loading, error, fetchMore } = useQuery<
ObjectRecordQueryResult<T>
>(findManyRecordsQuery, {
skip: skip || !objectMetadataItem || !currentWorkspaceMember,
variables: {
filter,
limit,
orderBy,
},
fetchPolicy: fetchPolicy,
onCompleted: (data) => {
if (!isDefined(data)) {
onCompleted?.([]);
}
const { data, loading, error, fetchMore } =
useQuery<RecordGqlOperationFindManyResult>(findManyRecordsQuery, {
skip: skip || !objectMetadataItem || !currentWorkspaceMember,
variables: {
filter,
limit,
orderBy,
},
fetchPolicy: fetchPolicy,
onCompleted: (data) => {
if (!isDefined(data)) {
onCompleted?.([]);
}
const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo;
const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo;
const records = getRecordsFromRecordConnection({
recordConnection: data?.[objectMetadataItem.namePlural],
}) as T[];
const records = getRecordsFromRecordConnection({
recordConnection: data?.[objectMetadataItem.namePlural],
}) as T[];
onCompleted?.(records, {
pageInfo,
totalCount: data?.[objectMetadataItem.namePlural]?.totalCount,
});
onCompleted?.(records, {
pageInfo,
totalCount: data?.[objectMetadataItem.namePlural]?.totalCount,
});
if (isDefined(data?.[objectMetadataItem.namePlural])) {
setLastCursor(pageInfo.endCursor ?? '');
setHasNextPage(pageInfo.hasNextPage ?? false);
}
},
onError: (error) => {
logError(
`useFindManyRecords for "${objectMetadataItem.namePlural}" error : ` +
error,
);
enqueueSnackBar(
`Error during useFindManyRecords for "${objectMetadataItem.namePlural}", ${error.message}`,
{
variant: 'error',
},
);
},
});
if (isDefined(data?.[objectMetadataItem.namePlural])) {
setLastCursor(pageInfo.endCursor ?? '');
setHasNextPage(pageInfo.hasNextPage ?? false);
}
},
onError: (error) => {
logError(
`useFindManyRecords for "${objectMetadataItem.namePlural}" error : ` +
error,
);
enqueueSnackBar(
`Error during useFindManyRecords for "${objectMetadataItem.namePlural}", ${error.message}`,
{
variant: 'error',
},
);
},
});
const fetchMoreRecords = useCallback(async () => {
if (hasNextPage) {
@ -138,7 +137,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const nextEdges =
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
let newEdges: ObjectRecordEdge<T>[] = [];
let newEdges: RecordGqlEdge[] = [];
if (isNonEmptyArray(previousEdges) && isNonEmptyArray(nextEdges)) {
newEdges = filterUniqueRecordEdgesByCursor([
@ -180,7 +179,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
totalCount:
fetchMoreResult?.[objectMetadataItem.namePlural].totalCount,
},
} as ObjectRecordQueryResult<T>);
} as RecordGqlOperationFindManyResult);
},
});
} catch (error) {
@ -219,7 +218,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const records = useMemo(
() =>
data?.[objectMetadataItem.namePlural]
? getRecordsFromRecordConnection({
? getRecordsFromRecordConnection<T>({
recordConnection: data?.[objectMetadataItem.namePlural],
})
: ([] as T[]),

View File

@ -2,18 +2,16 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { QueryFields } from '@/object-record/query-keys/types/QueryFields';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
export const useFindManyRecordsQuery = ({
objectNameSingular,
queryFields,
depth,
recordGqlFields,
computeReferences,
}: {
objectNameSingular: string;
queryFields?: QueryFields;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
computeReferences?: boolean;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
@ -25,8 +23,7 @@ export const useFindManyRecordsQuery = ({
const findManyRecordsQuery = generateFindManyRecordsQuery({
objectMetadataItem,
objectMetadataItems,
queryFields,
depth,
recordGqlFields,
computeReferences,
});

View File

@ -4,6 +4,9 @@ import { useQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined';
@ -11,32 +14,34 @@ import { isDefined } from '~/utils/isDefined';
export const useFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
objectRecordId = '',
recordGqlFields,
onCompleted,
skip,
depth,
}: ObjectMetadataItemIdentifier & {
objectRecordId: string | undefined;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
onCompleted?: (data: T) => void;
skip?: boolean;
depth?: number;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const computedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const { findOneRecordQuery } = useFindOneRecordQuery({
objectNameSingular,
depth,
recordGqlFields: computedRecordGqlFields,
});
const { data, loading, error } = useQuery<
{ [nameSingular: string]: T },
{ objectRecordId: string }
>(findOneRecordQuery, {
const { data, loading, error } = useQuery<{
[nameSingular: string]: RecordGqlNode;
}>(findOneRecordQuery, {
skip: !objectMetadataItem || !objectRecordId || skip,
variables: { objectRecordId },
onCompleted: (data) => {
const recordWithoutConnection = getRecordFromRecordNode({
const recordWithoutConnection = getRecordFromRecordNode<T>({
recordNode: { ...data[objectNameSingular] },
});
@ -50,7 +55,7 @@ export const useFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
const recordWithoutConnection = useMemo(
() =>
data?.[objectNameSingular]
? getRecordFromRecordNode({
? getRecordFromRecordNode<T>({
recordNode: data?.[objectNameSingular],
})
: undefined,

View File

@ -4,14 +4,15 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { capitalize } from '~/utils/string/capitalize';
export const useFindOneRecordQuery = ({
objectNameSingular,
depth,
recordGqlFields,
}: {
objectNameSingular: string;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
@ -30,7 +31,7 @@ export const useFindOneRecordQuery = ({
})${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem,
depth,
recordGqlFields,
})}
},
`;

View File

@ -1,12 +1,15 @@
import { useLazyQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
type UseLazyFindOneRecordParams = ObjectMetadataItemIdentifier & {
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
};
type FindOneRecordParams<T extends ObjectRecord> = {
@ -16,11 +19,17 @@ type FindOneRecordParams<T extends ObjectRecord> = {
export const useLazyFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
objectNameSingular,
depth,
recordGqlFields,
}: UseLazyFindOneRecordParams) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const { findOneRecordQuery } = useFindOneRecordQuery({
objectNameSingular,
depth,
recordGqlFields:
recordGqlFields ??
generateDepthOneRecordGqlFields({ objectMetadataItem }),
});
const [findOneRecord, { loading, error, data, called }] =

View File

@ -6,23 +6,23 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
import { capitalize } from '~/utils/string/capitalize';
type useUpdateOneRecordProps = {
objectNameSingular: string;
queryFields?: Record<string, any>;
depth?: number;
recordGqlFields?: Record<string, any>;
};
export const useUpdateOneRecord = <
UpdatedObjectRecord extends ObjectRecord = ObjectRecord,
>({
objectNameSingular,
queryFields,
depth = 1,
recordGqlFields,
}: useUpdateOneRecordProps) => {
const apolloClient = useApolloClient();
@ -30,12 +30,16 @@ export const useUpdateOneRecord = <
objectNameSingular,
});
const computedRecordGqlFields =
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
const getRecordFromCache = useGetRecordFromCache({
objectNameSingular,
});
const { updateOneRecordMutation } = useUpdateOneRecordMutation({
objectNameSingular,
recordGqlFields: computedRecordGqlFields,
});
const { objectMetadataItems } = useObjectMetadataItems();
@ -54,14 +58,13 @@ export const useUpdateOneRecord = <
}),
};
const cachedRecord = getRecordFromCache<UpdatedObjectRecord>(idToUpdate);
const cachedRecord = getRecordFromCache<ObjectRecord>(idToUpdate);
const cachedRecordWithConnection = getRecordNodeFromRecord<ObjectRecord>({
record: cachedRecord,
objectMetadataItem,
objectMetadataItems,
depth,
queryFields,
recordGqlFields: computedRecordGqlFields,
computeReferences: true,
});
@ -69,6 +72,7 @@ export const useUpdateOneRecord = <
...cachedRecord,
...sanitizedInput,
...{ id: idToUpdate },
...{ __typename: capitalize(objectMetadataItem.nameSingular) },
};
const optimisticRecordWithConnection =
@ -76,8 +80,7 @@ export const useUpdateOneRecord = <
record: optimisticRecord,
objectMetadataItem,
objectMetadataItems,
depth,
queryFields,
recordGqlFields: computedRecordGqlFields,
computeReferences: true,
});

View File

@ -5,18 +5,20 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { capitalize } from '~/utils/string/capitalize';
export const useUpdateOneRecordMutation = ({
objectNameSingular,
recordGqlFields,
computeReferences = false,
depth,
}: {
objectNameSingular: string;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
computeReferences?: boolean;
depth?: number;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
@ -28,6 +30,12 @@ export const useUpdateOneRecordMutation = ({
return { updateOneRecordMutation: EMPTY_MUTATION };
}
const appliedRecordGqlFields =
recordGqlFields ??
generateDepthOneRecordGqlFields({
objectMetadataItem,
});
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
const mutationResponseField = getUpdateOneRecordMutationResponseField(
@ -40,8 +48,8 @@ export const useUpdateOneRecordMutation = ({
{
objectMetadataItems,
objectMetadataItem,
depth,
computeReferences,
recordGqlFields: appliedRecordGqlFields,
},
)}
}

View File

@ -1,13 +1,14 @@
import { useRecoilCallback } from 'recoil';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
// TODO: refactor with scoped state later
export const useUpsertRecordFromState = () =>
useRecoilCallback(
({ set }) =>
<T extends { id: string }>(record: T) =>
<T extends ObjectRecord>(record: T) =>
set(recordStoreFamilyState(record.id), (previousRecord) =>
isDeeplyEqual(previousRecord, record) ? previousRecord : record,
),

View File

@ -1,26 +1,21 @@
import { useQuery } from '@apollo/client';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/multiple-objects/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { MultiObjectRecordQueryResult } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray';
export const useFindManyRecordsForMultipleMetadataItems = ({
objectMetadataItems,
export const useCombinedFindManyRecords = ({
operationSignatures,
skip = false,
depth = 2,
}: {
objectMetadataItems: ObjectMetadataItem[];
operationSignatures: RecordGqlOperationSignature[];
skip: boolean;
depth?: number;
}) => {
const findManyQuery = useGenerateFindManyRecordsForMultipleMetadataItemsQuery(
{
targetObjectMetadataItems: objectMetadataItems,
depth,
},
);
const findManyQuery = useGenerateCombinedFindManyRecordsQuery({
operationSignatures,
});
const { data } = useQuery<MultiObjectRecordQueryResult>(
findManyQuery ?? EMPTY_QUERY,

View File

@ -0,0 +1,109 @@
import { gql } from '@apollo/client';
import { isUndefined } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
import { capitalize } from '~/utils/string/capitalize';
export const useGenerateCombinedFindManyRecordsQuery = ({
operationSignatures,
}: {
operationSignatures: RecordGqlOperationSignature[];
}) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
if (!isNonEmptyArray(operationSignatures)) {
return null;
}
const filterPerMetadataItemArray = operationSignatures
.map(
({ objectNameSingular }) =>
`$filter${capitalize(objectNameSingular)}: ${capitalize(
objectNameSingular,
)}FilterInput`,
)
.join(', ');
const orderByPerMetadataItemArray = operationSignatures
.map(
({ objectNameSingular }) =>
`$orderBy${capitalize(objectNameSingular)}: ${capitalize(
objectNameSingular,
)}OrderByInput`,
)
.join(', ');
const lastCursorPerMetadataItemArray = operationSignatures
.map(
({ objectNameSingular }) =>
`$lastCursor${capitalize(objectNameSingular)}: String`,
)
.join(', ');
const limitPerMetadataItemArray = operationSignatures
.map(
({ objectNameSingular }) =>
`$limit${capitalize(objectNameSingular)}: Int`,
)
.join(', ');
const queryKeyWithObjectMetadataItemArray = operationSignatures.map(
(queryKey) => {
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.nameSingular === queryKey.objectNameSingular,
);
if (isUndefined(objectMetadataItem)) {
throw new Error(
`Object metadata item not found for object name singular: ${queryKey.objectNameSingular}`,
);
}
return { ...queryKey, objectMetadataItem };
},
);
return gql`
query CombinedFindManyRecords(
${filterPerMetadataItemArray},
${orderByPerMetadataItemArray},
${lastCursorPerMetadataItemArray},
${limitPerMetadataItemArray}
) {
${queryKeyWithObjectMetadataItemArray
.map(
({ objectMetadataItem, fields }) =>
`${objectMetadataItem.namePlural}(filter: $filter${capitalize(
objectMetadataItem.nameSingular,
)}, orderBy: $orderBy${capitalize(
objectMetadataItem.nameSingular,
)}, first: $limit${capitalize(
objectMetadataItem.nameSingular,
)}, after: $lastCursor${capitalize(
objectMetadataItem.nameSingular,
)}){
edges {
node ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems: objectMetadataItems,
objectMetadataItem,
recordGqlFields: fields,
})}
cursor
}
pageInfo {
hasNextPage
startCursor
endCursor
}
totalCount
}`,
)
.join('\n')}
}
`;
};

View File

@ -1,92 +0,0 @@
import { gql } from '@apollo/client';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
import { capitalize } from '~/utils/string/capitalize';
export const useGenerateFindManyRecordsForMultipleMetadataItemsQuery = ({
targetObjectMetadataItems,
depth,
}: {
targetObjectMetadataItems: ObjectMetadataItem[];
depth?: number;
}) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const capitalizedObjectNameSingulars = targetObjectMetadataItems.map(
({ nameSingular }) => capitalize(nameSingular),
);
if (!isNonEmptyArray(capitalizedObjectNameSingulars)) {
return null;
}
const filterPerMetadataItemArray = capitalizedObjectNameSingulars
.map(
(capitalizedObjectNameSingular) =>
`$filter${capitalizedObjectNameSingular}: ${capitalizedObjectNameSingular}FilterInput`,
)
.join(', ');
const orderByPerMetadataItemArray = capitalizedObjectNameSingulars
.map(
(capitalizedObjectNameSingular) =>
`$orderBy${capitalizedObjectNameSingular}: ${capitalizedObjectNameSingular}OrderByInput`,
)
.join(', ');
const lastCursorPerMetadataItemArray = capitalizedObjectNameSingulars
.map(
(capitalizedObjectNameSingular) =>
`$lastCursor${capitalizedObjectNameSingular}: String`,
)
.join(', ');
const limitPerMetadataItemArray = capitalizedObjectNameSingulars
.map(
(capitalizedObjectNameSingular) =>
`$limit${capitalizedObjectNameSingular}: Int`,
)
.join(', ');
return gql`
query FindManyRecordsMultipleMetadataItems(
${filterPerMetadataItemArray},
${orderByPerMetadataItemArray},
${lastCursorPerMetadataItemArray},
${limitPerMetadataItemArray}
) {
${targetObjectMetadataItems
.map(
(objectMetadataItem) =>
`${objectMetadataItem.namePlural}(filter: $filter${capitalize(
objectMetadataItem.nameSingular,
)}, orderBy: $orderBy${capitalize(
objectMetadataItem.nameSingular,
)}, first: $limit${capitalize(
objectMetadataItem.nameSingular,
)}, after: $lastCursor${capitalize(
objectMetadataItem.nameSingular,
)}){
edges {
node ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems: objectMetadataItems,
objectMetadataItem,
depth,
})}
cursor
}
pageInfo {
hasNextPage
startCursor
endCursor
}
totalCount
}`,
)
.join('\n')}
}
`;
};

View File

@ -1,5 +1,5 @@
import { OrderBy } from '@/object-metadata/types/OrderBy';
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
import { Field } from '~/generated/graphql';
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
import { isDefined } from '~/utils/isDefined';
@ -10,7 +10,7 @@ import { Sort } from '../types/Sort';
export const turnSortsIntoOrderBy = (
sorts: Sort[],
fields: Pick<Field, 'id' | 'name'>[],
): OrderByField => {
): RecordGqlOperationOrderBy => {
const fieldsById = mapArrayToObject(fields, ({ id }) => id);
const sortsOrderBy = Object.fromEntries(
sorts

View File

@ -1 +0,0 @@
export type QueryFields = Record<string, any>;

View File

@ -1,10 +0,0 @@
import { QueryFields } from '@/object-record/query-keys/types/QueryFields';
import { ObjectRecordQueryVariables } from '@/object-record/types/ObjectRecordQueryVariables';
export type QueryKey = {
objectNameSingular: string;
variables: ObjectRecordQueryVariables;
depth?: number;
fields?: QueryFields; // Todo: Fields should be required
fieldsFactory?: (fieldsFactoryParam: any) => QueryFields;
};

View File

@ -1,4 +1,5 @@
import { RecordChip } from '@/object-record/components/RecordChip';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useRelationField } from '../../hooks/useRelationField';
@ -12,7 +13,7 @@ export const RelationFieldDisplay = () => {
objectNameSingular={
fieldDefinition.metadata.relationObjectMetadataNameSingular
}
record={fieldValue}
record={fieldValue as unknown as ObjectRecord} // Todo: Fix this type
maxWidth={maxWidth}
/>
);

View File

@ -1,4 +1,4 @@
import { BooleanFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { BooleanFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
export const isMatchingBooleanFilter = ({
booleanFilter,

View File

@ -1,4 +1,4 @@
import { CurrencyFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { CurrencyFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
export const isMatchingCurrencyFilter = ({
currencyFilter,

View File

@ -1,6 +1,6 @@
import { DateTime } from 'luxon';
import { DateFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { DateFilter } from '@/object-record//graphql/types/RecordGqlOperationFilter';
export const isMatchingDateFilter = ({
dateFilter,

View File

@ -1,4 +1,4 @@
import { FloatFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { FloatFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
export const isMatchingFloatFilter = ({
floatFilter,

View File

@ -1,4 +1,4 @@
import { StringFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { StringFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
export const isMatchingStringFilter = ({
stringFilter,

View File

@ -1,7 +1,7 @@
import {
UUIDFilter,
UUIDFilterValue,
} from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
export const isMatchingUUIDFilter = ({
uuidFilter,

View File

@ -10,12 +10,12 @@ import {
FloatFilter,
FullNameFilter,
NotObjectRecordFilter,
ObjectRecordQueryFilter,
OrObjectRecordFilter,
RecordGqlOperationFilter,
StringFilter,
URLFilter,
UUIDFilter,
} from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { isMatchingBooleanFilter } from '@/object-record/record-filter/utils/isMatchingBooleanFilter';
import { isMatchingCurrencyFilter } from '@/object-record/record-filter/utils/isMatchingCurrencyFilter';
import { isMatchingDateFilter } from '@/object-record/record-filter/utils/isMatchingDateFilter';
@ -27,15 +27,15 @@ import { isDefined } from '~/utils/isDefined';
import { isEmptyObject } from '~/utils/isEmptyObject';
const isAndFilter = (
filter: ObjectRecordQueryFilter,
filter: RecordGqlOperationFilter,
): filter is AndObjectRecordFilter => 'and' in filter && !!filter.and;
const isOrFilter = (
filter: ObjectRecordQueryFilter,
filter: RecordGqlOperationFilter,
): filter is OrObjectRecordFilter => 'or' in filter && !!filter.or;
const isNotFilter = (
filter: ObjectRecordQueryFilter,
filter: RecordGqlOperationFilter,
): filter is NotObjectRecordFilter => 'not' in filter && !!filter.not;
export const isRecordMatchingFilter = ({
@ -44,7 +44,7 @@ export const isRecordMatchingFilter = ({
objectMetadataItem,
}: {
record: any;
filter: ObjectRecordQueryFilter;
filter: RecordGqlOperationFilter;
objectMetadataItem: ObjectMetadataItem;
}): boolean => {
if (Object.keys(filter).length === 0) {

View File

@ -5,11 +5,11 @@ import {
CurrencyFilter,
DateFilter,
FloatFilter,
ObjectRecordQueryFilter,
RecordGqlOperationFilter,
StringFilter,
URLFilter,
UUIDFilter,
} from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { Field } from '~/generated/graphql';
@ -27,8 +27,8 @@ export type ObjectDropdownFilter = Omit<Filter, 'definition'> & {
export const turnObjectDropdownFilterIntoQueryFilter = (
rawUIFilters: ObjectDropdownFilter[],
fields: Pick<Field, 'id' | 'name'>[],
): ObjectRecordQueryFilter | undefined => {
const objectRecordFilters: ObjectRecordQueryFilter[] = [];
): RecordGqlOperationFilter | undefined => {
const objectRecordFilters: RecordGqlOperationFilter[] = [];
for (const rawUIFilter of rawUIFilters) {
const correspondingField = fields.find(

View File

@ -6,7 +6,7 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
import { useRecordBoardQueryFields } from '@/object-record/record-index/hooks/useRecordBoardQueryFields';
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
@ -56,7 +56,7 @@ export const useLoadRecordIndexBoard = ({
recordIndexIsCompactModeActiveState,
);
const queryFields = useRecordBoardQueryFields({
const recordGqlFields = useRecordBoardRecordGqlFields({
objectMetadataItem,
recordBoardId,
});
@ -71,7 +71,7 @@ export const useLoadRecordIndexBoard = ({
objectNameSingular,
filter: requestFilters,
orderBy,
queryFields,
recordGqlFields,
});
const { setRecordCountInCurrentView } =

View File

@ -5,7 +5,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
import { useRecordTableQueryFields } from '@/object-record/record-index/hooks/useRecordTableQueryFields';
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
@ -49,7 +49,7 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const params = useFindManyParams(objectNameSingular);
const queryFields = useRecordTableQueryFields();
const recordGqlFields = useRecordTableRecordGqlFields();
const {
records,
@ -59,7 +59,7 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
queryStateIdentifier,
} = useFindManyRecords({
...params,
queryFields,
recordGqlFields,
onCompleted: () => {
setLastRowVisible(false);
setIsRecordTableInitialLoading(false);

View File

@ -5,7 +5,7 @@ import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getOb
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { isDefined } from '~/utils/isDefined';
export const useRecordBoardQueryFields = ({
export const useRecordBoardRecordGqlFields = ({
objectMetadataItem,
recordBoardId,
}: {
@ -33,7 +33,7 @@ export const useRecordBoardQueryFields = ({
identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true;
}
const queryFields: Record<string, any> = {
const recordGqlFields: Record<string, any> = {
id: true,
...Object.fromEntries(
visibleFieldDefinitions.map((visibleFieldDefinition) => [
@ -45,8 +45,8 @@ export const useRecordBoardQueryFields = ({
};
if (isDefined(kanbanFieldMetadataName)) {
queryFields[kanbanFieldMetadataName] = true;
recordGqlFields[kanbanFieldMetadataName] = true;
}
return queryFields;
return recordGqlFields;
};

View File

@ -2,12 +2,12 @@ import { useRecoilValue } from 'recoil';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
export const useRecordTableQueryFields = () => {
export const useRecordTableRecordGqlFields = () => {
const { visibleTableColumnsSelector } = useRecordTableStates();
const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector());
const queryFields: Record<string, any> = {
const recordGqlFields: Record<string, any> = {
id: true,
...Object.fromEntries(
visibleTableColumns.map((column) => [column.metadata.fieldName, true]),
@ -15,5 +15,5 @@ export const useRecordTableQueryFields = () => {
position: true,
};
return queryFields;
return recordGqlFields;
};

View File

@ -139,7 +139,6 @@ export const useExportTableData = ({
// Todo: this needs to be done on click on the Export not button, not to be reactive. Use Lazy query for example
const { totalCount, records, fetchMoreRecords } = useFindManyRecords({
...usedFindManyParams,
depth: 0,
limit: pageSize,
onCompleted: (_data, options) => {
setHasNextPage(options?.pageInfo?.hasNextPage ?? false);

View File

@ -17,7 +17,6 @@ export const RecordShowContainer = ({
const { record: activity, loading } = useFindOneRecord<Activity>({
objectRecordId,
objectNameSingular,
depth: 3,
});
const setRecordStore = useSetRecoilState(

View File

@ -0,0 +1,11 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
export const findOneRecordForShowPageOperationSignatureFactory: RecordGqlOperationSignatureFactory =
({ objectMetadataItem }: { objectMetadataItem: ObjectMetadataItem }) => ({
objectNameSingular: CoreObjectNameSingular.Activity,
variables: {},
fields: generateDepthOneRecordGqlFields({ objectMetadataItem }),
});

View File

@ -2,6 +2,7 @@ import { useRecoilCallback } from 'recoil';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
@ -23,7 +24,7 @@ export const useSetRecordTableData = ({
return useRecoilCallback(
({ set, snapshot }) =>
<T extends { id: string }>(newEntityArray: T[], totalCount: number) => {
<T extends ObjectRecord>(newEntityArray: T[], totalCount: number) => {
for (const entity of newEntityArray) {
// TODO: refactor with scoped state later
const currentEntity = snapshot

View File

@ -9,7 +9,7 @@ import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useM
import { FieldMetadataType } from '~/generated/graphql';
const query = gql`
query FindManyRecordsMultipleMetadataItems(
query CombinedFindManyRecords(
$filterNameSingular: NameSingularFilterInput
$orderByNameSingular: NameSingularOrderByInput
$lastCursorNameSingular: String

View File

@ -3,12 +3,12 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsByNamePluralMapSelector } from '@/object-metadata/states/objectMetadataItemsByNamePluralMapSelector';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
import { isDefined } from '~/utils/isDefined';
export type MultiObjectRecordQueryResult = {
[namePlural: string]: ObjectRecordConnection;
[namePlural: string]: RecordGqlConnection;
};
export const useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray =

View File

@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/multiple-objects/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import {
MultiObjectRecordQueryResult,
@ -84,9 +84,13 @@ export const useMultiObjectSearchMatchesSearchFilterAndSelectedItemsQuery = ({
});
const multiSelectQueryForSelectedIds =
useGenerateFindManyRecordsForMultipleMetadataItemsQuery({
targetObjectMetadataItems: objectMetadataItemsUsedInSelectedIdsQuery,
depth: 0,
useGenerateCombinedFindManyRecordsQuery({
operationSignatures: objectMetadataItemsUsedInSelectedIdsQuery.map(
(objectMetadataItem) => ({
objectNameSingular: objectMetadataItem.nameSingular,
variables: {},
}),
),
});
const {

View File

@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/multiple-objects/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import {
MultiObjectRecordQueryResult,
@ -82,11 +82,14 @@ export const useMultiObjectSearchMatchesSearchFilterAndToSelectQuery = ({
limit,
});
const multiSelectQuery =
useGenerateFindManyRecordsForMultipleMetadataItemsQuery({
targetObjectMetadataItems: nonSystemObjectMetadataItems,
depth: 0,
});
const multiSelectQuery = useGenerateCombinedFindManyRecordsQuery({
operationSignatures: nonSystemObjectMetadataItems.map(
(objectMetadataItem) => ({
objectNameSingular: objectMetadataItem.nameSingular,
variables: {},
}),
),
});
const {
loading: toSelectAndMatchesSearchFilterObjectRecordsLoading,

View File

@ -3,7 +3,7 @@ import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useGenerateFindManyRecordsForMultipleMetadataItemsQuery } from '@/object-record/multiple-objects/hooks/useGenerateFindManyRecordsForMultipleMetadataItemsQuery';
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem';
import {
MultiObjectRecordQueryResult,
@ -67,8 +67,13 @@ export const useMultiObjectSearchSelectedItemsQuery = ({
});
const multiSelectQueryForSelectedIds =
useGenerateFindManyRecordsForMultipleMetadataItemsQuery({
targetObjectMetadataItems: objectMetadataItemsUsedInSelectedIdsQuery,
useGenerateCombinedFindManyRecordsQuery({
operationSignatures: objectMetadataItemsUsedInSelectedIdsQuery.map(
(objectMetadataItem) => ({
objectNameSingular: objectMetadataItem.nameSingular,
variables: {},
}),
),
});
const {

View File

@ -2,7 +2,7 @@ import { isNonEmptyString } from '@sniptt/guards';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { FieldMetadataType } from '~/generated/graphql';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
@ -16,7 +16,7 @@ export const useSearchFilterPerMetadataItem = ({
searchFilterValue: string;
}) => {
const searchFilterPerMetadataItemNameSingular =
Object.fromEntries<ObjectRecordQueryFilter>(
Object.fromEntries<RecordGqlOperationFilter>(
objectMetadataItems
.map((objectMetadataItem) => {
if (searchFilterValue === '') return null;
@ -24,7 +24,7 @@ export const useSearchFilterPerMetadataItem = ({
const labelIdentifierFieldMetadataItem =
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
let searchFilter: ObjectRecordQueryFilter = {};
let searchFilter: RecordGqlOperationFilter = {};
if (isDefined(labelIdentifierFieldMetadataItem)) {
switch (labelIdentifierFieldMetadataItem.type) {

View File

@ -1,4 +1,6 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier';
export type EntityForSelect = ObjectRecordIdentifier & { record: ObjectRecord };
export type EntityForSelect = ObjectRecordIdentifier & {
record: ObjectRecord;
};

View File

@ -1 +1,4 @@
export type ObjectRecord = Record<string, any> & { id: string };
export type ObjectRecord = Record<string, any> & {
id: string;
__typename: string;
};

View File

@ -1,7 +0,0 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export type ObjectRecordEdge<T extends ObjectRecord = ObjectRecord> = {
__typename?: string;
node: T;
cursor: string;
};

View File

@ -1,6 +0,0 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
export type ObjectRecordQueryResult<T extends ObjectRecord> = {
[objectNamePlural: string]: ObjectRecordConnection<T>;
};

View File

@ -1,8 +0,0 @@
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
export type ObjectRecordQueryVariables = {
filter?: ObjectRecordQueryFilter;
orderBy?: OrderByField;
limit?: number;
};

View File

@ -1,9 +1,7 @@
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { RecordGqlEdge } from '@/object-record/graphql/types/RecordGqlEdge';
export const filterUniqueRecordEdgesByCursor = <
RecordType extends { id: string },
>(
arrayToFilter: ObjectRecordEdge<RecordType>[],
export const filterUniqueRecordEdgesByCursor = (
arrayToFilter: RecordGqlEdge[],
) => {
const seenCursors = new Set();

View File

@ -2,20 +2,18 @@ import gql from 'graphql-tag';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { QueryFields } from '@/object-record/query-keys/types/QueryFields';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { capitalize } from '~/utils/string/capitalize';
export const generateFindManyRecordsQuery = ({
objectMetadataItem,
objectMetadataItems,
depth,
queryFields,
recordGqlFields,
computeReferences,
}: {
objectMetadataItem: ObjectMetadataItem;
objectMetadataItems: ObjectMetadataItem[];
queryFields?: QueryFields;
depth?: number;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
computeReferences?: boolean;
}) => gql`
query FindMany${capitalize(
@ -32,8 +30,7 @@ query FindMany${capitalize(
node ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem,
depth,
queryFields,
recordGqlFields,
computeReferences,
})}
cursor

View File

@ -1,14 +0,0 @@
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
import { isDefined } from '~/utils/isDefined';
export const getChildRelationArray = ({
childRelation,
}: {
childRelation: any;
}) => {
if (isDefined(childRelation.edges) && Array.isArray(childRelation.edges)) {
return childRelation.edges.map((edge: ObjectRecordEdge) => edge.node);
} else {
return childRelation;
}
};

View File

@ -1,9 +1,9 @@
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { isDefined } from '~/utils/isDefined';
export const makeAndFilterVariables = (
filters: (ObjectRecordQueryFilter | undefined)[],
): ObjectRecordQueryFilter | undefined => {
filters: (RecordGqlOperationFilter | undefined)[],
): RecordGqlOperationFilter | undefined => {
const definedFilters = filters.filter(isDefined);
if (!definedFilters.length) return undefined;

View File

@ -1,9 +1,9 @@
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { isDefined } from '~/utils/isDefined';
export const makeOrFilterVariables = (
filters: (ObjectRecordQueryFilter | undefined)[],
): ObjectRecordQueryFilter | undefined => {
filters: (RecordGqlOperationFilter | undefined)[],
): RecordGqlOperationFilter | undefined => {
const definedFilters = filters.filter(isDefined);
if (!definedFilters.length) return undefined;

View File

@ -1,8 +0,0 @@
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
export const mapEdgeToObjectRecord = <T extends ObjectRecord>(
objectRecordEdge: ObjectRecordEdge<T>,
) => {
return objectRecordEdge.node as T;
};

View File

@ -8,18 +8,12 @@ import { isDefined } from '~/utils/isDefined';
export const prefillRecord = <T extends ObjectRecord>({
objectMetadataItem,
input,
depth = 1,
}: {
objectMetadataItem: ObjectMetadataItem;
input: Record<string, unknown>;
depth?: number;
}) => {
return Object.fromEntries(
objectMetadataItem.fields
.filter(
(fieldMetadataItem) =>
depth > 0 || fieldMetadataItem.type !== 'RELATION',
)
.map((fieldMetadataItem) => {
const inputValue = input[fieldMetadataItem.name];