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:
Weiko
2026-06-19 01:19:49 +02:00
committed by GitHub
parent 7b5ee8a7bc
commit 9f30915f6f
109 changed files with 544 additions and 1071 deletions
@@ -16,7 +16,7 @@ export type SettingsDataModelObjectPreviewProps = {
| 'icon'
| 'labelSingular'
| 'labelPlural'
| 'isCustom'
| 'applicationId'
| 'isRemote'
| 'nameSingular'
| 'color'
@@ -64,7 +64,7 @@ type SettingsDataModelObjectPreviewItemProps = {
| 'icon'
| 'labelSingular'
| 'labelPlural'
| 'isCustom'
| 'applicationId'
| 'isRemote'
| 'nameSingular'
| 'color'
@@ -1,3 +1,4 @@
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
@@ -80,21 +81,25 @@ export const SettingsDataModelObjectAboutForm = ({
useFormContext<SettingsDataModelObjectAboutFormValues>();
const { t } = useLingui();
const navigateSettings = useNavigateSettings();
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
const isLabelSyncedWithName = watch('isLabelSyncedWithName');
const labelSingular = watch('labelSingular');
const labelPlural = watch('labelPlural');
const isStandardObject =
isDefined(objectMetadataItem?.isCustom) && !objectMetadataItem.isCustom;
isDefined(objectMetadataItem) &&
!getIsMetadataItemCustom(objectMetadataItem);
const showObjectColorInIconPicker =
!isStandardObject &&
(!isDefined(objectMetadataItem) || objectMetadataItem.isCustom);
(!isDefined(objectMetadataItem) ||
getIsMetadataItemCustom(objectMetadataItem));
watch('description');
watch('icon');
const objectIconColor = watch('color');
const apiNameTooltipText =
!isDefined(objectMetadataItem) || objectMetadataItem.isCustom
!isDefined(objectMetadataItem) ||
getIsMetadataItemCustom(objectMetadataItem)
? isLabelSyncedWithName
? t`Deactivate "Synchronize Objects Labels and API Names" to set a custom API name`
: t`Input must be in camel case and cannot start with a number`
@@ -370,7 +375,7 @@ export const SettingsDataModelObjectAboutForm = ({
onChange(value);
const isCustomObject =
isDefined(objectMetadataItem) &&
objectMetadataItem.isCustom;
getIsMetadataItemCustom(objectMetadataItem);
const isbeingCreatedObject =
!isDefined(objectMetadataItem);
if (
@@ -1,4 +1,5 @@
import { isDDLLockedState } from '@/client-config/states/isDDLLockedState';
import { useGetIsMetadataItemCustom } from '@/object-metadata/hooks/useGetIsMetadataItemCustom';
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { getActiveFieldMetadataItems } from '@/object-metadata/utils/getActiveFieldMetadataItems';
@@ -50,6 +51,9 @@ export const SettingsDataModelObjectIdentifiersForm = ({
}: SettingsDataModelObjectIdentifiersFormProps) => {
const isDDLLocked = useAtomStateValue(isDDLLockedState);
const getIsMetadataItemCustom = useGetIsMetadataItemCustom();
const isCustomObject = getIsMetadataItemCustom(objectMetadataItem);
const readonly =
isObjectMetadataReadOnly({
objectMetadataItem,
@@ -130,7 +134,7 @@ export const SettingsDataModelObjectIdentifiersForm = ({
options={options}
value={value}
withSearchInput={label === t`Record label`}
disabled={!objectMetadataItem.isCustom || readonly}
disabled={!isCustomObject || readonly}
callToActionButton={
label === t`Record label`
? {