Query dynamic cache key computation (#12814)

In this PR:
- add query hashKey to ObjectMetadataItems query graphql cache to avoid
caching outdated queries
- improve performance by removing ResolveField at FieldLevel and adding
this at resolver level
This commit is contained in:
Charles Bochet
2025-06-24 14:04:00 +02:00
committed by GitHub
parent 48347095d2
commit 4ac208cf1c
6 changed files with 44 additions and 57 deletions

View File

@ -55,43 +55,6 @@ export class FieldMetadataResolver {
private readonly beforeUpdateOneField: BeforeUpdateOneField<UpdateFieldInput>,
) {}
@ResolveField(() => String, { nullable: true })
async label(
@Parent() fieldMetadata: FieldMetadataDTO,
@Context() context: I18nContext,
): Promise<string> {
return this.fieldMetadataService.resolveOverridableString(
fieldMetadata,
'label',
context.req.headers['x-locale'],
);
}
@ResolveField(() => String, { nullable: true })
async description(
@Parent() fieldMetadata: FieldMetadataDTO,
@Context() context: I18nContext,
): Promise<string> {
return this.fieldMetadataService.resolveOverridableString(
fieldMetadata,
'description',
context.req.headers['x-locale'],
);
}
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
@ResolveField(() => String, { nullable: true })
async icon(
@Parent() fieldMetadata: FieldMetadataDTO,
@Context() context: I18nContext,
): Promise<string> {
return this.fieldMetadataService.resolveOverridableString(
fieldMetadata,
'icon',
context.req.headers['x-locale'],
);
}
@UseGuards(SettingsPermissionsGuard(SettingPermissionType.DATA_MODEL))
@Mutation(() => FieldMetadataDTO)
async createOneField(

View File

@ -4,7 +4,7 @@ import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { i18n } from '@lingui/core';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import isEmpty from 'lodash.isempty';
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { APP_LOCALES } from 'twenty-shared/translations';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { DataSource, FindOneOptions, In, Repository } from 'typeorm';
@ -608,26 +608,18 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
return fieldMetadataInput;
}
async resolveOverridableString(
fieldMetadata: FieldMetadataDTO,
resolveOverridableString(
fieldMetadata: Pick<
FieldMetadataDTO,
'label' | 'description' | 'icon' | 'isCustom' | 'standardOverrides'
>,
labelKey: 'label' | 'description' | 'icon',
locale: keyof typeof APP_LOCALES | undefined,
): Promise<string> {
): string {
if (fieldMetadata.isCustom) {
return fieldMetadata[labelKey] ?? '';
}
if (!locale || locale === SOURCE_LOCALE) {
if (
fieldMetadata.standardOverrides &&
isDefined(fieldMetadata.standardOverrides[labelKey])
) {
return fieldMetadata.standardOverrides[labelKey] as string;
}
return fieldMetadata[labelKey] ?? '';
}
const translationValue =
// @ts-expect-error legacy noImplicitAny
fieldMetadata.standardOverrides?.translations?.[locale]?.[labelKey];

View File

@ -134,13 +134,14 @@ export class ObjectMetadataResolver {
async fieldsList(
@AuthWorkspace() workspace: Workspace,
@Parent() objectMetadata: ObjectMetadataDTO,
@Context() context: { loaders: IDataloaders },
@Context() context: { loaders: IDataloaders } & I18nContext,
): Promise<FieldMetadataDTO[]> {
try {
const fieldMetadataItems = await context.loaders.fieldMetadataLoader.load(
{
objectMetadata,
workspaceId: workspace.id,
locale: context.req.headers['x-locale'],
},
);