Skip to content

Conversation

@c8y3
Copy link
Contributor

@c8y3 c8y3 commented Nov 14, 2025

Implementation of DELETE /api/v2/global-tasks/{identifier}

  • Deprecated POST /global/tasks/delete/<int:cur_id>
  • documented some necessary method in .vulture.ignore
  • dispatched code from app.datamgmt.dashboard.dashboard_db into other files of the persistence layer to group requests by table
  • followed calling convention uniformly when calling call_modules_hook
  • added a test for endpoint GET /dim/tasks/list/{count} to ensure the case identifier is present in the response (and prevent a code cleanup I had wanted to perform)

This PR goes with the documentation PR#98 in iris-doc-src.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added delete capability for global tasks via REST API.
  • Improvements

    • Refactored comment deletion workflow across case management.
    • Centralized database deletion operations for consistency.
    • Enhanced module hook system for better extensibility.
  • Tests

    • Added test coverage for global task deletion.
    • Added test coverage for module task integration.

Elise17 added 30 commits October 8, 2025 14:04
@c8y3 c8y3 requested a review from whikernel November 14, 2025 13:50
@c8y3 c8y3 added the enhancement New feature or request label Nov 14, 2025
@c8y3 c8y3 linked an issue Nov 14, 2025 that may be closed by this pull request
9 tasks
@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • api_*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request refactors hook invocation signatures across the codebase to use positional arguments instead of keyword arguments, implements global task deletion functionality, consolidates database deletion operations through a centralized db_delete utility, and reorganizes imports by moving task/case listing functions from dashboard_db to domain-specific modules.

Changes

Cohort / File(s) Summary
Hook invocation signature refactoring (keyword → positional)
source/app/blueprints/rest/alerts_routes.py, source/app/blueprints/rest/case/case_assets_routes.py, source/app/blueprints/rest/case/case_evidences_routes.py, source/app/blueprints/rest/case/case_ioc_routes.py, source/app/blueprints/rest/case/case_notes_routes.py, source/app/blueprints/rest/case/case_tasks_routes.py, source/app/blueprints/rest/case/case_timeline_routes.py, source/app/blueprints/rest/dashboard_routes.py, source/app/blueprints/rest/dim_tasks_routes.py, source/app/blueprints/rest/manage/manage_cases_routes.py, source/app/business/*.py (alerts, assets, cases, comments, evidences, iocs, notes, tasks, reports), source/app/datamgmt/manage/manage_case_templates_db.py
Converted call_modules_hook invocations from keyword argument style (data=...) to positional argument passing, standardizing hook call signatures across routes and business logic.
Global tasks deletion feature
source/app/blueprints/rest/v2/global_tasks.py, source/app/business/global_tasks.py
Added DELETE endpoint, GlobalTasksOperations.delete() method, and global_tasks_delete() business function; renamed endpoints from create_customer/get_customer to create_global_task/get_global_task.
Database deletion consolidation
source/app/datamgmt/db_operations.py, source/app/datamgmt/alerts/alerts_db.py, source/app/datamgmt/case/case_*.py (assets, events, iocs, notes, rfiles, tasks), source/app/datamgmt/client/client_db.py, source/app/datamgmt/comments.py, source/app/datamgmt/datastore/datastore_db.py, source/app/datamgmt/global_tasks.py, source/app/datamgmt/manage/manage_groups_db.py
Added new db_delete() utility function and replaced direct SQLAlchemy session deletions (db.session.delete() + commit()) with centralized calls across database modules.
Import reorganization (function relocation)
source/app/blueprints/pages/dashboard/dashboard_routes.py, source/app/business/cases.py, source/app/business/tasks.py, source/app/datamgmt/case/case_db.py, source/app/datamgmt/case/case_tasks_db.py, source/app/datamgmt/global_tasks.py
Moved list_user_cases, list_user_reviews, list_user_tasks, and related functions from dashboard_db to domain-specific modules (case_db, case_tasks_db); removed legacy task/case retrieval logic.
Configuration and module management
pyproject.toml, source/app/iris_engine/module_handler/module_handler.py, source/app/datamgmt/iris_engine/modules_db.py, .vulture.ignore
Updated import-linter contracts; added deregister_module_from_hook() and related hook management functions; expanded .vulture.ignore entries for public functions.
Comment deletion flow updates
source/app/blueprints/rest/case/case_assets_routes.py, source/app/datamgmt/case/case_assets_db.py, source/app/datamgmt/comments.py
Enhanced asset comment deletion to retrieve comment object via get_comment() before deletion and updated delete_asset_comment() signature to accept Comments object instead of ID.
Public API additions
source/app/datamgmt/case/case_tasks_db.py, source/app/datamgmt/datastore/datastore_db.py, source/app/datamgmt/comments.py
Added list_user_tasks(), update_utask_status(), get_comment() functions; updated datastore_filter_tree() to return tuple with status string.
Test coverage
tests/tests_rest_global_tasks.py, tests/tests_rest_module_tasks.py
Added tests for global task deletion and module task REST endpoints.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • Hook signature consistency: Verify all call_modules_hook invocations follow the new positional argument pattern across ~20 files; ensure no mismatches in argument order or type.
  • Global tasks deletion flow: Review new DELETE endpoint in v2/global_tasks.py, business logic in business/global_tasks.py, and corresponding database operations for completeness and error handling.
  • db_delete() adoption: Ensure all usages of the new centralized deletion utility maintain equivalent error handling and transaction semantics compared to previous direct session management.
  • Import path changes and function migrations: Verify that functions moved from dashboard_dbcase_db / case_tasks_db do not break existing callers; check for any missed import updates.
  • Comment deletion refactor: Validate that delete_asset_comment() signature change to accept Comments object is propagated correctly and authorization checks are in place.
  • datastore_filter_tree() return signature change: Confirm all callers handle the new tuple return format with status string.

Possibly related issues

  • API v2 delete global task #973: The PR directly implements deletion of global tasks (business-layer global_tasks_delete, REST v2 DELETE endpoint, and comprehensive test coverage) addressing the API v2 "delete global task" functionality.

Possibly related PRs

  • Tidy imports #928: Both PRs modify the import-linter configuration in pyproject.toml, updating/adding import-linter contracts for architectural layering rules.
  • Auth with jwt #780: Both PRs modify iris_engine/module_handler/module_handler.py and refactor hook handling; PR #780 changes call_modules_hook to use iris_current_user while this PR standardizes hook invocation signatures.

Suggested reviewers

  • whikernel

Poem

🐰 Hops through the code with glee
Hooks now march in positional parade,
Tasks deleted with db_delete cascade,
Imports reorganized, modules dismayed,
And dashboards split three ways with pride!
The rabbit's refactor runs deep and wide.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.20% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Api v2 delete global task' clearly summarizes the main change: implementing the DELETE endpoint for global tasks in the v2 API.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@c8y3 c8y3 changed the base branch from api_v2_get_global_task to develop November 14, 2025 13:53
Copy link

@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: 4

Caution

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

⚠️ Outside diff range comments (2)
source/app/datamgmt/datastore/datastore_db.py (1)

403-554: datastore_filter_tree now returns a tuple; ensure all callers are updated.

datastore_filter_tree now returns (path_tree, 'Success') and (None, str(e)) on error, rather than just path_tree:

    except Exception as e:
        return None, str(e)
...
    return path_tree, 'Success'

This is a public contract change; any existing callers that expect a single return value will now get a tuple and likely break.

Please confirm that:

  • All internal call sites have been updated to unpack (tree, status) (or similar), and
  • The new status string is handled consistently (or consider returning a richer status object/enum if this is part of a broader pattern).
source/app/blueprints/rest/case/case_assets_routes.py (1)

407-420: Comment deletion misses asset/case binding, can break referential integrity.

The new logic:

comment = get_comment(iris_current_user, com_id)
if not comment:
    return response_error('You are not allowed to delete this comment')

delete_asset_comment(cur_id, comment)
call_modules_hook('on_postload_asset_comment_delete', com_id, caseid=caseid)

has good intent (only the author can delete their comment) but misses a key check:

  • get_comment only filters by comment_id and comment_user_id, not by asset or case.
  • delete_asset_comment(cur_id, comment) (in case_assets_db) assumes the comment is linked to the given asset via AssetComments. If it isn’t, the association row is not deleted, but db_delete(comment) still removes the comments row, leading to:
    • A likely FK violation if asset_comments.comment_id has a foreign key, or
    • Orphaned asset_comments rows if the DB doesn’t enforce referential integrity.

A safer pattern is to ensure the comment is both:

  1. Linked to the specified asset, and
  2. Owned by the current user.

You already have get_case_asset_comment imported; we can re-use it:

 def case_comment_asset_delete(cur_id, com_id, caseid):
-    comment = get_comment(iris_current_user, com_id)
-    if not comment:
-        return response_error('You are not allowed to delete this comment')
-
-    delete_asset_comment(cur_id, comment)
-
-    call_modules_hook('on_postload_asset_comment_delete', com_id, caseid=caseid)
+    # Ensure the comment belongs to this asset
+    comment = get_case_asset_comment(cur_id, com_id)
+    if not comment or comment.comment_user_id != iris_current_user.id:
+        return response_error('You are not allowed to delete this comment')
+
+    delete_asset_comment(cur_id, comment)
+
+    call_modules_hook('on_postload_asset_comment_delete', com_id, caseid=caseid)

This guarantees we only delete comments that are:

  • Attached to the asset identified by cur_id, and
  • Authored by the current user,

and avoids inconsistent AssetComments/Comments state.

🧹 Nitpick comments (3)
source/app/datamgmt/case/case_db.py (1)

269-278: Use .is_(None) for SQLAlchemy NULL comparisons.

Line 277 uses == None which can work but SQLAlchemy best practices recommend using .is_(None) for explicit NULL comparisons to ensure proper SQL generation across different database backends.

Apply this diff:

     return Cases.query.filter(
         Cases.owner_id == user_identifier,
-        Cases.close_date == None
+        Cases.close_date.is_(None)
     ).all()
source/app/blueprints/rest/dashboard_routes.py (1)

36-41: Hook refactor and global task imports look good; minor consistency nit.

  • Importing list_user_cases, list_user_reviews, list_user_tasks, and global task helpers from their dedicated datamgmt modules is cleaner and aligns with the PR’s refactor.
  • Switching to positional data when calling call_modules_hook in add_gtask and gtask_delete is correct and matches the helper’s signature.

One small consistency nit:

gtask = call_modules_hook(
    'on_postload_global_task_update', data=gtask, caseid=caseid)

Everywhere else in this file you now pass data positionally. You may want to align this call for style/readability:

-        gtask = call_modules_hook(
-            'on_postload_global_task_update', data=gtask, caseid=caseid)
+        gtask = call_modules_hook(
+            'on_postload_global_task_update', gtask, caseid=caseid)

Given the endpoint is being deprecated, the existing manual delete logic in gtask_delete is acceptable, but if you want to centralize behavior you could also delegate to the new global‑task delete helper later.

Also applies to: 203-236, 255-265, 274-295

source/app/datamgmt/global_tasks.py (1)

21-29: delete_global_task works but could be more defensive and reuse existing helpers.

The new helper:

def delete_global_task(task: GlobalTasks):
    GlobalTasks.query.filter(GlobalTasks.id == task.id).delete()
    db.session.commit()

is straightforward, but a couple of points to consider:

  • It assumes task is a non‑None GlobalTasks instance; if a caller passes None, this will raise AttributeError. If callers are guaranteed to resolve/validate beforehand (e.g. via a dedicated get/operations layer that raises ObjectNotFoundError), that’s fine; otherwise, a defensive early return or explicit exception would make the contract clearer.
  • Elsewhere in the codebase, deletions are being centralized via db_delete(element) in app.datamgmt.db_operations. For consistency you might want to use db_delete(task) here instead of a bulk query delete, unless you intentionally want to avoid loading the instance.

Example defensive variant:

-def delete_global_task(task: GlobalTasks):
-    GlobalTasks.query.filter(GlobalTasks.id == task.id).delete()
-    db.session.commit()
+def delete_global_task(task: GlobalTasks):
+    if task is None:
+        return
+
+    db.session.delete(task)
+    db.session.commit()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b252df8 and 8391039.

📒 Files selected for processing (43)
  • .vulture.ignore (1 hunks)
  • pyproject.toml (1 hunks)
  • source/app/blueprints/pages/dashboard/dashboard_routes.py (1 hunks)
  • source/app/blueprints/rest/alerts_routes.py (12 hunks)
  • source/app/blueprints/rest/case/case_assets_routes.py (7 hunks)
  • source/app/blueprints/rest/case/case_evidences_routes.py (2 hunks)
  • source/app/blueprints/rest/case/case_ioc_routes.py (4 hunks)
  • source/app/blueprints/rest/case/case_notes_routes.py (4 hunks)
  • source/app/blueprints/rest/case/case_tasks_routes.py (2 hunks)
  • source/app/blueprints/rest/case/case_timeline_routes.py (7 hunks)
  • source/app/blueprints/rest/dashboard_routes.py (6 hunks)
  • source/app/blueprints/rest/dim_tasks_routes.py (1 hunks)
  • source/app/blueprints/rest/manage/manage_cases_routes.py (2 hunks)
  • source/app/blueprints/rest/v2/global_tasks.py (3 hunks)
  • source/app/business/alerts.py (3 hunks)
  • source/app/business/assets.py (2 hunks)
  • source/app/business/cases.py (5 hunks)
  • source/app/business/comments.py (8 hunks)
  • source/app/business/evidences.py (3 hunks)
  • source/app/business/global_tasks.py (3 hunks)
  • source/app/business/iocs.py (3 hunks)
  • source/app/business/notes.py (3 hunks)
  • source/app/business/reports/reports.py (3 hunks)
  • source/app/business/tasks.py (4 hunks)
  • source/app/datamgmt/alerts/alerts_db.py (3 hunks)
  • source/app/datamgmt/case/case_assets_db.py (2 hunks)
  • source/app/datamgmt/case/case_db.py (1 hunks)
  • source/app/datamgmt/case/case_events_db.py (2 hunks)
  • source/app/datamgmt/case/case_iocs_db.py (2 hunks)
  • source/app/datamgmt/case/case_notes_db.py (3 hunks)
  • source/app/datamgmt/case/case_rfiles_db.py (2 hunks)
  • source/app/datamgmt/case/case_tasks_db.py (3 hunks)
  • source/app/datamgmt/client/client_db.py (2 hunks)
  • source/app/datamgmt/comments.py (3 hunks)
  • source/app/datamgmt/datastore/datastore_db.py (4 hunks)
  • source/app/datamgmt/db_operations.py (1 hunks)
  • source/app/datamgmt/global_tasks.py (2 hunks)
  • source/app/datamgmt/iris_engine/modules_db.py (3 hunks)
  • source/app/datamgmt/manage/manage_case_templates_db.py (4 hunks)
  • source/app/datamgmt/manage/manage_groups_db.py (2 hunks)
  • source/app/iris_engine/module_handler/module_handler.py (4 hunks)
  • tests/tests_rest_global_tasks.py (1 hunks)
  • tests/tests_rest_module_tasks.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (40)
source/app/business/notes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
tests/tests_rest_global_tasks.py (2)
source/app/blueprints/rest/v2/global_tasks.py (2)
  • create (43-51)
  • delete (61-68)
tests/iris.py (3)
  • create (60-61)
  • delete (69-70)
  • get (63-64)
tests/tests_rest_module_tasks.py (1)
tests/iris.py (1)
  • get_module_identifier_by_name (156-162)
source/app/blueprints/rest/case/case_ioc_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/manage/manage_groups_db.py (1)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/datamgmt/case/case_iocs_db.py (1)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/datamgmt/alerts/alerts_db.py (1)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/business/assets.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_notes_db.py (1)
source/app/datamgmt/db_operations.py (2)
  • db_create (22-24)
  • db_delete (27-29)
source/app/datamgmt/comments.py (2)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/models/comments.py (1)
  • Comments (33-48)
source/app/datamgmt/case/case_events_db.py (1)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/business/tasks.py (3)
source/app/datamgmt/case/case_tasks_db.py (1)
  • list_user_tasks (347-373)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/states.py (1)
  • update_tasks_state (94-95)
source/app/blueprints/rest/case/case_notes_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/blueprints/rest/case/case_timeline_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_tasks_db.py (3)
source/app/datamgmt/db_operations.py (2)
  • db_create (22-24)
  • db_delete (27-29)
source/app/models/models.py (3)
  • CaseTasks (459-482)
  • TaskStatus (450-456)
  • TaskAssignee (514-524)
source/app/models/cases.py (1)
  • Cases (46-114)
source/app/blueprints/rest/dim_tasks_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_db.py (2)
source/app/models/cases.py (1)
  • Cases (46-114)
source/app/models/models.py (1)
  • ReviewStatus (688-692)
source/app/blueprints/rest/case/case_evidences_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/iris_engine/module_handler/module_handler.py (1)
source/app/datamgmt/iris_engine/modules_db.py (1)
  • deregister_module_from_hook (262-269)
source/app/business/alerts.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/business/iocs.py (2)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_iocs_db.py (1)
  • delete_ioc (83-96)
source/app/datamgmt/iris_engine/modules_db.py (1)
source/app/models/models.py (2)
  • IrisModuleHook (614-628)
  • IrisHook (606-611)
source/app/blueprints/pages/dashboard/dashboard_routes.py (1)
source/app/datamgmt/case/case_tasks_db.py (1)
  • get_tasks_status (41-42)
source/app/business/reports/reports.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/business/evidences.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/business/comments.py (2)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_assets_db.py (1)
  • delete_asset_comment (362-368)
source/app/datamgmt/case/case_rfiles_db.py (1)
source/app/datamgmt/db_operations.py (2)
  • db_create (22-24)
  • db_delete (27-29)
source/app/blueprints/rest/v2/global_tasks.py (3)
source/app/blueprints/rest/endpoints.py (3)
  • response_api_deleted (55-56)
  • response_api_success (29-30)
  • response_api_not_found (72-73)
source/app/business/global_tasks.py (2)
  • global_tasks_get (42-46)
  • global_tasks_delete (49-55)
source/app/datamgmt/global_tasks.py (2)
  • get_global_task (56-79)
  • delete_global_task (27-29)
source/app/datamgmt/manage/manage_case_templates_db.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/blueprints/rest/case/case_assets_routes.py (3)
source/app/datamgmt/comments.py (1)
  • get_comment (141-145)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/case/case_assets_db.py (1)
  • delete_asset_comment (362-368)
source/app/datamgmt/client/client_db.py (3)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/models/errors.py (1)
  • ElementInUseError (49-51)
source/app/models/customers.py (1)
  • Client (32-44)
source/app/blueprints/rest/case/case_tasks_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/business/global_tasks.py (3)
source/app/datamgmt/global_tasks.py (2)
  • delete_global_task (27-29)
  • get_global_task_by_identifier (82-85)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/iris_engine/utils/tracker.py (1)
  • track_activity (28-64)
source/app/blueprints/rest/alerts_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/business/cases.py (6)
source/app/datamgmt/case/case_db.py (4)
  • case_db_exists (78-80)
  • list_user_cases (269-278)
  • case_db_save (235-249)
  • list_user_reviews (252-266)
source/app/models/cases.py (2)
  • Cases (46-114)
  • ReviewStatusList (199-204)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/manage/manage_cases_db.py (1)
  • delete_case (403-458)
source/app/iris_engine/utils/tracker.py (1)
  • track_activity (28-64)
source/app/models/errors.py (1)
  • BusinessProcessingError (22-32)
source/app/datamgmt/datastore/datastore_db.py (1)
source/app/datamgmt/db_operations.py (2)
  • db_create (22-24)
  • db_delete (27-29)
source/app/blueprints/rest/dashboard_routes.py (5)
source/app/datamgmt/case/case_db.py (2)
  • list_user_reviews (252-266)
  • list_user_cases (269-278)
source/app/datamgmt/case/case_tasks_db.py (2)
  • get_tasks_status (41-42)
  • list_user_tasks (347-373)
source/app/datamgmt/global_tasks.py (2)
  • list_global_tasks (32-53)
  • get_global_task (56-79)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/blueprints/rest/endpoints.py (1)
  • endpoint_deprecated (76-86)
source/app/datamgmt/case/case_assets_db.py (3)
source/app/datamgmt/db_operations.py (1)
  • db_delete (27-29)
source/app/models/comments.py (2)
  • Comments (33-48)
  • AssetComments (84-92)
source/app/blueprints/rest/v2/assets_routes/comments.py (1)
  • delete (98-108)
source/app/blueprints/rest/manage/manage_cases_routes.py (1)
source/app/iris_engine/module_handler/module_handler.py (1)
  • call_modules_hook (492-580)
source/app/datamgmt/global_tasks.py (2)
source/app/models/models.py (2)
  • GlobalTasks (527-548)
  • TaskStatus (450-456)
source/app/blueprints/rest/v2/global_tasks.py (2)
  • delete_global_task (90-91)
  • delete (61-68)
⏰ 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). (8)
  • GitHub Check: Database migration tests
  • GitHub Check: End to end tests
  • GitHub Check: Generate graphQL documentation
  • GitHub Check: Test API
  • GitHub Check: Test API
  • GitHub Check: Database migration tests
  • GitHub Check: End to end tests
  • GitHub Check: Generate graphQL documentation
🔇 Additional comments (61)
source/app/blueprints/pages/dashboard/dashboard_routes.py (1)

27-27: Import refactoring verified as complete.

The refactor is properly executed across the codebase. No stray imports from the old app.datamgmt.dashboard.dashboard_db location remain, and all four usages of get_tasks_status have been updated to the new location in app.datamgmt.case.case_tasks_db (dashboard_routes.py, rest/dashboard_routes.py, rest/case/case_tasks_routes.py, and pages/case/case_tasks_routes.py). The function behavior is unchanged and the refactoring aligns with the stated objective to reorganize persistence layer code by domain.

source/app/business/alerts.py (1)

88-88: LGTM! Standardized hook calling convention.

The migration from keyword arguments to positional arguments for call_modules_hook invocations is correct and consistent across all five call sites in this file. The changes align with the function signature and maintain existing behavior.

Also applies to: 138-138, 141-141, 144-144, 164-164

source/app/blueprints/rest/manage/manage_cases_routes.py (1)

191-191: LGTM! Hook invocations correctly standardized.

The refactor from keyword-argument style (data=case) to positional-argument passing aligns with the function signature and the project-wide standardization described in the PR objectives. All four invocations correctly pass the payload object as the second positional argument.

Also applies to: 225-225, 229-229, 236-236

source/app/business/reports/reports.py (3)

31-31: Preload hook standardization looks good.

The change from keyword to positional argument for report_id aligns with the repository-wide hook invocation standardization.


50-57: Postload hook standardization looks good.

The variable rename from _data to data and passing it as a positional argument aligns with the standardized calling convention.


63-63: Preload hook standardization looks good.

The change from keyword to positional argument for report_id aligns with the repository-wide hook invocation standardization.

source/app/datamgmt/client/client_db.py (1)

25-25: LGTM: Centralized deletion pattern applied correctly.

The refactor from direct ORM deletions to the db_delete utility maintains identical behavior while improving consistency across the codebase.

Also applies to: 144-144, 151-151

source/app/datamgmt/db_operations.py (1)

27-29: LGTM: Clean deletion utility.

The db_delete function provides a consistent interface for deletions, mirroring the existing db_create pattern.

source/app/datamgmt/iris_engine/modules_db.py (1)

23-23: LGTM: Centralized logging.

Switching to the centralized logger import improves consistency.

Also applies to: 245-245

source/app/datamgmt/case/case_rfiles_db.py (1)

23-23: LGTM: Consistent deletion pattern.

The refactor correctly applies the centralized db_delete utility.

Also applies to: 182-182

source/app/datamgmt/manage/manage_groups_db.py (1)

19-19: LGTM: Centralized deletion applied.

The refactor correctly uses the db_delete utility for group deletion.

Also applies to: 209-209

source/app/iris_engine/module_handler/module_handler.py (2)

413-417: LGTM: Helpful documentation.

The extended docstring clarifies the purpose of the caseid parameter in task retrieval.


35-35: Verify transaction commit in deregister_module_from_hook.

The refactor delegates hook deregistration to deregister_module_from_hook, but that function (in source/app/datamgmt/iris_engine/modules_db.py lines 262-269) doesn't commit the deletions. Ensure the missing commit is added.

Also applies to: 402-403

source/app/business/iocs.py (1)

59-59: LGTM: Hook invocation standardized.

The refactor from keyword (data=...) to positional arguments for call_modules_hook aligns with the PR-wide standardization effort.

Also applies to: 84-84, 97-97, 101-101

source/app/datamgmt/case/case_tasks_db.py (2)

25-25: LGTM: Centralized deletion pattern.

The refactor correctly applies the db_delete utility for comment deletion.

Also applies to: 316-316


347-373: LGTM: Well-structured query.

The function correctly joins multiple tables and filters out completed/canceled tasks.

source/app/business/assets.py (1)

50-50: LGTM! Hook invocation standardization applied correctly.

The refactoring consistently updates all three hook calls to use positional arguments instead of keyword data= arguments, aligning with the broader codebase standardization effort.

Also applies to: 62-62, 65-65

source/app/business/tasks.py (2)

26-26: LGTM! Import reorganization improves modularity.

Moving list_user_tasks from dashboard_db to case_tasks_db properly consolidates task-related data access functions into their domain-specific module.


41-41: LGTM! Hook invocations standardized correctly.

All four hook calls consistently updated to use positional arguments for the data payload, maintaining the caseid keyword argument where applicable.

Also applies to: 45-45, 57-57, 89-89

source/app/business/evidences.py (1)

38-38: LGTM! Hook invocation refactoring applied consistently.

All four evidence lifecycle hooks updated to use positional arguments for the payload, with caseid properly passed as a keyword argument where needed.

Also applies to: 55-55, 70-70, 72-72

source/app/datamgmt/case/case_notes_db.py (1)

25-25: LGTM! Deletion operations properly centralized.

Refactoring to use the db_delete helper function standardizes database deletion operations across the codebase, improving maintainability and consistency.

Also applies to: 70-70, 431-431

source/app/business/notes.py (1)

65-65: LGTM! Hook calls standardized correctly.

All four note lifecycle hooks updated to pass payloads as positional arguments, maintaining consistency with the broader refactoring effort.

Also applies to: 92-92, 107-107, 109-109

source/app/datamgmt/case/case_db.py (1)

252-266: LGTM! User reviews listing function implemented correctly.

The function properly filters cases by reviewer and excludes terminal review statuses ('Reviewed', 'Not reviewed'), which aligns with the typical workflow for pending reviews.

source/app/datamgmt/manage/manage_case_templates_db.py (1)

192-192: LGTM! Case template hook invocations standardized.

All four hook calls (two for tasks, two for notes) properly updated to use positional arguments for payloads during case template population, maintaining consistency with the codebase refactoring.

Also applies to: 203-203, 224-226, 238-238

.vulture.ignore (1)

1-5: LGTM! External API properly documented.

Correctly documents methods used by the iris-module-interface package, preventing false-positive unused code warnings while maintaining clear documentation of the external dependency.

tests/tests_rest_module_tasks.py (1)

31-41: LGTM! Test structure is clear and validates case identifier presence.

The test correctly verifies that the case identifier appears in the task list response, which aligns with the PR objective to prevent removal of this field.

source/app/blueprints/rest/alerts_routes.py (1)

372-378: LGTM! Hook invocations correctly refactored to use positional arguments.

The changes consistently update call_modules_hook invocations from keyword argument data= to positional argument, aligning with the function signature and the broader codebase refactoring.

source/app/blueprints/rest/case/case_timeline_routes.py (1)

103-103: LGTM! Consistent hook invocation refactoring.

All hook calls in this file now use positional arguments for the data parameter, consistent with the standardized calling convention.

source/app/business/comments.py (2)

118-118: LGTM! Hook invocations standardized to positional arguments.

All call_modules_hook invocations now use positional arguments for the data parameter, consistent with the codebase-wide refactoring.


306-306: LGTM! Updated to match new delete_asset_comment signature.

The call now correctly passes the comment object instead of comment_id, aligning with the updated delete_asset_comment signature that expects a Comments object (as seen in case_assets_db.py).

pyproject.toml (1)

42-54: LGTM! Import linter contracts refined to match architectural refactoring.

The updated contracts appropriately reflect the reorganized module boundaries:

  • Contract 1 broadens coverage to all of app.datamgmt while narrowing the forbidden scope to access_controls
  • Contract 2 specifically targets case_assets_db to prevent iris_user imports

These changes ensure the persistence layer maintains proper separation from the API layer.

source/app/blueprints/rest/dim_tasks_routes.py (1)

142-142: LGTM! Hook invocation standardized to positional arguments.

The change correctly uses positional arguments for hook_name and obj_targets, consistent with the standardized calling convention across the codebase.

source/app/datamgmt/case/case_events_db.py (2)

21-21: LGTM! Imported db_delete for centralized deletion.

Adding db_delete to the imports enables the centralized deletion pattern used at line 174.


174-174: LGTM! Deletion centralized through db_delete utility.

Replacing direct db.session.delete() and db.session.commit() calls with db_delete(comment) aligns with the codebase-wide refactoring to centralize deletion operations.

source/app/datamgmt/case/case_iocs_db.py (2)

21-21: LGTM! Imported db_delete for centralized deletion.

Adding db_delete to the imports enables the centralized deletion pattern used at line 278.


278-278: LGTM! Deletion centralized through db_delete utility.

Replacing direct db.session.delete() and db.session.commit() calls with db_delete(comment) aligns with the codebase-wide refactoring to centralize deletion operations.

source/app/datamgmt/comments.py (4)

18-18: LGTM!

The Optional type hint is appropriate for the new get_comment function that returns None when no comment is found.


23-23: LGTM!

Importing the centralized db_delete helper promotes consistency across the codebase.


110-110: LGTM!

Using the centralized db_delete utility is a good refactor that ensures consistent deletion behavior across the codebase.


141-145: LGTM!

The get_comment function properly scopes comment retrieval to the requesting user, which is important for authorization. The filtering by both comment_id and comment_user_id ensures users can only retrieve their own comments.

source/app/blueprints/rest/case/case_evidences_routes.py (1)

192-192: LGTM!

The refactor to pass hook data as a positional argument instead of using the data= keyword is consistent with the call_modules_hook signature and aligns with similar changes across the codebase.

Also applies to: 228-228

source/app/business/global_tasks.py (3)

25-25: LGTM!

Importing the necessary functions from the new global_tasks module enables the delete functionality.


36-36: LGTM!

Refactoring to pass the global_task as a positional argument is consistent with the standardized calling convention for call_modules_hook.


49-55: LGTM!

The global_tasks_delete function follows the established pattern:

  1. Pre-delete hook for extension points
  2. Actual deletion via the persistence layer
  3. Post-delete hook for notifications
  4. Activity tracking

This is consistent with similar delete operations elsewhere in the codebase.

source/app/blueprints/rest/case/case_tasks_routes.py (1)

233-233: LGTM!

Refactoring hook invocations to use positional arguments aligns with the standardized calling convention across the codebase.

Also applies to: 273-273

tests/tests_rest_global_tasks.py (1)

51-64: LGTM!

The new tests provide good coverage for the delete functionality:

  1. test_delete_global_task_should_return_204 verifies successful deletion returns the correct HTTP status
  2. test_get_global_task_should_return_404_when_deleted ensures deleted tasks are no longer retrievable

These tests follow the existing patterns and validate the expected REST behavior.

source/app/blueprints/rest/case/case_ioc_routes.py (1)

182-182: LGTM!

All call_modules_hook invocations have been consistently updated to use positional arguments, aligning with the standardized calling convention across the codebase.

Also applies to: 194-194, 307-307, 345-345

source/app/blueprints/rest/case/case_notes_routes.py (1)

141-141: LGTM!

All hook invocations have been refactored to pass data as positional arguments, maintaining consistency with the standardized calling convention.

Also applies to: 210-210, 429-429, 465-465

source/app/datamgmt/alerts/alerts_db.py (2)

75-75: LGTM!

Adding db_delete to the imports enables consistent use of the centralized deletion utility.


1285-1285: LGTM!

Both deletion call sites now use the centralized db_delete utility, promoting consistency across the codebase.

Also applies to: 1424-1424

source/app/datamgmt/datastore/datastore_db.py (1)

252-253: db_delete in tight loops changes transactional semantics and may hurt performance.

Using db_delete here centralizes deletion, but because db_delete commits on each call, the behavior differs from the typical pattern of deleting many rows and committing once:

  • In datastore_iter_deletion and datastore_delete_files_of_path, you now potentially commit once per path/file.
  • In datastore_delete_file, you commit immediately for the single file (which is fine, but different from batching with other work).

Implications:

  • More round‑trips and contention if many files/paths are deleted in one request.
  • Loss of “all‑or‑nothing” behavior when multiple deletions are logically part of one operation (you can end up with some files deleted and others not if a later delete fails).

If the intent is to keep atomic multi‑step deletions, consider:

  • Introducing a non‑committing helper (e.g., db_delete_nocommit) and doing a single db.session.commit() at the end of the outer operation, or
  • Passing a flag to db_delete to control committing, and using commit=False in loops.

Example refactor sketch for the loop (conceptual):

-        db_delete(dsf_list_item)
+        db.session.delete(dsf_list_item)
...
-    return
+    db.session.commit()
+    return

[uggest_recommended_refactor]

Also applies to: 264-271, 328-343

source/app/blueprints/rest/case/case_assets_routes.py (1)

47-48: Hook invocation refactor to positional data is consistent.

Across asset creation, bulk upload, update, commenting, and comment delete hooks you’ve switched from data=... to passing the payload as the second positional argument:

  • on_preload_asset_create (single and CSV upload)
  • on_postload_asset_create
  • on_preload_asset_update
  • on_postload_asset_commented
  • on_postload_asset_comment_delete

This matches call_modules_hook(hook_name, data, caseid=...) and keeps the call style consistent with the rest of the codebase. No functional issues spotted here.

Also applies to: 170-171, 247-248, 257-258, 309-310, 379-380, 417-418

source/app/blueprints/rest/v2/global_tasks.py (5)

31-35: Imports for delete behavior are appropriate and used

The added imports response_api_deleted and global_tasks_delete are correctly referenced in the new delete flow and keep REST and business responsibilities separated.


55-57: read() naming cleanup is correct and behavior-preserving

Using global_task for both retrieval and serialization improves clarity and remains consistent with global_tasks_get and the schema dump. No functional issues.


61-68: DELETE operation wiring and error handling look sound

The new delete() method correctly reuses global_tasks_get for existence checking, delegates deletion to global_tasks_delete, returns response_api_deleted() on success, and maps ObjectNotFoundError to response_api_not_found(). This matches the established pattern and HTTP semantics.


76-85: Route renames to global_task terminology are consistent

The create_global_task and get_global_task endpoints are cleanly mapped to the corresponding operations and keep ac_api_requires in place; naming now aligns with the resource (global_tasks).


88-91: New DELETE /global-tasks/int:identifier endpoint is correctly hooked up

The DELETE route is registered on the same blueprint, uses an int identifier consistent with global_tasks_get, and delegates to GlobalTasksOperations.delete under the same access control decorator. This completes the REST surface for global task deletion.

source/app/business/cases.py (4)

31-35: Refactored imports to case_db and models are coherent

Importing case_db_exists, list_user_cases, case_db_save, list_user_reviews, Cases, and ReviewStatusList from their domain-specific modules matches how they’re used later (existence checks, listing, saving, and review status handling). This improves cohesion of persistence-related logic.

Also applies to: 58-59


136-136: on_postload_case_create hook call simplified without behavior change

call_modules_hook('on_postload_case_create', case) drops an unnecessary extra positional argument while still passing the created case as the hook data, consistent with the hook function signature. No functional impact.


152-157: Case delete hooks now use positional data and explicit caseid

Passing case_identifier as the positional data argument and caseid=case_identifier for both on_preload_case_delete and on_postload_case_delete keeps hook data explicit and aligned with other callers, while preserving prior behavior (data and caseid were already the same value).


186-191: Alert post-update hooks now use positional data consistently

Both on_postload_alert_update and on_postload_alert_resolution_update now pass the alert object positionally with caseid=case.case_id, matching the call_modules_hook contract and broader positional-argument refactor without altering semantics.

else:
raise BusinessProcessingError('Report error', data='Unknown report format.')
call_modules_hook('on_postload_activities_report_create', data=report_id, caseid=caseid)
call_modules_hook('on_postload_activities_report_create', report_id, caseid=caseid)
Copy link

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

Verify inconsistent data passed to postload hook.

Unlike the investigation report's postload hook (line 57), which passes a data dictionary containing the encoded file, file path, case details, and user name, this activities report postload hook only passes report_id. This inconsistency may cause issues if hooks expect similar data structures or result in missing functionality.

Please verify whether this difference is intentional or if the activities report should also:

  1. Encode the generated file (similar to lines 47-48)
  2. Create a data dictionary with report details (similar to lines 50-56)
  3. Pass that dictionary to the postload hook

Run the following script to check how these hooks are implemented in active modules:


🏁 Script executed:

#!/bin/bash
# Search for implementations of both postload hooks to understand expected data structures

echo "=== Searching for on_postload_report_create implementations ==="
rg -n -A 10 "on_postload_report_create" --type py

echo -e "\n=== Searching for on_postload_activities_report_create implementations ==="
rg -n -A 10 "on_postload_activities_report_create" --type py

Length of output: 3431


🏁 Script executed:

#!/bin/bash
# Get the full context of both functions to understand data structures

echo "=== Context of generate_report function (lines 40-60) ==="
sed -n '40,60p' source/app/business/reports/reports.py

echo -e "\n=== Context of generate_activities_report function (lines 62-82) ==="
sed -n '62,82p' source/app/business/reports/reports.py

echo -e "\n=== Search for actual hook implementations in modules ==="
find source -name "*.py" -type f -exec grep -l "def on_postload_report_create\|def on_postload_activities_report_create" {} \;

Length of output: 1949


🏁 Script executed:

#!/bin/bash
# Search more broadly for hook implementations that might not have exact function defs

echo "=== Search for any references to on_postload_activities_report_create ==="
rg -n "on_postload_activities_report_create" --type py -B2 -A2

echo -e "\n=== Check if there are handler patterns in modules dir ==="
find source -type d -name "modules" -o -name "plugins"

echo -e "\n=== Search for call_modules_hook to understand how hooks are invoked ==="
rg -n "def call_modules_hook" --type py -A 5

Length of output: 1863


Pass consistent data structure to on_postload_activities_report_create hook.

The activities report postload hook (line 80) should mirror the investigation report's hook (line 57) by passing a data dictionary with report details and encoded file content, rather than just report_id.

Update line 80 to:

  1. Encode the generated file (similar to lines 47-48)
  2. Create a data dictionary with report_id, file_path, case_id, user_name, and file
  3. Pass that dictionary instead of just report_id

Both hooks share the same description ("before download of the document"), confirming they should follow the same pattern for consistency and to enable hooks to access the same contextual information.

🤖 Prompt for AI Agents
In source/app/business/reports/reports.py around line 80, the call to
call_modules_hook('on_postload_activities_report_create', report_id,
caseid=caseid) must be changed to pass the same data dictionary used for the
investigation report hook: after generating the file, base64-encode its bytes
(as done on lines ~47-48), build a dict with keys report_id, file_path, case_id,
user_name, and file (the encoded content), and call
call_modules_hook('on_postload_activities_report_create', data_dict) instead of
passing just report_id so hooks receive the same contextual info.

Comment on lines 355 to 359
def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
return AssetComments.query.filter(
return Comments.query.join(AssetComments.comment).filter(
AssetComments.comment_asset_id == asset_id,
AssetComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
Comments.comment_user_id,
Comments.comment_case_id,
User.name,
User.user
).join(
AssetComments.comment
).join(
Comments.user
Comments.comment_id == comment_id
).first()
Copy link

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

Comment lookup & deletion need tighter asset binding (potential runtime errors).

Two issues here:

  1. Join target in get_case_asset_comment:

    Comments.query.join(AssetComments.comment) relies on a relationship defined on AssetComments, while the query’s base entity is Comments. This is a non‑standard pattern in SQLAlchemy and may fail or be brittle depending on the SQLAlchemy version and configuration.

    To make this robust, it’s safer to join explicitly on the association table, mirroring get_case_asset_comments:

    -def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
    -    return Comments.query.join(AssetComments.comment).filter(
    -        AssetComments.comment_asset_id == asset_id,
    -        Comments.comment_id == comment_id
    -    ).first()
    +def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
    +    return Comments.query.join(
    +        AssetComments,
    +        Comments.comment_id == AssetComments.comment_id,
    +    ).filter(
    +        AssetComments.comment_asset_id == asset_id,
    +        Comments.comment_id == comment_id,
    +    ).first()
  2. delete_asset_comment assumes the comment belongs to the given asset:

    The function deletes links for (asset_id, comment.comment_id) and then unconditionally calls db_delete(comment). If the caller passes a Comments instance that is not linked to this asset (e.g. wrong asset_id), the AssetComments.delete() will delete 0 rows, but db_delete(comment) will still attempt to remove the comments row. With a FK from asset_comments.comment_id to comments.comment_id, this can cause an integrity error or leave orphans, depending on DB constraints.

    This is made more likely by the current caller (case_comment_asset_delete) fetching the comment via get_comment(iris_current_user, com_id), which does not check the asset or case.

    I recommend either:

    • Enforcing the asset binding at the caller (preferred; see comment on case_comment_asset_delete in case_assets_routes.py), so this function is only ever called with a comment linked to asset_id, or
    • Making the function defensive, e.g.:
    def delete_asset_comment(asset_id, comment: Comments):
    -    AssetComments.query.filter(
    -        AssetComments.comment_asset_id == asset_id,
    -        AssetComments.comment_id == comment.comment_id
    -    ).delete()
    -
    -    db_delete(comment)
    +    deleted_links = AssetComments.query.filter(
    +        AssetComments.comment_asset_id == asset_id,
    +        AssetComments.comment_id == comment.comment_id,
    +    ).delete()
    +
    +    if not deleted_links:
    +        # Nothing was linked to this asset; avoid deleting the underlying comment
    +        db.session.commit()
    +        return
    +
    +    db_delete(comment)

    This keeps the referential integrity safe even if a caller passes a mismatched asset/comment pair.

Also applies to: 362-369


🏁 Script executed:

#!/bin/bash
# Examine the case_assets_db.py file around the functions in question
fd case_assets_db.py --type f | head -5

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look for model definitions and relationships
fd -e py --type f | xargs rg -l "class AssetComments|class Comments" | head -5

Length of output: 254


🏁 Script executed:

#!/bin/bash
# Find case_assets_routes.py to see the caller
fd case_assets_routes.py --type f | head -3

Length of output: 108


🏁 Script executed:

#!/bin/bash
# Get context around the functions mentioned
rg -n "def get_case_asset_comment|def delete_asset_comment|def get_case_asset_comments" source/app/datamgmt/case/ -A 10

Length of output: 2236


🏁 Script executed:

#!/bin/bash
# Look at the full file structure to understand imports and relationships
cat source/app/datamgmt/case/case_assets_db.py | head -100

Length of output: 3358


🏁 Script executed:

#!/bin/bash
# Find model definitions
rg -n "class Comments|class AssetComments" --type py -A 8 | head -60

Length of output: 4762


🏁 Script executed:

#!/bin/bash
# Check case_assets_routes.py for the caller mentioned
rg -n "case_comment_asset_delete|get_comment" source/app/datamgmt/case/case_assets_routes.py -B 3 -A 10

Length of output: 86


🏁 Script executed:

#!/bin/bash
# Look for the join patterns in both functions
rg -n "\.join\(" source/app/datamgmt/case/case_assets_db.py -B 2 -A 5

Length of output: 1638


🏁 Script executed:

#!/bin/bash
# Get full definition of delete_asset_comment
rg -n "def delete_asset_comment" source/app/datamgmt/case/case_assets_db.py -A 20

Length of output: 487


🏁 Script executed:

#!/bin/bash
# Get full definition of get_case_asset_comment
rg -n "def get_case_asset_comment" source/app/datamgmt/case/case_assets_db.py -A 10

Length of output: 840


🏁 Script executed:

#!/bin/bash
# Check for FK constraints in models
rg -n "ForeignKey|relationship" --type py source/app/datamgmt/ -C 2 | grep -A 5 -B 5 "comment"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look at get_case_asset_comments to compare join patterns
rg -n "def get_case_asset_comments" source/app/datamgmt/case/case_assets_db.py -A 15

Length of output: 515


🏁 Script executed:

#!/bin/bash
# Check SQLAlchemy version or imports
rg -n "from sqlalchemy|import sqlalchemy" source/app/datamgmt/case/ | head -10

Length of output: 733


🏁 Script executed:

#!/bin/bash
# Look for db_delete function definition
rg -n "def db_delete" source/ -A 5

Length of output: 198


🏁 Script executed:

#!/bin/bash
# Check the actual AssetComments model structure
fd models.py --type f -path "*/datamgmt/case/*" | xargs cat | head -200

Length of output: 168


🏁 Script executed:

#!/bin/bash
# Get all relationship definitions in case module
rg -n "class.*Comments|class.*Asset" source/app/datamgmt/case/ --type py -A 20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for the models module
fd -e py -path "*/datamgmt/*" -name models | xargs ls -la

Length of output: 2318


🏁 Script executed:

#!/bin/bash
# Search more broadly for model definitions
rg "^class (AssetComments|Comments)" --type py -A 10

Length of output: 8588


🏁 Script executed:

#!/bin/bash
# Check imports in case_assets_db.py to find where models come from
head -50 source/app/datamgmt/case/case_assets_db.py

Length of output: 1914


🏁 Script executed:

#!/bin/bash
# Understand the models better - find their definitions
fd models --type f | xargs cat | grep -A 15 "class Comments\|class AssetComments"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Try a different approach - list all Python files in datamgmt
find source/app/datamgmt -name "*.py" -type f | head -20

Length of output: 813


🏁 Script executed:

#!/bin/bash
# Look at the structure
ls -la source/app/datamgmt/

Length of output: 1411


🏁 Script executed:

#!/bin/bash
# Look for model files
ls -la source/app/datamgmt/case/

Length of output: 784


🏁 Script executed:

#!/bin/bash
# Check what's available
pwd && find . -name "case_assets_db.py" 2>/dev/null | head -3

Length of output: 63


🏁 Script executed:

#!/bin/bash
# Direct inspection
cat source/app/datamgmt/case/case_assets_db.py

Length of output: 10651


🏁 Script executed:

#!/bin/bash
# If the above fails, check the directory structure
ls -R source/app/datamgmt/ | head -50

Length of output: 681


🏁 Script executed:

#!/bin/bash
# Try to find Python files related to comments
find . -name "*.py" -type f | xargs grep -l "class Comments" 2>/dev/null | head -5

Length of output: 290


🏁 Script executed:

#!/bin/bash
# Look for the database models in common locations
find . -type f -name "*.py" -path "*/models*" 2>/dev/null | head -10

Length of output: 325


🏁 Script executed:

#!/bin/bash
# Check core or shared locations
ls -la source/app/database/ 2>/dev/null || ls -la source/app/db/ 2>/dev/null || ls -la source/app/ | grep -i model

Length of output: 58


🏁 Script executed:

#!/bin/bash
# Basic file structure check
ls -la source/app/ | head -20

Length of output: 1186


🏁 Script executed:

#!/bin/bash
# Simplified: just try to read the file in question
cat source/app/datamgmt/case/case_assets_db.py 2>/dev/null | wc -l && echo "---" && cat source/app/datamgmt/case/case_assets_db.py 2>/dev/null | sed -n '350,375p'

Length of output: 749


🏁 Script executed:

#!/bin/bash
# Get the functions and surrounding context
python3 << 'PYTHON_EOF'
import os
try:
    with open('source/app/datamgmt/case/case_assets_db.py', 'r') as f:
        lines = f.readlines()
        # Show lines 350-380 to get the functions in context
        for i, line in enumerate(lines[349:380], start=350):
            print(f"{i}: {line.rstrip()}")
except FileNotFoundError as e:
    print(f"File not found: {e}")
    # Try to list what exists
    for root, dirs, files in os.walk('source/app'):
        for f in files:
            if 'case' in f.lower() and f.endswith('.py'):
                print(os.path.join(root, f))
PYTHON_EOF

Length of output: 893


🏁 Script executed:

#!/bin/bash
# Find callers of delete_asset_comment and get_case_asset_comment
rg -n "delete_asset_comment|get_case_asset_comment" source/app/blueprints/ --type py -B 2 -A 5

Length of output: 3961


🏁 Script executed:

#!/bin/bash
# Check both case_assets_routes files
cat source/app/blueprints/pages/case/case_assets_routes.py | head -200

Length of output: 5375


🏁 Script executed:

#!/bin/bash
# Check REST routes
cat source/app/blueprints/rest/case/case_assets_routes.py | head -200

Length of output: 8073


🏁 Script executed:

#!/bin/bash
# Search for get_comment function used in caller context
rg -n "def get_comment|\.get_comment\(" source/app/ --type py -B 2 -A 3

Length of output: 395


🏁 Script executed:

#!/bin/bash
# Look at the full models/comments.py file to see relationships and FK definitions
cat source/app/models/comments.py

Length of output: 3650


🏁 Script executed:

#!/bin/bash
# Check if there's SQLAlchemy documentation or version info
cat source/app/db.py

Length of output: 1166


🏁 Script executed:

#!/bin/bash
# Search for ondelete cascade or FK constraints in models
rg -n "ondelete|cascade|ForeignKey" source/app/models/comments.py -A 2 -B 2

Length of output: 1832


🏁 Script executed:

#!/bin/bash
# Look for any tests that might call these functions
find . -name "*test*" -type f -path "*/case*" -o -path "*/asset*" | grep -i test | head -10

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Search the datamgmt comments module
cat source/app/datamgmt/case/case_comments.py

Length of output: 1239


🏁 Script executed:

#!/bin/bash
# Look for any usage patterns of the problematic functions
rg -rn "get_case_asset_comment\|delete_asset_comment" source/app/ --type py

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Test SQLAlchemy join syntax validity - check imports and version
grep -r "sqlalchemy" source/app/db.py source/app/models/comments.py | head -20

Length of output: 645


🏁 Script executed:

#!/bin/bash
# Extract full comments.py file
wc -l source/app/models/comments.py && head -150 source/app/models/comments.py

Length of output: 3684


🏁 Script executed:

#!/bin/bash
# Get the relationships and FK info from AssetComments specifically
sed -n '84,93p' source/app/models/comments.py

Length of output: 319


🏁 Script executed:

#!/bin/bash
# Check pyproject.toml for SQLAlchemy version
grep -i sqlalchemy pyproject.toml

Length of output: 176


🏁 Script executed:

#!/bin/bash
# Search for where delete_asset_comment is actually called
rg "delete_asset_comment" source/ --type py -B 5 -A 10

Length of output: 6994


🏁 Script executed:

#!/bin/bash
# Search for where get_case_asset_comment is called
rg "get_case_asset_comment" source/ --type py -B 5 -A 10

Length of output: 10101


🏁 Script executed:

#!/bin/bash
# Look for comment delete routes more broadly
rg "delete.*comment|comment.*delete" source/app/blueprints/ --type py -l

Length of output: 758


🏁 Script executed:

#!/bin/bash
# Examine the assets routes more carefully
rg -n "comment" source/app/blueprints/pages/case/case_assets_routes.py

Length of output: 416


🏁 Script executed:

#!/bin/bash
# Check REST routes for assets
rg -n "comment" source/app/blueprints/rest/case/case_assets_routes.py

Length of output: 2992


🏁 Script executed:

#!/bin/bash
# Look for all asset route files
fd case_assets -type f --path "*/blueprints/*"

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Test if the join syntax is valid by checking SQLAlchemy behavior
python3 << 'PYEOF'
# Check what the join statement does
test_code = """Comments.query.join(AssetComments.comment).filter(...)"""
print("Analyzing join pattern:")
print(test_code)
print("\nThis attempts to join on a relationship property.")
print("SQLAlchemy behavior: relationship properties can be used as join targets")
print("However, when base entity is Comments and you join on AssetComments.comment (rel to Comments),")
print("this creates: Comments JOIN AssetComments ON <relationship>, which can be ambiguous")
PYEOF

Length of output: 381


🏁 Script executed:

#!/bin/bash
# Final check: look for any configuration or dialect-specific code
rg "dialect|engine|bind" source/app/db.py | head -20

Length of output: 77


Fix join pattern and add validation before deleting comments.

Two issues confirmed:

  1. Join pattern (lines 355-359): Inconsistent with get_case_asset_comments. Use explicit join on the association table instead of relationship navigation:

    def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
    -    return Comments.query.join(AssetComments.comment).filter(
    +    return Comments.query.join(
    +        AssetComments,
    +        Comments.comment_id == AssetComments.comment_id,
    +    ).filter(
            AssetComments.comment_asset_id == asset_id,
            Comments.comment_id == comment_id
        ).first()
  2. Deletion without validation (lines 362-368): Route caller fetches comment via get_comment(user, com_id), which only validates user ownership, not asset binding. If a mismatched asset/comment pair reaches delete_asset_comment, the AssetComments delete finds no rows but db_delete(comment) still executes, potentially orphaning the comment. Add defensive validation:

    def delete_asset_comment(asset_id, comment: Comments):
    -    AssetComments.query.filter(
    +    deleted_links = AssetComments.query.filter(
            AssetComments.comment_asset_id == asset_id,
            AssetComments.comment_id == comment.comment_id
    -    ).delete()
    +    ).delete()
    +
    +    if not deleted_links:
    +        db.session.commit()
    +        return
    
        db_delete(comment)
📝 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
def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
return AssetComments.query.filter(
return Comments.query.join(AssetComments.comment).filter(
AssetComments.comment_asset_id == asset_id,
AssetComments.comment_id == comment_id
).with_entities(
Comments.comment_id,
Comments.comment_text,
Comments.comment_date,
Comments.comment_update_date,
Comments.comment_uuid,
Comments.comment_user_id,
Comments.comment_case_id,
User.name,
User.user
).join(
AssetComments.comment
).join(
Comments.user
Comments.comment_id == comment_id
).first()
def get_case_asset_comment(asset_id, comment_id) -> Optional[Comments]:
return Comments.query.join(
AssetComments,
Comments.comment_id == AssetComments.comment_id,
).filter(
AssetComments.comment_asset_id == asset_id,
Comments.comment_id == comment_id
).first()
🤖 Prompt for AI Agents
In source/app/datamgmt/case/case_assets_db.py around lines 355-368, the
get_case_asset_comment uses relationship navigation in the join and should
instead join explicitly on the association table (AssetComments) like
get_case_asset_comments; change the join to join(AssetComments,
AssetComments.comment_id == Comments.comment_id) and filter on
AssetComments.comment_asset_id and Comments.comment_id. Also add defensive
validation in the delete path (lines ~362-368): after loading the Comment (via
get_comment) verify an associated AssetComments row exists for that asset_id and
comment_id; if no association exists, abort/raise and do not call
db_delete(comment) to avoid orphaning the comment; only delete both association
and comment when the validation passes.

Comment on lines +376 to +392
def update_utask_status(task_id, status, case_id):
if task_id != 0:
task = CaseTasks.query.filter(
CaseTasks.id == task_id,
CaseTasks.task_case_id == case_id
).first()
if task:
try:
task.task_status_id = status

db.session.commit()
return True

except:
pass

return False
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace bare except with specific exception handling.

The bare except: clause (lines 389-390) silently swallows all exceptions, including KeyboardInterrupt and SystemExit, making debugging difficult and potentially masking critical errors.

Apply this diff:

 def update_utask_status(task_id, status, case_id):
     if task_id != 0:
         task = CaseTasks.query.filter(
                 CaseTasks.id == task_id,
                 CaseTasks.task_case_id == case_id
         ).first()
         if task:
             try:
                 task.task_status_id = status
 
                 db.session.commit()
                 return True
 
-            except:
-                pass
+            except Exception as e:
+                logger.exception(f"Failed to update task {task_id} status: {e}")
+                db.session.rollback()
 
     return False

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

🤖 Prompt for AI Agents
In source/app/datamgmt/case/case_tasks_db.py around lines 376 to 392, replace
the bare "except:" that swallows all errors with specific exception handling:
catch broad runtime/database exceptions (e.g., Exception or SQLAlchemyError) as
a variable, call db.session.rollback() on error, log or re-raise the caught
exception (use the logger or raise after logging) and then return False; ensure
KeyboardInterrupt/SystemExit are not swallowed by not using a bare except.

Comment on lines +262 to +269
def deregister_module_from_hook(module_id: int, iris_hook_name: str):
hooks = IrisModuleHook.query.filter(
IrisModuleHook.module_id == module_id,
IrisHook.hook_name == iris_hook_name,
IrisModuleHook.hook_id == IrisHook.id
).all()
for hook in hooks:
db.session.delete(hook)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing commit after deletions.

The function deletes IrisModuleHook records in a loop but never commits the transaction. Deletions won't persist to the database.

Apply this diff:

 def deregister_module_from_hook(module_id: int, iris_hook_name: str):
     hooks = IrisModuleHook.query.filter(
         IrisModuleHook.module_id == module_id,
         IrisHook.hook_name == iris_hook_name,
         IrisModuleHook.hook_id == IrisHook.id
     ).all()
     for hook in hooks:
         db.session.delete(hook)
+    db.session.commit()
📝 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
def deregister_module_from_hook(module_id: int, iris_hook_name: str):
hooks = IrisModuleHook.query.filter(
IrisModuleHook.module_id == module_id,
IrisHook.hook_name == iris_hook_name,
IrisModuleHook.hook_id == IrisHook.id
).all()
for hook in hooks:
db.session.delete(hook)
def deregister_module_from_hook(module_id: int, iris_hook_name: str):
hooks = IrisModuleHook.query.filter(
IrisModuleHook.module_id == module_id,
IrisHook.hook_name == iris_hook_name,
IrisModuleHook.hook_id == IrisHook.id
).all()
for hook in hooks:
db.session.delete(hook)
db.session.commit()
🤖 Prompt for AI Agents
In source/app/datamgmt/iris_engine/modules_db.py around lines 262 to 269, the
function deregister_module_from_hook deletes IrisModuleHook rows in a loop but
never commits the transaction; add a db.session.commit() after the deletion loop
(or replace the loop with a single query delete and then commit) so the
deletions are persisted to the database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API v2 delete global task

4 participants