Identify standard field do deploy until IS_WORKSPACE_CREATION_V2_ENABLED is enabled in prod (#16981)
# Introduction fixes https://github.com/twentyhq/twenty/issues/16905 Do not merge until `IS_WORKSPACE_CREATION_V2_ENABLED` has been activated by default, and so sync metadata has been deprecated by doing so. As the sync metadata will attempt to insert `null` `applicationId` and `universalIdentifier` values while creating a workspace In this PR we're introducing a new `SyncableEntityRequired` which enforces the non nullable `applicationId` and `universalIdentifier` on extending entity In this PR we also migrate the field metadata entity to extend the required ## Identification upgrade command This command will search for workspace field metadata entities that aren't associated to an applicationId, dispatch them to either the workspace-custom `applicationId` or the twenty-standard `applicationId`. For the standard entities it will also set their universal identifier based on the `STANDARD_OBJECTS` const hashmap ## Typeorm migration As the non nullable `applicationId` and `universalIdentifier`migration won't pass in the first we've been using the save point and upgrade command migration fallback pattern ## Tests Tested the command on a prod extract locally Both `twenty-eng` and `twenty-for-twenty` have unexpected standard objects Please note that we will deprecate the `isCustom` and `standardId` col later in the future ### Twenty-eng ```ts [Nest] 98971 - 01/01/2026, 3:18:00 PM LOG [IdentifyStandardEntitiesCommand] Successfully validated 600/600 field metadata update(s) for workspace 9870323e-22c3-4d14-9b7f-5bdc84f7d6ee (309 custom, 291 standard) [Nest] 98971 - 01/01/2026, 3:18:00 PM WARN [IdentifyStandardEntitiesCommand] Found 35 warning(s) while processing field metadata for workspace 9870323e-22c3-4d14-9b7f-5bdc84f7d6ee. These fields will become custom. ``` ### Twenty for twenty ### Just created workspace
This commit is contained in:
+1
@@ -161,6 +161,7 @@ export class FieldMetadataDTO<T extends FieldMetadataType = FieldMetadataType> {
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
|
||||
// TODO prastoin make non nullable once MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand has passed in production @Field(() => UUIDScalarType, { nullable: true })
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
applicationId?: string;
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permis
|
||||
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 { SyncableEntityRequired } from 'src/engine/workspace-manager/types/syncable-entity-required.interface';
|
||||
|
||||
@Entity('fieldMetadata')
|
||||
@Check(
|
||||
@@ -56,7 +56,7 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti
|
||||
export class FieldMetadataEntity<
|
||||
TFieldMetadataType extends FieldMetadataType = FieldMetadataType,
|
||||
>
|
||||
extends SyncableEntity
|
||||
extends SyncableEntityRequired
|
||||
implements Required<FieldMetadataEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
||||
+16
-11
@@ -1,4 +1,7 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
type NonNullableRequired,
|
||||
} from 'twenty-shared/types';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
@@ -11,7 +14,9 @@ import {
|
||||
|
||||
type BuildDefaultFlatFieldMetadataForCustomObjectArgs = {
|
||||
workspaceId: string;
|
||||
flatObjectMetadata: Pick<FlatObjectMetadata, 'id' | 'applicationId'>;
|
||||
flatObjectMetadata: NonNullableRequired<
|
||||
Pick<FlatObjectMetadata, 'id' | 'applicationId'>
|
||||
>;
|
||||
};
|
||||
|
||||
export type DefaultFlatFieldForCustomObjectMaps = ReturnType<
|
||||
@@ -57,7 +62,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const nameFieldId = v4();
|
||||
@@ -94,7 +99,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const createdAtFieldId = v4();
|
||||
@@ -131,7 +136,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const updatedAtFieldId = v4();
|
||||
@@ -168,7 +173,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const deletedAtFieldId = v4();
|
||||
@@ -205,7 +210,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const createdByFieldId = v4();
|
||||
@@ -241,7 +246,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const updatedByFieldId = v4();
|
||||
@@ -277,7 +282,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const positionFieldId = v4();
|
||||
@@ -314,7 +319,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
relationTargetObjectMetadataId: null,
|
||||
settings: null,
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
const searchVectorFieldId = v4();
|
||||
@@ -354,7 +359,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
generatedType: 'STORED',
|
||||
},
|
||||
morphId: null,
|
||||
applicationId: applicationId ?? null,
|
||||
applicationId,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user