## 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,