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:
Charles Bochet
2026-03-18 11:56:19 +01:00
committed by GitHub
parent 87efaf2ff8
commit 58336fb70f
5 changed files with 41 additions and 10 deletions
@@ -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'),
);