Optimize metadata queries (#7013)

In this PR:

1. Refactor guards to avoid duplicated queries: WorkspaceAuthGuard and
UserAuthGuard only check for existence of workspace and user in the
request without querying the database
This commit is contained in:
Charles Bochet
2024-09-13 19:11:32 +02:00
committed by Charles Bochet
parent cf8b1161cc
commit 523df5398a
132 changed files with 818 additions and 6372 deletions

View File

@ -39,32 +39,6 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
isNullable
createdAt
updatedAt
fromRelationMetadata {
id
relationType
toObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
isRemote
}
toFieldMetadataId
}
toRelationMetadata {
id
relationType
fromObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
isRemote
}
fromFieldMetadataId
}
defaultValue
options
relationDefinition {

View File

@ -39,29 +39,27 @@ export const query = gql`
isNullable
createdAt
updatedAt
fromRelationMetadata {
id
relationType
toObjectMetadata {
relationDefinition {
relationId
direction
sourceObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
toFieldMetadataId
}
toRelationMetadata {
id
relationType
fromObjectMetadata {
sourceFieldMetadata {
id
name
}
targetObjectMetadata {
id
dataSourceId
nameSingular
namePlural
isSystem
}
fromFieldMetadataId
targetFieldMetadata {
id
name
}
}
defaultValue
options

View File

@ -1,10 +1,10 @@
import { ReactNode } from 'react';
import { MockedProvider } from '@apollo/client/testing';
import { act, renderHook } from '@testing-library/react';
import { ReactNode } from 'react';
import { RecoilRoot } from 'recoil';
import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCreateOneRelationMetadataItem';
import { RelationMetadataType } from '~/generated/graphql';
import { RelationDefinitionType } from '~/generated/graphql';
import {
query,
@ -42,7 +42,7 @@ describe('useCreateOneRelationMetadataItem', () => {
await act(async () => {
const res = await result.current.createOneRelationMetadataItem({
relationType: RelationMetadataType.OneToOne,
relationType: RelationDefinitionType.OneToOne,
field: {
label: 'label',
},

View File

@ -1,11 +1,7 @@
import { useRecoilCallback } from 'recoil';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { RelationType } from '@/settings/data-model/types/RelationType';
import {
FieldMetadataType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldMetadataItem } from '../types/FieldMetadataItem';
@ -17,39 +13,19 @@ export const useGetRelationMetadata = () =>
}: {
fieldMetadataItem: Pick<
FieldMetadataItem,
'fromRelationMetadata' | 'toRelationMetadata' | 'type'
'type' | 'relationDefinition'
>;
}) => {
if (fieldMetadataItem.type !== FieldMetadataType.Relation) return null;
const relationMetadata =
fieldMetadataItem.fromRelationMetadata ||
fieldMetadataItem.toRelationMetadata;
const relationDefinition = fieldMetadataItem.relationDefinition;
if (!relationMetadata) return null;
const relationFieldMetadataId =
'toFieldMetadataId' in relationMetadata
? relationMetadata.toFieldMetadataId
: relationMetadata.fromFieldMetadataId;
if (!relationFieldMetadataId) return null;
const relationType =
relationMetadata.relationType === RelationMetadataType.OneToMany &&
fieldMetadataItem.toRelationMetadata
? 'MANY_TO_ONE'
: (relationMetadata.relationType as RelationType);
const relationObjectMetadataNameSingular =
'toObjectMetadata' in relationMetadata
? relationMetadata.toObjectMetadata.nameSingular
: relationMetadata.fromObjectMetadata.nameSingular;
if (!relationDefinition) return null;
const relationObjectMetadataItem = snapshot
.getLoadable(
objectMetadataItemFamilySelector({
objectName: relationObjectMetadataNameSingular,
objectName: relationDefinition.targetObjectMetadata.nameSingular,
objectNameType: 'singular',
}),
)
@ -59,7 +35,7 @@ export const useGetRelationMetadata = () =>
const relationFieldMetadataItem =
relationObjectMetadataItem.fields.find(
(field) => field.id === relationFieldMetadataId,
(field) => field.id === relationDefinition.targetFieldMetadata.id,
);
if (!relationFieldMetadataItem) return null;
@ -67,7 +43,7 @@ export const useGetRelationMetadata = () =>
return {
relationFieldMetadataItem,
relationObjectMetadataItem,
relationType,
relationType: relationDefinition.direction,
};
},
[],

View File

@ -3,7 +3,6 @@ import { ThemeColor } from 'twenty-ui';
import {
Field,
Object as MetadataObject,
Relation,
RelationDefinition,
RelationDefinitionType,
} from '~/generated-metadata/graphql';
@ -18,31 +17,9 @@ export type FieldMetadataItemOption = {
export type FieldMetadataItem = Omit<
Field,
| '__typename'
| 'fromRelationMetadata'
| 'toRelationMetadata'
| 'defaultValue'
| 'options'
| 'settings'
| 'relationDefinition'
'__typename' | 'defaultValue' | 'options' | 'settings' | 'relationDefinition'
> & {
__typename?: string;
fromRelationMetadata?:
| (Pick<Relation, 'id' | 'toFieldMetadataId' | 'relationType'> & {
toObjectMetadata: Pick<
Relation['toObjectMetadata'],
'id' | 'nameSingular' | 'namePlural' | 'isSystem' | 'isRemote'
>;
})
| null;
toRelationMetadata?:
| (Pick<Relation, 'id' | 'fromFieldMetadataId' | 'relationType'> & {
fromObjectMetadata: Pick<
Relation['fromObjectMetadata'],
'id' | 'nameSingular' | 'namePlural' | 'isSystem' | 'isRemote'
>;
})
| null;
defaultValue?: any;
options?: FieldMetadataItemOption[] | null;
relationDefinition?: {

View File

@ -1,5 +1,4 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
@ -20,17 +19,15 @@ export const formatFieldMetadataItemAsFieldDefinition = ({
labelWidth,
}: FieldMetadataItemAsFieldDefinitionProps): FieldDefinition<FieldMetadata> => {
const relationObjectMetadataItem =
field.toRelationMetadata?.fromObjectMetadata ||
field.fromRelationMetadata?.toObjectMetadata;
field.relationDefinition?.targetObjectMetadata;
const relationFieldMetadataId =
field.toRelationMetadata?.fromFieldMetadataId ||
field.fromRelationMetadata?.toFieldMetadataId;
field.relationDefinition?.targetFieldMetadata.id;
const fieldDefintionMetadata = {
fieldName: field.name,
placeHolder: field.label,
relationType: parseFieldRelationType(field),
relationType: field.relationDefinition?.direction,
relationFieldMetadataId,
relationObjectMetadataNameSingular:
relationObjectMetadataItem?.nameSingular ?? '',

View File

@ -2,6 +2,7 @@ import { RelationType } from '@/settings/data-model/types/RelationType';
import {
CreateRelationInput,
Field,
RelationDefinitionType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
@ -24,8 +25,8 @@ export const formatRelationMetadataInput = (
// => Transform into ONE_TO_MANY and invert "from" and "to" data.
const isManyToOne = input.relationType === 'MANY_TO_ONE';
const relationType = isManyToOne
? RelationMetadataType.OneToMany
: (input.relationType as RelationMetadataType);
? RelationDefinitionType.OneToMany
: (input.relationType as RelationDefinitionType);
const { field: fromField, objectMetadataId: fromObjectMetadataId } =
isManyToOne ? input.connect : input;
const { field: toField, objectMetadataId: toObjectMetadataId } = isManyToOne
@ -51,7 +52,7 @@ export const formatRelationMetadataInput = (
fromLabel,
fromName,
fromObjectMetadataId,
relationType,
relationType: relationType as unknown as RelationMetadataType,
toDescription,
toIcon,
toLabel,

View File

@ -4,7 +4,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import {
FieldMetadataType,
RelationMetadataType,
RelationDefinitionType,
} from '~/generated-metadata/graphql';
import { FieldMetadataItem } from '../types/FieldMetadataItem';
@ -17,10 +17,7 @@ export const mapFieldMetadataToGraphQLQuery = ({
computeReferences = false,
}: {
objectMetadataItems: ObjectMetadataItem[];
field: Pick<
FieldMetadataItem,
'name' | 'type' | 'toRelationMetadata' | 'fromRelationMetadata'
>;
field: Pick<FieldMetadataItem, 'name' | 'type' | 'relationDefinition'>;
relationrecordFields?: Record<string, any>;
computeReferences?: boolean;
}): any => {
@ -49,12 +46,12 @@ export const mapFieldMetadataToGraphQLQuery = ({
if (
fieldType === FieldMetadataType.Relation &&
field.toRelationMetadata?.relationType === RelationMetadataType.OneToMany
field.relationDefinition?.direction === RelationDefinitionType.ManyToOne
) {
const relationMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id ===
(field.toRelationMetadata as any)?.fromObjectMetadata?.id,
field.relationDefinition?.targetObjectMetadata.id,
);
if (isUndefined(relationMetadataItem)) {
@ -73,12 +70,12 @@ ${mapObjectMetadataToGraphQLQuery({
if (
fieldType === FieldMetadataType.Relation &&
field.fromRelationMetadata?.relationType === RelationMetadataType.OneToMany
field.relationDefinition?.direction === RelationDefinitionType.OneToMany
) {
const relationMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id ===
(field.fromRelationMetadata as any)?.toObjectMetadata?.id,
field.relationDefinition?.targetObjectMetadata.id,
);
if (isUndefined(relationMetadataItem)) {

View File

@ -1,55 +0,0 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { FieldDefinitionRelationType } from '@/object-record/record-field/types/FieldDefinition';
import {
FieldMetadataType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
export const parseFieldRelationType = (
field: FieldMetadataItem | undefined,
): FieldDefinitionRelationType | undefined => {
if (!field || field.type !== FieldMetadataType.Relation) return;
const config: Record<
RelationMetadataType,
{ from: FieldDefinitionRelationType; to: FieldDefinitionRelationType }
> = {
[RelationMetadataType.ManyToMany]: {
from: 'FROM_MANY_OBJECTS',
to: 'TO_MANY_OBJECTS',
},
[RelationMetadataType.OneToMany]: {
from: 'FROM_MANY_OBJECTS',
to: 'TO_ONE_OBJECT',
},
[RelationMetadataType.ManyToOne]: {
from: 'TO_ONE_OBJECT',
to: 'FROM_MANY_OBJECTS',
},
[RelationMetadataType.OneToOne]: {
from: 'FROM_ONE_OBJECT',
to: 'TO_ONE_OBJECT',
},
};
if (
isDefined(field.fromRelationMetadata) &&
field.fromRelationMetadata.relationType in config
) {
return config[field.fromRelationMetadata.relationType].from;
}
if (
isDefined(field.toRelationMetadata) &&
field.toRelationMetadata.relationType in config
) {
return config[field.toRelationMetadata.relationType].to;
}
throw new Error(
`Cannot determine field relation type for field : ${JSON.stringify(
field,
)}.`,
);
};

View File

@ -6,7 +6,6 @@ import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metada
import {
FieldMetadataType,
RelationDefinitionType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStringSchema';
@ -16,24 +15,6 @@ export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
createdAt: z.string().datetime(),
defaultValue: z.any().optional(),
description: z.string().trim().nullable().optional(),
fromRelationMetadata: z
.object({
__typename: z.literal('relation').optional(),
id: z.string().uuid(),
relationType: z.nativeEnum(RelationMetadataType),
toFieldMetadataId: z.string().uuid(),
toObjectMetadata: z.object({
__typename: z.literal('object').optional(),
dataSourceId: z.string().uuid(),
id: z.string().uuid(),
isRemote: z.boolean(),
isSystem: z.boolean(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),
}),
})
.nullable()
.optional(),
icon: z.string().startsWith('Icon').trim().nullable(),
id: z.string().uuid(),
isActive: z.boolean(),
@ -84,24 +65,6 @@ export const fieldMetadataItemSchema = (existingLabels?: string[]) => {
})
.nullable()
.optional(),
toRelationMetadata: z
.object({
__typename: z.literal('relation').optional(),
id: z.string().uuid(),
relationType: z.nativeEnum(RelationMetadataType),
fromFieldMetadataId: z.string().uuid(),
fromObjectMetadata: z.object({
__typename: z.literal('object').optional(),
id: z.string().uuid(),
dataSourceId: z.string().uuid(),
isRemote: z.boolean(),
isSystem: z.boolean(),
namePlural: z.string().trim().min(1),
nameSingular: z.string().trim().min(1),
}),
})
.nullable()
.optional(),
type: z.nativeEnum(FieldMetadataType),
updatedAt: z.string().datetime(),
}) satisfies z.ZodType<FieldMetadataItem>;