Skip to content

Auto delete old esign sessions#28

Open
chris-adam wants to merge 1 commit intomainfrom
PARAF-379/session_auto_cleanup
Open

Auto delete old esign sessions#28
chris-adam wants to merge 1 commit intomainfrom
PARAF-379/session_auto_cleanup

Conversation

@chris-adam
Copy link
Copy Markdown
Contributor

@chris-adam chris-adam commented Mar 25, 2026

Summary by CodeRabbit

  • New Features

    • Automatic cleanup of expired e-sign sessions (90-day expiry) enabled by default.
    • Configuration toggle to enable/disable automatic session cleanup.
    • Cleanup is throttled to run at most once per 24-hour period.
  • Documentation

    • Changelog updated to note the automatic session cleanup.
  • Tests

    • Added tests covering cleanup behavior, throttling, and the enable/disable flag.

Comment thread src/imio/esign/browser/settings.py Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a registry boolean to enable/disable throttled auto-cleanup of e-sign sessions older than 90 days; SessionsListingView triggers the cleanup after authorization.

Changes

Cohort / File(s) Summary
Settings
src/imio/esign/browser/settings.py
Added auto_cleanup_enabled Bool registry field (default True) with UI label/description.
Config helpers
src/imio/esign/config.py
Added get_esign_registry_auto_cleanup_enabled(default=True) and set_esign_registry_auto_cleanup_enabled(value) to read/write the registry flag.
Cleanup implementation
src/imio/esign/utils.py
Added SESSION_EXPIRY_DAYS = 90, CLEANUP_THROTTLE_HOURS = 24, and cleanup_expired_sessions() which checks registry flag, enforces a 24h throttle via annot["last_cleanup"], logs and removes expired non-draft sessions, and updates last_cleanup.
Integration
src/imio/esign/browser/views.py
SessionsListingView.__call__ now invokes cleanup_expired_sessions() after available() and wraps it in a broad try/except that logs exceptions.
Tests
src/imio/esign/tests/test_utils.py
Added test_cleanup_expired_sessions and updated imports to exercise cleanup behavior, throttle, draft exemption, and registry enable/disable handling.
Changelog
CHANGES.rst
Documented automatic deletion of e-sign sessions older than 90 days in unreleased entry and updated author attribution.

Sequence Diagram

sequenceDiagram
    actor Client
    participant View as SessionsListingView
    participant Utils as cleanup_expired_sessions()
    participant Config as get_esign_registry_auto_cleanup_enabled()
    participant Registry as Plone Registry
    participant Store as Session Store

    Client->>View: __call__()
    View->>View: available() (auth)
    View->>Utils: cleanup_expired_sessions()
    Utils->>Config: get_esign_registry_auto_cleanup_enabled()
    Config->>Registry: read `imio.esign.auto_cleanup_enabled`
    Registry-->>Config: value
    Config-->>Utils: enabled/disabled

    alt enabled
        Utils->>Store: read annot["last_cleanup"]
        Store-->>Utils: last_cleanup
        alt within throttle window
            Utils->>Utils: return (throttled)
        else
            Utils->>Store: list sessions with last_update < now - 90d
            Store-->>Utils: expired sessions
            loop per expired session
                Utils->>Utils: log session metadata
                Utils->>Store: remove_session(id)
            end
            Utils->>Store: annot["last_cleanup"] = now
        end
    else disabled
        Utils->>Utils: return (disabled)
    end

    Utils-->>View: done
    View->>View: delegate to parent __call__
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • gbastien
  • sgeulette

Poem

🐰 I hop through old sessions with whiskers keen,

Ninety days passed, I tidy the scene,
Once a day I peek, then gently sweep,
I log every tale before sending to sleep,
Servers breathe lighter — a rabbit's small feat.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Auto delete old esign sessions' clearly and concisely summarizes the main objective: automatic deletion of expired esign sessions older than 90 days. It is specific enough to understand the primary change.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PARAF-379/session_auto_cleanup

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

Comment thread src/imio/esign/browser/views.py Outdated
def __call__(self):
if not self.available():
raise Unauthorized
cleanup_expired_sessions()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ici, je lance le cleanup à chaque fois que quelqu'un visite la vue @@parapheo (et qu'il y a pas eu de cleanup dans les 24 dernières heures). Est-ce que c'est la meilleure manière de faire ? Je pensais aussi dans la fonction get_session_annotation mais ça fait beaucoup de calls.

Comment thread src/imio/esign/utils.py
@coveralls
Copy link
Copy Markdown

coveralls commented Mar 25, 2026

Coverage Report for CI Build 24141156622

Coverage increased (+0.08%) to 76.308%

Details

  • Coverage increased (+0.08%) from the base build.
  • Patch coverage: 14 uncovered changes across 4 files (49 of 63 lines covered, 77.78%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
src/imio/esign/browser/views.py 15 7 46.67%
src/imio/esign/utils.py 37 33 89.19%
src/imio/esign/browser/table.py 4 3 75.0%
src/imio/esign/services/external_session_feedback.py 1 0 0.0%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1300
Covered Lines: 992
Line Coverage: 76.31%
Coverage Strength: 0.76 hits per line

💛 - Coveralls

Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/imio/esign/utils.py`:
- Around line 487-495: The expiration scan currently does session["last_update"]
and will raise on malformed records; in the cleanup code (referencing annot,
annot["sessions"], last_cleanup, SESSION_EXPIRY_DAYS and CLEANUP_THROTTLE_HOURS
called from SessionsListingView.__call__), change the comprehension to
defensively handle bad entries: iterate items(), ensure each session is a dict
(or has .get), fetch last_update via session.get("last_update") and verify it's
a datetime (or comparable) before comparing to cutoff, skipping (and optionally
logging) any records that are missing/invalid so a single bad session cannot
abort the whole cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 478db144-55c2-4d21-bd28-fae58ca22793

📥 Commits

Reviewing files that changed from the base of the PR and between a905a58 and bce929f.

📒 Files selected for processing (4)
  • src/imio/esign/browser/settings.py
  • src/imio/esign/browser/views.py
  • src/imio/esign/config.py
  • src/imio/esign/utils.py

Comment thread src/imio/esign/utils.py
@chris-adam chris-adam force-pushed the PARAF-379/session_auto_cleanup branch 2 times, most recently from 5f777d5 to 0da791b Compare March 25, 2026 10:37
Copy link
Copy Markdown

@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)
src/imio/esign/tests/test_utils.py (1)

844-852: Restore previous registry value instead of hardcoding True in test cleanup.

Current cleanup can overwrite pre-existing suite state. Capture the original value and restore that exact value for better test isolation.

♻️ Suggested test hardening
-from imio.esign.config import set_registry_auto_cleanup_enabled
+from imio.esign.config import get_registry_auto_cleanup_enabled
+from imio.esign.config import set_registry_auto_cleanup_enabled
...
-        set_registry_auto_cleanup_enabled(False)
-        self.addCleanup(set_registry_auto_cleanup_enabled, True)
+        previous_auto_cleanup = get_registry_auto_cleanup_enabled()
+        self.addCleanup(set_registry_auto_cleanup_enabled, previous_auto_cleanup)
+        set_registry_auto_cleanup_enabled(False)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/imio/esign/tests/test_utils.py` around lines 844 - 852, The test
currently hardcodes restoring registry auto-cleanup with
set_registry_auto_cleanup_enabled(True), which can overwrite prior suite state;
before mutating it call set_registry_auto_cleanup_enabled to read and store the
original value (e.g. orig_auto_cleanup = ...), use
set_registry_auto_cleanup_enabled(False) for the test, register
self.addCleanup(set_registry_auto_cleanup_enabled, orig_auto_cleanup) to restore
the exact previous value, and replace any later hardcoded
set_registry_auto_cleanup_enabled(True) calls with
set_registry_auto_cleanup_enabled(orig_auto_cleanup) or rely on the registered
cleanup to restore state; update references around the existing
set_registry_auto_cleanup_enabled, addCleanup, and annot["last_cleanup"] lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/imio/esign/browser/views.py`:
- Around line 61-65: The call to cleanup_expired_sessions() in
SessionsListingView.__call__ can raise and abort the request; wrap that call in
a try/except that catches all exceptions, logs the error (or records it via
existing logger) and continues without re-raising so the view returns normally
(still enforcing the Unauthorized guard and then calling
super(SessionsListingView, self).__call__()). Ensure the exception handling is
minimal and does not change the control flow on success.

---

Nitpick comments:
In `@src/imio/esign/tests/test_utils.py`:
- Around line 844-852: The test currently hardcodes restoring registry
auto-cleanup with set_registry_auto_cleanup_enabled(True), which can overwrite
prior suite state; before mutating it call set_registry_auto_cleanup_enabled to
read and store the original value (e.g. orig_auto_cleanup = ...), use
set_registry_auto_cleanup_enabled(False) for the test, register
self.addCleanup(set_registry_auto_cleanup_enabled, orig_auto_cleanup) to restore
the exact previous value, and replace any later hardcoded
set_registry_auto_cleanup_enabled(True) calls with
set_registry_auto_cleanup_enabled(orig_auto_cleanup) or rely on the registered
cleanup to restore state; update references around the existing
set_registry_auto_cleanup_enabled, addCleanup, and annot["last_cleanup"] lines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2dfc6f98-837d-4c5f-8981-f30e12395d02

📥 Commits

Reviewing files that changed from the base of the PR and between bce929f and 5f777d5.

📒 Files selected for processing (6)
  • CHANGES.rst
  • src/imio/esign/browser/settings.py
  • src/imio/esign/browser/views.py
  • src/imio/esign/config.py
  • src/imio/esign/tests/test_utils.py
  • src/imio/esign/utils.py
✅ Files skipped from review due to trivial changes (2)
  • CHANGES.rst
  • src/imio/esign/browser/settings.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/imio/esign/config.py
  • src/imio/esign/utils.py

Comment thread src/imio/esign/browser/views.py
@chris-adam chris-adam force-pushed the PARAF-379/session_auto_cleanup branch 4 times, most recently from e2a06f1 to 313b75d Compare March 27, 2026 13:40
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
src/imio/esign/utils.py (1)

492-495: ⚠️ Potential issue | 🟠 Major

Harden against malformed session records.

Direct dictionary access to session["last_update"] and session["state"] will raise KeyError if any session record is missing these keys, aborting the entire cleanup. Since this is called from SessionsListingView.__call__, a single corrupt record can break the listing page.

🛠️ Proposed defensive fix
     cutoff = now - timedelta(days=SESSION_EXPIRY_DAYS)
-    expired = [
-        sid for sid, session in annot["sessions"].items()
-        if session["last_update"] < cutoff and session["state"] != "draft"
-    ]
+    expired = []
+    for sid, session in annot["sessions"].items():
+        last_update = session.get("last_update")
+        state = session.get("state")
+        if not isinstance(last_update, datetime):
+            logger.warning(
+                "Auto-cleanup: skipping session %s with invalid last_update=%r",
+                sid, last_update
+            )
+            continue
+        if state == "draft":
+            continue
+        if last_update < cutoff:
+            expired.append(sid)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/imio/esign/utils.py` around lines 492 - 495, The list comprehension that
builds expired uses direct indexing of session["last_update"] and
session["state"], which can raise KeyError for malformed records; update the
logic in the SessionsListingView.__call__ path (the code creating expired from
sid, session in annot["sessions"].items()) to use session.get("last_update") and
session.get("state"), validate the last_update value (e.g., ensure it's not None
and comparable to cutoff) and treat missing/invalid keys as non-expired (skip
them) so a single corrupt session record won't abort the cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/imio/esign/utils.py`:
- Around line 492-495: The list comprehension that builds expired uses direct
indexing of session["last_update"] and session["state"], which can raise
KeyError for malformed records; update the logic in the
SessionsListingView.__call__ path (the code creating expired from sid, session
in annot["sessions"].items()) to use session.get("last_update") and
session.get("state"), validate the last_update value (e.g., ensure it's not None
and comparable to cutoff) and treat missing/invalid keys as non-expired (skip
them) so a single corrupt session record won't abort the cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78ea0695-49c7-4aa5-860a-769d73595bda

📥 Commits

Reviewing files that changed from the base of the PR and between d187f1b and 313b75d.

📒 Files selected for processing (6)
  • CHANGES.rst
  • src/imio/esign/browser/settings.py
  • src/imio/esign/browser/views.py
  • src/imio/esign/config.py
  • src/imio/esign/tests/test_utils.py
  • src/imio/esign/utils.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGES.rst
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/imio/esign/browser/settings.py
  • src/imio/esign/config.py
  • src/imio/esign/browser/views.py
  • src/imio/esign/tests/test_utils.py

Copy link
Copy Markdown
Member

@sgeulette sgeulette left a comment

Choose a reason for hiding this comment

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

tester si tout est fonctionnel après effacement d'une session. Quid viewlet d'info sur un élément associé.
Il pourrait être intéressant de déplacer l'info sur chaque élément de la session pour pouvoir toujours indiquer que cet élément a été signé dans la session xx à la date du yy, et l'afficher dans le viewlet.

Comment thread src/imio/esign/browser/settings.py Outdated
Comment thread src/imio/esign/utils.py
@chris-adam chris-adam force-pushed the PARAF-379/session_auto_cleanup branch from 313b75d to 6413f78 Compare April 8, 2026 14:16
@chris-adam chris-adam changed the title Auto delete esign sessions older than 90 days Auto delete old esign sessions Apr 8, 2026
@chris-adam
Copy link
Copy Markdown
Contributor Author

@sgeulette J'ai mis à jour selon test commentaires. J'ai aussi ajouté un mot dans la description de l'état pour indiquer qu'une session sera supprimée.

Jean m'a informé qu'ils ont changé le délai de suppression à 100 jours au lieu de 90. Il m'a aussi dit que le délai est compté à partir de la création de la session.
Les sessions non finalisées sont aussi supprimées à terme dans Luxtrust.
Ils vont aussi ajouter un code de notification quand ils suppriment une session dans leur DB du microservice.

Peut-être aussi voir avec @gbastien si on veut garder une trace quelque part des sessions supprimées

@chris-adam chris-adam force-pushed the PARAF-379/session_auto_cleanup branch from 6413f78 to a7b41c3 Compare April 8, 2026 14:35
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.

3 participants