Drop legacy rolePermissionFlag.flag column + fallback logic (#20730)
## Summary - **New fast migration** `2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts`: - `DROP CONSTRAINT IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE` - `ALTER COLUMN permissionFlagId SET NOT NULL` - `DROP COLUMN flag` - `down()` repopulates `flag` from the catalog via `permissionFlagId` and restores the old unique. - **Entity**: `RolePermissionFlagEntity` hides the `flag` column by using the new decorator + drops old `@Unique` decorator; `permissionFlagId` and the `permissionFlag` relation become non-nullable. - **Deletes** the synthesizer `synthesize-flat-permission-flag-from-flag.util.ts` and every fallback branch that used it (`from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts`, `from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts`, `permissions.service.ts`, `workspace-roles-permissions-cache.service.ts`, `fromRoleEntityToRoleDto.util.ts`, `flat-role-permission-flag-validator.service.ts`, `role-permission-flag.service.ts:getEffectiveUniversalIdentifier`). - **Write path**: ~~drops `flag` from `CreateRolePermissionFlagInput`, the create util, and the application-manifest converter.~~ - **Metadata configs**: ~~removes `flag` from `all-entity-properties-configuration-by-metadata-name.constant.ts` (rolePermissionFlag block)~~ and flips `permissionFlag.isNullable` to `false` in `all-many-to-one-metadata-relations.constant.ts`. ### Why the `flag` field stays declared in the entity The decorator (`@WasRemovedInUpgrade`) is the right tool for the lifecycle marker, but it's a **reflect-metadata** runtime decorator — TypeScript can't see it at compile time. So while the adapter ([`UpgradeAwareEntityMetadataAdapter`](packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts)) now correctly flips `isSelect`/`isInsert`/`isUpdate` to `false` once the drop migration's cursor is crossed, the *static* TypeScript types derived from `RolePermissionFlagEntity` (`UniversalFlatRolePermissionFlag`, `FlatRolePermissionFlag`, `MetadataEntityPropertyConfiguration<'rolePermissionFlag'>`, etc.) still see `flag` as a required scalar property — because the entity declares it. That means every producer of one of those derived types must include `flag`: - `from-create-role-permission-flag-input-to-flat-role-permission-flag-to-create.util.ts` plumbs it through. - `from-permission-flag-to-universal-flat-role-permission-flag.util.ts` (the application-manifest converter) sets `flag: permissionFlag.flag`. - `all-entity-properties-configuration-by-metadata-name.constant.ts` has a `flag` entry under `rolePermissionFlag`. - `CreateRolePermissionFlagInput` keeps the `flag` field. - `RolePermissionFlagService.upsertPermissionFlags` passes `flag: permissionFlag.key as PermissionFlagType` to the create util. Explored phantom-brand approach (`RemovedInUpgrade<T>` wrapper on the field type, key-filter mapped type applied inside `ScalarFlatEntity` / `UniversalFlatEntityFrom`) but previous commands could have `flag === undefined` (downcast from the brand since we can't compare with UpgradeMigrationName like we do with a decorator). That's a **silent-read** failure mode: compiles fine, comparisons against `flag` silently always-false, no error surfaces. Probably worth too much risk for what's a small amount of plumbing? The eventual full deletion of `flag` (entity field included) is a future cleanup once we drop cross-upgrade support for versions ≤ 2.6 Note: Not sure if this PR (and even the decorator) is really needed in the end, seems we need to keep a lot of code in place to handle legacy. Maybe a simple noop [At]Deprecated is enough @charlesBochet (and a migration to set the column nullable if that was not the case before + remove associated constraints)
This commit is contained in:
+2
@@ -151,5 +151,7 @@ describe('UpgradeAwareEntityMetadataAdapter', () => {
|
||||
expect(visibleColumn.isSelect).toBe(true);
|
||||
expect(visibleColumn.isInsert).toBe(true);
|
||||
expect(visibleColumn.isUpdate).toBe(true);
|
||||
|
||||
expect(metadata.columns).toEqual([visibleColumn]);
|
||||
});
|
||||
});
|
||||
|
||||
+8
@@ -23,6 +23,7 @@ type EntityMetadataSnapshot = {
|
||||
tableName: string;
|
||||
tablePath: string;
|
||||
givenTableName: string | undefined;
|
||||
canonicalColumns: ReadonlyArray<ColumnMetadata>;
|
||||
columnDatabaseNamesByPropertyName: ReadonlyMap<string, string>;
|
||||
columnSelectByPropertyName: ReadonlyMap<string, boolean>;
|
||||
columnInsertByPropertyName: ReadonlyMap<string, boolean>;
|
||||
@@ -135,6 +136,7 @@ export class UpgradeAwareEntityMetadataAdapter implements OnModuleInit {
|
||||
tableName: metadata.tableName,
|
||||
tablePath: metadata.tablePath,
|
||||
givenTableName: metadata.givenTableName,
|
||||
canonicalColumns: [...metadata.columns],
|
||||
columnDatabaseNamesByPropertyName,
|
||||
columnSelectByPropertyName,
|
||||
columnInsertByPropertyName,
|
||||
@@ -277,9 +279,15 @@ export class UpgradeAwareEntityMetadataAdapter implements OnModuleInit {
|
||||
metadata.givenTableName = resolved.effectiveTableName;
|
||||
}
|
||||
|
||||
metadata.columns = [...snapshot.canonicalColumns];
|
||||
|
||||
for (const column of metadata.columns) {
|
||||
this.applyColumnShape({ column, snapshot, resolved });
|
||||
}
|
||||
|
||||
metadata.columns = metadata.columns.filter(
|
||||
(column) => !resolved.hiddenPropertyNames.has(column.propertyName),
|
||||
);
|
||||
}
|
||||
|
||||
private applyColumnShape({
|
||||
|
||||
Reference in New Issue
Block a user