fix(metadata): remove deprecated isCustom from Objects and Fields (#21799)
## Context Follow-up to #21228, which deprecated `isCustom` on object/field metadata but kept it exposed because the frontend still relied on it. This removes it from the GraphQL API and the frontend entirely. ## Implementation ### Server - Remove `isCustom` `@Field` from the `Object`, `Field`, and `MinimalObjectMetadata` GraphQL types - Remove the `isCustom` `@ResolveField` resolvers and the `isCustomLoader` dataloader (+ payload/interface) - Remove `isCustom` as an internal `@HideField()` on the Object/Field DTOs used by the i18n standard-override gate > Use an explicit isStandard instead (which is the correct gating) ### Frontend - Add `getIsMetadataItemCustom` helper + `useGetIsMetadataItemCustom` hook: an item is custom when `applicationId === currentWorkspace.workspaceCustomApplication.id` - Migrate all consumers off `objectMetadataItem.isCustom` / `fieldMetadataItem.isCustom`; `isRecordFieldReadOnly` now takes a precomputed `isFieldCustom` - Drop `isCustom` from the metadata fragment/mutations/minimal query, FE types, zod schemas, and mock generators; regenerate GraphQL types ## Notes - Breaking change on the (already-deprecated) `Object.isCustom` / `Field.isCustom` GraphQL fields and the `isCustom` filter - FE semantic is "belongs to the workspace custom app" (third-party-app objects/fields are treated as non-custom) - `isCustom` on IndexMetadata / View / Skill / Agent is a separate column and is untouched - Breaking changes on REST metadata API
This commit is contained in:
+4
@@ -50,6 +50,8 @@ export class CommandMenuItemResolver {
|
||||
commandMenuItem,
|
||||
fieldName: 'label',
|
||||
objectMetadataLoader: context.loaders.objectMetadataLoader,
|
||||
standardApplicationIdLoader:
|
||||
context.loaders.standardApplicationIdLoader,
|
||||
workspaceId: workspace.id,
|
||||
locale: context.req.locale,
|
||||
})) ?? ''
|
||||
@@ -66,6 +68,7 @@ export class CommandMenuItemResolver {
|
||||
commandMenuItem,
|
||||
fieldName: 'shortLabel',
|
||||
objectMetadataLoader: context.loaders.objectMetadataLoader,
|
||||
standardApplicationIdLoader: context.loaders.standardApplicationIdLoader,
|
||||
workspaceId: workspace.id,
|
||||
locale: context.req.locale,
|
||||
});
|
||||
@@ -81,6 +84,7 @@ export class CommandMenuItemResolver {
|
||||
commandMenuItem,
|
||||
fieldName: 'icon',
|
||||
objectMetadataLoader: context.loaders.objectMetadataLoader,
|
||||
standardApplicationIdLoader: context.loaders.standardApplicationIdLoader,
|
||||
workspaceId: workspace.id,
|
||||
locale: context.req.locale,
|
||||
});
|
||||
|
||||
+20
-3
@@ -6,7 +6,10 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { type ObjectMetadataLoaderPayload } from 'src/engine/dataloaders/dataloader.service';
|
||||
import {
|
||||
type ObjectMetadataLoaderPayload,
|
||||
type StandardApplicationIdLoaderPayload,
|
||||
} from 'src/engine/dataloaders/dataloader.service';
|
||||
import {
|
||||
CommandMenuItemException,
|
||||
CommandMenuItemExceptionCode,
|
||||
@@ -15,10 +18,9 @@ import { type CommandMenuItemDTO } from 'src/engine/metadata-modules/command-men
|
||||
import { type CreateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/create-command-menu-item.input';
|
||||
import { type UpdateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/update-command-menu-item.input';
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
import { isObjectMetadataCommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/utils/is-object-metadata-command-menu-item-payload.util';
|
||||
import { interpolateNavigationCommandMenuItemField } from 'src/engine/metadata-modules/command-menu-item/utils/interpolate-navigation-command-menu-item-field.util';
|
||||
import { isObjectMetadataCommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/utils/is-object-metadata-command-menu-item-payload.util';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-create-command-menu-item-input-to-flat-command-menu-item-to-create.util';
|
||||
import { fromDeleteCommandMenuItemInputToFlatCommandMenuItemOrThrow } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-delete-command-menu-item-input-to-flat-command-menu-item-or-throw.util';
|
||||
import { fromFlatCommandMenuItemToCommandMenuItemDto } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-flat-command-menu-item-to-command-menu-item-dto.util';
|
||||
@@ -26,6 +28,7 @@ import { fromUpdateCommandMenuItemInputToFlatCommandMenuItemToUpdateOrThrow } fr
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
||||
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
@@ -445,6 +448,7 @@ export class CommandMenuItemService {
|
||||
commandMenuItem,
|
||||
fieldName,
|
||||
objectMetadataLoader,
|
||||
standardApplicationIdLoader,
|
||||
workspaceId,
|
||||
locale,
|
||||
}: {
|
||||
@@ -454,6 +458,10 @@ export class CommandMenuItemService {
|
||||
ObjectMetadataLoaderPayload,
|
||||
ObjectMetadataDTO | null
|
||||
>;
|
||||
standardApplicationIdLoader: DataLoader<
|
||||
StandardApplicationIdLoaderPayload,
|
||||
string
|
||||
>;
|
||||
workspaceId: string;
|
||||
locale: keyof typeof APP_LOCALES | undefined;
|
||||
}): Promise<string | undefined> {
|
||||
@@ -463,10 +471,19 @@ export class CommandMenuItemService {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const standardApplicationId = await standardApplicationIdLoader.load({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const isStandardApp = isDefined(objectMetadata)
|
||||
? objectMetadata.applicationId === standardApplicationId
|
||||
: false;
|
||||
|
||||
return interpolateNavigationCommandMenuItemField({
|
||||
commandMenuItem,
|
||||
fieldName,
|
||||
objectMetadata,
|
||||
isStandardApp,
|
||||
locale,
|
||||
i18nInstance: this.i18nService.getI18nInstance(locale ?? SOURCE_LOCALE),
|
||||
});
|
||||
|
||||
+10
@@ -47,6 +47,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -59,6 +60,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'shortLabel',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -71,6 +73,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'icon',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -90,6 +93,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: null,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -102,6 +106,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: null,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -119,6 +124,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'shortLabel',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -137,6 +143,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: customObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -154,6 +161,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'icon',
|
||||
objectMetadata: customObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -171,6 +179,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: null,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
@@ -188,6 +197,7 @@ describe('interpolateNavigationCommandMenuItemField', () => {
|
||||
fieldName: 'label',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
locale: undefined,
|
||||
isStandardApp: true,
|
||||
i18nInstance: mockI18nInstance,
|
||||
});
|
||||
|
||||
|
||||
+4
-2
@@ -9,16 +9,17 @@ export type NavigationInterpolationObjectMetadata = {
|
||||
labelSingular: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
isCustom: boolean;
|
||||
standardOverrides?: ObjectStandardOverridesDTO | null;
|
||||
};
|
||||
|
||||
export const buildNavigationInterpolationContext = ({
|
||||
objectMetadata,
|
||||
isStandardApp,
|
||||
locale,
|
||||
i18nInstance,
|
||||
}: {
|
||||
objectMetadata: NavigationInterpolationObjectMetadata;
|
||||
isStandardApp: boolean;
|
||||
locale: keyof typeof APP_LOCALES | undefined;
|
||||
i18nInstance: I18n;
|
||||
}): Record<string, unknown> => {
|
||||
@@ -27,7 +28,6 @@ export const buildNavigationInterpolationContext = ({
|
||||
labelSingular: objectMetadata.labelSingular,
|
||||
description: objectMetadata.description ?? undefined,
|
||||
icon: objectMetadata.icon ?? undefined,
|
||||
isCustom: objectMetadata.isCustom,
|
||||
standardOverrides: objectMetadata.standardOverrides ?? undefined,
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ export const buildNavigationInterpolationContext = ({
|
||||
'labelPlural',
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
);
|
||||
|
||||
const resolvedIcon = resolveObjectMetadataStandardOverride(
|
||||
@@ -43,6 +44,7 @@ export const buildNavigationInterpolationContext = ({
|
||||
'icon',
|
||||
locale,
|
||||
i18nInstance,
|
||||
isStandardApp,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+3
@@ -16,12 +16,14 @@ export const interpolateNavigationCommandMenuItemField = ({
|
||||
commandMenuItem,
|
||||
fieldName,
|
||||
objectMetadata,
|
||||
isStandardApp,
|
||||
locale,
|
||||
i18nInstance,
|
||||
}: {
|
||||
commandMenuItem: CommandMenuItemDTO;
|
||||
fieldName: 'label' | 'shortLabel' | 'icon';
|
||||
objectMetadata: ObjectMetadataDTO | null;
|
||||
isStandardApp: boolean;
|
||||
locale: keyof typeof APP_LOCALES | undefined;
|
||||
i18nInstance: I18n;
|
||||
}): string | undefined => {
|
||||
@@ -44,6 +46,7 @@ export const interpolateNavigationCommandMenuItemField = ({
|
||||
|
||||
const context = buildNavigationInterpolationContext({
|
||||
objectMetadata,
|
||||
isStandardApp,
|
||||
locale,
|
||||
i18nInstance,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user