Skip to content

Conversation

@Anchel123
Copy link
Contributor

@Anchel123 Anchel123 commented Nov 13, 2025

Summary by CodeRabbit

  • New Features

    • Personal Access Tokens UI: create, copy, view details, and revoke tokens with expiration options
    • Real-time memory usage and node/edge counts (SSE) in graph info and management views
    • New element creation panel for nodes/edges with improved attribute/label controls
  • Improvements

    • Lazy-loading per-cell in tables and improved value display/expansion
    • Enhanced query history, toolbar, and add-mode flows for graph edits
  • Tests

    • End-to-end suites for tokens and graph info panel added

✏️ Tip: You can customize this high-level summary in your review settings.

Anchel123 and others added 30 commits October 15, 2025 13:53
- Updated the `fetchInfo` function to accept a `name` parameter for more flexible API calls.
- Introduced a new `getMemoryUsage` function to fetch and process memory usage data for the specified graph.
- Modified the `GraphInfo` class to include memory usage as part of its state management.
- Updated the `runQuery` function to incorporate memory usage when creating graph information.
- Adjusted the `GraphInfoPanel` component to display labels, relationships, and property keys directly from the context.
- Bumped FalkorDB version in package.json and package-lock.json to 6.3.1.
- Refactored memory usage handling in the GraphInfo class to support nested memory values.
- Introduced new API routes for counting nodes and edges in the graph.
- Updated the GraphInfoPanel to display memory usage alongside nodes and edges count.
- Enhanced TableComponent to support lazy loading of cell values for improved performance.
- Added `Tab` import to the `providers.tsx` file for enhanced functionality.
- Adjusted z-index in the `DropdownMenuContent` component to improve layering and visibility of dropdown menus.
- Included the `password` field in the user object within the authentication options to enhance user data handling.
- Added "+graph.memory" permission to both "Read-Write" and "Read-Only" roles to improve memory management capabilities.
- Improved the structure of user role permissions by defining a separate READ_ONLY_ROLE array for better maintainability.
- Updated the ROLE map to include additional permissions for "Read-Write" and "Read-Only" roles, enhancing user access control.
- Added a semicolon to the `loadCell` function type definition for consistency.
- Updated the API endpoint in the `getMemoryUsage` function to use `prepareArg(name)` for improved argument handling.
- Moved the calculation of `query` and `url` to a different position in `ProvidersWithSession` for better readability.
- Updated the default refresh interval from 10 to 30 in `ProvidersWithSession`.
- Increased the maximum allowed value for the refresh interval from 60 to 600 in `BrowserSettings`.
- Introduced a new `GraphInfoPage` class for managing interactions with the graph info panel in end-to-end tests.
- Added methods to retrieve node and edge counts, property keys count, and handle button interactions in the graph info panel.
- Enhanced the `EditorComponent` to include a `data-value` attribute for the editor container and added a clear editor button.
- Updated the `GraphPage` class to include a method for clicking the clear editor input.
- Created comprehensive tests for the graph info panel, validating its functionality and real-time updates after graph modifications.
- Renamed the `clearEditor` locator in `GraphPage` for clarity.
- Uncommented and organized test cases in `graphInfo.spec.ts` to improve readability and maintainability.
- Added functionality to clear the editor input in the graph info panel tests to ensure accurate query validation.
- Enhanced tests to validate real-time updates in the graph info panel after node and edge deletions.
- Redirected output of the `npm start` command to `nextjs.log` for better debugging during Playwright tests.
- This change applies to multiple test shards and configurations to ensure consistent logging across all test runs.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (7)
app/components/TableComponent.tsx (2)

152-168: Allow retry after load failures

When loadFn rejects, the code removes the spinner but leaves the cell key inside loadAttemptedRef.current (line 518 adds it, but catch never removes it). Subsequent renders treat the cell as "already attempted", so it will never retry even if the backend recovers or the user revisits the row.

Apply this diff to enable retries after failures:

         }).catch((error) => {
             // Only log error if not aborted
             if (!abortController.signal.aborted) {
                 // eslint-disable-next-line no-console
                 console.error(`Failed to load cell ${cellKey}:`, error);
             }

             // Remove from loading set on error
             setLoadingCells(prev => {
                 const newSet = new Set(prev);
                 newSet.delete(cellKey);
                 return newSet;
             });
+
+            // Allow retry by removing from load attempted set
+            loadAttemptedRef.current.delete(cellKey);

             // Clean up abort controller
             abortControllersRef.current.delete(cellKey);
         });

517-520: Fix: avoid state updates during render

handleLoadLazyCell is invoked at line 519 while rendering each cell (inside the row.cells.map at line 509), but it immediately calls setLoadingCells at line 112. React forbids setState during render; the first lazy cell encountered will throw "Cannot update a component while rendering" and the table will crash.

Defer the load to an effect: during render, collect pending cells into a ref, then trigger handleLoadLazyCell from useEffect:

+    const pendingLoadsRef = useRef<Array<{rowName: string, cellIndex: number, loadFn: () => Promise<any>}>>([]);
+
+    useEffect(() => {
+        if (pendingLoadsRef.current.length > 0) {
+            const loads = [...pendingLoadsRef.current];
+            pendingLoadsRef.current = [];
+            loads.forEach(({rowName, cellIndex, loadFn}) => {
+                handleLoadLazyCell(rowName, cellIndex, loadFn);
+            });
+        }
+    }, [visibleRows, handleLoadLazyCell]);

Then in the render path (around line 517-520), replace the direct call:

-                                            if (isLazyCell && !cell.value && !loadingCells.has(cellKey) && !loadAttemptedRef.current.has(cellKey)) {
-                                                loadAttemptedRef.current.add(cellKey);
-                                                handleLoadLazyCell(row.name, j, cell.loadCell);
-                                            }
+                                            if (isLazyCell && !cell.value && !loadingCells.has(cellKey) && !loadAttemptedRef.current.has(cellKey)) {
+                                                loadAttemptedRef.current.add(cellKey);
+                                                pendingLoadsRef.current.push({rowName: row.name, cellIndex: j, loadFn: cell.loadCell});
+                                            }
app/api/graph/model.ts (1)

220-232: Unresolved: GraphInfo.empty() throws when memoryUsage is undefined.

Line 229 calls new Map(memoryUsage) but memoryUsage is optional. When undefined, new Map(undefined) throws TypeError: undefined is not iterable. This was flagged in a previous review and appears unaddressed.

app/components/Header.tsx (3)

115-122: Unresolved: Remove commented-out SCHEMAS code.

This was flagged in a previous review. Commented-out code creates technical debt. Either delete it or use a feature flag.


144-146: Unresolved: Multi-line template literal preserves whitespace in tooltip.

This was flagged in a previous review. The title will display with unintended indentation. Use a single-line string.


148-158: Unresolved: Chat opens without settings validation when navigateToSettings is false.

This was flagged in a previous review. When navigateToSettings is false but model or secretKey is missing, the chat panel opens without validation.

app/components/ForceGraph.tsx (1)

341-347: Unresolved: Schema route missing GET handler for node neighbors.

The URL changed from a fixed path to /api/${type}/${graph.Id}/${node.id}. For type="schema", the backend route at app/api/schema/[schema]/[element]/_route.ts only has POST and DELETE handlers, not GET. This will cause runtime failures.

🧹 Nitpick comments (5)
app/components/TableComponent.tsx (1)

528-528: Consider aligning editable check with cellKey approach

Line 528 still checks row.cells[0]?.value === editable, but elsewhere (lines 582, 677) editable is compared to cellKey. This inconsistency is a leftover from the index-based approach. Since this only affects padding in loading cells, the impact is minimal, but for consistency you might remove or update this condition.

Apply this diff if you want to remove the outdated check:

                                                    <TableCell
                                                        className={cn(
                                                            j + 1 !== row.cells.length && "border-r",
-                                                            row.cells[0]?.value === editable && (cell.type !== "readonly" && cell.type !== "object") && "p-2",
                                                            cell.type === "object" && "p-1",
                                                            "border-border"
                                                        )}
lib/utils.ts (1)

316-347: Reduce redundant key lookups in getNodeDisplayText.

The key lookup logic is duplicated in both the find callback and the subsequent block. Extract the key resolution into a reusable helper or cache the result.

 export const getNodeDisplayText = (node: Node, displayTextPriority: TextPriority[]) => {
   const { data: nodeData } = node;
 
-  const displayText = displayTextPriority.find(({ name, ignore }) => {
-      const key = ignore
+  const resolveKey = (name: string, ignore: boolean) =>
+    ignore
           ? Object.keys(nodeData).find(
               (k) => k.toLowerCase() === name.toLowerCase()
           )
           : name;
 
+  const displayText = displayTextPriority.find(({ name, ignore }) => {
+      const key = resolveKey(name, ignore);
       return (
           key &&
           nodeData[key] &&
           typeof nodeData[key] === "string" &&
           nodeData[key].trim().length > 0
       );
   });
 
   if (displayText) {
-      const key = displayText.ignore
-          ? Object.keys(nodeData).find(
-              (k) => k.toLowerCase() === displayText.name.toLowerCase()
-          )
-          : displayText.name;
-
+      const key = resolveKey(displayText.name, displayText.ignore);
       if (key) {
           return String(nodeData[key]);
       }
   }
 
   return String(node.id);
 }
app/components/ForceGraph.tsx (1)

615-622: Early return guard is redundant.

The guard at lines 615-622 checks the same variables that were just assigned in lines 600-603 within the if block. This guard will only be reached if the if (relationship) block was skipped, meaning textWidth etc. were just set. Consider restructuring for clarity.

-                    if (
-                        textWidth === undefined ||
-                        textHeight === undefined ||
-                        textAscent === undefined ||
-                        textDescent === undefined
-                    ) {
-                        return
-                    }
+                    // At this point, all text metrics are guaranteed to be defined
app/graph/toolbar.tsx (1)

283-294: Info button uses cursor-default but has no interactive purpose.

The Info button displays a tooltip via the title attribute but sets cursor-default, suggesting it's non-interactive. Consider using a <span> or purely informational element instead of a <Button> if no click action is intended. Alternatively, if keeping it as a button for consistency, ensure keyboard accessibility is maintained.

Also, the multi-line title attribute may not render well across browsers. Consider using a proper Tooltip component for better control over formatting.

app/graph/Selector.tsx (1)

144-166: Include setHistoryQuery only if it changes; consider whether it should be in the dependency array.

The dependency chain identified is accurate: when historyQuery changes, afterSearchCallback is recreated, triggering resetHistoryFilters to be recreated, which then fires the useEffect at lines 192-194. However, setHistoryQuery is a stable state setter function (type: Dispatch<SetStateAction<HistoryQuery>>) that never changes across renders, so its inclusion in the afterSearchCallback dependency array at line 142 is unnecessary. While the code follows exhaustive-deps rules, consider whether this is intentional or if it could be safely removed or handled differently. The real performance concern depends on how frequently historyQuery is updated by the parent component in app/providers.tsx (line 49). If updates are unavoidable, the current memoization pattern is already appropriate and further optimization may not be needed.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7146579 and 7c0a9f0.

📒 Files selected for processing (10)
  • app/api/graph/model.ts (10 hunks)
  • app/components/ForceGraph.tsx (9 hunks)
  • app/components/Header.tsx (8 hunks)
  • app/components/TableComponent.tsx (15 hunks)
  • app/components/ui/Button.tsx (3 hunks)
  • app/graph/DeleteElement.tsx (3 hunks)
  • app/graph/GraphView.tsx (8 hunks)
  • app/graph/Selector.tsx (12 hunks)
  • app/graph/toolbar.tsx (5 hunks)
  • lib/utils.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/graph/DeleteElement.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.tsx

⚙️ CodeRabbit configuration file

Review React components for proper hooks usage, accessibility, and performance.

Files:

  • app/components/Header.tsx
  • app/components/ui/Button.tsx
  • app/components/TableComponent.tsx
  • app/components/ForceGraph.tsx
  • app/graph/toolbar.tsx
  • app/graph/GraphView.tsx
  • app/graph/Selector.tsx
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • lib/utils.ts
  • app/api/graph/model.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • app/api/graph/model.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
🧬 Code graph analysis (6)
app/components/Header.tsx (1)
app/components/provider.ts (1)
  • BrowserSettingsContext (155-214)
app/components/TableComponent.tsx (1)
lib/utils.ts (2)
  • Row (85-89)
  • cn (91-93)
app/components/ForceGraph.tsx (2)
app/api/graph/model.ts (2)
  • Node (55-66)
  • Link (68-84)
lib/utils.ts (2)
  • getNodeDisplayText (316-347)
  • securedFetch (140-162)
app/graph/toolbar.tsx (2)
lib/utils.ts (1)
  • cn (91-93)
app/components/ui/Button.tsx (1)
  • Props (11-21)
lib/utils.ts (1)
app/api/graph/model.ts (3)
  • MemoryValue (117-117)
  • Node (55-66)
  • Link (68-84)
app/graph/Selector.tsx (1)
app/graph/toolbar.tsx (1)
  • Toolbar (32-335)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: build
🔇 Additional comments (53)
app/components/ui/Button.tsx (3)

20-20: LGTM: Clean tooltip positioning prop.

The optional tooltipSide prop provides standard tooltip placement control without breaking existing usage.


55-55: LGTM: Proper prop destructuring.

The tooltipSide prop is correctly integrated into the component signature.


82-82: LGTM: Tooltip positioning and formatting enhancements.

The side prop enables flexible tooltip placement, and whitespace-pre-line allows multi-line tooltip content. The whitespace class applies to all Button tooltips globally (not just Delete variants), enabling consistent newline handling across the UI. Verification confirms no existing Button tooltips contain newline characters, so this change is backwards-compatible and safe.

lib/utils.ts (7)

10-12: LGTM on imports and constant.

The new MemoryValue import and MEMORY_USAGE_VERSION_THRESHOLD constant are appropriately added.


25-25: LGTM on Panel type extension.

Adding "add" to the Panel union type cleanly supports the new add-element functionality.


58-62: LGTM on LazyCell and Cell union extension.

The LazyCell type with optional value and async loadCell is well-structured for deferred loading.

Also applies to: 77-78


85-89: LGTM on Row type extension.

Adding the name field to Row is straightforward.


106-111: LGTM on indicator updates in SSE and fetch helpers.

Setting the indicator to "online" on successful results improves UI feedback consistency.

Also applies to: 140-162


256-275: LGTM on getMemoryUsage implementation.

The function properly handles errors by returning an empty Map when result.ok is false.


402-406: Type guard uses labels property which exists on both Node and Link types.

The isTwoNodes guard checks for !!e.labels to identify nodes. However, per the Link type in app/api/graph/model.ts, links don't have a labels property, so this works correctly. Consider adding a comment for clarity.

app/api/graph/model.ts (7)

8-8: LGTM on Value type simplification.

Removing recursive Value[] simplifies the type while maintaining needed flexibility.


117-118: LGTM on MemoryValue type.

Recursive type definition using Map<string, MemoryValue> correctly models nested memory data.


148-150: LGTM on Relationship extension.

Adding textAscent and textDescent for text metrics caching is appropriate.


166-167: LGTM on GraphInfo memoryUsage integration.

The new memoryUsage field, constructor parameter, getter, and clone logic are correctly implemented.

Also applies to: 176-177, 182-183, 202-204, 210-217


234-248: LGTM on GraphInfo.create() with required memoryUsage.

The factory properly passes memoryUsage through to empty().


464-495: LGTM on calculateLinkCurve implementation.

The method correctly computes curvature based on parallel links and self-loops with appropriate spacing.


745-747: LGTM on refactored curve assignment.

Centralizing curvature calculation via calculateLinkCurve improves maintainability.

app/components/Header.tsx (5)

24-30: LGTM on prop rename and signature update.

Renaming displayChat to navigateToSettings in Props and updating the function signature is consistent.

Also applies to: 40-40


44-44: LGTM on context destructuring.

Extracting displayChat from chatSettings alongside model and secretKey is correct.


56-56: LGTM on showCreate logic.

The role check is appropriate for controlling create button visibility.


84-84: LGTM on gap reduction.

Reducing gap-6 to gap-4 is a minor layout adjustment.

Also applies to: 164-164


180-180: LGTM on dropdown padding reduction.

Reducing p-4 to p-2 is a minor styling adjustment.

app/components/ForceGraph.tsx (11)

7-9: LGTM on imports update.

Adding useCallback, useMemo, and getNodeDisplayText supports the new degree-aware and display text logic.


104-114: LGTM on force configuration constants.

Well-documented constants for link distance, strength, collision, and degree thresholds improve readability and tunability.


116-123: LGTM on getEndpointId helper.

Robust handling of Node objects, numbers, and string representations with proper NaN checking.


161-179: LGTM on nodeDegreeMap computation.

The useMemo correctly computes node degrees from links, handling various endpoint representations.


258-298: LGTM on degree-aware link force.

The distance and strength functions properly scale based on node degrees with appropriate thresholds and decay formulas.


301-305: LGTM on degree-aware collision force.

Using Math.sqrt(degree) for radius scaling provides reasonable growth for high-degree nodes.


318-321: LGTM on charge force tuning and dependency update.

Capping distanceMax and including nodeDegreeMap in dependencies ensures proper reactivity.

Also applies to: 326-326


339-339: LGTM on handleGetNodeDisplayText callback.

Memoizing with displayTextPriority dependency is correct.


393-395: LGTM on consistent use of handleGetNodeDisplayText.

Node display text is consistently derived via the memoized callback throughout click handling and rendering.

Also applies to: 447-447, 494-494


574-612: LGTM on text metrics caching with ascent/descent.

Extending the cache to include textAscent and textDescent reduces repeated measurements.


631-638: LGTM on background dimension calculation.

Using 0.7 multiplier for background width/height provides appropriate padding.

app/graph/GraphView.tsx (9)

6-6: LGTM on ScrollText import.

Adding ScrollText icon for Metadata tab is appropriate.


37-41: LGTM on new add-element props.

The setIsAddNode, setIsAddEdge, isAddNode, and isAddEdge props are properly typed and destructured.

Also applies to: 60-64


66-66: LGTM on graphName destructuring.

Extracting graphName from GraphContext for passing to Toolbar.


78-82: Verify Metadata tab enablement logic.

The condition historyQuery.currentQuery && historyQuery.currentQuery.metadata.length > 0 && graph.Metadata.length > 0 && historyQuery.currentQuery.explain.length > 0 is complex. Ensure this matches the intended behavior—metadata tab should only enable when all four conditions are true.


84-91: LGTM on default tab selection simplification.

The logic clearly defaults to "Table" only when there are no elements but data exists.


125-126: LGTM on layout class adjustments.

Minor styling tweaks for gap and flex layout.

Also applies to: 146-146, 155-155, 161-161


130-143: LGTM on Toolbar props wiring.

The conditional setIsAddEdge ensures it's only passed when exactly two nodes are selected, using the isTwoNodes pattern (checking !!e.labels).


167-199: LGTM on tab button enablement and titles.

Dynamic disabled and title props based on isTabEnabled provide clear user feedback.


197-197: LGTM on Metadata icon change.

ScrollText icon is more semantically appropriate for metadata than the previous icon.

app/graph/toolbar.tsx (6)

1-5: LGTM!

Imports are clean and appropriate for the new functionality. useSession from next-auth/react aligns with the session-based UI gating pattern used elsewhere in the codebase.


12-26: LGTM!

Props interface is well-structured with the renamed props (setIsAddNode, setIsAddEdge, isAddNode, isAddEdge) and the addition of graphName. The optional setIsAddEdge prop allows conditional rendering of the Add Edge button.


295-305: Add Node button toggles state correctly.

The implementation correctly toggles isAddNode via setIsAddNode(!isAddNode). The data-testid is properly set for E2E testing.


306-319: Add Edge button conditional rendering is correct.

The button only renders when setIsAddEdge is defined, which aligns with the prop contract. Toggle logic is consistent with Add Node.


279-281: Consider null-safety for session?.user.role.

The condition session?.user.role !== "Read-Only" will evaluate to true when session is null or session.user is undefined. If the intent is to hide these buttons for unauthenticated users, consider an explicit check:

-graphName && session?.user.role !== "Read-Only" &&
+graphName && session?.user && session.user.role !== "Read-Only" &&

337-340: LGTM!

Default props are correctly updated to reflect the new prop structure.

app/graph/Selector.tsx (5)

121-127: LGTM!

Good use of useMemo to avoid recomputing filters on every render. The conditional logic efficiently handles both cases: using graphNames when the list is large enough, or deriving unique names from queries otherwise.


130-141: Functional state update prevents unnecessary re-renders.

Good refactor using setHistoryQuery(prev => ...) to avoid stale state and only update when prev.counter !== 0.


293-315: handleDeleteQuery correctly handles edge cases.

The function properly:

  • Guards against missing state
  • Updates localStorage
  • Calculates next counter position
  • Filters the query from filtered list using timestamp

The use of timestamp as a unique identifier for filtering is appropriate.


467-470: Editor options adjusted for horizontal scrolling.

Setting horizontal: "auto" and wordWrap: "off" enables horizontal scrolling for long queries. This is a reasonable UX choice for query editors.


560-575: Conditional setIsAddEdge logic is well-implemented.

The condition selectedElements.length === 2 && selectedElements.every(e => !!e.labels) ensures the Add Edge action is only available when exactly two nodes (elements with labels) are selected, which is semantically correct for creating an edge between nodes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (3)
app/providers.tsx (2)

70-70: refreshInterval default mismatch: useState(10) vs localStorage fallback 30.

Initial state is 10 (line 70) but hydration defaults to 30 (line 420). First-time users see 10 initially, then 30 after reload.

-  const [refreshInterval, setRefreshInterval] = useState(10)
+  const [refreshInterval, setRefreshInterval] = useState(30)

Also applies to: 420-420


258-271: fetchInfo checks graphName but receives name param—use passed param.

The function accepts name as a parameter but the early return at line 259 checks graphName (component state). This could cause issues if called with a different graph name.

-  const fetchInfo = useCallback(async (type: string, name: string) => {
-    if (!graphName) return []
+  const fetchInfo = useCallback(async (type: string, name: string) => {
+    if (!name) return []
 
-    const result = await securedFetch(`/api/graph/${name}/info?type=${type}`, {
+    const result = await securedFetch(`/api/graph/${prepareArg(name)}/info?type=${type}`, {

Also note: name should be encoded with prepareArg for URL safety.

app/graph/page.tsx (1)

101-113: fetchInfo should encode graph name for URL safety.

Unlike the version in providers.tsx, this fetchInfo doesn't use prepareArg to encode the graph name.

-        const result = await securedFetch(`/api/graph/${graphName}/info?type=${type}`, {
+        const result = await securedFetch(`/api/graph/${prepareArg(graphName)}/info?type=${type}`, {
♻️ Duplicate comments (4)
app/graph/selectGraph.tsx (1)

279-279: Inconsistent role check: use sessionRole for consistency.

Line 279 uses session?.user.role !== "Read-Only" while the rest of the file uses sessionRole. When session is undefined, this evaluates to true, briefly showing admin UI.

-                        session?.user.role !== "Read-Only" &&
+                        sessionRole && sessionRole !== "Read-Only" &&
app/providers.tsx (2)

175-175: Duplicate dependency: navigateToSettings appears twice.

-  }), [displayChat, navigateToSettings, contentPersistence, defaultQuery, hasChanges, lastLimit, limit, model, navigateToSettings, newContentPersistence, newDefaultQuery, newLimit, newModel, newRefreshInterval, newRunDefaultQuery, newSecretKey, newTimeout, refreshInterval, runDefaultQuery, secretKey, timeout, displayTextPriority, newDisplayTextPriority, replayTutorial, tutorialOpen])
+  }), [displayChat, navigateToSettings, contentPersistence, defaultQuery, hasChanges, lastLimit, limit, model, newContentPersistence, newDefaultQuery, newLimit, newModel, newRefreshInterval, newRunDefaultQuery, newSecretKey, newTimeout, refreshInterval, runDefaultQuery, secretKey, timeout, displayTextPriority, newDisplayTextPriority, replayTutorial, tutorialOpen, showMemoryUsage])

Note: Also add showMemoryUsage since it's now part of the context value (line 124).


139-140: Plaintext secretKey in localStorage remains a security consideration.

As noted in prior review, storing secretKey unencrypted in localStorage poses security risks if it's an API key or sensitive token. Consider server-side session storage or client-side encryption if this is sensitive data.

Also applies to: 422-423

app/graph/page.tsx (1)

192-229: Guard graphName before API call in handleCreateElement.

As noted in prior review, if graphName is falsy, the API URL becomes api/graph/undefined/-1. Add an early return.

 const handleCreateElement = useCallback(async (attributes: [string, Value][], label: string[]) => {
+    if (!graphName) {
+        return false
+    }
     const fakeId = "-1"
🧹 Nitpick comments (7)
app/graph/selectGraph.tsx (1)

100-106: Memory loader returns <1 for undefined values—verify intent.

getMemoryUsage returns an empty Map on failure (per lib/utils.ts). If total_graph_sz_mb is missing, memoryMap.get("total_graph_sz_mb") returns undefined, and the fallback '<1' displays. If the API call failed entirely, showing <1 MB may mislead users. Consider returning an error indicator or empty string on failure.

app/api/graph/[graph]/memory/route.ts (2)

12-27: Double authentication: getDBVersion() calls getClient() internally.

Both this handler and getDBVersion() call getClient(), resulting in two auth checks per request. Consider passing the already-authenticated client to avoid redundant session lookups.

-    const res = await getDBVersion();
-
-    if (!res.ok) {
-      return NextResponse.json(
-        {
-          message: `Failed to retrieve database version: ${await res.text()}`,
-        },
-        { status: 400 }
-      );
-    }
-
-    const [name, version] = (await res.json()).result || ["", 0];
+    const { client } = session;
+    
+    let name: string;
+    let version: number;
+    try {
+      const moduleInfo = await (await client.connection).moduleList();
+      [name, version] = [moduleInfo[0][1], moduleInfo[0][3]];
+    } catch (err) {
+      return NextResponse.json(
+        { message: `Failed to retrieve database version: ${(err as Error).message}` },
+        { status: 400 }
+      );
+    }

40-52: Graph name should be validated before use.

The graph parameter is used directly in client.selectGraph(graph). While FalkorDB likely handles invalid graph names, consider validating or sanitizing the input to provide clearer error messages.

app/graph/graphInfo.tsx (2)

40-50: Memory usage tooltip may show undefined.

Line 46 displays MemoryUsage.get("total_graph_sz_mb") in the tooltip without a fallback. If the value is 0 (falsy but defined), the tooltip would show 0 MB which is correct, but the outer || "<1" on line 43 would show <1. Consider using nullish coalescing for consistency.

-                                        <p className="truncate pointer-events-auto SofiaSans">{MemoryUsage.get("total_graph_sz_mb") || "<1"} MB</p>
+                                        <p className="truncate pointer-events-auto SofiaSans">{MemoryUsage.get("total_graph_sz_mb") ?? "<1"} MB</p>
                                     </TooltipTrigger>
                                     <TooltipContent>
-                                        {MemoryUsage.get("total_graph_sz_mb")} MB
+                                        {MemoryUsage.get("total_graph_sz_mb") ?? "<1"} MB
                                     </TooltipContent>

133-144: Consider filtering Relationships for empty names like Labels.

Labels are filtered with .filter(({ name }) => !!name) but Relationships are not. If empty relationship names are possible, this could render broken buttons.

-                    {Array.from(Relationships.values()).map(({ name, color }) => (
+                    {Array.from(Relationships.values()).filter(({ name }) => !!name).map(({ name, color }) => (
app/api/auth/DBVersion/route.ts (2)

1-4: Tighten the ESLint disable scope and suggest PR title

You only need to suppress import/prefer-default-export here; disabling @typescript-eslint/no-unused-vars on the handler can hide real unused-parameter issues in the future.

-// eslint-disable-next-line import/prefer-default-export, @typescript-eslint/no-unused-vars
+// eslint-disable-next-line import/prefer-default-export

Suggested PR title (conventional commit):

feat(auth): add DB version inspection endpoint


15-31: Harden moduleList parsing and refine error/status handling

Two things here could be more robust:

  1. Result indexing without shape checks

result[0][1] and result[0][3] assume moduleList() always returns at least one module with a fixed tuple shape. If the DB has no modules, or the response shape changes, this will throw and be treated as a generic 400.

Consider validating the structure and making the intent (name/version) explicit:

-    try {
-      const result = await (await client.connection).moduleList();
-      return NextResponse.json({ result: [result[0][1], result[0][3]] }, { status: 200 });
-    } catch (error) {
-      console.error(error);
-      return NextResponse.json(
-        { message: (error as Error).message },
-        { status: (error as Error).message.includes("NOPERM") ? 200 : 400 }
-      );
-    }
+    try {
+      const conn = await client.connection;
+      const result = await conn.moduleList();
+
+      if (!Array.isArray(result) || !Array.isArray(result[0]) || result[0].length < 4) {
+        console.error("Unexpected moduleList response:", result);
+        return NextResponse.json(
+          { message: "Unexpected moduleList response from FalkorDB" },
+          { status: 500 }
+        );
+      }
+
+      const [, moduleName, , moduleVersion] = result[0];
+
+      return NextResponse.json({ result: [moduleName, moduleVersion] }, { status: 200 });
+    } catch (error: unknown) {
+      console.error(error);
+
+      const message = error instanceof Error ? error.message : String(error);
+      const noPerm = message.includes("NOPERM");
+
+      return NextResponse.json(
+        {
+          message: noPerm
+            ? "Missing permissions to list modules"
+            : "Failed to read DB version",
+        },
+        { status: noPerm ? 200 : 500 }
+      );
+    }
  1. Status codes and message exposure
  • Treating all non‑NOPERM errors as 400 blurs client vs server responsibility; most failures from moduleList() (connection issues, server errors, unexpected shapes) are better surfaced as 5xx.
  • Returning raw error.message to clients (both inner and outer catch) may leak internal details; a short, generic message is usually safer while you keep the full error in logs.

Please verify the exact moduleList() response shape in your client library and adjust the guards/destructuring accordingly, and double‑check that returning 200 on "NOPERM" is intentional from an API‑contract perspective.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c0a9f0 and 942a77b.

📒 Files selected for processing (7)
  • app/api/auth/DBVersion/route.ts (1 hunks)
  • app/api/graph/[graph]/memory/route.ts (1 hunks)
  • app/components/provider.ts (2 hunks)
  • app/graph/graphInfo.tsx (7 hunks)
  • app/graph/page.tsx (10 hunks)
  • app/graph/selectGraph.tsx (10 hunks)
  • app/providers.tsx (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/provider.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • app/api/auth/DBVersion/route.ts
  • app/api/graph/[graph]/memory/route.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • app/api/auth/DBVersion/route.ts
  • app/api/graph/[graph]/memory/route.ts
**/*.tsx

⚙️ CodeRabbit configuration file

Review React components for proper hooks usage, accessibility, and performance.

Files:

  • app/graph/selectGraph.tsx
  • app/graph/page.tsx
  • app/providers.tsx
  • app/graph/graphInfo.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
🧬 Code graph analysis (5)
app/api/auth/DBVersion/route.ts (1)
app/api/auth/[...nextauth]/options.ts (2)
  • session (381-399)
  • getClient (403-480)
app/graph/selectGraph.tsx (2)
app/api/auth/[...nextauth]/options.ts (1)
  • session (381-399)
lib/utils.ts (4)
  • prepareArg (164-166)
  • getMemoryUsage (256-275)
  • getSSEGraphResult (95-138)
  • Row (85-89)
app/api/graph/[graph]/memory/route.ts (3)
app/api/auth/DBVersion/route.ts (1)
  • GET (5-32)
app/api/auth/[...nextauth]/options.ts (2)
  • session (381-399)
  • getClient (403-480)
lib/utils.ts (1)
  • MEMORY_USAGE_VERSION_THRESHOLD (12-12)
app/providers.tsx (2)
lib/utils.ts (6)
  • securedFetch (140-162)
  • getQueryWithLimit (284-314)
  • prepareArg (164-166)
  • getSSEGraphResult (95-138)
  • getMemoryUsage (256-275)
  • MEMORY_USAGE_VERSION_THRESHOLD (12-12)
app/api/graph/model.ts (4)
  • MemoryValue (117-117)
  • GraphInfo (159-299)
  • GraphInfo (419-421)
  • GraphInfo (423-425)
app/graph/graphInfo.tsx (2)
app/components/provider.ts (2)
  • GraphContext (217-237)
  • BrowserSettingsContext (156-215)
app/api/graph/model.ts (9)
  • MemoryUsage (202-204)
  • Labels (194-196)
  • Labels (363-365)
  • Labels (367-369)
  • Relationships (198-200)
  • Relationships (375-377)
  • Relationships (379-381)
  • PropertyKeys (186-188)
  • PropertyKeys (190-192)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (11)
app/graph/selectGraph.tsx (2)

126-149: LGTM on row building logic.

The handleSetRows callback correctly:

  • Uses sessionRole to determine cell editability
  • Conditionally adds memory usage column based on showMemoryUsage
  • Includes appropriate dependencies

151-160: Dependency arrays now include handleSetRows—addresses prior feedback.

Both useEffect hooks at lines 151-156 and 158-160 correctly depend on handleSetRows, which transitively depends on sessionRole. This ensures rows rebuild when the session role becomes available.

app/api/graph/[graph]/memory/route.ts (1)

7-59: Overall structure is sound.

The handler properly:

  • Authenticates requests
  • Feature-gates based on DB version
  • Uses appropriate HTTP status codes (400 for client errors, 500 for server errors)
  • Follows Next.js 15 async params pattern
app/graph/graphInfo.tsx (2)

86-97: Labels filtered for empty names—good defensive coding.

The filter ({ name }) => !!name ensures empty label names don't render broken buttons.


8-11: Clean context consumption with destructuring.

The destructuring pattern for extracting Labels, Relationships, PropertyKeys, and MemoryUsage from graphInfo is clean and readable.

app/providers.tsx (1)

392-406: DB version check effect is well-structured.

The async IIFE pattern properly checks authentication status before fetching, and the version threshold check correctly enables memory usage features.

app/graph/page.tsx (4)

115-143: Graph info fetch with memory usage integration looks correct.

The effect properly:

  • Guards on graphName
  • Conditionally fetches memory usage based on showMemoryUsage
  • Creates GraphInfo with all required params
  • Sets up interval for periodic refresh
  • Returns cleanup function

180-190: handleSetIsAdd currying pattern is clean.

The helper creates toggled setters that manage mutual exclusivity between add-node and add-edge modes while updating panel state appropriately.


320-344: Add panel rendering handles both node and edge creation.

The logic correctly:

  • Provides close handler that resets all add states
  • Renders node creation panel when isAddNode is true
  • Guards edge creation with isTwoNodes check
  • Passes selected nodes to edge creation panel

196-201: API correctly interprets type: boolean for node/edge distinction.

The endpoint validates and uses the boolean type field as expected: type: true creates nodes, type: false creates edges. The Zod schema at createSchemaElement explicitly defines type: z.boolean(), and the POST handler at app/api/graph/[graph]/[element]/route.ts (lines 91-105) correctly branches on this boolean to execute the appropriate CREATE query.

app/api/auth/DBVersion/route.ts (1)

5-14: GET handler + getClient usage look consistent and correct

The getClient() pattern with the instanceof NextResponse guard matches the shared auth flow and cleanly reuses the central connection management, so the control flow here looks good and maintainable. Based on learnings, this also keeps the session-based password field separate from PAT pwd usage via getClient, which is what we want for this route.

MuhammadQadora and others added 2 commits November 30, 2025 15:13
…-new-text-to-cypher-version

Feature/update pipeline to get new text to cypher version
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/release-image.yml (1)

5-9: Review the default cypher_version value.

The hardcoded default v0.1.5 may drift from the actual latest stable release. Consider either omitting a default (to force explicit input) or dynamically determining it during workflow initialization.

Confirm whether v0.1.5 is actively maintained and remains compatible with the current browser application.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94c6e47 and 2797ef5.

📒 Files selected for processing (1)
  • .github/workflows/release-image.yml (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: Setup and cache dependencies

Comment on lines +20 to +28
- name: Set up variables
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "CYPHER_VERSION=${{ github.event.inputs.cypher_version }}" >> $GITHUB_ENV
else
# Get the latest released version from GitHub releases
LATEST_VERSION=$(curl -s https://api.github.com/repos/falkordb/text-to-cypher/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
echo "CYPHER_VERSION=$LATEST_VERSION" >> $GITHUB_ENV
fi
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files .github/workflows/ | head -20

Repository: FalkorDB/falkordb-browser

Length of output: 166


🏁 Script executed:

cat .github/workflows/release-image.yml

Repository: FalkorDB/falkordb-browser

Length of output: 1778


🌐 Web query:

GitHub API rate limit unauthenticated requests per hour documentation

💡 Result:

Unauthenticated REST API requests are limited to 60 requests per hour. (docs.github.com)

Citations:


🌐 Web query:

gh cli pre-installed ubuntu-latest GitHub Actions

💡 Result:

Short answer: Yes — the GitHub-hosted ubuntu-latest runner includes the GitHub CLI (gh) by default. [1][2]

Notes:

  • If you run inside a container image or on a self‑hosted runner that doesn't include it, gh may be missing and you must install it yourself (or use a setup action). [2][3]

Sources:

  1. actions/runner-images (list of included software for ubuntu-latest). [GitHub].
  2. setup-gh / marketplace README (states hosted runners include gh by default; needed only for containers/self‑hosted). [GitHub Marketplace].
  3. GitHub Docs — customizing GitHub‑hosted runners (how to install software in jobs). [GitHub Docs].

🏁 Script executed:

# Verify the syntax of the proposed solution in the review comment
# The review shows a diff with what appears to be misplaced validation logic
python3 << 'EOF'
# Check if the proposed diff syntax is correct
proposed_code = """
      - name: Set up variables
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            echo "CYPHER_VERSION=${{ github.event.inputs.cypher_version }}" >> $GITHUB_ENV
          else
            # Get the latest released version from GitHub releases
            LATEST_VERSION=$(gh release list --repo falkordb/text-to-cypher --limit 1 --json tagName -q '.[0].tagName')
            echo "CYPHER_VERSION=$LATEST_VERSION" >> $GITHUB_ENV
          fi
          # Validate that version was set
          if [ -z "$CYPHER_VERSION" ]; then
            echo "ERROR: CYPHER_VERSION is empty"
            exit 1
          fi
"""

print("Proposed code structure:")
print(proposed_code)
print("\nValidation: The code looks syntactically correct.")
print("- Uses gh CLI with --json flag")
print("- gh CLI outputs JSON and uses -q to query tagName")
print("- Validation happens after fi (correct placement)")
EOF

Repository: FalkorDB/falkordb-browser

Length of output: 935


🏁 Script executed:

# Check if GITHUB_TOKEN would be available in this workflow
grep -n "env:" .github/workflows/release-image.yml || echo "No explicit env section found at top level"

Repository: FalkorDB/falkordb-browser

Length of output: 111


🌐 Web query:

gh release list --json tagName query syntax GitHub CLI

💡 Result:

You can output release tag names with gh like this:

  • To list tagName for all releases:
    gh release list --limit 30 --json tagName --jq '.[].tagName'. [1]

  • To get the single latest release tag:
    gh release list --limit 1 --json tagName --jq '.[].tagName'. [1][2]

Notes:

  • --json accepts a comma-separated list of fields (createdAt,isDraft,isPrerelease,name,publishedAt,tagName, etc.). Use --jq with a jq expression to extract values. [1]

Sources: GitHub CLI manual (gh release list --json fields). [1] Example usage on StackOverflow. [2]


🌐 Web query:

GitHub Actions default GITHUB_TOKEN available gh cli authentication

💡 Result:

Short answer: Yes — the Actions-generated token can be used by gh. Set the token in the job step environment (prefer GH_TOKEN) and ensure the job permissions you need.

How:

  • gh will read GH_TOKEN (preferred) or GITHUB_TOKEN from the environment (GH_TOKEN takes precedence). [1][2]
  • Example step:
    env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    run: gh
    (Also set required permissions under jobs.*.permissions in the workflow.) [1][2][3]

Notes:

  • Some older gh versions had issues when trying to run gh auth login with GITHUB_TOKEN present — the recommended pattern is to export GH_TOKEN (or pass token to gh via stdin with --with-token) rather than attempting interactive login. [4][2]

Sources:
[1] GitHub Docs — Use GITHUB_TOKEN for authentication in a workflow.
[2] GitHub CLI manual — gh auth login / environment (GH_TOKEN/GITHUB_TOKEN precedence).
[3] GitHub Blog — examples of using GITHUB_TOKEN with gh in Actions.
[4] cli/cli issue about GITHUB_TOKEN and gh auth login.


Replace fragile grep/sed parsing with jq or gh CLI, but use correct gh syntax.

The curl + grep/sed approach to parse the GitHub API response is error-prone and will break if the JSON structure changes. Additionally, unauthenticated GitHub API requests are rate-limited to 60/hour, which risks workflow failures on subsequent pushes to main.

Apply this diff to use the GitHub CLI with automatic authentication:

       - name: Set up variables
         run: |
           if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
             echo "CYPHER_VERSION=${{ github.event.inputs.cypher_version }}" >> $GITHUB_ENV
           else
             # Get the latest released version from GitHub releases
-            LATEST_VERSION=$(curl -s https://api.github.com/repos/falkordb/text-to-cypher/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+            LATEST_VERSION=$(gh release list --repo falkordb/text-to-cypher --limit 1 --json tagName --jq '.[0].tagName')
             echo "CYPHER_VERSION=$LATEST_VERSION" >> $GITHUB_ENV
+          fi
+          # Validate that version was set
+          if [ -z "$CYPHER_VERSION" ]; then
+            echo "ERROR: CYPHER_VERSION is empty"
+            exit 1
           fi

The gh CLI is pre-installed on ubuntu-latest and automatically uses the GITHUB_TOKEN from the job context, eliminating rate-limit issues and providing robust error handling. The validation step ensures the workflow fails loudly if the version cannot be resolved.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
.github/workflows/release-image.yml lines 20-28: replace the fragile
curl+grep/sed GitHub API parsing with the gh CLI to fetch the latest release
tag, write it to $GITHUB_ENV as CYPHER_VERSION, and validate that a non-empty
value was obtained; ensure the workflow uses the preinstalled gh (which
leverages GITHUB_TOKEN), run gh api repos/:owner/:repo/releases/latest or
equivalent gh command to extract tag_name, set CYPHER_VERSION in the
environment, and explicitly fail the step if the resolved version is empty so
the workflow fails loudly on resolution errors.

Anchel123 and others added 7 commits November 30, 2025 16:45
…ts array instead of individual selectedElement. Update related functions and props across ForceGraph, GraphView, PaginationList, Selector, and Toolbar components for improved state management.
…eleteSelected prop and remove selectedElement state. Update related logic to manage selectedElements array for improved selection handling.
…s across components for improved selection handling. Update related functions in ForceGraph, GraphView, DataPanel, and SchemaView to align with new prop type.
- Add storage abstraction with file and FalkorDB backends
- Support API_TOKEN_FALKORDB_URL for production storage
- Default to .data/api_tokens.json for local development
- Consolidate token storage logic in lib/token-storage
…ent for handleSetSelectedElements, improving clarity in selection logic.
Fix #1262 Refactor selection handling in graph components to use selectedElemen…
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
app/components/PaginationList.tsx (1)

146-150: Guard Enter key handler when there is no valid item

On Enter, you directly index items[hoverIndex]. When the current page has no items (e.g., empty filteredList) or hoverIndex is out of range, this becomes undefined and will throw at .text. This is a correctness issue and will surface as a runtime error on keyboard use. As per coding guidelines, keyboard flows should be robust and not crash on edge cases.

Consider tightening the handler like this:

-                        if (e.key === "Enter") {
-                            e.preventDefault()
-                            onClick(typeof items[hoverIndex] === "string" ? items[hoverIndex] : items[hoverIndex].text, e)
-                        }
+                        if (e.key === "Enter") {
+                            e.preventDefault()
+                            if (hoverIndex < 0 || hoverIndex >= items.length) return
+                            const currentItem = items[hoverIndex]
+                            onClick(
+                                typeof currentItem === "string" ? currentItem : currentItem.text,
+                                e
+                            )
+                        }

Also applies to: 217-239, 250-257

♻️ Duplicate comments (6)
app/components/ForceGraph.tsx (1)

337-343: Verify backend supports GET endpoint for schema.

The API path uses /api/${type}/${graph.Id}/${node.id} where type can be "schema". Per previous review, the schema route handler (app/api/schema/[schema]/[element]/_route.ts) may be missing a GET handler.

#!/bin/bash
# Check if schema route has GET handler
rg -n "export async function GET" app/api/schema/
app/graph/Selector.tsx (1)

190-201: Overlapping useEffect hooks call resetHistoryFilters twice.

The effect at lines 190-192 runs when resetHistoryFilters changes, and the effect at lines 194-201 also calls resetHistoryFilters when queriesOpen changes. This can cause resetHistoryFilters to execute twice in succession.

Consider consolidating into a single effect:

-    useEffect(() => {
-        resetHistoryFilters()
-    }, [resetHistoryFilters])
-
     useEffect(() => {
         if (!queriesOpen) {
             setIsLoading(false)
             setTab("text")
             searchQueryRef.current?.blur()
         }
         resetHistoryFilters()
     }, [queriesOpen, resetHistoryFilters])
app/schema/DataPanel.tsx (3)

337-337: Inconsistent optional chaining - potential runtime error.

This line uses session?.user.role while line 353 uses session?.user?.role. If session.user is undefined during auth transitions, accessing .role directly will throw.

-                                type && session?.user.role !== "Read-Only" &&
+                                type && session?.user?.role !== "Read-Only" &&

516-516: Same optional chaining concern.

-                                            session?.user.role !== "Read-Only" && (
+                                            session?.user?.role !== "Read-Only" && (

715-715: Same optional chaining concern.

-                        session?.user.role !== "Read-Only" &&
+                        session?.user?.role !== "Read-Only" &&
app/graph/page.tsx (1)

191-201: Guard against missing graphName before API call.

The handleCreateElement function constructs a URL with graphName but doesn't early-return if it's falsy, risking requests to api/graph/undefined/....

     const handleCreateElement = useCallback(async (attributes: [string, Value][], label: string[]) => {
+        if (!graphName) {
+            return false
+        }
         const fakeId = "-1"
         const result = await securedFetch(`api/graph/${prepareArg(graphName)}/${fakeId}`, {
🧹 Nitpick comments (3)
app/graph/page.tsx (1)

286-289: Redundant setSelectedElements calls.

Line 288 calls handleSetSelectedElements() which internally calls setSelectedElements([]) and sets panel. Line 289 then calls setSelectedElements([]) again unconditionally. This is redundant.

         if (panel === "data") handleSetSelectedElements()
-        else setSelectedElements([])

Or consolidate to a single call regardless of panel state.

app/components/PaginationList.tsx (2)

121-129: isDeleteSelected integration is coherent (minor style tweak possible)

The new isDeleteSelected?: (item: T) => boolean hook-up, including deleteSelected derivation and defaultProps, is consistent with the existing isSelected pattern and generics.

If you want to shave a bit of boilerplate, you could simplify the per-item check using optional chaining:

-const deleteSelected = isDeleteSelected ? isDeleteSelected(item) : false
+const deleteSelected = isDeleteSelected?.(item) ?? false

Also applies to: 135-138, 253-254, 351-352


2-2: Avoid manufacturing a custom “rightclick” event object

Passing the actual React event objects through onClick is good, but the onContextMenu path currently creates a plain object with a non‑standard type: "rightclick" by spreading e. That:

  • Deviates from the standard event type ("contextmenu"), which callers might reasonably key off.
  • Loses the original prototype, which could surprise code that relies on methods/properties behaving exactly like a React MouseEvent.

You can keep the API and behavior while simplifying to just forward the original event:

-                                            onClick={(e) => {
-                                                onClick(text, e)
-                                            }}
-                                            onContextMenu={(e) => {
-                                                e.preventDefault()
-                                                const syntheticEvent = {
-                                                    ...e,
-                                                    type: "rightclick" as const
-                                                } as typeof e & { type: "rightclick" }
-                                                onClick(text, syntheticEvent)
-                                            }}
+                                            onClick={(e) => {
+                                                onClick(text, e)
+                                            }}
+                                            onContextMenu={(e) => {
+                                                e.preventDefault()
+                                                onClick(text, e)
+                                            }}

Callers that care about right‑clicks can distinguish using evt.type === "contextmenu" or evt.button === 2.

Also applies to: 121-124, 237-238, 286-296

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2797ef5 and 42998e2.

📒 Files selected for processing (9)
  • app/components/ForceGraph.tsx (12 hunks)
  • app/components/PaginationList.tsx (12 hunks)
  • app/graph/GraphView.tsx (9 hunks)
  • app/graph/Selector.tsx (12 hunks)
  • app/graph/page.tsx (10 hunks)
  • app/graph/toolbar.tsx (6 hunks)
  • app/schema/DataPanel.tsx (10 hunks)
  • app/schema/SchemaView.tsx (2 hunks)
  • app/schema/_page.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

Review React components for proper hooks usage, accessibility, and performance.

Files:

  • app/schema/_page.tsx
  • app/components/ForceGraph.tsx
  • app/graph/toolbar.tsx
  • app/components/PaginationList.tsx
  • app/graph/GraphView.tsx
  • app/graph/page.tsx
  • app/graph/Selector.tsx
  • app/schema/SchemaView.tsx
  • app/schema/DataPanel.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
🧬 Code graph analysis (7)
app/schema/_page.tsx (2)
lib/utils.ts (4)
  • securedFetch (140-162)
  • prepareArg (164-166)
  • isTwoNodes (403-406)
  • cn (91-93)
app/schema/DataPanel.tsx (1)
  • DataPanel (30-731)
app/components/ForceGraph.tsx (2)
app/api/graph/model.ts (2)
  • Node (55-66)
  • Link (68-84)
lib/utils.ts (2)
  • getNodeDisplayText (316-347)
  • securedFetch (140-162)
app/graph/toolbar.tsx (4)
app/api/graph/model.ts (2)
  • Node (55-66)
  • Link (68-84)
lib/utils.ts (2)
  • GraphRef (21-23)
  • cn (91-93)
app/components/ui/Button.tsx (1)
  • Props (11-21)
app/components/provider.ts (1)
  • GraphContext (217-237)
app/components/PaginationList.tsx (3)
lib/utils.ts (1)
  • cn (91-93)
app/api/graph/model.ts (1)
  • Query (17-26)
app/components/ui/Button.tsx (1)
  • Props (11-21)
app/graph/GraphView.tsx (4)
app/components/provider.ts (1)
  • GraphContext (217-237)
lib/utils.ts (2)
  • Tab (51-51)
  • cn (91-93)
app/graph/toolbar.tsx (1)
  • Toolbar (31-333)
app/graph/GraphDetails.tsx (1)
  • GraphDetails (8-106)
app/schema/SchemaView.tsx (1)
app/api/graph/model.ts (2)
  • Node (55-66)
  • Link (68-84)
app/schema/DataPanel.tsx (5)
app/api/graph/model.ts (3)
  • Node (55-66)
  • Link (68-84)
  • Graph (301-1101)
app/graph/DataPanel.tsx (1)
  • DataPanel (25-207)
app/components/ui/Button.tsx (1)
  • Props (11-21)
app/components/provider.ts (1)
  • IndicatorContext (267-270)
app/api/auth/[...nextauth]/options.ts (1)
  • session (381-399)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: Setup and cache dependencies
🔇 Additional comments (23)
app/schema/DataPanel.tsx (2)

14-14: LGTM - Import and signature updates align with the multi-element selection refactor.

The import source change, setObject signature update to accept optional array, and destructuring setIndicator from context are consistent with the broader PR changes.

Also applies to: 25-25, 30-32


53-59: LGTM - Escape handler updated correctly.

The change from setObject(undefined) to setObject() aligns with the new signature (el?: (Node | Link)[]) => void.

app/schema/_page.tsx (2)

59-63: LGTM - Panel size calculation updated for new states.

The memoized panelSize correctly derives from selectedElements, isAddNode, and isAddEdge.


65-79: LGTM - Selection handler with mutual exclusivity.

The handleSetSelectedElements correctly resets add-mode flags when elements are selected, maintaining mutually exclusive states.

app/schema/SchemaView.tsx (2)

14-29: LGTM - Props interface updated for multi-element selection.

The setSelectedElements signature change to accept optional array aligns with the broader refactor.


57-59: LGTM - Effect dependency cleanup.

Dependency array correctly reflects the removal of setSelectedElement prop.

app/components/ForceGraph.tsx (4)

114-121: LGTM - Endpoint ID normalization helper.

The getEndpointId helper correctly handles all possible endpoint types (Node object, number, string) with proper null safety.


157-175: LGTM - Degree map computation is well-structured.

The nodeDegreeMap useMemo correctly initializes nodes with 0 degree and increments for each link endpoint. Dependencies are appropriate.


254-294: LGTM - Degree-aware link forces.

The distance and strength calculations based on node degree are well-implemented with clear thresholds and decay formulas.


408-418: LGTM - Right-click handler simplified.

The removal of isAddElement/setSelectedNodes logic and consolidation to single/multi-select via ctrl-key is cleaner.

app/graph/GraphView.tsx (3)

20-39: LGTM - Props interface extended for add-mode controls.

The new setIsAddNode, setIsAddEdge, isAddNode, and isAddEdge props are properly typed and the setSelectedElements signature update is consistent with the refactor.


74-78: LGTM - Tab enablement logic simplified.

The Graph tab is now always enabled, with Metadata requiring both metadata content and explain data. This is a reasonable UX improvement.


125-137: LGTM - Toolbar integration with add-mode controls.

The conditional setIsAddEdge based on having exactly 2 nodes selected correctly enables edge creation only when valid.

app/graph/toolbar.tsx (2)

12-25: LGTM - Props interface updated for new add-mode controls.

The interface correctly declares graphName, updated setSelectedElements signature, and the new isAddNode/isAddEdge/setIsAddNode/setIsAddEdge props with setIsAddEdge being optional.


277-329: LGTM - Action buttons replace dropdown menu.

The direct Info, Add Node, and Add Edge buttons provide clearer UX than the previous dropdown approach. The conditional rendering of the Add Edge button based on setIsAddEdge availability is correct.

app/graph/page.tsx (2)

179-189: LGTM - Curried add-mode handler.

The handleSetIsAdd pattern cleanly manages mutual exclusivity between node/edge add modes while coordinating panel state.


117-142: LGTM - Memory usage integration.

The GraphInfo.create call now properly includes memory usage data when showMemoryUsage is enabled, with appropriate fallback to empty Map.

app/graph/Selector.tsx (4)

119-125: LGTM - Memoized filters computation.

The filters memoization with conditional logic based on query count vs graphNames length is a reasonable optimization.


142-164: LGTM - Centralized history filter reset.

The resetHistoryFilters callback properly handles graph-scoped filtering when a matching graphName exists, with fallback to all queries.


291-311: LGTM - Multi-select deletion handler.

The handleDeleteQuery correctly filters out selected indices, updates localStorage, and recalculates counter/query state.


583-598: LGTM - Schema selector toolbar integration.

The conditional rendering properly gates toolbar display and passes the new add-mode props when in Schema mode.

app/components/PaginationList.tsx (2)

32-37: Centralized selection/delete/hover styling looks solid

Extracting getItemClassName, threading selected/deleteSelected/hover through getSeparator and getQueryElement, and reusing it on the <li> keeps the visual states consistent for both metadata chips and row borders/text. I don’t see issues with this refactor.

Also applies to: 39-42, 57-119, 262-263, 270-273


338-341: >> pagination button math is consistent

The updated disabled condition stepCounter > pageCount - 6 lines up with the click handler guard prev < pageCount - 5, so you can’t jump past the last page, and the control naturally disables once you’re within the last (up to) 5 pages. Behavior looks correct even for small pageCount values.

<div className={cn("flex flex-row-reverse gap-2", label === "Schema" && "h-full")}>
{
graph.Id &&
graphName && session?.user.role !== "Read-Only" &&
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Optional chaining concern - potential runtime error.

Using session?.user.role can throw if session exists but session.user is undefined during auth transitions.

-                    graphName && session?.user.role !== "Read-Only" &&
+                    graphName && session?.user?.role !== "Read-Only" &&
🤖 Prompt for AI Agents
In app/graph/toolbar.tsx around line 279, the expression uses session?.user.role
which can throw if session exists but session.user is undefined during auth
transitions; update the condition to safely access role (e.g., use
session?.user?.role or guard session.user before accessing role) so the check
becomes null-safe and avoids runtime errors during transient auth states.

Comment on lines +250 to +285
const getCurrentPanel = () => {
if (selectedElements.length !== 0) {
return (
<DataPanel
object={selectedElements[selectedElements.length - 1]}
setObject={handleSetSelectedElements}
schema={schema}
setLabels={setLabels}
/>
)
}

if (isAddNode) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddNode}
type
/>
)
}

if (isTwoNodes(selectedElements)) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddEdge}
selectedNodes={selectedElements}
setSelectedNodes={setSelectedElements}
type={false}
/>
)
}

return null
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Logic issue: Edge creation panel condition is unreachable.

The isTwoNodes(selectedElements) check at line 272 will never be reached because selectedElements.length !== 0 at line 251 returns early with DataPanel. When two nodes are selected for edge creation, DataPanel will render instead of CreateElementPanel.

Consider restructuring to check isAddEdge state:

     const getCurrentPanel = () => {
-        if (selectedElements.length !== 0) {
+        if (selectedElements.length !== 0 && !isAddEdge) {
             return (
                 <DataPanel
                     object={selectedElements[selectedElements.length - 1]}
                     setObject={handleSetSelectedElements}
                     schema={schema}
                     setLabels={setLabels}
                 />
             )
         }

         if (isAddNode) {
             return (
                 <SchemaCreateElement
                     onCreate={handleCreateElement}
                     setIsAdd={handleSetIsAddNode}
                     type
                 />
             )
         }

-        if (isTwoNodes(selectedElements)) {
+        if (isAddEdge && isTwoNodes(selectedElements)) {
             return (
                 <SchemaCreateElement
                     onCreate={handleCreateElement}
                     setIsAdd={handleSetIsAddEdge}
                     selectedNodes={selectedElements}
                     setSelectedNodes={setSelectedElements}
                     type={false}
                 />
             )
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getCurrentPanel = () => {
if (selectedElements.length !== 0) {
return (
<DataPanel
object={selectedElements[selectedElements.length - 1]}
setObject={handleSetSelectedElements}
schema={schema}
setLabels={setLabels}
/>
)
}
if (isAddNode) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddNode}
type
/>
)
}
if (isTwoNodes(selectedElements)) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddEdge}
selectedNodes={selectedElements}
setSelectedNodes={setSelectedElements}
type={false}
/>
)
}
return null
}
const getCurrentPanel = () => {
if (selectedElements.length !== 0 && !isAddEdge) {
return (
<DataPanel
object={selectedElements[selectedElements.length - 1]}
setObject={handleSetSelectedElements}
schema={schema}
setLabels={setLabels}
/>
)
}
if (isAddNode) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddNode}
type
/>
)
}
if (isAddEdge && isTwoNodes(selectedElements)) {
return (
<SchemaCreateElement
onCreate={handleCreateElement}
setIsAdd={handleSetIsAddEdge}
selectedNodes={selectedElements}
setSelectedNodes={setSelectedElements}
type={false}
/>
)
}
return null
}
🤖 Prompt for AI Agents
In app/schema/_page.tsx around lines 250 to 285, the current early return for
selectedElements (lines ~251) causes the isTwoNodes(selectedElements) branch
(line ~272) to never run, so the edge-creation panel is unreachable; reorder the
checks so edge creation is handled before the generic selectedElements
DataPanel: specifically, check the isAddEdge (or isTwoNodes(selectedElements)
combined with an isAddEdge flag) case first and render the SchemaCreateElement
for edges when that state is set, then fall back to rendering DataPanel for
single selection and other cases; update the props passed accordingly (keep
type={false} for edges) and ensure state setters (setIsAddEdge /
setSelectedElements) are used in that branch.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
app/api/auth/tokens/route.ts (1)

145-161: Expiration parameter validation needs hardening.

The validation for expiresAt and ttlSeconds doesn't guard against Invalid Date or string coercion issues, as previously noted.

🧹 Nitpick comments (19)
app/api/auth/tokens/route.ts (3)

45-47: Consider defensive checks for timestamp conversion.

If storage returns invalid numeric values (NaN, undefined, or non-numeric), new Date(token.created_at * 1000) yields an Invalid Date, and toISOString() will throw a RangeError. This would bubble up as a generic 500 error rather than graceful handling.

-      created_at: new Date(token.created_at * 1000).toISOString(),
-      expires_at: token.expires_at > 0 ? new Date(token.expires_at * 1000).toISOString() : null,
-      last_used: token.last_used > 0 ? new Date(token.last_used * 1000).toISOString() : null,
+      created_at: Number.isFinite(token.created_at) ? new Date(token.created_at * 1000).toISOString() : null,
+      expires_at: token.expires_at > 0 && Number.isFinite(token.expires_at) ? new Date(token.expires_at * 1000).toISOString() : null,
+      last_used: token.last_used > 0 && Number.isFinite(token.last_used) ? new Date(token.last_used * 1000).toISOString() : null,

236-242: Use 201 Created for resource creation.

REST convention is to return 201 Created when a new resource is successfully created.

     return NextResponse.json(
       {
         message: "Token created successfully",
         token
       },
-      { status: 200 }
+      { status: 201 }
     );

201-203: Type-unsafe password access.

The (user as any).password cast bypasses type safety. If the user object doesn't have a password (e.g., different auth flow), this silently encrypts an empty string. Consider adding explicit type handling or a guard.

-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      const password = (user as any).password || '';
+      const password = 'password' in user && typeof user.password === 'string' 
+        ? user.password 
+        : '';

Based on learnings, session tokens store password while PATs use pwd. Verify this access pattern aligns with the session token flow.

.env.local.template (1)

2-7: Env variables look coherent; consider satisfying dotenv‑linter hints

The added variables (CHAT_URL, NEXTAUTH_URL, PORT, API_TOKEN_STORAGE_PATH) line up with the new token/chat flows and file‑based storage path. Functionally this is fine.

If you’re running dotenv-linter in CI, you may want to:

  • Reorder keys so NEXTAUTH_SECRET comes before NEXTAUTH_URL, and API_TOKEN_STORAGE_PATH comes before CHAT_URL.
  • Add a trailing newline at the end of the file.

These are purely stylistic and not required for correctness.

lib/token-storage/FileTokenStorage.ts (1)

21-55: Behavior on corrupted JSON gracefully degrades but drops all tokens

If the JSON file becomes unreadable, readTokens logs the error and returns an empty array. That’s a reasonable “fail closed” approach (no tokens are treated as valid), but it does mean all existing tokens become effectively invisible until the file is fixed.

If you want stronger safety around operational incidents, consider:

  • Surfacing a more explicit error in admin UIs, or
  • Backing up the corrupted file before overwriting it on the next write.

Not urgent, just something to be aware of.

lib/token-storage/falkordb-client.ts (1)

53-94: PAT FalkorDB client behavior looks robust for typical usage

The PAT‑specific client does a good job of:

  • Reusing an existing connection when healthy (via ping),
  • Falling back from API_TOKEN_FALKORDB_URL to PAT_FALKORDB_* envs, and
  • Verifying connectivity before returning the client.

For most workloads this should be sufficient. If you ever expect extremely high concurrency at startup, you might later consider guarding against multiple simultaneous connect calls by serializing the initial connection, but that’s an optimization rather than a blocker.

e2e/logic/POM/settingsTokensPage.ts (5)

1-1: Unnecessary global eslint disable

There are no any-typed values in this file, so /* eslint-disable @typescript-eslint/no-explicit-any */ is redundant and slightly weakens linting. Consider removing it or narrowing the disable to where it’s actually needed.


55-61: Scope row locators to the token table to avoid accidental matches

Helpers like tokenRowByName, getRowByText, isTokenRowVisible, getTokenCount, and getAllTokenNames all use this.page.getByRole("row"), which can pick up unrelated rows if other tables or row-like components exist on the page. This can cause subtle test failures as the Settings page evolves.

You can make these more robust by scoping them to this.tokenTable, for example:

-  private tokenRowByName(name: string): Locator {
-    return this.page.getByRole("row").filter({ hasText: name });
-  }
+  private tokenRowByName(name: string): Locator {
+    return this.tokenTable.getByRole("row").filter({ hasText: name });
+  }

and similarly for getRowByText, getTokenCount, and getAllTokenNames.

Also applies to: 86-88, 196-203, 304-311, 353-371


33-45: Reduce reliance on fixed timeouts; favor condition-based waits

Methods like waitFor, navigateToTokensTab, clickGenerateToken, confirmGenerateToken, generateToken, copyToken, dismissTokenDisplay, and revokeToken use hard-coded waitForTimeout values (300–1000 ms). These can be flaky across environments and CI load.

Where possible, prefer condition-based waits (e.g., expect(locator).toBeVisible(), waiting for specific rows/toasts to appear/disappear, or waiting for network responses) instead of arbitrary sleeps. You already have interactWhenVisible and tokenTable.waitFor, so extending that pattern here would make the POM more resilient.

Also applies to: 47-49, 81-85, 223-227, 229-232, 252-255, 262-270, 283-287, 290-293, 314-318


34-41: Consider making token-specific locators more targeted

Locators like generatedTokenInput (input[readonly].first()), copyTokenButton (name: /Copy/i), and dismissTokenButton (name: /I've saved my token/i) may accidentally match other controls if the UI grows.

If the UI exposes data attributes or test IDs for the token display (e.g., data-testid="generated-token", data-testid="copy-token"), consider switching to those for long-term stability. The current approach is fine for now but somewhat brittle.

Also applies to: 73-79, 147-154, 275-280


353-371: Align cell text handling between getAllTokenNames and getTokenDetails

getAllTokenNames trims cell text (name.trim()), but getTokenDetails returns raw textContent() for name, created, lastUsed, and expires. If the table markup introduces extra whitespace or line breaks, assertions like expect(details?.name).toBe(tokenName) may fail while getAllTokenNames behaves differently.

To keep behavior consistent and avoid whitespace-related flakes, consider trimming all cell texts in getTokenDetails as well:

-        name: nameIdx >= 0 ? (await cells[nameIdx]?.textContent() || "") : "",
-        created: createdIdx >= 0 ? (await cells[createdIdx]?.textContent() || null) : null,
-        lastUsed: lastUsedIdx >= 0 ? (await cells[lastUsedIdx]?.textContent() || null) : null,
-        expires: expiresIdx >= 0 ? (await cells[expiresIdx]?.textContent() || null) : null,
+        name: nameIdx >= 0 ? ((await cells[nameIdx]?.textContent())?.trim() || "") : "",
+        created: createdIdx >= 0 ? ((await cells[createdIdx]?.textContent())?.trim() || null) : null,
+        lastUsed: lastUsedIdx >= 0 ? ((await cells[lastUsedIdx]?.textContent())?.trim() || null) : null,
+        expires: expiresIdx >= 0 ? ((await cells[expiresIdx]?.textContent())?.trim() || null) : null,

Also applies to: 377-401

e2e/tests/settingsTokens.spec.ts (8)

57-91: Simplify async reduce usage for sequential token generation

Using tokenNames.reduce(async (prevPromise, tokenName) => { ... }) works but is harder to read than a simple for...of loop and doesn’t actually take advantage of reduce.

You can simplify without changing behavior:

-      await tokenNames.reduce(async (previousPromise, tokenName) => {
-        await previousPromise;
-        const token = await settingsTokensPage.generateToken(tokenName);
-        expect(token).not.toBeNull();
-        await settingsTokensPage.dismissTokenDisplay();
-        return Promise.resolve();
-      }, Promise.resolve());
+      for (const tokenName of tokenNames) {
+        const token = await settingsTokensPage.generateToken(tokenName);
+        expect(token).not.toBeNull();
+        await settingsTokensPage.dismissTokenDisplay();
+      }

Same for the cleanup reduce below.


131-144: Reducing flakiness in “no name” validation test

The test correctly verifies that the generate-confirm button stays disabled when the token name is empty. The explicit waitFor(300) introduces timing sensitivity though.

Because isGenerateTokenConfirmButtonDisabled() already uses locator state, you can usually rely on Playwright’s auto-waiting and drop or increase the arbitrary delay, or assert directly with await expect(locator).toBeDisabled() if you expose the button locator from the POM.


164-194: Reuse POM helper instead of direct row/button interaction

In the “Cancel token revocation” test, you reach into the row and click the first button directly:

const row = settingsTokensPage.getRowByText(tokenName);
await row.getByRole("button").click();

Since SettingsTokensPage already exposes clickRevokeButtonByName, using it here would keep tests aligned with the POM abstraction and reduce duplication of locator logic:

await settingsTokensPage.clickRevokeButtonByName(tokenName);
await settingsTokensPage.cancelRevokeToken();

Functionally equivalent, but easier to maintain if the revoke button changes.


220-270: Token count-based assertions may be sensitive to external state

The revocation tests use getTokenCount() and expect the count to decrease by 1 or 2. This is fine as long as:

  • No other tests mutate tokens concurrently for the same user, and
  • getTokenCount() only reflects the PAT table rows.

If in the future tests are run fully parallel or other tables appear on the Settings page, these count-based checks could become flaky. If that happens, consider asserting more directly on the presence/absence of rows by name (as you already do elsewhere) instead of relative counts.


315-337: Test name vs. assertion gap for “dialog dismisses after refresh”

This test is named and commented as validating that the token generation success dialog disappears after a page refresh, but the only assertion is that the token appears in the list.

To enforce the intended behavior, you might also assert that the success dialog (or its dismiss button) is no longer interactable after refresh — for example, by:

  • Adding a POM helper that checks the visibility of the success dialog/dismiss button, and
  • Asserting it’s not visible or catching an expected failure when trying to interact with it.

That would align the assertion with the test’s stated purpose.


425-455: Assertions on exact API messages may be brittle

These API tests assert exact response messages:

expect(response.message).toBe("Token created successfully");
...
expect(revokeResponse.message).toContain("revoked successfully");

If backend wording changes slightly (punctuation, capitalization, additional context), these will fail even though semantics remain correct. If you want the tests to be more future-proof, consider:

  • Asserting on HTTP status codes and core fields (token, tokens, count, etc.), and/or
  • Using toContain or regex for messages where exact phrasing isn’t critical.

This trades a bit of strictness for better long-term stability.


628-734: Nice role-based coverage; consider small DRY helpers

The “Basic Functionality - All User Types” block verifies that:

  • ReadOnly and ReadWrite users can generate tokens via UI.
  • Each can revoke their own tokens, with counts decreasing as expected.

There’s some repetition across these tests (generate token name, create via API or UI, open SettingsTokensPage, navigate, verify existence, revoke, optionally check counts). If this suite grows further, extracting tiny helpers (e.g., openTokensAsRole, createUserTokenViaApi) could make the tests shorter and easier to extend, but the current structure is perfectly serviceable.


1-735: Suggested conventional-commit style PR titles

Not code-related, but for this change set you might consider PR titles like:

  • test: add e2e coverage for PAT settings tokens UI and API
  • feat: introduce PAT settings tokens page object and end-to-end tests

Either would fit conventional-commit conventions while reflecting the scope here.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42998e2 and 0b9645b.

📒 Files selected for processing (14)
  • .env.local.template (1 hunks)
  • .gitignore (1 hunks)
  • app/api/auth/tokenUtils.ts (3 hunks)
  • app/api/auth/tokens/[tokenId]/route.ts (1 hunks)
  • app/api/auth/tokens/credentials/route.ts (1 hunks)
  • app/api/auth/tokens/route.ts (2 hunks)
  • e2e/logic/POM/settingsTokensPage.ts (1 hunks)
  • e2e/tests/settingsTokens.spec.ts (1 hunks)
  • lib/token-storage/FalkorDBTokenStorage.ts (1 hunks)
  • lib/token-storage/FileTokenStorage.ts (1 hunks)
  • lib/token-storage/ITokenStorage.ts (1 hunks)
  • lib/token-storage/StorageFactory.ts (1 hunks)
  • lib/token-storage/falkordb-client.ts (1 hunks)
  • lib/token-storage/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/api/auth/tokens/[tokenId]/route.ts
  • app/api/auth/tokenUtils.ts
  • app/api/auth/tokens/credentials/route.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • lib/token-storage/StorageFactory.ts
  • lib/token-storage/FileTokenStorage.ts
  • lib/token-storage/falkordb-client.ts
  • e2e/logic/POM/settingsTokensPage.ts
  • e2e/tests/settingsTokens.spec.ts
  • lib/token-storage/index.ts
  • lib/token-storage/FalkorDBTokenStorage.ts
  • app/api/auth/tokens/route.ts
  • lib/token-storage/ITokenStorage.ts
e2e/**/*

⚙️ CodeRabbit configuration file

Review test code for proper assertions, test coverage, and maintainability.

Files:

  • e2e/logic/POM/settingsTokensPage.ts
  • e2e/tests/settingsTokens.spec.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • app/api/auth/tokens/route.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
📚 Learning: 2025-11-11T13:42:45.194Z
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.

Applied to files:

  • lib/token-storage/falkordb-client.ts
  • lib/token-storage/FalkorDBTokenStorage.ts
  • app/api/auth/tokens/route.ts
🧬 Code graph analysis (5)
lib/token-storage/StorageFactory.ts (2)
lib/token-storage/ITokenStorage.ts (1)
  • ITokenStorage (34-78)
lib/token-storage/index.ts (1)
  • ITokenStorage (2-2)
lib/token-storage/FileTokenStorage.ts (1)
lib/token-storage/ITokenStorage.ts (3)
  • ITokenStorage (34-78)
  • TokenData (4-18)
  • TokenFetchOptions (23-28)
e2e/logic/POM/settingsTokensPage.ts (2)
e2e/logic/POM/headerComponent.ts (1)
  • HeaderComponent (6-152)
e2e/infra/utils.ts (1)
  • interactWhenVisible (192-200)
e2e/tests/settingsTokens.spec.ts (4)
e2e/infra/ui/browserWrapper.ts (1)
  • BrowserWrapper (14-87)
e2e/logic/api/apiCalls.ts (1)
  • ApiCalls (88-602)
e2e/logic/POM/settingsTokensPage.ts (1)
  • SettingsTokensPage (6-436)
e2e/infra/utils.ts (1)
  • getRandomString (186-190)
lib/token-storage/FalkorDBTokenStorage.ts (2)
lib/token-storage/ITokenStorage.ts (3)
  • ITokenStorage (34-78)
  • TokenData (4-18)
  • TokenFetchOptions (23-28)
lib/token-storage/falkordb-client.ts (1)
  • executePATQuery (106-114)
🪛 dotenv-linter (4.0.0)
.env.local.template

[warning] 4-4: [UnorderedKey] The NEXTAUTH_SECRET key should go before the NEXTAUTH_URL key

(UnorderedKey)


[warning] 7-7: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 7-7: [UnorderedKey] The API_TOKEN_STORAGE_PATH key should go before the CHAT_URL key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: Setup and cache dependencies
🔇 Additional comments (12)
app/api/auth/tokens/route.ts (1)

63-104: LGTM!

The GET handler has clean authentication flow, proper error propagation, and sensible fallback defaults. The non-null assertion on line 91 is safe given the preceding error check.

.gitignore (1)

33-35: Ignore .data/ for local token storage is appropriate

Ignoring the .data/ directory aligns with the new file-based token storage and helps avoid committing local PAT/token data. Looks good as-is.

lib/token-storage/ITokenStorage.ts (1)

4-78: Token storage interfaces are clear and aligned with implementations

TokenData, TokenFetchOptions, and ITokenStorage line up well with both file‑based and FalkorDB implementations (including timestamp semantics and is_active/expiry handling). The comments about Unix timestamps and -1 sentinels make the contract explicit. No issues from a typing/abstraction standpoint.

lib/token-storage/StorageFactory.ts (1)

1-71: StorageFactory selection & defaults look solid

The singleton factory cleanly abstracts storage selection (FalkorDB vs file) based on API_TOKEN_FALKORDB_URL, and the default path logic matches the file‑based implementation. reset() plus the isFalkorDBStorage/isFileStorage helpers are handy for tests and diagnostics. No changes needed here.

lib/token-storage/falkordb-client.ts (1)

1-3: Deep import of FalkorDBOptions may be brittle across library upgrades

You’re importing FalkorDBOptions from falkordb/dist/src/falkordb, which reaches into the package’s internal structure. That can break on library updates if the internal path changes.

If the library exposes this type from its public entrypoint, it would be safer to import from "falkordb" instead (or use a local type alias based on the connection options you actually pass).

Please double‑check the current falkordb docs to see whether FalkorDBOptions (or an equivalent type) is publicly exported and update the import accordingly if possible.

lib/token-storage/index.ts (1)

1-14: Centralized token-storage exports are well structured

Bringing all token storage types, implementations, the factory, and FalkorDB helpers under a single lib/token-storage entrypoint is a nice cleanup and makes consumers’ imports much simpler. No issues here.

Suggested PR title (conventional commit style):
feat: add token storage backends and PAT FalkorDB integration

lib/token-storage/FalkorDBTokenStorage.ts (1)

39-82: Token listing and filtering logic is consistent with the file backend

The fetchTokens implementation:

  • Returns only t.is_active = true tokens,
  • Applies username/host/port filtering for non‑admin callers, and
  • Sorts by created_at DESC on the DB side.

This matches the behavior in FileTokenStorage.fetchTokens and should give consistent results across backends. No changes needed here.

e2e/logic/POM/settingsTokensPage.ts (1)

407-435: Nice dynamic header handling for token details

getTableColumnHeaders + hasColumnHeader + getTokenDetails make the POM robust against column order changes and optional columns (Username/Role for admins only). This is a solid pattern for UI tests where table layouts may evolve.

e2e/tests/settingsTokens.spec.ts (4)

13-21: Setup/teardown pattern is clear and isolated

Creating a fresh BrowserWrapper and ApiCalls in beforeEach and closing the browser in afterEach keeps tests isolated and makes lifecycle explicit. This works well with the POM and API helper abstractions you’ve introduced.


22-55: Good coverage of token expirations; UI coupling note

The parametrized expirationOptions suite nicely covers “never” and all TTL variants and then validates both the generated token and presence in the list. The extra getTokenDetails check on expires adds confidence.

Just keep in mind these tests assume the UI string for non-expiring tokens is exactly "Never". If that label ever changes (e.g., to "No expiration"), these assertions will start failing; if you want them less brittle, you could switch to a more tolerant check (contains/regex) or assert on structure rather than exact text.


95-127: Admin details test is strong; be aware of UI text assumptions

The “admin-specific columns” test effectively validates:

  • API-created token appears in the UI.
  • Admin-only columns (Username, Role) are present.
  • getTokenDetails returns expected values (name, created, lastUsed).

Like the earlier suite, this assumes lastUsed displays exactly "Never" for new tokens. If that label ever changes (capitalization, wording), this assertion will fail even though behavior is correct, so consider whether strict equality is desired here.


516-626: Multi-user isolation scenarios are well covered

The four isolation tests comprehensively validate that:

  • ReadWrite cannot see ReadOnly or Admin tokens.
  • ReadOnly cannot see ReadWrite or Admin tokens.
  • Each scenario both creates and cleans up tokens via the appropriate user-scoped APIs.

The structure is consistent and the cleanup logic ensures tests don’t leak state across runs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
e2e/logic/api/apiCalls.ts (3)

427-478: Tighten response types for admin token helpers

Nice job wiring admin token helpers and introducing ListTokensResponse / TokenDetailsResponse. A few methods still use Promise<any>, which weakens type safety:

  • generateToken(...): Promise<any>
  • revokeToken(tokenId: string): Promise<any>

Given you already have token response typings, consider introducing concrete types here as well (e.g., GenerateTokenResponse, RevokeTokenResponse) and using them both in the function signatures and casts:

-  async revokeToken(tokenId: string): Promise<any> {
+  async revokeToken(tokenId: string): Promise<RevokeTokenResponse> {
     try {
       const headers = await getAdminToken();
       const result = await deleteRequest(
         `${urls.api.tokenUrl}tokens/${tokenId}`,
         headers
       );
-      return await result.json();
+      return (await result.json()) as RevokeTokenResponse;
     } catch (error) {
       throw new Error(
         `Failed to revoke token. \n Error: ${(error as Error).message}`
       );
     }
   }

Same idea for generateToken. This will make the e2e helpers more self-documenting and reduce accidental misuse of the parsed JSON.


480-496: Revoke-all token logic now correctly attempts all revocations

Switching revokeAllUserTokens to Promise.allSettled and summarizing failures ensures every token is attempted for revocation, and partial failures are surfaced with a clear error message:

const results = await Promise.allSettled(
  tokens.map((tokenId) => this.revokeToken(tokenId))
);

This addresses the earlier fail‑fast behavior from Promise.all; the implementation looks good.


498-579: Improve cleanup robustness and typing in multi-user token helpers

The multi-user helpers are much improved—generateTokenAsUser encapsulates credential‑based token creation, and both listTokensAsUser and revokeTokenAsUser now revoke their temporary token via this.revokeToken(token_id).

Two follow-ups would make these safer and clearer:

  1. Ensure cleanup runs even on failures

    Right now, the temporary token is revoked only after a successful list/revoke call. If generateTokenAsUser or the main request fails, that token is never cleaned up. Wrapping the cleanup in a finally (and guarding against missing token_id) avoids leaks:

    async listTokensAsUser(username: string, password: string): Promise<ListTokensResponse> {
      let token_id: string | undefined;
      let token: string | undefined;
    
      try {
        const loginResponse = await this.generateTokenAsUser(username, password);
        ({ token, token_id } = loginResponse);
    
        const result = await getRequest(
          `${urls.api.tokenUrl}tokens`,
          { Authorization: `Bearer ${token}` }
        );
        return (await result.json()) as ListTokensResponse;
      } catch (error) {
        throw new Error(
          `Failed to list tokens as user ${username}. \n Error: ${(error as Error).message}`
        );
      } finally {
        if (token_id) {
          try {
            await this.revokeToken(token_id);
          } catch {
            // Optionally log cleanup failure; avoid masking the main error.
          }
        }
      }
    }

    A similar pattern can be applied to revokeTokenAsUser.

  2. Avoid Promise<any> on these helpers

    • listTokensAsUser can likely return Promise<ListTokensResponse>.
    • revokeTokenAsUser can use the same revoke response type you choose for revokeToken.

    This keeps multi-user flows aligned with the admin helpers and improves type safety in tests.

🧹 Nitpick comments (7)
e2e/logic/api/responses/settingsResponses.ts (1)

1-7: Interfaces look fine; consider clarifying config structure

The response interfaces are syntactically correct and usable as e2e typings. If you want to make future maintenance easier, consider either documenting what config represents (especially the tuple [string, number]) or replacing the tuple with an object with named fields to make call sites more self-explanatory.

e2e/logic/api/responses/userResponses.ts (2)

1-7: LGTM with an optional type safety enhancement.

The interface structure is correct and well-defined. Consider typing role as a union type if the set of roles is known (e.g., 'admin' | 'user' | 'viewer') for stricter type safety in your e2e tests.


9-15: Consider consolidating duplicate interfaces.

CreateUsersResponse and DeleteUsersResponse have identical structures. You could reduce duplication by defining a shared type:

export interface MessageResponse {
  message: string;
}

export type CreateUsersResponse = MessageResponse;
export type DeleteUsersResponse = MessageResponse;

Or use the single interface for both operations if that better reflects your API design.

e2e/logic/api/responses/tokenResponse.ts (1)

1-19: Avoid duplicating the Token shape in TokenDetailsResponse

TokenDetailsResponse repeats the exact fields of Token. To keep types DRY and easier to evolve, you can alias the details type to Token:

-export interface TokenDetailsResponse {
-  token_id: string;
-  name: string;
-  createdAt: string;
-  expiresAt: string | null;
-  lastUsed: string | null;
-}
+export type TokenDetailsResponse = Token;

This way, any future changes to the token shape only need to be made in one place.

e2e/logic/api/responses/graphResponses.ts (1)

1-54: Minor naming and typing tweaks for graph responses

Two small nits here:

  • The interface name DuplicateGraphresponse breaks the usual PascalCase convention used by the other types. Renaming it to DuplicateGraphResponse (and updating imports/usages) would improve consistency.
  • RunQueryResponse.data is typed as Array<Record<string, any>> under a file-level no-explicit-any disable. For e2e this is acceptable, but if you find yourself consuming this data more heavily, consider tightening it to unknown or a narrower shape and casting at the call sites.

These are non-blocking and can be addressed later if you prefer.

e2e/logic/api/apiCalls.ts (2)

45-97: Harden SSE error handling for unexpected error payloads

In getSSEGraphResult, the "error" listener assumes event.data is JSON with a { message } shape:

const { message } = JSON.parse(event.data);

If the server ever emits a non‑JSON error or omits message, this will throw inside the event handler and bypass your intended reject, making failures harder to debug.

You can make this more robust and also standardize on rejecting with an Error:

-      evtSource.addEventListener("error", (event: MessageEvent) => {
-        handled = true;
-        const { message } = JSON.parse(event.data);
-        evtSource.close();
-        reject(message);
-      });
+      evtSource.addEventListener("error", (event: MessageEvent) => {
+        handled = true;
+        let message = "Unknown error";
+        try {
+          const parsed = JSON.parse(event.data);
+          if (parsed && typeof parsed.message === "string") {
+            message = parsed.message;
+          }
+        } catch {
+          // Fallback to generic message if payload is not JSON
+        }
+        evtSource.close();
+        reject(new Error(message));
+      });

Callers that use (error as Error).message will then behave more predictably.


592-601: Use schema-specific response type for removeSchema

removeSchema currently returns Promise<RemoveGraphResponse>:

async removeSchema(schemaName: string): Promise<RemoveGraphResponse> {

Given you have a dedicated RemoveSchemaResponse type in e2e/logic/api/responses/schemaResponses.ts, it would be clearer to use that here:

-import { AddSchemaResponse, SchemaListResponse } from "./responses/schemaResponses";
+import {
+  AddSchemaResponse,
+  SchemaListResponse,
+  RemoveSchemaResponse,
+} from "./responses/schemaResponses";
...
-  async removeSchema(schemaName: string): Promise<RemoveGraphResponse> {
+  async removeSchema(schemaName: string): Promise<RemoveSchemaResponse> {

Even if both shapes are currently { message: string }, this makes the intent explicit and helps future readers.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b9645b and e68c267.

📒 Files selected for processing (26)
  • e2e/logic/api/apiCalls.ts (6 hunks)
  • e2e/logic/api/responses/LoginResponse.ts (0 hunks)
  • e2e/logic/api/responses/addGraphResponse.ts (0 hunks)
  • e2e/logic/api/responses/addSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/authResponses.ts (1 hunks)
  • e2e/logic/api/responses/changeGraphNameResponse.ts (0 hunks)
  • e2e/logic/api/responses/createUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/deleteUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/duplicateGraph.ts (0 hunks)
  • e2e/logic/api/responses/getGraphsResponse.ts (0 hunks)
  • e2e/logic/api/responses/getSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/getSettingsRoleValue.ts (0 hunks)
  • e2e/logic/api/responses/getUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphAttributeResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphCountResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphNodeResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphResponses.ts (1 hunks)
  • e2e/logic/api/responses/logoutResponse.ts (0 hunks)
  • e2e/logic/api/responses/modifySettingsRoleResponse.ts (0 hunks)
  • e2e/logic/api/responses/removeGraphResponse.ts (0 hunks)
  • e2e/logic/api/responses/removeSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/runQueryResponse.ts (0 hunks)
  • e2e/logic/api/responses/schemaResponses.ts (1 hunks)
  • e2e/logic/api/responses/settingsResponses.ts (1 hunks)
  • e2e/logic/api/responses/tokenResponse.ts (1 hunks)
  • e2e/logic/api/responses/userResponses.ts (1 hunks)
💤 Files with no reviewable changes (19)
  • e2e/logic/api/responses/removeGraphResponse.ts
  • e2e/logic/api/responses/getUsersResponse.ts
  • e2e/logic/api/responses/getSettingsRoleValue.ts
  • e2e/logic/api/responses/LoginResponse.ts
  • e2e/logic/api/responses/modifySettingsRoleResponse.ts
  • e2e/logic/api/responses/deleteUsersResponse.ts
  • e2e/logic/api/responses/getGraphsResponse.ts
  • e2e/logic/api/responses/addGraphResponse.ts
  • e2e/logic/api/responses/addSchemaResponse.ts
  • e2e/logic/api/responses/duplicateGraph.ts
  • e2e/logic/api/responses/logoutResponse.ts
  • e2e/logic/api/responses/removeSchemaResponse.ts
  • e2e/logic/api/responses/changeGraphNameResponse.ts
  • e2e/logic/api/responses/runQueryResponse.ts
  • e2e/logic/api/responses/graphNodeResponse.ts
  • e2e/logic/api/responses/graphAttributeResponse.ts
  • e2e/logic/api/responses/createUsersResponse.ts
  • e2e/logic/api/responses/getSchemaResponse.ts
  • e2e/logic/api/responses/graphCountResponse.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/apiCalls.ts
e2e/**/*

⚙️ CodeRabbit configuration file

Review test code for proper assertions, test coverage, and maintainability.

Files:

  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/apiCalls.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/apiCalls.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: app/api/auth/[...nextauth]/options.ts:9-10
Timestamp: 2025-11-11T13:42:45.194Z
Learning: In app/api/auth/[...nextauth]/options.ts for the FalkorDB browser application: The jwt() callback stores 'password' field for NextAuth session tokens (cookie-based auth), while Personal Access Tokens (PATs) created by /api/auth/login store 'pwd' field (application-encrypted). These are two separate JWT flows: session tokens are never used with Bearer auth and PATs are never used with session auth. The jwt() callback must keep 'password' field for the session() callback to work correctly.
🧬 Code graph analysis (1)
e2e/logic/api/apiCalls.ts (2)
e2e/infra/utils.ts (1)
  • getAdminToken (146-184)
e2e/logic/api/responses/tokenResponse.ts (2)
  • ListTokensResponse (9-11)
  • TokenDetailsResponse (13-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Setup and cache dependencies
  • GitHub Check: build
  • GitHub Check: Setup and cache dependencies
🔇 Additional comments (3)
e2e/logic/api/responses/authResponses.ts (1)

1-13: Auth response typings look correct

AuthCredentialsResponse and LogoutResponse are narrowly scoped, stringly-typed shapes appropriate for these e2e auth calls; no issues spotted.

e2e/logic/api/responses/schemaResponses.ts (1)

1-14: Schema response interfaces look appropriate

The AddSchemaResponse, SchemaListResponse, and RemoveSchemaResponse shapes are consistent with how schema APIs are usually exercised in tests; no issues from a typing perspective.

e2e/logic/api/apiCalls.ts (1)

176-191: Graph and settings API payload changes look consistent

The following adjustments all look coherent with a body‑centric API surface:

  • changeGraphName now PATCHes the destination graph with { sourceName } in the body instead of relying on query parameters.
  • duplicateGraph uses a PATCH to /duplicate with { sourceName, ...data }, which is easier to extend with extra options.
  • deleteGraphNode broadens its data payload to Record<string, string | boolean>, which is suitable for boolean flags alongside string fields.
  • modifySettingsRole now posts { value: roleValue } to settingsConfig + roleName, matching the other body-based configuration endpoints.

No issues here from a correctness or type-safety standpoint.

Also applies to: 206-222, 295-305, 357-365

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.

6 participants