Compare commits

..
Author SHA1 Message Date
shamoon e049148f7b Chore: add a null guard for sidebar hidden items 2026-11-08 11:32:43 -08:00
7 changed files with 16 additions and 102 deletions

No files matched your search

-8
View File
@@ -1871,14 +1871,6 @@ processing. This only has an effect if
Defaults to `0 1 * * *`, once per day.
#### [`PAPERLESS_SESSION_CLEANUP_TASK_CRON=<cron expression>`](#PAPERLESS_SESSION_CLEANUP_TASK_CRON) {#PAPERLESS_SESSION_CLEANUP_TASK_CRON}
: Configures the schedule to remove expired sessions from the session store.
Django does not prune these automatically, so without this they would
accumulate indefinitely.
Defaults to `0 3 * * *`, once per day at 03:00.
## Share links
#### [`PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON=<cron expression>`](#PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON) {#PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON}
@@ -260,6 +260,18 @@ describe('SettingsService', () => {
).toBe(false)
})
it('handles null values for sidebar hidden items', () => {
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)
settingsService.set(SETTINGS_KEYS.SIDEBAR_HIDDEN_ITEMS, null)
expect(
settingsService.sidebarItemIsHidden(HideableSidebarItemID.Workflows)
).toBe(false)
})
it('updates setting signals when settings are reinitialized', () => {
let req = httpTestingController.expectOne(
`${environment.apiBaseUrl}ui_settings/`
+4 -2
View File
@@ -765,7 +765,9 @@ export class SettingsService {
sidebarItemIsHidden(item: HideableSidebarItemID): boolean {
return (
this.sidebarHiddenItemsEditing() ?? this.hiddenSidebarItems()
this.sidebarHiddenItemsEditing() ??
this.hiddenSidebarItems() ??
[]
).includes(item)
}
@@ -774,7 +776,7 @@ export class SettingsService {
visible: boolean
): void {
const hiddenItems = new Set(
this.sidebarHiddenItemsEditing() ?? this.hiddenSidebarItems()
this.sidebarHiddenItemsEditing() ?? this.hiddenSidebarItems() ?? []
)
if (visible) {
hiddenItems.delete(item)
-17
View File
@@ -4,7 +4,6 @@ import shutil
import uuid
import zipfile
from collections.abc import Callable
from importlib import import_module
from pathlib import Path
from tempfile import TemporaryDirectory
from tempfile import mkstemp
@@ -926,19 +925,3 @@ def cleanup_expired_share_link_bundles() -> None:
)
if count:
logger.info("Deleted %s expired share link bundle(s)", count)
@shared_task
def clear_expired_sessions() -> None:
"""
Removes expired sessions from the session store. Django does not prune these automatically.
"""
engine = import_module(settings.SESSION_ENGINE)
try:
engine.SessionStore.clear_expired()
logger.info("Cleared expired sessions")
except NotImplementedError:
logger.warning(
"Session engine '%s' does not support clearing expired sessions",
settings.SESSION_ENGINE,
)
-54
View File
@@ -612,57 +612,3 @@ class TestApplyAISuggestionsTask(DirectoriesMixin, TestCase):
apply_suggestions.assert_not_called()
self.assertIn("no longer exists", "".join(cm.output))
@pytest.mark.django_db
class TestClearExpiredSessions:
def test_clears_only_expired_sessions(self) -> None:
"""
GIVEN:
- One expired and one still-valid session in the session store
WHEN:
- The clear_expired_sessions task runs
THEN:
- Only the expired session is removed
"""
from django.contrib.sessions.models import Session
now = timezone.now()
Session.objects.create(
session_key="expired",
session_data="",
expire_date=now - timedelta(days=1),
)
Session.objects.create(
session_key="valid",
session_data="",
expire_date=now + timedelta(days=1),
)
tasks.clear_expired_sessions()
assert set(Session.objects.values_list("session_key", flat=True)) == {"valid"}
def test_unsupported_engine_logs_warning(
self,
caplog: pytest.LogCaptureFixture,
) -> None:
"""
GIVEN:
- A session engine that does not support clearing expired sessions
WHEN:
- The clear_expired_sessions task runs
THEN:
- The error is swallowed and a warning is logged
"""
fake_engine = mock.Mock()
fake_engine.SessionStore.clear_expired.side_effect = NotImplementedError
with (
mock.patch("documents.tasks.import_module", return_value=fake_engine),
override_settings(SESSION_ENGINE="some.unsupported.engine"),
caplog.at_level("WARNING", logger="paperless.tasks"),
):
tasks.clear_expired_sessions()
assert "does not support clearing expired sessions" in caplog.text
-11
View File
@@ -165,17 +165,6 @@ def parse_beat_schedule() -> dict:
"expires": 23.0 * 60.0 * 60.0,
},
},
{
"name": "Clear expired sessions",
"env_key": "PAPERLESS_SESSION_CLEANUP_TASK_CRON",
# Default daily at 03:00
"env_default": "0 3 * * *",
"task": "documents.tasks.clear_expired_sessions",
"options": {
# 1 hour before default schedule sends again
"expires": 23.0 * 60.0 * 60.0,
},
},
]
for task in tasks:
# Either get the environment setting or use the default
@@ -182,7 +182,6 @@ def make_expected_schedule(
workflow_expire = 59.0 * 60.0
llm_index_expire = 23.0 * 60.0 * 60.0
share_link_cleanup_expire = 23.0 * 60.0 * 60.0
session_cleanup_expire = 23.0 * 60.0 * 60.0
schedule: dict[str, Any] = {
"Check all e-mail accounts": {
@@ -249,14 +248,6 @@ def make_expected_schedule(
"headers": {"trigger_source": "scheduled"},
},
},
"Clear expired sessions": {
"task": "documents.tasks.clear_expired_sessions",
"schedule": crontab(minute=0, hour="3"),
"options": {
"expires": session_cleanup_expire,
"headers": {"trigger_source": "scheduled"},
},
},
}
overrides = overrides or {}
@@ -307,7 +298,6 @@ class TestParseBeatSchedule:
"PAPERLESS_WORKFLOW_SCHEDULED_TASK_CRON": "disable",
"PAPERLESS_LLM_INDEX_TASK_CRON": "disable",
"PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON": "disable",
"PAPERLESS_SESSION_CLEANUP_TASK_CRON": "disable",
},
{},
id="all-disabled",