Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { triggerUpdateGroupByQueriesOptimisticEffect } from '@/apollo/optimistic
import { triggerUpdateRelationsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationsOptimisticEffect';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
import { isObjectRecordConnection } from '@/object-record/cache/utils/isObjectRecordConnection';
import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/isObjectRecordConnectionWithRefs';
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
Expand Down Expand Up @@ -34,16 +35,36 @@ export const triggerDestroyRecordsOptimisticEffect = ({
rootQueryCachedResponse,
{ readField },
) => {
const rootQueryCachedResponseIsNotACachedObjectRecordConnection =
!isObjectRecordConnectionWithRefs(
if (
!isObjectRecordConnection(
objectMetadataItem.nameSingular,
rootQueryCachedResponse,
);

if (rootQueryCachedResponseIsNotACachedObjectRecordConnection) {
)
) {
return rootQueryCachedResponse;
}

const totalCount = readField<number | undefined>(
'totalCount',
rootQueryCachedResponse,
);

const newTotalCount = isDefined(totalCount)
? Math.max(totalCount - recordsToDestroy.length, 0)
: undefined;

if (
!isObjectRecordConnectionWithRefs(
objectMetadataItem.nameSingular,
rootQueryCachedResponse,
)
) {
return {
...rootQueryCachedResponse,
totalCount: newTotalCount,
};
}

const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;

const recordIdsToDestroy = recordsToDestroy.map(({ id }) => id);
Expand All @@ -52,11 +73,6 @@ export const triggerDestroyRecordsOptimisticEffect = ({
rootQueryCachedObjectRecordConnection,
);

const totalCount = readField<number | undefined>(
'totalCount',
rootQueryCachedObjectRecordConnection,
);

const nextCachedEdges =
cachedEdges?.filter((cachedEdge) => {
const nodeId = readField<string>('id', cachedEdge.node);
Expand All @@ -65,14 +81,15 @@ export const triggerDestroyRecordsOptimisticEffect = ({
}) || [];

if (nextCachedEdges.length === cachedEdges?.length)
return rootQueryCachedObjectRecordConnection;
return {
...rootQueryCachedObjectRecordConnection,
totalCount: newTotalCount,
};

return {
...rootQueryCachedObjectRecordConnection,
edges: nextCachedEdges,
totalCount: isDefined(totalCount)
? totalCount - recordIdsToDestroy.length
: undefined,
totalCount: newTotalCount,
};
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
import { type FieldMetadataItemRelation } from '@/object-metadata/types/FieldMetadataItemRelation';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById';
import { isObjectRecordConnection } from '@/object-record/cache/utils/isObjectRecordConnection';
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { type ApolloCache } from '@apollo/client';
import { isArray } from '@sniptt/guards';
import { FieldMetadataType, type ObjectPermissions } from 'twenty-shared/types';
import {
FieldMetadataType,
RelationType,
type ObjectPermissions,
} from 'twenty-shared/types';
import {
computeMorphRelationFieldName,
CustomError,
Expand Down Expand Up @@ -147,12 +150,12 @@ const triggerUpdateRelationOptimisticEffect = ({
}

const currentFieldValueOnSourceRecord:
| RecordGqlConnection
| RecordGqlConnectionEdgesRequired
| RecordGqlNode
| null = currentSourceRecord?.[fieldMetadataItemOnSourceRecord.name];

const updatedFieldValueOnSourceRecord:
| RecordGqlConnection
| RecordGqlConnectionEdgesRequired
| RecordGqlNode
| null = updatedSourceRecord?.[fieldMetadataItemOnSourceRecord.name];

Expand All @@ -179,6 +182,7 @@ const triggerUpdateRelationOptimisticEffect = ({
CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH.includes(
targetObjectMetadata.nameSingular as CoreObjectNameSingular,
);

const gqlFieldNameOnTargetRecord =
targetFieldMetadataFullObject.type === FieldMetadataType.RELATION
? targetFieldMetadataFullObject.name
Expand All @@ -189,7 +193,10 @@ const triggerUpdateRelationOptimisticEffect = ({
sourceObjectMetadataItem.nameSingular,
targetObjectMetadataNamePlural: sourceObjectMetadataItem.namePlural,
});
if (shouldCascadeDeleteTargetRecords) {
if (
shouldCascadeDeleteTargetRecords &&
targetRecordsToDetachFrom.length > 0
) {
triggerDestroyRecordsOptimisticEffect({
cache,
objectMetadataItem: fullTargetObjectMetadataItem,
Expand All @@ -198,7 +205,10 @@ const triggerUpdateRelationOptimisticEffect = ({
upsertRecordsInStore,
objectPermissionsByObjectMetadataId,
});
} else if (isDefined(currentSourceRecord)) {
} else if (
isDefined(currentSourceRecord) &&
targetRecordsToDetachFrom.length > 0
) {
targetRecordsToDetachFrom.forEach((targetRecordToDetachFrom) => {
triggerDetachRelationOptimisticEffect({
cache,
Expand Down Expand Up @@ -306,12 +316,12 @@ const triggerUpdateMorphRelationOptimisticEffect = ({
}

const currentFieldValueOnSourceRecord:
| RecordGqlConnection
| RecordGqlConnectionEdgesRequired
| RecordGqlNode
| null = currentSourceRecord?.[gqlFieldMorphRelation];

const updatedFieldValueOnSourceRecord:
| RecordGqlConnection
| RecordGqlConnectionEdgesRequired
| RecordGqlNode
| null = updatedSourceRecord?.[gqlFieldMorphRelation];

Expand All @@ -338,7 +348,10 @@ const triggerUpdateMorphRelationOptimisticEffect = ({
CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH.includes(
targetObjectMetadata.nameSingular as CoreObjectNameSingular,
);
if (shouldCascadeDeleteTargetRecords) {
if (
shouldCascadeDeleteTargetRecords &&
targetRecordsToDetachFrom.length > 0
) {
triggerDestroyRecordsOptimisticEffect({
cache,
objectMetadataItem: fullTargetObjectMetadataItem,
Expand All @@ -347,7 +360,10 @@ const triggerUpdateMorphRelationOptimisticEffect = ({
objectPermissionsByObjectMetadataId,
upsertRecordsInStore,
});
} else if (isDefined(currentSourceRecord)) {
} else if (
isDefined(currentSourceRecord) &&
targetRecordsToDetachFrom.length > 0
) {
targetRecordsToDetachFrom.forEach((targetRecordToDetachFrom) => {
triggerDetachRelationOptimisticEffect({
cache,
Expand Down Expand Up @@ -387,7 +403,7 @@ const triggerUpdateMorphRelationOptimisticEffect = ({
};

const extractTargetRecordsFromRelation = (
value: RecordGqlConnection | RecordGqlNode | null,
value: RecordGqlConnectionEdgesRequired | RecordGqlNode | null,
relation: FieldMetadataItemRelation,
): RecordGqlNode[] => {
// TODO investigate on the root cause of array injection here, should never occurs
Expand All @@ -399,9 +415,9 @@ const extractTargetRecordsFromRelation = (
if (!isDefined(relation)) {
throw new Error('Relation found is undefined');
}
if (isObjectRecordConnection(relation, value)) {
return value.edges.map(({ node }) => node);
if (relation.type === RelationType.ONE_TO_MANY) {
return value.edges.map(({ node }: { node: RecordGqlNode }) => node);
}

return [value];
return [value as RecordGqlNode];
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const ApolloCoreProvider = ({
}) => {
const apolloCoreClient = useApolloFactory();

window.__APOLLO_CLIENT__ = apolloCoreClient;

return (
<ApolloCoreClientContext.Provider value={apolloCoreClient}>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { type RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';

export type RecordGqlRefConnection = Omit<RecordGqlConnection, 'edges'> & {
export type RecordGqlRefConnection = Omit<
RecordGqlConnectionEdgesRequired,
'edges'
> & {
edges: RecordGqlRefEdge[];
};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
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 { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';
import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';

Expand Down Expand Up @@ -40,5 +40,5 @@ export const getRecordConnectionFromRecords = <T extends ObjectRecord>({
}),
...(withPageInfo && { pageInfo: getEmptyPageInfo() }),
...(withPageInfo && { totalCount: records.length }),
} as RecordGqlConnection;
} as RecordGqlConnectionEdgesRequired;
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';

export const getRecordsFromRecordConnection = <T extends ObjectRecord>({
recordConnection,
}: {
recordConnection: RecordGqlConnection;
recordConnection: RecordGqlConnectionEdgesRequired;
}): T[] => {
return recordConnection?.edges?.map((edge) =>
getRecordFromRecordNode<T>({ recordNode: edge.node }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type StoreValue } from '@apollo/client';
import { z } from 'zod';

import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { assertUnreachable } from 'twenty-shared/utils';
import { RelationType } from '~/generated-metadata/graphql';
import { capitalize } from 'twenty-shared/utils';

export const isObjectRecordConnection = (
relation: NonNullable<FieldMetadataItem['relation']>,
value: unknown,
): value is RecordGqlConnection => {
switch (relation.type) {
case RelationType.ONE_TO_MANY: {
return true;
}
case RelationType.MANY_TO_ONE:
return false;
default: {
return assertUnreachable(relation.type);
}
}
objectNameSingular: string,
storeValue: StoreValue,
): storeValue is RecordGqlConnection => {
const objectConnectionTypeName = `${capitalize(
objectNameSingular,
)}Connection`;
const objectEdgeTypeName = `${capitalize(objectNameSingular)}Edge`;
const cachedObjectConnectionSchema = z.object({
__typename: z.literal(objectConnectionTypeName),
edges: z.array(
z
.object({
__typename: z.literal(objectEdgeTypeName),
node: z.object({
__ref: z.string().startsWith(`${capitalize(objectNameSingular)}:`),
}),
})
.optional(),
),
});
const cachedConnectionValidation =
cachedObjectConnectionSchema.safeParse(storeValue);

return cachedConnectionValidation.success;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { type Nullable } from 'twenty-ui/utilities';

export type RecordGqlConnection = {
__typename?: string;
edges: RecordGqlEdge[];
pageInfo: {
edges?: RecordGqlEdge[];
pageInfo?: {
__typename?: Nullable<string>;
hasNextPage?: Nullable<boolean>;
hasPreviousPage?: Nullable<boolean>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';

export type RecordGqlConnectionEdgesRequired = Omit<
RecordGqlConnection,
'edges'
> &
Required<Pick<RecordGqlConnection, 'edges'>>;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';

export type RecordGqlOperationFindDuplicatesResult = {
[objectNamePlural: string]: RecordGqlConnection[];
[objectNamePlural: string]: RecordGqlConnectionEdgesRequired[];
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';

export type RecordGqlOperationFindManyResult = {
[objectNamePlural: string]: RecordGqlConnection;
[objectNamePlural: string]: RecordGqlConnectionEdgesRequired;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
import { type RecordGqlConnectionEdgesRequired } from '@/object-record/graphql/types/RecordGqlConnectionEdgesRequired';

export type RecordGqlOperationSearchResult = {
[objectNamePlural: string]: RecordGqlConnection;
[objectNamePlural: string]: RecordGqlConnectionEdgesRequired;
};
Loading
Loading