Skip to content

feat: generic tables, format-version policy, maintenance summary#194

Merged
v-kessler merged 5 commits into
mainfrom
feat-generic-tables-and-protection
Jun 8, 2026
Merged

feat: generic tables, format-version policy, maintenance summary#194
v-kessler merged 5 commits into
mainfrom
feat-generic-tables-and-protection

Conversation

@v-kessler

@v-kessler v-kessler commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Generic tables (Lance, Delta, Vortex, …) — full data-plane via new /lakekeeper/v1/.../generic-tables SDK, plus management-side actions/assignments/protection. Unified list with format icons inside NamespaceTables, detail page, tree integration, brand icons.
  • Iceberg format-version policy — per-warehouse allowed versions + default. Read-only chips in WarehouseDetails; editable in the create-warehouse flow and the renamed "Catalog Settings" update dialog.
  • Maintenance summary in TaskManager — per-queue last-run / next-run cards with Run now / Reschedule / Cancel via existing controlTasks. Shown when the manager is scoped to a single entity.
  • pickable mode on WarehousesNavigationTree+ button per row for resource picking (used by Cedar Resolve in console-plus-components).
  • Spec refresh — pulled latest OSS management/iceberg OpenAPI and added generic-table-open-api.yaml; SDKs regenerated.

Test plan

  • Browse a namespace with mixed Iceberg + Lance tables — both appear under the Tables tab with correct format icons.
  • Open a Lance table → detail page renders with permissions/protection/tasks tabs.
  • Drop a Lance table → it appears in Deleted; undrop restores it.
  • Warehouse detail page shows the allowed/default format-version chips.
  • "Add new warehouse" — set Iceberg policy to {v2, v3} default v2 → warehouse created with that policy.
  • Kebab → "Catalog Settings" → flip allowed/default → save updates both deletion + format-policy in one request batch.
  • Open a table with running maintenance tasks → maintenance summary shows last/next per queue; Run now / Reschedule / Cancel work via the existing endpoints.
  • Tree search and per-namespace expansion correctly list generic tables.
  • Old cached tree state in localStorage migrates cleanly (load-more nodes stripped, format badges fall back to "G" only for unknown formats).

Notes

  • ObjectType.DELETION_PROFILE was renamed to ObjectType.CATALOG_SETTINGS. The new dialog covers both deletion and format-version policy.
  • LoQE (DuckDB WASM) stays Iceberg-only — Lance/Delta/Vortex DuckDB extensions have no WASM build today.
  • Generic-table properties / doc / base-location are write-once at create time. UI displays them read-only on the detail page; TableCreate / TableRegister show a "Roadmap" tab for the Generic format.

BEGIN_COMMIT_OVERRIDE
feat(ui): add generic-table (Lance/Delta/Vortex/…) support across navigation, list, detail, and search
feat(ui): unify Iceberg + generic tables under one Tables tab with format icons and type filter
feat(ui): add Iceberg format-version policy editor to warehouse create and "Catalog Settings" dialogs
feat(ui): rename "Change Deletion" to "Catalog Settings" combining deletion + format policy in one update
feat(ui): add per-queue maintenance summary (last/next/run-now/reschedule/cancel) inside TaskManager
feat(ui): add `pickable` mode + `+` action button to WarehousesNavigationTree
chore(ui): refresh OSS OpenAPI specs and regenerate management/iceberg/generic-table SDKs
END_COMMIT_OVERRIDE

Summary by CodeRabbit

  • New Features
    • Manage generic tables (create, list, view, rename, delete) alongside Iceberg tables.
    • Deletion-protection and credentials loading for generic tables.
    • Warehouse-level Iceberg format version policy (allowed/default) and UI to configure it.
    • Permission assignment and authorizer checks for generic tables; permission matrix support.
    • Improved navigation/search showing generic-table results and unified table lists.
    • Idempotency support for mutation endpoints and new referenced-by context for loads.

v-kessler and others added 3 commits June 2, 2026 12:43
- regrouped warehouse add/catalog-settings dialog under a single 3-col General Settings block (soft deletion + iceberg format policy)
- TaskManager: added per-entity maintenance summary card (last/next per queue + Run now / Reschedule / Cancel via controlTasks)
- WarehousesNavigationTree: added pickable mode with `+` row action emitting `pick`; only-full-page tokens drive Load more; strip stale load-more on cache restore
- NamespaceTables: brand format icons (Iceberg/Lance/Delta/Vortex) with mdi-alpha-g fallback
- TableCreate / TableRegister: roadmap tab for generic table format
- regenerated management openapi types

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@v-kessler, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 35 minutes and 1 second. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 76828bf5-22ad-4f07-9f53-e7be86d99bf0

📥 Commits

Reviewing files that changed from the base of the PR and between 4c27854 and 205cae6.

📒 Files selected for processing (2)
  • src/components/NamespaceTables.vue
  • src/components/WarehousesNavigationTree.vue

Walkthrough

This PR adds comprehensive generic-table support to the console-components library, including new OpenAPI specifications for data-plane and management APIs, permission infrastructure for authorization and catalog actions, backend API integration, reusable permission composables, new generic-table UI components, updates to existing table and warehouse views, task and navigation enhancements, and public API exports.

Changes

Generic Tables Feature

Layer / File(s) Summary
OpenAPI Contracts and Build Configuration
justfile, openapi/generic-table-open-api.yaml, openapi/management-open-api.yaml, openapi/rest-catalog-open-api.yaml
New generic-table data-plane OpenAPI spec with endpoints for CRUD, credentials, and rename operations. Management API extended with generic-table permissions endpoints, actions retrieval, and format-version policy. Build recipes added to download and generate generic-table SDK client. REST Catalog API gains idempotency-key and referenced-by parameters.
Permission Types and Action Definitions
src/common/enums.ts, src/common/interfaces.ts, src/common/permissionActions.ts
New CATALOG_SETTINGS object type and GenericTable relation type. Assignment collection extended to include generic-table assignments. Catalog and authorizer action arrays defined for generic-table operations.
Backend API Integration and Storage
src/plugins/functions.ts, src/stores/permissions.ts
Generic-table SDK client initialized with authorization headers. Functions added for listing, loading, creating, dropping, renaming, and protecting generic tables. Warehouse format-version policy setter added. OpenFGA-gated assignment getters/setters for generic tables. Permission store extended with generic-table permission lookup.
Permission Composables for Generic Tables
src/composables/useAuthorizerPermissions.ts, src/composables/useCatalogPermissions.ts
New composables useGenericTablePermissions and useGenericTableAuthorizerPermissions for catalog and delegation permissions. Warehouse permissions extended with canSetFormatVersionPolicy flag.
Generic Table Display Components
src/components/GenericTableHeader.vue, src/components/GenericTableOverview.vue, src/components/NamespaceGenericTables.vue
New header component for table breadcrumb navigation. Overview component displays table metadata and recursive delete protection toggle. Namespace component provides searchable, paginated table listing with delete and rename actions.
Unified Table and Generic Table Listing
src/components/NamespaceTables.vue
Refactored to display Iceberg and generic tables in a unified view with type filtering. Table rows carry source and format fields for icon rendering and source-based routing/deletion. Separate load functions for Iceberg (paginated) and generic tables (eager).
Warehouse Format Version Policy Configuration
src/components/WarehouseAddDialog.vue, src/components/WarehouseDetails.vue, src/components/WarehouseHeader.vue, src/components/WarehouseActionsMenu.vue
Warehouse dialogs and details now show Iceberg format-version configuration (allowed versions, default selection). Catalog settings dialog groups deletion and format policy options. Handler updated to emit unified catalog settings events.
Permission Assignment and Authorization UI
src/components/PermissionAssignDialog.vue, src/components/PermissionManager.vue, src/components/PermissionMatrixDialog.vue
Permission dialogs extended to support generic-table relation selection and assignment updates. PermissionManager initializes generic-table composable when relation type is generic-table. Matrix dialog adds generic-table resource selection with namespace/table name parsing.
Task Management and Utility Component Updates
src/components/TaskManager.vue, src/components/TaskDetails.vue, src/components/TableCreate.vue, src/components/TableRegister.vue, src/components/WarehousesNavigationTree.vue
TaskManager extended with entity-scoped maintenance queues and per-queue action buttons for generic tables. TaskDetails maps generic-table to orange color and mdi-table-multiple icon. Table creation/registration dialogs refactored to tabs with generic-table roadmap placeholders. Navigation tree loads generic tables, supports format-aware icons, pickable mode, and cache migration for missing table formats.
Public API and Component Registration
src/index.ts
Exports new generic-table components, permission composables, and generated management/data-plane types for public use via the plugin system.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • lakekeeper/console-components#81: Earlier updates to permission composables that this PR builds upon with generic-table and warehouse format-policy extensions.

🐰 A rabbit hops through tables, both Iceberg and generic,
Permission trees grow tall with authorization majestic,
Format versions aligned, protection toggled with care,
New UI paths discovered everywhere! 🌱✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-generic-tables-and-protection

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/TableRegister.vue (1)

467-473: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset formatTab in resetForm() to avoid sticky tab state.

If the dialog is closed on Generic, reopening keeps Generic selected. Reset this to 'iceberg' in Line 467.

Suggested patch
 function resetForm() {
+  formatTab.value = 'iceberg';
   tableEntries.value = [createEmptyEntry()];
   registrationDone.value = false;
   totalAttempted.value = 0;
   succeededCount.value = 0;
   importError.value = '';
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TableRegister.vue` around lines 467 - 473, The resetForm()
function currently reinitializes form state but forgets to reset the formatTab
reactive value, causing the dialog to reopen with the previously selected tab
(e.g., Generic); update resetForm() to set formatTab to 'iceberg' so the tab
selection is cleared when the form/dialog is reset—modify the resetForm function
(search for resetForm and formatTab in TableRegister.vue) to assign
formatTab.value = 'iceberg' alongside the other resets.
src/components/TableCreate.vue (1)

334-339: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset formatTab when closing the dialog.

After closing on the Generic tab, reopening keeps the previous tab selection. Line 334 should also reset formatTab to 'iceberg' so the dialog always opens in its default mode.

Suggested patch
 function resetForm() {
+  formatTab.value = 'iceberg';
   tableName.value = '';
   fields.value = [];
   error.value = null;
   success.value = false;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/TableCreate.vue` around lines 334 - 339, The resetForm
function currently clears tableName, fields, error, and success but doesn't
reset the dialog tab state; update resetForm to also reset formatTab to its
default by setting formatTab (the reactive holding the tab, e.g.,
formatTab.value) to 'iceberg' so the dialog always opens on the default Generic
tab; locate resetForm in TableCreate.vue and add the assignment alongside the
other resets.
src/components/WarehousesNavigationTree.vue (1)

557-616: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Search result clicks ignore pickable mode and still navigate.

In pickable mode, tree row clicks are suppressed, but search-result clicks still emit navigate. This can break resource-picker flow by routing instead of selecting.

Suggested patch
 async function navigateToSearchResult(result: {
   id: string;
   name: string;
   namespace: string;
   type: string;
   warehouseId: string;
   namespaceId: string;
 }) {
+  if (props.pickable) {
+    emit('pick', {
+      type: result.type,
+      warehouseId: result.warehouseId,
+      namespaceId: result.namespaceId,
+      name: result.name,
+      id: result.id,
+    });
+    dismissSearch();
+    return;
+  }
+
   const apiNamespace = namespacePathToApiFormat(result.namespaceId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/WarehousesNavigationTree.vue` around lines 557 - 616,
navigateToSearchResult currently always emits a 'navigate' event and ignores the
component's pickable mode, causing search clicks to route instead of selecting;
update navigateToSearchResult to check the pickable prop/flag (e.g.
this.pickable or props.pickable) and if pickable is true, do not emit 'navigate'
— instead emit the appropriate selection event (e.g. 'pick' or 'select') with
the result payload (id, type, warehouseId, namespaceId, name) or simply return
after setting any UI state, otherwise proceed with the existing permission
checks, tree expansion, saveWarehouseSubtreeState, dismissSearch and
emit('navigate', ...). Ensure you reference the navigateToSearchResult function
and the emit call so the conditional is placed before emitting navigation.
🧹 Nitpick comments (3)
openapi/generic-table-open-api.yaml (1)

279-282: 💤 Low value

Consider adding maxItems constraint to arrays (static analysis hint).

Static analysis flagged that the stack array lacks a maxItems constraint. The same applies to other arrays like identifiers (line 349) and storage-credentials (line 362). Adding bounds can provide defense-in-depth against malformed responses.

Since this spec is downloaded from the upstream lakekeeper repo via the update-openapi-generic-table recipe, any changes would need to be coordinated there first.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi/generic-table-open-api.yaml` around lines 279 - 282, The OpenAPI
arrays (the "stack" array and the other array schemas "identifiers" and
"storage-credentials") lack a defensive maxItems bound; update each array schema
definition to include a reasonable maxItems value (e.g., based on domain
expectations) so consumers and linters have an upper bound, by adding a maxItems
field alongside the existing type/items in the "stack", "identifiers" and
"storage-credentials" definitions; coordinate this change with the upstream
lakekeeper spec since this file is generated from the
update-openapi-generic-table recipe.

Source: Linters/SAST tools

openapi/management-open-api.yaml (2)

4063-4075: ⚡ Quick win

Model generic-table identifiers explicitly in the check schemas.

Both new generic-table branches still inherit table-id / table from TabularIdentOrUuid. OpenAPI generators will ignore the alias note in the description, so regenerated clients will expose a generic-table payload with table-shaped keys instead of generic-table-id / generic-table. If generic-table checks are meant to be first-class in the generated SDK, add explicit generic-table variants instead of documenting aliases in prose.

Also applies to: 4196-4208, 8090-8094

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi/management-open-api.yaml` around lines 4063 - 4075, The OpenAPI
schema for the "generic-table" branch currently composes from
TabularIdentOrUuid, which carries properties named table-id/table that will be
exposed by generators despite the prose alias; update the schemas to model
explicit generic-table identifier variants instead of inheriting
TabularIdentOrUuid. Concretely, replace or augment the allOf usage for the
"generic-table" property (the block referencing TabularIdentOrUuid and
LakekeeperGenericTableAction) with a dedicated schema that defines a
generic-table-id (or generic-table) property name and its type, and ensure
corresponding schemas at the other occurrences (the blocks around line ranges
4196-4208 and 8090-8094) are similarly updated so generated clients receive
explicit generic-table identifier fields rather than relying on description
aliases.

4419-4438: ⚡ Quick win

Encode the format-version bounds in the schema.

These fields are only constrained in the description right now. Generated clients will therefore accept [], duplicates, or unsupported values like 999 as type-valid input. Adding minItems: 1, uniqueItems: true, and enum: [1, 2, 3] here would make the regenerated contract match the documented rules.

Suggested schema tightening
         allowed-format-versions:
           type: array
+          minItems: 1
+          uniqueItems: true
           items:
             type: integer
             format: int32
+            enum: [1, 2, 3]

         default-format-version:
           type:
             - integer
             - 'null'
           format: int32
+          enum: [1, 2, 3, null]

Also applies to: 8424-8440

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi/management-open-api.yaml` around lines 4419 - 4438, The schema for
allowed-format-versions and default-format-version is only documented but not
enforced; update the OpenAPI schema for the property named
allowed-format-versions to include minItems: 1, uniqueItems: true, and constrain
the items with enum: [1, 2, 3] (rather than only type/format) so clients cannot
pass empty lists, duplicates, or unsupported numbers; also add an enum: [1, 2,
3] to default-format-version (keeping its nullable/integer type) so the default
value is one of the allowed versions; make the same changes for the other
occurrence referenced in the comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openapi/management-open-api.yaml`:
- Around line 5298-5316: The schema exposes "default-format-version" as nullable
and forces clients to reimplement the fallback; change the API to return the
resolved default by making "default-format-version" non-nullable/required in the
warehouse response and ensure the server computes this value server-side using
the existing policy (resolve to v2 if present in "allowed-format-versions",
otherwise the highest allowed version). Update the OpenAPI schema entry for
allowed-format-versions and default-format-version so default-format-version is
required and documented as the effective, resolved value, and verify the
warehouse read/detail handlers produce that resolved integer rather than leaving
it null.

In `@src/components/GenericTableOverview.vue`:
- Around line 124-126: Replace raw console.error calls in the
GenericTableOverview.vue catch blocks with the shared handleError(...) handler:
call handleError(error, '<context string>', true) so the user gets consistent
snackbar feedback. Specifically, update the catch in the generic-table data
loader to use handleError(error, 'loading generic table data', true) and update
the catch handling protection failures (the other occurrence) to use
handleError(error, 'checking table protection', true); reference the handleError
function and the GenericTableOverview component when locating these catch
blocks.
- Around line 103-124: loadTableData currently assigns response.table into the
existing table object and only updates genericTableId when a match is found,
which can leave stale table fields and an incorrect genericTableId if the lookup
misses; before assigning new table data and before calling listGenericTables,
clear or reset table (e.g., replace with an empty object or reset known keys)
and set genericTableId.value = undefined (or null) to ensure stale IDs are not
reused, then Object.assign(table, response.table) and proceed with the
listGenericTables lookup and setting of genericTableId; ensure
recursiveDeleteProtection.value is set explicitly from response.table.protected
after the table reset.
- Around line 129-142: The catch block in setProtection currently toggles
recursiveDeleteProtection.value on failure which misrepresents the real state;
before calling functions.setGenericTableProtection (inside setProtection)
capture the current state (e.g., const previous =
recursiveDeleteProtection.value), use pendingProtectionValue.value to
optimistically update or set after success, and on error restore
recursiveDeleteProtection.value = previous (instead of assigning
!recursiveDeleteProtection.value); reference setProtection,
recursiveDeleteProtection, pendingProtectionValue, and
functions.setGenericTableProtection to locate and implement this change.

In `@src/components/NamespaceGenericTables.vue`:
- Around line 212-214: Replace the console.error in the catch block for the
delete/drop flow with the shared error handler: call
functions.handleError(error, `Failed to drop generic-table ${item.name}`, true)
from the same method (the delete/drop function in NamespaceGenericTables.vue),
ensuring the local `functions` object is imported or available in the component
setup; this routes the error through the standard handler and shows user
notification instead of logging to console.
- Line 149: The watcher only reacts to props.namespacePath so changes to the
warehouse route param can leave data stale; update the watcher to watch both
props.namespacePath and props.warehouseId (or a computed tuple like () =>
[props.namespacePath, props.warehouseId]) and call loadGenericTables when either
changes, ensuring the existing loadGenericTables function is reused and the
watcher registration (watch(..., loadGenericTables)) is updated accordingly.

In `@src/components/NamespaceTables.vue`:
- Around line 285-287: Replace the direct console.error call in the delete/drop
catch block with the standardized error handler: call handleError(error, `Failed
to drop ${item.source}-${item.name}`, true) so the failure uses the app's
centralized error/context/notification flow; locate the catch in the drop/delete
method where item is available (in NamespaceTables.vue) and swap
console.error(...) for handleError(...) using that message and notify=true.
- Around line 268-273: The paginationCheck uses loadedRows.length (mixing
generic + Iceberg rows) so the Iceberg-only pagination trigger can fire too
early; change the check to use only the count of Iceberg rows (or a dedicated
icebergRows collection) instead of loadedRows.length. In paginationCheck (and
where loadIceberg and paginationToken are used) compute icebergCount (e.g.,
loadedRows.filter(r => r.source === 'iceberg').length) or maintain icebergRows
and then replace the condition `option.page * option.itemsPerPage ==
loadedRows.length && paginationToken.value != ''` with a check against
icebergCount (or icebergRows.length) so fetching more Iceberg items is based
solely on Iceberg rows.

In `@src/components/TaskManager.vue`:
- Around line 675-700: The catch blocks in runQueueNow and cancelQueueTask
currently swallow errors; update each catch to accept the error and call
handleError(error, '<action> queue task', true) (e.g., for runQueueNow use
handleError(error, 'run-now queue task', true) and for cancelQueueTask use
handleError(error, 'cancel queue task', true)); also apply the same pattern to
the other task-control functions in this component (the reschedule/cancel/resume
handlers around the other task-control blocks) so all API errors route through
handleError(error, <context>, true).
- Around line 709-714: The current openReschedule function slices the UTC ISO
string (iso.slice(0,16)) and places that value into rescheduleAt, which treats
it as local time and can shift the intended instant; instead parse the stored
ISO timestamp into a Date and format a local "YYYY-MM-DDTHH:MM" string (using
getFullYear/getMonth/getDate/getHours/getMinutes) and assign that to
rescheduleAt.value so the datetime-local input reflects the correct local wall
time; keep rescheduleTarget.value and rescheduleDialog.value assignments
unchanged and update any helper or inline logic in openReschedule to produce the
local-formatted string.

In `@src/components/WarehouseAddDialog.vue`:
- Around line 358-361: The helper pickDefaultFromAllowed currently calls
Math.max(...allowed) which returns -Infinity for an empty array; change
pickDefaultFromAllowed to guard for an empty allowed (e.g., if allowed.length
=== 0) and return a safe fallback (for example 2) otherwise keep the existing
logic (prefer 2 if present, else Math.max). Apply the same empty-array guard to
the other places in this file where Math.max(...allowed) is used to compute
default format versions so you don't propagate -Infinity into
default-format-version/formatPolicy.default in update/create payloads.

In `@src/components/WarehouseHeader.vue`:
- Around line 170-187: The current batch update using Promise.all([...]) can
leave settings partially applied if one promise rejects and prevents
loadWarehouse() from running; change the pattern in the block that builds calls
(the calls array and invocations of functions.updateWarehouseDeleteProfile and
functions.setWarehouseFormatVersionPolicy) to use Promise.allSettled(calls),
iterate the results to detect which updates succeeded or failed, surface precise
error(s) to the user (or log them) based on the rejected entries, and always
call loadWarehouse() afterwards to reconcile UI state with backend; ensure the
same change is applied to the similar block around lines 195-197.
- Line 196: Replace the raw console.error call in the catalog-settings failure
path with the shared error handler: call handleError(...) instead of
console.error('Failed to update catalog settings:', error), passing the caught
error object plus a clear context string (e.g. "Failed to update catalog
settings") and the notify flag as appropriate so the failure in the
WarehouseHeader component routes through the centralized handler.

In `@src/composables/useAuthorizerPermissions.ts`:
- Around line 690-696: In loadPermissions(), when genericTableIdRef.value or
warehouseIdRef.value are missing the function currently returns early but leaves
permissions populated; update loadPermissions (referencing genericTableIdRef,
warehouseIdRef, permissions, and config.enabledPermissions.value) to explicitly
clear permissions.value = [] before returning for the missing-ID case so stale
grants are not shown; keep the existing branch that clears permissions when
config.enabledPermissions.value is false.

In `@src/composables/useCatalogPermissions.ts`:
- Around line 750-752: In loadPermissions(), when the early-return triggers
because genericTableIdRef.value or warehouseIdRef.value is unset, clear the
cached permissions so stale values don't persist; update the branch that
currently returns early to reset the permissions state (e.g., set
permissions/permissionsRef to an empty value or initial state) before returning.
Target the loadPermissions function and the genericTableIdRef, warehouseIdRef,
and permissions (or permissionsRef) symbols so the change is applied where the
IDs are checked and the function currently exits without clearing permissions.

In `@src/stores/permissions.ts`:
- Around line 97-99: Replace the direct console.error call when whId is missing
with the centralized error handler: call functions.handleError(...) passing a
descriptive Error (or message) mentioning "Failed to load generic table
permissions: warehouseId not available", include relevant context (e.g.,
whId/permissions load action) and set notify=true (or the appropriate flag) so
the snackbar pipeline is used; keep the early return of [] in the same place.
Target the block that checks whId in the permissions loader function in
src/stores/permissions.ts and update it to use functions.handleError instead of
console.error.

---

Outside diff comments:
In `@src/components/TableCreate.vue`:
- Around line 334-339: The resetForm function currently clears tableName,
fields, error, and success but doesn't reset the dialog tab state; update
resetForm to also reset formatTab to its default by setting formatTab (the
reactive holding the tab, e.g., formatTab.value) to 'iceberg' so the dialog
always opens on the default Generic tab; locate resetForm in TableCreate.vue and
add the assignment alongside the other resets.

In `@src/components/TableRegister.vue`:
- Around line 467-473: The resetForm() function currently reinitializes form
state but forgets to reset the formatTab reactive value, causing the dialog to
reopen with the previously selected tab (e.g., Generic); update resetForm() to
set formatTab to 'iceberg' so the tab selection is cleared when the form/dialog
is reset—modify the resetForm function (search for resetForm and formatTab in
TableRegister.vue) to assign formatTab.value = 'iceberg' alongside the other
resets.

In `@src/components/WarehousesNavigationTree.vue`:
- Around line 557-616: navigateToSearchResult currently always emits a
'navigate' event and ignores the component's pickable mode, causing search
clicks to route instead of selecting; update navigateToSearchResult to check the
pickable prop/flag (e.g. this.pickable or props.pickable) and if pickable is
true, do not emit 'navigate' — instead emit the appropriate selection event
(e.g. 'pick' or 'select') with the result payload (id, type, warehouseId,
namespaceId, name) or simply return after setting any UI state, otherwise
proceed with the existing permission checks, tree expansion,
saveWarehouseSubtreeState, dismissSearch and emit('navigate', ...). Ensure you
reference the navigateToSearchResult function and the emit call so the
conditional is placed before emitting navigation.

---

Nitpick comments:
In `@openapi/generic-table-open-api.yaml`:
- Around line 279-282: The OpenAPI arrays (the "stack" array and the other array
schemas "identifiers" and "storage-credentials") lack a defensive maxItems
bound; update each array schema definition to include a reasonable maxItems
value (e.g., based on domain expectations) so consumers and linters have an
upper bound, by adding a maxItems field alongside the existing type/items in the
"stack", "identifiers" and "storage-credentials" definitions; coordinate this
change with the upstream lakekeeper spec since this file is generated from the
update-openapi-generic-table recipe.

In `@openapi/management-open-api.yaml`:
- Around line 4063-4075: The OpenAPI schema for the "generic-table" branch
currently composes from TabularIdentOrUuid, which carries properties named
table-id/table that will be exposed by generators despite the prose alias;
update the schemas to model explicit generic-table identifier variants instead
of inheriting TabularIdentOrUuid. Concretely, replace or augment the allOf usage
for the "generic-table" property (the block referencing TabularIdentOrUuid and
LakekeeperGenericTableAction) with a dedicated schema that defines a
generic-table-id (or generic-table) property name and its type, and ensure
corresponding schemas at the other occurrences (the blocks around line ranges
4196-4208 and 8090-8094) are similarly updated so generated clients receive
explicit generic-table identifier fields rather than relying on description
aliases.
- Around line 4419-4438: The schema for allowed-format-versions and
default-format-version is only documented but not enforced; update the OpenAPI
schema for the property named allowed-format-versions to include minItems: 1,
uniqueItems: true, and constrain the items with enum: [1, 2, 3] (rather than
only type/format) so clients cannot pass empty lists, duplicates, or unsupported
numbers; also add an enum: [1, 2, 3] to default-format-version (keeping its
nullable/integer type) so the default value is one of the allowed versions; make
the same changes for the other occurrence referenced in the comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84b45e35-023e-4a1a-8fc5-0b1e5b1154ff

📥 Commits

Reviewing files that changed from the base of the PR and between a9fe32f and 11d1471.

⛔ Files ignored due to path filters (31)
  • src/assets/delta.svg is excluded by !**/*.svg
  • src/assets/iceberg.svg is excluded by !**/*.svg
  • src/assets/lance.png is excluded by !**/*.png
  • src/assets/vortex_logo.svg is excluded by !**/*.svg
  • src/assets/vortex_logo_dark_theme.svg is excluded by !**/*.svg
  • src/gen/generic-table/client.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/client/client.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/client/index.ts is excluded by !**/gen/**
  • src/gen/generic-table/client/types.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/client/utils.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/auth.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/bodySerializer.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/params.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/pathSerializer.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/queryKeySerializer.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/serverSentEvents.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/types.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/core/utils.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/index.ts is excluded by !**/gen/**
  • src/gen/generic-table/sdk.gen.ts is excluded by !**/gen/**
  • src/gen/generic-table/types.gen.ts is excluded by !**/gen/**
  • src/gen/iceberg/client/types.gen.ts is excluded by !**/gen/**
  • src/gen/iceberg/core/params.gen.ts is excluded by !**/gen/**
  • src/gen/iceberg/index.ts is excluded by !**/gen/**
  • src/gen/iceberg/sdk.gen.ts is excluded by !**/gen/**
  • src/gen/iceberg/types.gen.ts is excluded by !**/gen/**
  • src/gen/management/client/types.gen.ts is excluded by !**/gen/**
  • src/gen/management/core/params.gen.ts is excluded by !**/gen/**
  • src/gen/management/index.ts is excluded by !**/gen/**
  • src/gen/management/sdk.gen.ts is excluded by !**/gen/**
  • src/gen/management/types.gen.ts is excluded by !**/gen/**
📒 Files selected for processing (28)
  • justfile
  • openapi/generic-table-open-api.yaml
  • openapi/management-open-api.yaml
  • openapi/rest-catalog-open-api.yaml
  • src/common/enums.ts
  • src/common/interfaces.ts
  • src/common/permissionActions.ts
  • src/components/GenericTableHeader.vue
  • src/components/GenericTableOverview.vue
  • src/components/NamespaceGenericTables.vue
  • src/components/NamespaceTables.vue
  • src/components/PermissionAssignDialog.vue
  • src/components/PermissionManager.vue
  • src/components/PermissionMatrixDialog.vue
  • src/components/TableCreate.vue
  • src/components/TableRegister.vue
  • src/components/TaskDetails.vue
  • src/components/TaskManager.vue
  • src/components/WarehouseActionsMenu.vue
  • src/components/WarehouseAddDialog.vue
  • src/components/WarehouseDetails.vue
  • src/components/WarehouseHeader.vue
  • src/components/WarehousesNavigationTree.vue
  • src/composables/useAuthorizerPermissions.ts
  • src/composables/useCatalogPermissions.ts
  • src/index.ts
  • src/plugins/functions.ts
  • src/stores/permissions.ts

Comment on lines +5298 to +5316
- allowed-format-versions
properties:
allowed-format-versions:
type: array
items:
type: integer
format: int32
description: |-
Iceberg table format versions that may be created in, or upgraded to,
within this warehouse.
default-format-version:
type:
- integer
- 'null'
format: int32
description: |-
Default Iceberg table format version applied when a create-table request
does not specify one. When absent, resolves to v2 if allowed, otherwise
the highest allowed version.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Return the resolved default-format-version in warehouse responses.

The response currently makes every consumer reimplement the fallback rules from prose to know the effective default. Since this PR surfaces the policy in read-only/detail flows, that invites drift between clients and server behavior. Prefer making default-format-version required once the warehouse policy has been normalized server-side.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi/management-open-api.yaml` around lines 5298 - 5316, The schema
exposes "default-format-version" as nullable and forces clients to reimplement
the fallback; change the API to return the resolved default by making
"default-format-version" non-nullable/required in the warehouse response and
ensure the server computes this value server-side using the existing policy
(resolve to v2 if present in "allowed-format-versions", otherwise the highest
allowed version). Update the OpenAPI schema entry for allowed-format-versions
and default-format-version so default-format-version is required and documented
as the effective, resolved value, and verify the warehouse read/detail handlers
produce that resolved integer rather than leaving it null.

Comment thread src/components/GenericTableOverview.vue
Comment thread src/components/GenericTableOverview.vue
Comment thread src/components/GenericTableOverview.vue
Comment thread src/components/NamespaceGenericTables.vue Outdated
Comment thread src/components/WarehouseHeader.vue Outdated
Comment thread src/components/WarehouseHeader.vue Outdated
Comment thread src/composables/useAuthorizerPermissions.ts
Comment thread src/composables/useCatalogPermissions.ts
Comment thread src/stores/permissions.ts
v-kessler and others added 2 commits June 8, 2026 12:30
- centralize error handling: replace console.error with functions.handleError
  in drop flows (NamespaceTables / NamespaceGenericTables), task-control
  actions (TaskManager runQueueNow / cancelQueueTask / stopTask /
  confirmCancelTask / confirmRunTaskNow), generic-table load + setProtection,
  catalog-settings update (WarehouseHeader), and the missing-warehouseId path
  in stores/permissions.ts
- GenericTableOverview: clear stale `table` + `genericTableId` before reload;
  setProtection captures previous state and restores on failure instead of
  toggling
- NamespaceGenericTables: watcher now reacts to warehouseId too
- NamespaceTables: paginationCheck triggers on icebergCount (filtering by
  source) so generic rows no longer break the Iceberg page boundary
- TaskManager: openReschedule formats the existing scheduled-for as a local
  wall-clock string for `datetime-local`; slicing UTC ISO shifted the value
  by the user's offset
- WarehouseAddDialog: pickDefaultFromAllowed guards empty input so
  Math.max(...[]) cannot leak -Infinity into the payload
- WarehouseHeader: updateCatalogSettings uses Promise.allSettled so a
  partial failure still reloads the warehouse and reports per-call failures
- useAuthorizer/useCatalogPermissions (generic-table): clear permissions
  before the missing-id early return so stale grants do not leak across
  route changes
- TableCreate / TableRegister: resetForm also resets formatTab to 'iceberg'
  so the dialog reopens on the default tab
- WarehousesNavigationTree: navigateToSearchResult honors `pickable` and
  emits `pick` instead of `navigate` so Cedar Resolve selects from search

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The detail pages (TableOverview, ViewOverview, generic-table page) already
render a friendly not-found / forbidden alert via pageError, so calling
loadX before navigating just adds a redundant round-trip and a less
informative snackbar — and worst case it blocks the user from even seeing
the resource breadcrumbs.

Removed the pre-check + snackbar early-return from:
  - NamespaceTables.routeToRow (unified Iceberg + generic flow)
  - WarehousesNavigationTree.handleNavigate (table/view/generic-table)
  - WarehousesNavigationTree.navigateToSearchResult

Click → page opens → detail page handles 403/404 inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@v-kessler

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

@v-kessler: I’ll review the PR changes now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@v-kessler v-kessler merged commit 4fcbb4a into main Jun 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant