[Feature]: Select/deselect all sources in a Notebook #223#231
[Feature]: Select/deselect all sources in a Notebook #223#231MasterAffan wants to merge 1 commit intolfnovo:mainfrom
Conversation
|
@lfnovo pls have a look |
|
can you please add hacktoberfest label in the pull req |
| useEffect(() => { | ||
| if (!open) return | ||
|
|
||
| const loadSources = async () => { | ||
| setIsLoading(true) | ||
| try { | ||
| if (operation === 'add') { | ||
| // For add: fetch all sources and filter out ones already in notebook | ||
| const allSources = await sourcesApi.list({ | ||
| limit: 100, | ||
| offset: 0, | ||
| sort_by: 'created', | ||
| sort_order: 'desc', | ||
| }) | ||
| const sourcesToAdd = allSources.filter(s => !currentSourceIds.has(s.id)) | ||
| setAvailableSources(sourcesToAdd) | ||
| } else { | ||
| // For remove: use sources already in the notebook | ||
| setAvailableSources(currentNotebookSources || []) | ||
| } | ||
| } catch (error) { | ||
| console.error('Error loading sources:', error) | ||
| setAvailableSources([]) | ||
| } finally { | ||
| setIsLoading(false) | ||
| } | ||
| } | ||
|
|
||
| loadSources() | ||
| }, [open, operation, currentSourceIds, currentNotebookSources]) |
There was a problem hiding this comment.
🟠 HIGH - Race condition in async useEffect without cancellation
Category: bug
Description:
Async fetch operation in useEffect lacks cancellation. If dialog reopens or dependencies change rapidly, stale responses can overwrite current state.
Suggestion:
Add a cleanup function with a cancelled flag or AbortController to prevent stale responses from updating state after unmount or dependency change.
Confidence: 88%
Rule: react_async_race_condition
| for source_id in request.source_ids: | ||
| try: | ||
| if request.operation == "add": | ||
| # Check if source exists | ||
| source = await Source.get(source_id) | ||
| if not source: | ||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": False, | ||
| "error": "Source not found" | ||
| }) | ||
| continue | ||
|
|
||
| # Check if reference already exists (idempotency) | ||
| existing_ref = await repo_query( | ||
| "SELECT * FROM reference WHERE out = $source_id AND in = $notebook_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| # If reference doesn't exist, create it | ||
| if not existing_ref: | ||
| await repo_query( | ||
| "RELATE $source_id->reference->$notebook_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": True, | ||
| "message": "Source added to notebook successfully" | ||
| }) | ||
|
|
||
| elif request.operation == "remove": | ||
| # Delete the reference record linking source to notebook | ||
| await repo_query( | ||
| "DELETE FROM reference WHERE out = $notebook_id AND in = $source_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": True, | ||
| "message": "Source removed from notebook successfully" | ||
| }) | ||
|
|
||
| except Exception as e: | ||
| logger.error( | ||
| f"Error processing source {source_id} for notebook {notebook_id}: {str(e)}" | ||
| ) | ||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": False, | ||
| "error": str(e) | ||
| }) |
There was a problem hiding this comment.
🔴 CRITICAL - N+1 database queries in bulk operation loop
Category: performance
Description:
Loop executes Source.get() and multiple repo_query() calls for each source_id, causing severe performance degradation at scale
Suggestion:
Batch fetch all sources with a single query: sources = await Source.get_many(request.source_ids), then process in-memory. For reference operations, consider bulk RELATE query or batched operations.
Confidence: 92%
Rule: perf_n_plus_one_queries
| for source_id in request.source_ids: | ||
| try: | ||
| if request.operation == "add": | ||
| # Check if source exists | ||
| source = await Source.get(source_id) | ||
| if not source: | ||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": False, | ||
| "error": "Source not found" | ||
| }) | ||
| continue | ||
|
|
||
| # Check if reference already exists (idempotency) | ||
| existing_ref = await repo_query( | ||
| "SELECT * FROM reference WHERE out = $source_id AND in = $notebook_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| # If reference doesn't exist, create it | ||
| if not existing_ref: | ||
| await repo_query( | ||
| "RELATE $source_id->reference->$notebook_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": True, | ||
| "message": "Source added to notebook successfully" | ||
| }) | ||
|
|
||
| elif request.operation == "remove": | ||
| # Delete the reference record linking source to notebook | ||
| await repo_query( | ||
| "DELETE FROM reference WHERE out = $notebook_id AND in = $source_id", | ||
| { | ||
| "notebook_id": ensure_record_id(notebook_id), | ||
| "source_id": ensure_record_id(source_id), | ||
| }, | ||
| ) | ||
|
|
||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": True, | ||
| "message": "Source removed from notebook successfully" | ||
| }) | ||
|
|
||
| except Exception as e: | ||
| logger.error( | ||
| f"Error processing source {source_id} for notebook {notebook_id}: {str(e)}" | ||
| ) | ||
| results.append({ | ||
| "source_id": source_id, | ||
| "success": False, | ||
| "error": str(e) | ||
| }) |
There was a problem hiding this comment.
🟠 HIGH - Missing transaction handling with rollback
Category: performance
Description:
Bulk operation with multiple database statements lacks transaction wrapping, risking partial failures and data inconsistency
Suggestion:
Wrap the loop in a database transaction with try/except to commit on success and rollback on failure. Use transaction context manager or begin/commit/rollback pattern.
Confidence: 87%
Rule: py_add_transaction_handling_with_rollback
|
Description
Summary
Why this helps
Implementation details
API (generic shape)
UI/UX
How to test
Affected areas (indicative)
Notes
Checklist
Demo Video
2025-10-27.12-08-56.mp4
Linked issues