-
-
Notifications
You must be signed in to change notification settings - Fork 62
[fix] Dynamically updating notification verb #334 #413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Made verb field in notification nullable. Created a property verb under AbstractNotification in models that prioritizes verb from NOTIFICATION_TYPES and uses DB as a fallback. Fixes openwisp#334
Same as above Fixes openwisp#334.
|
I think the CI builds are failing because the ./manage.py makemigrations isn't being run in the workflow, the migration I added solved the issue of the NOT NULL constraint on verb when I tested locally. |
nemesifier
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pranshustuff.
We need a test which replicates the bug described in #334. The test should fail without your patch and should pass with it.
Please also make sure the CI build passes.
See more comments below.
Added a test to verify correct behaviour and specified exceptions in the try-except in the verb property in models.py Fixes openwisp#334
|
I've made the requested changes. |
It should pass CI bulds now I think. Fixes openwisp#334
nemesifier
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests and QA checks are failing, the developer docs of OpenWISP RADIUS explain how to run tests and QA checks.
This reverts commit 5b8f80b.
Removed verb field from DB, verb now updates from config from NOTIFICATION_TYPES. Fixes openwisp#334
|
Ok locally, it passed the tests, on sample app too. I added a test that mimics the bug in test_notifications.py. |
Same as above, should pass QA checks. Fixes openwisp#334.
openwisp_notifications/migrations/0012_remove_notification_verb.py
Outdated
Show resolved
Hide resolved
Not deleting DB field verb, so it is backwards compatible. Changed instances of .verb to .resolved_verb. Resolved_verb property allows .verb as fallback. Fixes openwisp#334.
|
I made the requested changes. |
| "email_subject": "[{site.name}] Default Notification Subject", | ||
| "message": ( | ||
| "Default notification with {notification.verb} and level {notification.level}" | ||
| "Default notification with {notification.resolved_verb} and level {notification.level}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will break the existing integration with other OpenWISP apps.
We have a context manager notification_render_attributes , Can we set the verb using notification type in the context manage and then clear the field (set to None) in cleanup process?
openwisp-notifications/openwisp_notifications/base/models.py
Lines 45 to 81 in b356e1d
| @contextmanager | |
| def notification_render_attributes(obj, **attrs): | |
| """ | |
| This context manager sets temporary attributes on | |
| the notification object to allowing rendering of | |
| notification. | |
| It can only be used to set aliases of the existing attributes. | |
| By default, it will set the following aliases: | |
| - actor_link -> actor_url | |
| - action_link -> action_url | |
| - target_link -> target_url | |
| """ | |
| defaults = { | |
| "actor_link": "actor_url", | |
| "action_link": "action_url", | |
| "target_link": "target_url", | |
| } | |
| defaults.update(attrs) | |
| for target_attr, source_attr in defaults.items(): | |
| setattr(obj, target_attr, getattr(obj, source_attr)) | |
| # In Django 5.1+, GenericForeignKey fields defined in parent models can no longer | |
| # be overridden in child models (https://code.djangoproject.com/ticket/36295). | |
| # To avoid multiple database queries, we explicitly set these attributes here | |
| # using our cached _related_object method instead of relying on the default | |
| # GenericForeignKey accessor which would bypass our caching mechanism. | |
| setattr(obj, "actor", obj._related_object("actor")) | |
| setattr(obj, "action_object", obj._related_object("action_object")) | |
| setattr(obj, "target", obj._related_object("target")) | |
| yield obj | |
| for attr in defaults.keys(): | |
| delattr(obj, attr) | |
It should behave similarly to the resolved_verb property.
db_verb = obj.verb
# we give preference to the verb stored in the database
verb = obj.verb or config.get('verb')
# In cleanup
obj.verb = db_verb
Do you think a solution like this will solve our problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not tested it yet. But I think it's a plausible solution.
I am a little busy at the moment. I'll get back to you sometime around next week after investigating this approach.
Thanks again for the review!
WalkthroughThe changes implement a solution to prevent notification verbs from persisting in the database and being disconnected from configuration updates. The verb field is made nullable with Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In `@openwisp_notifications/tests/test_notifications.py`:
- Around line 1533-1537: The test currently unregisters the notification type
and sets the DB verb via notification.__dict__["verb"] then only asserts
notification.verb; update the subTest so it also asserts that
notification.resolved_verb equals the DB fallback ("db verb"). Locate the
subTest block around unregister_notification_type("default") and add a check for
notification.resolved_verb to ensure the resolved_verb fallback path returns the
stored DB value when the type is unregistered.
🧹 Nitpick comments (1)
openwisp_notifications/base/models.py (1)
104-113: Consider usinglogger.exceptionfor better debuggability.
logging.exceptionlogs the exception and the traceback, whilelogging.erroronly logs the exception. The former is more appropriate when logging an exception, as the traceback is often useful for debugging.Since this code is inside an exception handler, switching to
logger.exceptionwould automatically include the traceback without needing to passexc_info=True.♻️ Proposed fix
`@property` def resolved_verb(self): config = {} try: config = get_notification_configuration(self.type) except NotificationRenderException as e: - logger.error( + logger.exception( "Couldn't get notification config for type %s : %s", self.type, e ) return config.get("verb") or self.verb
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
openwisp_notifications/base/models.pyopenwisp_notifications/base/notifications.pyopenwisp_notifications/handlers.pyopenwisp_notifications/migrations/0012_alter_notification_verb.pyopenwisp_notifications/templates/openwisp_notifications/default_message.mdopenwisp_notifications/tests/test_notifications.pyopenwisp_notifications/types.pytests/openwisp2/sample_notifications/apps.pytests/openwisp2/sample_notifications/migrations/0004_alter_notification_verb.py
💤 Files with no reviewable changes (1)
- openwisp_notifications/handlers.py
🧰 Additional context used
🧬 Code graph analysis (4)
openwisp_notifications/migrations/0012_alter_notification_verb.py (1)
tests/openwisp2/sample_notifications/migrations/0004_alter_notification_verb.py (1)
Migration(6-20)
openwisp_notifications/tests/test_notifications.py (3)
openwisp_notifications/base/models.py (2)
resolved_verb(105-113)message(255-257)openwisp_notifications/tests/test_helpers.py (2)
mock_notification_types(74-89)unregister_notification_type(64-66)openwisp_notifications/types.py (2)
get_notification_configuration(42-50)unregister_notification_type(93-100)
openwisp_notifications/base/models.py (2)
openwisp_notifications/types.py (1)
get_notification_configuration(42-50)openwisp_notifications/exceptions.py (1)
NotificationRenderException(1-6)
tests/openwisp2/sample_notifications/migrations/0004_alter_notification_verb.py (1)
openwisp_notifications/migrations/0012_alter_notification_verb.py (1)
Migration(6-20)
🪛 Ruff (0.14.14)
openwisp_notifications/migrations/0012_alter_notification_verb.py
[warning] 8-10: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
[warning] 12-20: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
openwisp_notifications/base/models.py
[warning] 110-112: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
tests/openwisp2/sample_notifications/migrations/0004_alter_notification_verb.py
[warning] 8-10: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
[warning] 12-20: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
⏰ 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: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
🔇 Additional comments (7)
openwisp_notifications/migrations/0012_alter_notification_verb.py (1)
1-20: LGTM!The migration correctly alters the
verbfield to be nullable (null=True,blank=True), aligning with the PR objective to allow dynamic verb resolution from configuration. The static analysis warnings (RUF012) about mutable class attributes are false positives—this is standard Django migration syntax.openwisp_notifications/base/notifications.py (1)
52-52: LGTM!Making the
verbfield nullable is the correct approach for backward compatibility. Existing notifications retain their stored verb (used as fallback), while new notifications can rely entirely on configuration-based resolution via theresolved_verbproperty.openwisp_notifications/types.py (1)
12-14: LGTM!The message templates correctly use
{notification.resolved_verb}instead of the raw{notification.verb}. This ensures notifications dynamically reflect verb changes in the configuration, which is the core fix for issue#334.Also applies to: 25-27
openwisp_notifications/templates/openwisp_notifications/default_message.md (1)
1-6: LGTM!The template correctly uses
{{ notification.resolved_verb }}in both the head block and body, ensuring dynamically resolved verbs are displayed throughout the notification message.tests/openwisp2/sample_notifications/apps.py (1)
27-27: LGTM!The sample app correctly demonstrates the updated pattern using
{notification.resolved_verb}in the message template.openwisp_notifications/tests/test_notifications.py (1)
410-410: LGTM!The existing test assertions are correctly updated to use
resolved_verbinstead ofverb, ensuring tests validate the new dynamic resolution behavior.Also applies to: 473-473, 576-577, 586-586
tests/openwisp2/sample_notifications/migrations/0004_alter_notification_verb.py (1)
1-20: LGTM — migration cleanly aligns the sample app schema with nullableverb.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| with self.subTest("Test fallback to database verb"): | ||
| unregister_notification_type("default") | ||
| notification.__dict__["verb"] = "db verb" | ||
| self.assertEqual(notification.verb, "db verb") | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test doesn't verify resolved_verb fallback to DB value.
The subtest "Test fallback to database verb" sets the DB verb but only asserts notification.verb == "db verb". It should also verify that resolved_verb correctly returns this fallback value after the notification type is unregistered.
💚 Proposed fix to complete the fallback assertion
with self.subTest("Test fallback to database verb"):
unregister_notification_type("default")
notification.__dict__["verb"] = "db verb"
self.assertEqual(notification.verb, "db verb")
+ self.assertEqual(notification.resolved_verb, "db verb")📝 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.
| with self.subTest("Test fallback to database verb"): | |
| unregister_notification_type("default") | |
| notification.__dict__["verb"] = "db verb" | |
| self.assertEqual(notification.verb, "db verb") | |
| with self.subTest("Test fallback to database verb"): | |
| unregister_notification_type("default") | |
| notification.__dict__["verb"] = "db verb" | |
| self.assertEqual(notification.verb, "db verb") | |
| self.assertEqual(notification.resolved_verb, "db verb") |
🤖 Prompt for AI Agents
In `@openwisp_notifications/tests/test_notifications.py` around lines 1533 - 1537,
The test currently unregisters the notification type and sets the DB verb via
notification.__dict__["verb"] then only asserts notification.verb; update the
subTest so it also asserts that notification.resolved_verb equals the DB
fallback ("db verb"). Locate the subTest block around
unregister_notification_type("default") and add a check for
notification.resolved_verb to ensure the resolved_verb fallback path returns the
stored DB value when the type is unregistered.
Checklist
Reference to Existing Issue
Fixes #334.
Description of Changes
Screenshot
simplescreenrecorder-2025-10-01_15.25.40.mp4