fix: navigation menu item type backfill and frontend loading (#18730)
## Summary - Move `BackfillNavigationMenuItemTypeCommand` from the 1-19 to the 1-20 upgrade path and split the DB transaction into two phases (data backfill, then schema changes) to avoid the PostgreSQL error "cannot ALTER TABLE because it has pending trigger events." - Fix backfill logic to prefer `OBJECT` over `VIEW` for navigation menu items that have `targetObjectMetadataId`, and correct already mis-typed items. Tighten the `CHECK` constraint to enforce `viewId IS NULL` for `OBJECT` type items. - On the frontend, force `navigationMenuItems` into `staleEntityKeys` when the server's `minimalMetadata` response omits the collection hash (happens when the Redis cache hasn't been warmed after an upgrade), ensuring the sidebar loads navigation items. ## Test plan - [ ] Upgrade from 1.18 or 1.19 to 1.20 and verify the migration completes without errors - [ ] Verify navigation menu items of type `OBJECT` do not have a `viewId` set in the database - [ ] Sign out and sign in — confirm navigation menu items appear in the sidebar on first load - [ ] Verify `VIEW`-typed items also appear correctly in the sidebar Made with [Cursor](https://cursor.com)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { FIND_MINIMAL_METADATA } from '@/metadata-store/graphql/queries/findMinimalMetadata';
|
||||
import {
|
||||
ALL_METADATA_ENTITY_KEYS,
|
||||
metadataStoreState,
|
||||
type MetadataEntityKey,
|
||||
} from '@/metadata-store/states/metadataStoreState';
|
||||
@@ -31,6 +32,8 @@ export const useLoadMinimalMetadata = () => {
|
||||
|
||||
const staleEntityKeys: MetadataEntityKey[] = [];
|
||||
|
||||
const entityKeysWithServerHash = new Set<MetadataEntityKey>();
|
||||
|
||||
if (isDefined(collectionHashes)) {
|
||||
for (const { collectionName, hash } of collectionHashes) {
|
||||
const entityKey = mapAllMetadataNameToEntityKey(collectionName);
|
||||
@@ -39,6 +42,8 @@ export const useLoadMinimalMetadata = () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
entityKeysWithServerHash.add(entityKey);
|
||||
|
||||
const entry = store.get(metadataStoreState.atomFamily(entityKey));
|
||||
|
||||
if (entry.currentCollectionHash !== hash) {
|
||||
@@ -52,6 +57,15 @@ export const useLoadMinimalMetadata = () => {
|
||||
}
|
||||
}
|
||||
|
||||
for (const entityKey of ALL_METADATA_ENTITY_KEYS) {
|
||||
if (
|
||||
!entityKeysWithServerHash.has(entityKey) &&
|
||||
!staleEntityKeys.includes(entityKey)
|
||||
) {
|
||||
staleEntityKeys.push(entityKey);
|
||||
}
|
||||
}
|
||||
|
||||
const objectsEntry = store.get(
|
||||
metadataStoreState.atomFamily('objectMetadataItems'),
|
||||
);
|
||||
|
||||
-3
@@ -4,7 +4,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AddMissingSystemFieldsToStandardObjectsCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-add-missing-system-fields-to-standard-objects.command';
|
||||
import { BackfillMessageChannelMessageAssociationMessageFolderCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-message-channel-message-association-message-folder.command';
|
||||
import { BackfillMissingStandardViewsCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-missing-standard-views.command';
|
||||
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-navigation-menu-item-type.command';
|
||||
import { BackfillSystemFieldsIsSystemCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-system-fields-is-system.command';
|
||||
import { FixInvalidStandardUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-fix-invalid-standard-universal-identifiers.command';
|
||||
import { SeedServerIdCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-seed-server-id.command';
|
||||
@@ -35,7 +34,6 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
AddMissingSystemFieldsToStandardObjectsCommand,
|
||||
BackfillMessageChannelMessageAssociationMessageFolderCommand,
|
||||
BackfillMissingStandardViewsCommand,
|
||||
BackfillNavigationMenuItemTypeCommand,
|
||||
FixInvalidStandardUniversalIdentifiersCommand,
|
||||
SeedServerIdCommand,
|
||||
],
|
||||
@@ -44,7 +42,6 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
AddMissingSystemFieldsToStandardObjectsCommand,
|
||||
BackfillMessageChannelMessageAssociationMessageFolderCommand,
|
||||
BackfillMissingStandardViewsCommand,
|
||||
BackfillNavigationMenuItemTypeCommand,
|
||||
FixInvalidStandardUniversalIdentifiersCommand,
|
||||
SeedServerIdCommand,
|
||||
],
|
||||
|
||||
+22
-4
@@ -11,7 +11,7 @@ import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-19:backfill-navigation-menu-item-type',
|
||||
name: 'upgrade:1-20:backfill-navigation-menu-item-type',
|
||||
description:
|
||||
'Backfill navigation menu item type based on existing columns, then apply NOT NULL and CHECK constraints',
|
||||
})
|
||||
@@ -52,15 +52,29 @@ export class BackfillNavigationMenuItemTypeCommand extends ActiveOrSuspendedWork
|
||||
try {
|
||||
await this.backfillType(queryRunner);
|
||||
await this.cleanConflictingColumns(queryRunner);
|
||||
await makeNavigationMenuItemTypeNotNullQueries(queryRunner);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(
|
||||
`Rolling back BackfillNavigationMenuItemTypeCommand data backfill: ${error.message}`,
|
||||
);
|
||||
|
||||
await queryRunner.release();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
await makeNavigationMenuItemTypeNotNullQueries(queryRunner);
|
||||
await queryRunner.commitTransaction();
|
||||
this.logger.log('Successfully run BackfillNavigationMenuItemTypeCommand');
|
||||
this.hasRunOnce = true;
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(
|
||||
`Rolling back BackfillNavigationMenuItemTypeCommand: ${error.message}`,
|
||||
`Rolling back BackfillNavigationMenuItemTypeCommand schema changes: ${error.message}`,
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
@@ -71,7 +85,7 @@ export class BackfillNavigationMenuItemTypeCommand extends ActiveOrSuspendedWork
|
||||
queryRunner: ReturnType<DataSource['createQueryRunner']>,
|
||||
): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`UPDATE "core"."navigationMenuItem" SET "type" = 'VIEW' WHERE "type" IS NULL AND "viewId" IS NOT NULL`,
|
||||
`UPDATE "core"."navigationMenuItem" SET "type" = 'OBJECT' WHERE "type" = 'VIEW' AND "targetObjectMetadataId" IS NOT NULL AND "targetRecordId" IS NULL`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
@@ -82,6 +96,10 @@ export class BackfillNavigationMenuItemTypeCommand extends ActiveOrSuspendedWork
|
||||
`UPDATE "core"."navigationMenuItem" SET "type" = 'OBJECT' WHERE "type" IS NULL AND "targetObjectMetadataId" IS NOT NULL AND "targetRecordId" IS NULL`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`UPDATE "core"."navigationMenuItem" SET "type" = 'VIEW' WHERE "type" IS NULL AND "viewId" IS NOT NULL`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`UPDATE "core"."navigationMenuItem" SET "type" = 'LINK' WHERE "type" IS NULL AND "link" IS NOT NULL`,
|
||||
);
|
||||
+3
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { BackfillCommandMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-command-menu-items.command';
|
||||
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command';
|
||||
import { BackfillPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-page-layouts.command';
|
||||
import { SeedCliApplicationRegistrationCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-seed-cli-application-registration.command';
|
||||
import { ApplicationRegistrationModule } from 'src/engine/core-modules/application/application-registration/application-registration.module';
|
||||
@@ -31,12 +32,14 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
],
|
||||
providers: [
|
||||
BackfillCommandMenuItemsCommand,
|
||||
BackfillNavigationMenuItemTypeCommand,
|
||||
BackfillPageLayoutsCommand,
|
||||
SeedCliApplicationRegistrationCommand,
|
||||
MigrateRichTextToTextCommand,
|
||||
],
|
||||
exports: [
|
||||
BackfillCommandMenuItemsCommand,
|
||||
BackfillNavigationMenuItemTypeCommand,
|
||||
BackfillPageLayoutsCommand,
|
||||
SeedCliApplicationRegistrationCommand,
|
||||
MigrateRichTextToTextCommand,
|
||||
|
||||
+2
-3
@@ -30,7 +30,7 @@ import { MigrateWorkspacePicturesCommand } from 'src/database/commands/upgrade-v
|
||||
import { AddMissingSystemFieldsToStandardObjectsCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-add-missing-system-fields-to-standard-objects.command';
|
||||
import { BackfillMessageChannelMessageAssociationMessageFolderCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-message-channel-message-association-message-folder.command';
|
||||
import { BackfillMissingStandardViewsCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-missing-standard-views.command';
|
||||
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-navigation-menu-item-type.command';
|
||||
import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command';
|
||||
import { BackfillSystemFieldsIsSystemCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-backfill-system-fields-is-system.command';
|
||||
import { FixInvalidStandardUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-fix-invalid-standard-universal-identifiers.command';
|
||||
import { SeedServerIdCommand } from 'src/database/commands/upgrade-version-command/1-19/1-19-seed-server-id.command';
|
||||
@@ -84,11 +84,11 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly addMissingSystemFieldsToStandardObjectsCommand: AddMissingSystemFieldsToStandardObjectsCommand,
|
||||
protected readonly backfillMessageChannelMessageAssociationMessageFolderCommand: BackfillMessageChannelMessageAssociationMessageFolderCommand,
|
||||
protected readonly backfillMissingStandardViewsCommand: BackfillMissingStandardViewsCommand,
|
||||
protected readonly backfillNavigationMenuItemTypeCommand: BackfillNavigationMenuItemTypeCommand,
|
||||
protected readonly fixRoleAndAgentUniversalIdentifiersCommand: FixInvalidStandardUniversalIdentifiersCommand,
|
||||
protected readonly seedServerIdCommand: SeedServerIdCommand,
|
||||
|
||||
// 1.20 Commands
|
||||
protected readonly backfillNavigationMenuItemTypeCommand: BackfillNavigationMenuItemTypeCommand,
|
||||
protected readonly backfillCommandMenuItemsCommand: BackfillCommandMenuItemsCommand,
|
||||
protected readonly backfillPageLayoutsCommand: BackfillPageLayoutsCommand,
|
||||
protected readonly seedCliApplicationRegistrationCommand: SeedCliApplicationRegistrationCommand,
|
||||
@@ -135,7 +135,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
this.addMissingSystemFieldsToStandardObjectsCommand,
|
||||
this.backfillMessageChannelMessageAssociationMessageFolderCommand,
|
||||
this.backfillMissingStandardViewsCommand,
|
||||
this.backfillNavigationMenuItemTypeCommand,
|
||||
this.seedServerIdCommand,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user