Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

ahmedxgouda
Copy link
Collaborator

Proposed change

Resolves #2390

Add the PR description here.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Summary by CodeRabbit

  • New Features
    • One-time reminders now auto-delete after sending.
    • Reminder setup is workspace-aware in Slack, improving multi-workspace support.
  • Refactor
    • Reminders now link to channels directly (EntityChannel) instead of string IDs, and scheduling uses channel names in displays.
    • Slack scheduling updated to use channel associations and new send-and-delete flow.
    • Improved error handling and clearer messages during reminder setup.
  • Chores
    • Database migrations to support channel association changes.
    • Admin: removed unused channel search fields from EntityChannel admin.
  • Tests
    • Expanded and updated tests for new flows and error paths.

Walkthrough

Refactors reminder/calendar flow to use EntityChannel and GoogleCalendarClient: replaces Reminder.channel_id with entity_channel, updates scheduler APIs to use entity_channel PKs, changes Slack handlers/commands to be workspace-aware, adds migrations, and updates tests and admin UI accordingly.

Changes

Cohort / File(s) Summary of changes
Nest handler refactor
backend/apps/nest/handlers/calendar_events.py
Added get_calendar_id(user_id, event_number); refactored set_reminder signature to accept EntityChannel, GoogleCalendarClient, member, google_calendar_id, and updated Reminder creation to use entity_channel. Removed prior GoogleAccountAuthorization/member lookups.
Reminder model & migrations
backend/apps/nest/models/reminder.py, backend/apps/nest/migrations/0014_remove_reminder_channel_id_reminder_entity_channel.py, backend/apps/nest/migrations/0015_alter_reminder_entity_channel.py
Replaced channel_id CharField with entity_channel ForeignKey to owasp.EntityChannel. Added migrations removing channel_id and adding/altering entity_channel.
Schedulers (base & Slack)
backend/apps/nest/schedulers/calendar_events/base.py, backend/apps/nest/schedulers/calendar_events/slack.py
Changed scheduler public APIs to use channel_id: int (entity_channel.pk). Added send_and_delete and adjusted send_message/send_and_update to resolve EntityChannel and use its Slack channel id.
Slack command surface
backend/apps/slack/commands/command.py, backend/apps/slack/commands/nestbot.py
Added CommandBase.get_workspace_id(command); updated nestbot to pass workspace_id into reminder setup flow.
Slack common handlers
backend/apps/slack/common/handlers/calendar_events.py
Extended get_setting_reminder_blocks to accept workspace_id; added auth/entity/channel resolution, GoogleCalendarClient creation, new error branches, and now calls set_reminder with the new parameters. Updated display of channel names to use entity_channel.channel.name.
Tests updated
backend/tests/apps/nest/*, backend/tests/apps/slack/*
Updated many tests to use EntityChannel instead of channel_id; adjusted mocks to supply GoogleCalendarClient, member, and google_calendar_id; added tests for send_and_delete and updated expectations across handler/scheduler tests.
OWASP merge migration
backend/apps/owasp/migrations/0055_merge_20251008_1253.py
Added empty merge migration (dependencies consolidated, no operations).
Admin tweak
backend/apps/slack/admin/entity_channel.py
Removed search_button and channel_search from EntityChannelAdmin fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

backend

Suggested reviewers

  • kasya
  • arkid15r

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning In addition to the targeted Reminder model and scheduler updates, the PR includes unrelated UI changes to the EntityChannelAdmin in the Slack admin module and introduces a new generic get_workspace_id method in the command base, which are not required by issue #2390. Remove or isolate the admin UI modifications and the standalone command helper into a separate PR so that this changeset remains focused solely on the Reminder model integration and related handlers.
Description Check ❓ Inconclusive The provided description is largely placeholder text and does not summarize the actual code changes beyond referencing the linked issue, leaving the reader without insight into what was modified. Please update the pull request description with a brief overview of the key changes, such as the model field replacement, handler and scheduler updates, and test adjustments.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the primary change implemented in this PR, namely adding an entity_channel field to the Reminder model and removing the old channel_id field, which aligns directly with the main objective of issue #2390.
Linked Issues Check ✅ Passed The PR fully implements issue #2390 by replacing the Reminder model’s channel_id field with an EntityChannel foreign key, updating all related Nest handlers, Slack schedulers, and tests to use the new field, thus satisfying the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

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

🧹 Nitpick comments (1)
backend/apps/slack/common/handlers/calendar_events.py (1)

121-154: Consider using get_or_create for EntityChannel.

The current implementation uses EntityChannel.objects.get() which will raise DoesNotExist if the channel association doesn't exist. The error message "is not linked to your account" suggests users should link channels first, but it's unclear how users perform this linking.

Consider whether EntityChannel.objects.get_or_create() would be more appropriate to automatically establish the link when a user sets a reminder for a channel they have access to:

channel, created = EntityChannel.objects.get_or_create(
    channel_id=conversation.pk,
    channel_type=content_type_conversation,
    entity_id=member.pk,
    entity_type=content_type_member,
    defaults={
        'platform': EntityChannel.Platform.SLACK,
        'is_active': True,
    }
)

If manual linking is required, ensure documentation or UI guidance explains the linking process.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9710778 and 4e18dcd.

⛔ Files ignored due to path filters (1)
  • backend/poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • backend/apps/nest/handlers/calendar_events.py (3 hunks)
  • backend/apps/nest/migrations/0014_remove_reminder_channel_id_reminder_entity_channel.py (1 hunks)
  • backend/apps/nest/models/reminder.py (2 hunks)
  • backend/apps/nest/schedulers/calendar_events/base.py (3 hunks)
  • backend/apps/nest/schedulers/calendar_events/slack.py (1 hunks)
  • backend/apps/owasp/migrations/0055_merge_20251008_1253.py (1 hunks)
  • backend/apps/slack/admin/entity_channel.py (0 hunks)
  • backend/apps/slack/commands/command.py (1 hunks)
  • backend/apps/slack/commands/nestbot.py (1 hunks)
  • backend/apps/slack/common/handlers/calendar_events.py (2 hunks)
💤 Files with no reviewable changes (1)
  • backend/apps/slack/admin/entity_channel.py
🧰 Additional context used
🧬 Code graph analysis (5)
backend/apps/slack/commands/nestbot.py (2)
backend/apps/slack/common/handlers/calendar_events.py (1)
  • get_setting_reminder_blocks (107-181)
backend/apps/slack/commands/command.py (2)
  • get_user_id (78-88)
  • get_workspace_id (90-100)
backend/apps/nest/handlers/calendar_events.py (3)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/owasp/models/event.py (1)
  • Event (25-378)
backend/apps/nest/clients/google_calendar.py (1)
  • GoogleCalendarClient (9-42)
backend/apps/nest/schedulers/calendar_events/base.py (1)
backend/apps/nest/schedulers/calendar_events/slack.py (2)
  • send_message (13-21)
  • send_and_update (24-27)
backend/apps/slack/common/handlers/calendar_events.py (7)
backend/apps/nest/clients/google_calendar.py (1)
  • GoogleCalendarClient (9-42)
backend/apps/nest/handlers/calendar_events.py (2)
  • get_calendar_id (15-22)
  • set_reminder (44-86)
backend/apps/nest/models/google_account_authorization.py (2)
  • GoogleAccountAuthorization (28-225)
  • authorize (55-93)
backend/apps/nest/schedulers/calendar_events/slack.py (1)
  • SlackScheduler (9-27)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/slack/models/conversation.py (1)
  • Conversation (15-104)
backend/apps/slack/blocks.py (1)
  • markdown (21-34)
backend/apps/nest/schedulers/calendar_events/slack.py (3)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/slack/apps.py (1)
  • SlackConfig (13-26)
backend/apps/nest/schedulers/calendar_events/base.py (3)
  • BaseScheduler (9-64)
  • send_message (55-58)
  • send_and_update (61-64)
⏰ 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). (4)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: CodeQL (python)
🔇 Additional comments (13)
backend/apps/owasp/migrations/0055_merge_20251008_1253.py (2)

1-1: Ignore Django version discrepancy. Migrations are generated with Django 5.2.x, and the project’s pyproject.toml and lock file specify Django ^5.2 (currently 5.2.7), so the metadata aligns.

Likely an incorrect or invalid review comment.


6-12: Merge migration verified — parent migrations exist. Both dependencies (0053_merge_20250918_1659 and 0054_event_event_end_date_desc_idx) are present; no further action required.

backend/apps/nest/schedulers/calendar_events/base.py (1)

55-61: LGTM!

The parameter type change from str to int for channel_id is correct and aligns with the migration to use EntityChannel.pk.

backend/apps/slack/commands/command.py (1)

90-100: LGTM!

The new get_workspace_id method follows the same pattern as get_user_id and correctly extracts the workspace ID from the Slack command payload.

backend/apps/slack/commands/nestbot.py (1)

33-35: LGTM!

The updated call correctly passes the workspace ID using the newly added get_workspace_id method, aligning with the expanded function signature.

backend/apps/nest/schedulers/calendar_events/slack.py (2)

5-5: LGTM!

The import of EntityChannel is necessary for the new implementation.


24-27: LGTM!

The updated signature correctly accepts channel_id: int and delegates to the updated send_message method.

backend/apps/slack/common/handlers/calendar_events.py (3)

107-118: LGTM!

The updated function signature and new imports correctly support the EntityChannel-based flow and Google Calendar integration.


156-166: LGTM!

The exception handling provides clear, user-friendly error messages for various failure scenarios.


167-181: LGTM!

The return logic correctly prioritizes error messages and provides a comprehensive success message with all reminder details.

backend/apps/nest/handlers/calendar_events.py (3)

15-22: LGTM!

The get_calendar_id function correctly retrieves and validates the cached Google Calendar ID, raising an appropriate error for invalid or expired event numbers.


44-52: LGTM!

The updated function signature correctly reflects the shift to EntityChannel-based channels and direct client/member passing, removing the need for internal lookups.


54-86: LGTM!

The validation logic and reminder creation flow correctly use the new entity_channel parameter. The get_or_create call properly uses entity_channel=channel instead of the old channel_id.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 9, 2025

@ahmedxgouda ahmedxgouda marked this pull request as ready for review October 9, 2025 19:40
Copy link
Contributor

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

Caution

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

⚠️ Outside diff range comments (1)
backend/apps/slack/common/handlers/calendar_events.py (1)

107-181: Fix conversation lookup before landing.

Conversation.objects.get(name=args.channel.lstrip("#"), workspace__slack_workspace_id=workspace_id) assumes the CLI is passing a channel name. The previous flow worked with channel_id, and Slack slash commands typically deliver the stable channel ID (e.g., C12345). If a user supplies the ID—as they do today—this query will raise Conversation.DoesNotExist, and the reminder flow always fails with "Channel 'C12345' does not exist in this workspace." We should keep resolving by the Slack ID (and still trim a leading # for name input as a fallback) to avoid breaking the existing command.

Apply something along these lines to restore ID support:

-        conversation = Conversation.objects.get(
-            name=args.channel.lstrip("#"),
-            workspace__slack_workspace_id=workspace_id,
-        )
+        channel_lookup = args.channel.lstrip("#")
+        conversation = Conversation.objects.filter(
+            workspace__slack_workspace_id=workspace_id,
+        ).filter(
+            models.Q(slack_channel_id=channel_lookup)
+            | models.Q(name__iexact=channel_lookup)
+        ).first()
+        if not conversation:
+            raise Conversation.DoesNotExist

(You can still tighten the fallback, but we need to honor the Slack ID path before merging.)

♻️ Duplicate comments (1)
backend/apps/nest/schedulers/calendar_events/slack.py (1)

3-31: Handle missing EntityChannel rows so jobs don’t crash.

If the channel is deleted after scheduling, Line 15 raises EntityChannel.DoesNotExist, causing the RQ job to fail and the reminder never to be cleaned up. Please guard the lookup and log before returning so the worker keeps running.

+import logging
+
 from apps.nest.schedulers.calendar_events.base import BaseScheduler
 from apps.nest.utils.calendar_events import update_reminder_schedule_date
 from apps.owasp.models.entity_channel import EntityChannel
 from apps.slack.apps import SlackConfig
+
+logger = logging.getLogger(__name__)

     @staticmethod
     def send_message(message: str, channel_id: int):
         """Send message to the specified Slack channel."""
-        entity_channel = EntityChannel.objects.get(pk=channel_id)
+        try:
+            entity_channel = EntityChannel.objects.get(pk=channel_id)
+        except EntityChannel.DoesNotExist:
+            logger.warning("EntityChannel pk=%s not found; skipping send.", channel_id)
+            return
🧹 Nitpick comments (2)
backend/tests/apps/slack/common/handlers/calendar_events_test.py (1)

173-241: Consider adding a Slack-ID case after the fix.

Once the handler resolves channels by Slack ID again, it’d be great to extend this suite with an argument like channel="C12345" to lock in that regression guard.

backend/tests/apps/nest/schedulers/calendar_events/slack_test.py (1)

27-38: Align test inputs with integer channel IDs.

Production code now calls SlackScheduler.send_and_delete() with an EntityChannel primary key (int). Keeping "C123456" here masks type mismatches and would let the test pass even if we regressed back to string IDs. Please swap both the invocation and the expectation to use an int (e.g., 5) so the test exercises the real contract.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e18dcd and a9b2c15.

📒 Files selected for processing (11)
  • backend/apps/nest/migrations/0015_alter_reminder_entity_channel.py (1 hunks)
  • backend/apps/nest/models/reminder.py (2 hunks)
  • backend/apps/nest/schedulers/calendar_events/base.py (2 hunks)
  • backend/apps/nest/schedulers/calendar_events/slack.py (1 hunks)
  • backend/apps/slack/common/handlers/calendar_events.py (4 hunks)
  • backend/tests/apps/nest/handlers/calendar_events_test.py (9 hunks)
  • backend/tests/apps/nest/models/reminder_schedule_test.py (4 hunks)
  • backend/tests/apps/nest/models/reminder_test.py (2 hunks)
  • backend/tests/apps/nest/schedulers/calendar_events/base_test.py (5 hunks)
  • backend/tests/apps/nest/schedulers/calendar_events/slack_test.py (1 hunks)
  • backend/tests/apps/slack/common/handlers/calendar_events_test.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/nest/models/reminder.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-10T15:32:12.688Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#2211
File: backend/apps/nest/controllers/calendar_events.py:0-0
Timestamp: 2025-09-10T15:32:12.688Z
Learning: In the backend/apps/nest/controllers/calendar_events.py file, the scheduled_time parameter in the schedule_reminder function is guaranteed to be timezone-aware, so explicit timezone awareness validation is not needed.

Applied to files:

  • backend/apps/nest/schedulers/calendar_events/base.py
🧬 Code graph analysis (9)
backend/tests/apps/nest/models/reminder_test.py (2)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/nest/models/reminder.py (1)
  • Reminder (6-38)
backend/tests/apps/nest/schedulers/calendar_events/slack_test.py (1)
backend/apps/nest/schedulers/calendar_events/slack.py (3)
  • SlackScheduler (9-37)
  • send_message (13-21)
  • send_and_delete (24-31)
backend/tests/apps/nest/handlers/calendar_events_test.py (6)
backend/apps/nest/handlers/calendar_events.py (3)
  • get_calendar_id (15-22)
  • schedule_reminder (25-41)
  • set_reminder (44-86)
backend/apps/nest/models/reminder.py (1)
  • Reminder (6-38)
backend/apps/nest/models/reminder_schedule.py (1)
  • ReminderSchedule (6-66)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/owasp/models/event.py (1)
  • Event (25-378)
backend/apps/nest/clients/google_calendar.py (1)
  • get_event (38-42)
backend/apps/nest/schedulers/calendar_events/base.py (1)
backend/apps/nest/schedulers/calendar_events/slack.py (3)
  • send_and_delete (24-31)
  • send_and_update (34-37)
  • send_message (13-21)
backend/apps/slack/common/handlers/calendar_events.py (7)
backend/apps/nest/clients/google_calendar.py (1)
  • GoogleCalendarClient (9-42)
backend/apps/nest/handlers/calendar_events.py (2)
  • get_calendar_id (15-22)
  • set_reminder (44-86)
backend/apps/nest/models/google_account_authorization.py (2)
  • GoogleAccountAuthorization (28-225)
  • authorize (55-93)
backend/apps/nest/schedulers/calendar_events/slack.py (1)
  • SlackScheduler (9-37)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/slack/models/conversation.py (1)
  • Conversation (15-104)
backend/apps/slack/blocks.py (1)
  • markdown (21-34)
backend/tests/apps/nest/models/reminder_schedule_test.py (2)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/nest/models/reminder.py (1)
  • Reminder (6-38)
backend/tests/apps/nest/schedulers/calendar_events/base_test.py (2)
backend/apps/nest/schedulers/calendar_events/base.py (3)
  • BaseScheduler (8-64)
  • send_and_delete (55-58)
  • send_and_update (61-64)
backend/apps/nest/schedulers/calendar_events/slack.py (2)
  • send_and_delete (24-31)
  • send_and_update (34-37)
backend/tests/apps/slack/common/handlers/calendar_events_test.py (4)
backend/apps/nest/models/reminder_schedule.py (1)
  • ReminderSchedule (6-66)
backend/apps/nest/models/google_account_authorization.py (1)
  • GoogleAccountAuthorization (28-225)
backend/tests/apps/slack/commands/slack_sync_messages_test.py (2)
  • mock_member (58-62)
  • mock_conversation (49-55)
backend/apps/slack/common/handlers/calendar_events.py (1)
  • get_setting_reminder_blocks (107-181)
backend/apps/nest/schedulers/calendar_events/slack.py (4)
backend/apps/owasp/models/entity_channel.py (1)
  • EntityChannel (8-66)
backend/apps/slack/apps.py (1)
  • SlackConfig (13-26)
backend/apps/nest/schedulers/calendar_events/base.py (4)
  • BaseScheduler (8-64)
  • send_message (49-52)
  • send_and_delete (55-58)
  • send_and_update (61-64)
backend/apps/nest/models/reminder_schedule.py (1)
  • ReminderSchedule (6-66)
⏰ 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). (1)
  • GitHub Check: Run frontend e2e tests
🔇 Additional comments (10)
backend/tests/apps/nest/handlers/calendar_events_test.py (4)

24-40: Cache interactions covered well.

Happy to see the cache-hit/miss paths asserted with the exact key format and error messaging.


41-101: Comprehensive happy-path exercise.

The stacked patches model the transactional flow end-to-end and assert each critical call, which gives strong coverage of the new EntityChannel path.


102-200: Input validation scenarios look solid.

Great job capturing each failure mode (minutes_before, missing event, past reminder, bad recurrence) and asserting on the raised messages.


201-284: schedule_reminder coverage remains tight.

The success path and both guard-rail checks (past time, invalid recurrence) are exercised cleanly with the updated Reminder payload.

backend/apps/slack/common/handlers/calendar_events.py (2)

12-30: Nice UX refinement.

Referencing entity_channel.channel.name makes the cancellation message far clearer than surfacing a raw identifier.


83-104: Reminder listings read better.

Surfacing the resolved channel name keeps the Slack copy human-friendly—good polish.

backend/tests/apps/slack/common/handlers/calendar_events_test.py (2)

130-159: 👍 Reminder listing test stays meaningful.

The MagicMock scaffolding keeps the assertions on channel names and ordering intact after the entity_channel switch.


242-377: Error handling coverage looks thorough.

Each of the raised exception paths (ValidationError, ValueError, ServerNotFoundError) now has a focused test, which keeps the Slack copy stable.

backend/apps/nest/migrations/0015_alter_reminder_entity_channel.py (1)

1-24: Migration aligns with model.

The AlterField mirrors the model definition (CASCADE + related_name) and depends on the right upstream migrations.

backend/tests/apps/nest/models/reminder_test.py (1)

1-35: Model tests keep pace.

Switching to the EntityChannel fixture keeps the string/verbose-name assertions relevant after the model change.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant