import { FieldMetadataDefaultValue, FieldMetadataOptions, FieldMetadataSettings, FieldMetadataType, } from 'twenty-shared/types'; import { Check, Column, CreateDateColumn, Entity, Index, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryGeneratedColumn, Relation, Unique, UpdateDateColumn, } from 'typeorm'; import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator'; import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator'; import { ADD_IS_SYSTEM_SIDE_EFFECT_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-15/is-system-side-effect-upgrade-command-name.constant'; import { ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-metadata-overrides-column-upgrade-command-name.constant'; import { RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME } from 'src/engine/metadata-modules/object-metadata/constants/rename-is-ui-read-only-to-is-ui-editable-upgrade-command-name.constant'; import { type FieldMetadataOverrides } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-overrides.type'; import { AssignIfIsGivenFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-if-is-given-field-metadata-type.type'; import { AssignTypeIfIsMorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-type-if-is-morph-or-relation-field-metadata-type.type'; import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity'; import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity'; import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; import { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type'; import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity'; // This entity is used as a reference test case for type utilities in: // Modifying relations or properties may require updating type test expectations for Typecheck to pass. @Entity('fieldMetadata') @Check( 'CHK_FIELD_METADATA_MORPH_RELATION_REQUIRES_MORPH_ID', `("type" != 'MORPH_RELATION') OR ("type" = 'MORPH_RELATION' AND "morphId" IS NOT NULL)`, ) @Index('IDX_FIELD_METADATA_RELATION_TARGET_FIELD_METADATA_ID', [ 'relationTargetFieldMetadataId', ]) @Index('IDX_FIELD_METADATA_RELATION_TARGET_OBJECT_METADATA_ID', [ 'relationTargetObjectMetadataId', ]) @Unique('IDX_FIELD_METADATA_NAME_OBJECT_METADATA_ID_WORKSPACE_ID_UNIQUE', [ 'name', 'objectMetadataId', 'workspaceId', ]) @Index('IDX_FIELD_METADATA_OBJECT_METADATA_ID_WORKSPACE_ID', [ 'objectMetadataId', 'workspaceId', ]) @Index('IDX_FIELD_METADATA_WORKSPACE_ID', ['workspaceId']) export class FieldMetadataEntity< TFieldMetadataType extends FieldMetadataType = FieldMetadataType, > extends SyncableEntity implements Required { @PrimaryGeneratedColumn('uuid') id: string; @Column({ nullable: false, type: 'uuid' }) objectMetadataId: string; @ManyToOne(() => ObjectMetadataEntity, (object) => object.fields, { onDelete: 'CASCADE', nullable: false, }) @JoinColumn({ name: 'objectMetadataId' }) @Index('IDX_FIELD_METADATA_OBJECT_METADATA_ID', ['objectMetadataId']) object: Relation; @Column({ nullable: false, type: 'varchar', }) type: TFieldMetadataType; @Column({ nullable: false }) name: string; @Column({ nullable: false }) label: string; @Column({ nullable: true, type: 'jsonb' }) defaultValue: JsonbProperty>; @Column({ nullable: true, type: 'text' }) description: string | null; @Column({ nullable: true, type: 'varchar' }) icon: string | null; @WasIntroducedInUpgrade({ upgradeCommandName: ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME, }) @Column({ type: 'jsonb', nullable: true }) overrides: JsonbProperty | null; /** * @deprecated Superseded by `overrides`; kept readable for previous-release * pods during a rolling deploy. Dropped by the dormant * DropMetadataStandardOverridesColumnFastInstanceCommand when 2.20 is current; * add @WasRemovedInUpgrade then (its validator rejects a dormant 2.20 step). */ @Column({ type: 'jsonb', nullable: true }) standardOverrides: WasRemovedInUpgrade | null>; @Column('jsonb', { nullable: true }) options: JsonbProperty>; @Column('jsonb', { nullable: true }) settings: JsonbProperty>; @WasRemovedInUpgrade({ upgradeCommandName: '2.12.0_DropIsCustomFromObjectAndFieldMetadataFastInstanceCommand_1780579070012', }) @Column({ type: 'boolean', default: false }) isCustom: WasRemovedInUpgrade; @Column({ default: false }) isActive: boolean; @Column({ default: false }) isSystem: boolean; @WasIntroducedInUpgrade({ upgradeCommandName: ADD_IS_SYSTEM_SIDE_EFFECT_UPGRADE_COMMAND_NAME, }) @Column({ nullable: false, default: false, type: 'boolean' }) isSystemSideEffect: boolean; @WasIntroducedInUpgrade({ upgradeCommandName: RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME, }) @Column({ default: true }) isUIEditable: boolean; // Superseded by isUIEditable. Intentionally NOT @WasRemovedInUpgrade: dropping // it in 2.13 would break the previous release's pods mid rolling-deploy, since // they still SELECT it. The WasRemovedInUpgrade type is kept so callers may // omit it; the decorator + physical drop are deferred (core-team-issues#2542). @Column({ type: 'boolean', default: false }) isUIReadOnly: WasRemovedInUpgrade; // Is this really nullable ? @Column({ nullable: true, default: true, type: 'boolean' }) isNullable: boolean | null; // Derived at flat-entity cache build time from the existence of a // single-field UNIQUE IndexMetadata covering this field — never persisted // on this entity. Kept on the type so flat-entity consumers continue to // read field.isUnique without per-call derivation; the PG column was // dropped by 1798300000000-drop-field-metadata-is-unique-column.ts. isUnique: boolean | null; @Column({ default: false }) isLabelSyncedWithName: boolean; @Column({ nullable: true, type: 'uuid' }) relationTargetFieldMetadataId: AssignTypeIfIsMorphOrRelationFieldMetadataType< string, TFieldMetadataType >; @OneToOne( () => FieldMetadataEntity, (fieldMetadata) => fieldMetadata.relationTargetFieldMetadataId, { nullable: true }, ) @JoinColumn({ name: 'relationTargetFieldMetadataId' }) relationTargetFieldMetadata: AssignTypeIfIsMorphOrRelationFieldMetadataType< Relation, TFieldMetadataType >; @Column({ nullable: true, type: 'uuid' }) relationTargetObjectMetadataId: AssignTypeIfIsMorphOrRelationFieldMetadataType< string, TFieldMetadataType >; @ManyToOne(() => ObjectMetadataEntity, { onDelete: 'CASCADE', nullable: true, }) @JoinColumn({ name: 'relationTargetObjectMetadataId' }) relationTargetObjectMetadata: AssignTypeIfIsMorphOrRelationFieldMetadataType< Relation, TFieldMetadataType >; @Column({ nullable: true, type: 'uuid' }) morphId: AssignIfIsGivenFieldMetadataType< string, TFieldMetadataType, FieldMetadataType.MORPH_RELATION >; @OneToMany( () => IndexFieldMetadataEntity, (indexFieldMetadata) => indexFieldMetadata.indexMetadata, { cascade: true, }, ) indexFieldMetadatas: Relation; @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; @UpdateDateColumn({ type: 'timestamptz' }) updatedAt: Date; @OneToMany( () => FieldPermissionEntity, (fieldPermission) => fieldPermission.fieldMetadata, ) fieldPermissions: Relation; @OneToMany(() => ViewFieldEntity, (viewField) => viewField.fieldMetadata) viewFields: Relation; @OneToMany(() => ViewFilterEntity, (viewFilter) => viewFilter.fieldMetadata) viewFilters: Relation; @OneToMany( () => ViewEntity, (view) => view.kanbanAggregateOperationFieldMetadata, ) kanbanAggregateOperationViews: Relation; @OneToMany(() => ViewEntity, (view) => view.calendarFieldMetadata) calendarViews: Relation; @OneToMany(() => ViewEntity, (view) => view.mainGroupByFieldMetadata) mainGroupByFieldMetadataViews: Relation; @OneToMany(() => ViewSortEntity, (viewSort) => viewSort.fieldMetadata) viewSorts: Relation; @OneToMany( () => SearchFieldMetadataEntity, (searchFieldMetadata) => searchFieldMetadata.fieldMetadata, ) searchFieldMetadatas: Relation; }