-
-
Notifications
You must be signed in to change notification settings - Fork 116
feat: Migrate badge notifications from polling to WebSocket (Issue #637) #727
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
Conversation
## Overview
Replaces Apollo Client polling (30s intervals) with real-time WebSocket-based
notification delivery for ALL notification types (BADGE, REPLY, MENTION,
THREAD_REPLY, moderation actions). Provides instant notification delivery
with zero latency and dramatically reduces server load.
## Backend Changes
### WebSocket Consumer
- **New file**: `config/websocket/consumers/notification_updates.py`
- `NotificationUpdatesConsumer`: Async WebSocket consumer for real-time notifications
- User-specific channel groups (`notification_user_{user_id}`) prevent IDOR
- Handles NOTIFICATION_CREATED, NOTIFICATION_UPDATED, NOTIFICATION_DELETED
- Heartbeat/ping-pong for connection health monitoring
- Graceful error handling and cleanup on disconnect
### Signal Broadcasting
- **Modified**: `opencontractserver/notifications/signals.py`
- Added `broadcast_notification_via_websocket()` helper function
- Integrated WebSocket broadcasting in all signal handlers:
- `create_reply_notification` (REPLY notifications)
- `create_mention_notification` (MENTION notifications)
- `create_badge_notification` (BADGE notifications)
- `create_moderation_notification` (moderation actions)
- Broadcasts to user-specific channel groups via Django Channels layer
- Bulk notifications properly broadcast individual items
- Failures don't break signal handlers (notifications still save to DB)
### ASGI Routing
- **Modified**: `config/asgi.py`
- Registered `ws/notification-updates/` WebSocket endpoint
- Uses existing authentication middleware (Auth0/JWT)
- Logs registered WebSocket patterns on startup
## Frontend Changes
### WebSocket Hook
- **New file**: `frontend/src/hooks/useNotificationWebSocket.ts`
- `useNotificationWebSocket`: Complete WebSocket connection lifecycle management
- Auto-reconnection with exponential backoff (2s → 4s → 8s → 16s, max 8x)
- Heartbeat monitoring every 30s to detect stale connections
- Integrated with `useNetworkStatus` for mobile screen unlock reconnection
- Callbacks for notification created/updated/deleted events
- Recent notifications buffer (last 50) for UI state
- Connection state tracking (disconnected/connecting/connected/error)
### Badge Notifications Migration
- **Modified**: `frontend/src/hooks/useBadgeNotifications.ts`
- Completely refactored from polling to WebSocket
- Maintains backward-compatible interface (`newBadges`, `clearNewBadges`)
- Added `connectionState` export for debugging/UI feedback
- Zero latency badge delivery (instant vs 0-30s polling delay)
- Removed Apollo Client polling logic entirely
### WebSocket URL Helper
- **Modified**: `frontend/src/components/chat/get_websockets.ts`
- Added `getNotificationUpdatesWebSocket()` function
- Handles protocol conversion (http→ws, https→wss)
- Token parameter encoding for authentication
## Testing
### Backend Tests
- **New file**: `opencontractserver/tests/test_notification_websocket.py`
- Authentication and authorization tests
- IDOR prevention (user isolation) tests
- Ping/pong and heartbeat functionality
- Channel layer broadcasting tests
- Multi-user concurrent connection tests
- Disconnect cleanup verification
- Signal integration tests
## Security
- **IDOR Prevention**: User-specific channel groups ensure users only receive their own notifications
- **Authentication**: WebSocket middleware validates JWT/Auth0 tokens before accepting connections
- **Authorization**: Consumer validates user is authenticated before subscribing to channel
- **No data leakage**: Notifications filtered on backend, not frontend
- **Rate limiting**: Exponential backoff prevents reconnection spam
## Performance
- **User-specific channels**: Each user has dedicated channel group (not global broadcast)
- **Database indexes**: Leverages existing indexes on Notification model
- **Bulk operations**: Thread participant notifications use bulk_create + batch broadcasting
- **Efficient serialization**: Minimal JSON payloads with only essential fields
- **Connection pooling**: Multiple tabs for same user share channel group
- **Graceful degradation**: WebSocket failures don't break notification creation
## Impact
- **Zero latency**: Badge awards appear instantly (vs 0-30s polling delay)
- **Reduced server load**: Eliminates continuous polling from all connected clients
- **Scalability**: WebSocket connections more efficient than repeated HTTP polls
- **Real-time UX**: Instant feedback for all notification types
- **Mobile friendly**: Auto-reconnection on screen unlock via useNetworkStatus integration
## Documentation
- **Updated**: `CHANGELOG.md`
- Added comprehensive feature description
- Technical architecture details
- Security and performance notes
- File locations and line numbers
Closes #637
Code Review for PR #727 - WebSocket-based Real-time NotificationsThis is an excellent implementation that successfully migrates from polling to WebSocket-based notifications. The code is well-structured, thoroughly tested, and follows the project's conventions. Below is my detailed review: ✅ Strengths1. Architecture & Design
2. Backend ImplementationWebSocket Consumer (
Signal Integration (
3. Frontend ImplementationuseNotificationWebSocket Hook:
useBadgeNotifications Migration:
4. Testing
5. Documentation
🔍 Issues & RecommendationsCritical Issues: None ✅High Priority1. Missing Frontend TestsLocation: Issue: No frontend component tests were added for the new WebSocket hook. According to CLAUDE.md, frontend tests are critical and should follow the wrapper pattern. Recommendation: Add component tests for // frontend/tests/useNotificationWebSocket.spec.tsx
import { test, expect } from '@playwright/experimental-ct-react';
import { useNotificationWebSocket } from '../src/hooks/useNotificationWebSocket';
test('should connect and receive notifications', async ({ mount }) => {
// Mock WebSocket server
// Test connection lifecycle
// Test message handling
// Test reconnection logic
});Impact: Without frontend tests, we can't verify the WebSocket hook behavior in the browser environment, particularly edge cases like reconnection and network failures. Medium Priority2. Potential Memory Leak in
|
…637) - Remove unused database_sync_to_async import from notification consumer - Add explanatory comments for eslint-disable rules in useNotificationWebSocket - Improve error logging with exception type names for better debugging - Add defensive fallback for created_at field in bulk_create scenarios 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Real-Time Notification System via WebSocketSummaryThis PR successfully migrates badge notifications from Apollo Client polling (30s intervals) to real-time WebSocket delivery. The implementation is well-architected, secure, and includes comprehensive testing. Overall, this is high-quality work that follows best practices. ✅ Strengths1. Excellent Security Implementation
2. Robust Error Handling
3. Performance Optimizations
4. Comprehensive Testing
5. Clean Architecture
|
) - Remove polling interval argument from useBadgeNotifications() call in App.tsx (hook was refactored to use WebSocket, no longer needs interval) - Change NotificationWebSocketTestCase to inherit from WebsocketFixtureBaseTestCase which provides the required self.token attribute for WebSocket auth tests - Fix import ordering in asgi.py (isort)
PR Review: WebSocket-Based Real-Time NotificationsExcellent work on migrating from polling to WebSocket-based notifications! This is a well-architected change that significantly improves performance and user experience. Here's my detailed review: ✅ StrengthsArchitecture & Design
Security
Performance
Code Quality
🔍 Issues & RecommendationsCritical IssuesNone found - This is production-ready code. Minor Issues1. Missing Apollo Cache Invalidation (Low Priority)Location: The old polling implementation used Apollo Client's cache, but the new WebSocket implementation doesn't invalidate the cache when notifications arrive. If other components query Recommendation: Either:
2. Potential Race Condition on Page Load (Low Priority)Location: On WebSocket connect ( recentNotificationsRef.current = [];If a notification arrives between page load and WebSocket connection, it could be missed. The old polling implementation would catch these on first poll. Recommendation: Consider fetching recent unread notifications on mount before clearing the array, or document this edge case. 3. Heartbeat Interval Not Configurable per Connection (Minor)Location: The heartbeat interval is hardcoded to 30s in the connection logic, even though it's a configurable option. This works correctly, but the interval check could be moved outside the Current: heartbeatRef.current = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, heartbeatInterval); // Uses option correctlyThis is actually fine - no change needed, just noting it works as expected. 4. Missing Network Status Dependency (Very Minor)Location: The Recommendation: No action needed - the code works correctly as-is. Suggestions for Future Enhancements
📝 Code Quality NotesBest Practices Followed ✅
Adherence to CLAUDE.md ✅
🧪 Testing NotesBackend Tests (
|
| Category | Rating | Notes |
|---|---|---|
| Architecture | ⭐⭐⭐⭐⭐ | Clean, scalable, well-separated |
| Security | ⭐⭐⭐⭐⭐ | IDOR prevention, auth validation |
| Performance | ⭐⭐⭐⭐⭐ | Zero latency, reduced load |
| Code Quality | ⭐⭐⭐⭐⭐ | Excellent docs, types, error handling |
| Testing | ⭐⭐⭐⭐☆ | Great backend tests, missing frontend |
| Documentation | ⭐⭐⭐⭐⭐ | Comprehensive CHANGELOG, docstrings |
Overall: ⭐⭐⭐⭐⭐ Excellent work!
This is a well-executed migration that significantly improves the notification system. The code is production-ready, secure, and maintainable. Great attention to detail on security, error handling, and documentation.
Reviewed by: Claude Sonnet 4.5
Reviewed on: 2025-12-27
…ass import conflict
Code Review - PR #727: Real-Time Notification System via WebSocketThis is an excellent implementation that successfully migrates the notification system from polling to WebSocket-based real-time delivery. The code is well-architected, thoroughly tested, and follows the project's established patterns. Below is my comprehensive review: ✅ StrengthsArchitecture & Design
Security
Performance
Testing
🔍 Potential Issues & SuggestionsCritical Issues: None FoundMinor Suggestions
Code Quality
📊 Performance Considerations
🔒 Security Considerations
🎯 Test Coverage AssessmentBackend Tests (
|
Test Fixes: - Add required `creator` field to Badge creation in tests (BaseOCModel requires it) - Fix import from non-existent `opencontractserver.utils.auth` module to use `graphql_jwt.shortcuts.get_token` instead - Handle `asyncio.CancelledError` in IDOR prevention test (WebSocket timeout behavior) - Fix variable reference from `user2_token` to `user2` in IDOR test - Add proper cleanup handling for WebSocket connections after timeouts New Tests: - `test_invalid_json_handling`: Verify consumer gracefully handles malformed JSON - `test_unknown_message_type_handling`: Verify consumer handles unknown message types Code Review Improvements: - Move failure count reset from CONNECTED handler to ws.onopen (fallback for delayed/missing CONNECTED message) - Add documentation about notification buffer clearing design decision - Add rate limiting documentation note to WebSocket consumer docstring 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review - WebSocket Notification System (PR #727)Overall, this is an excellent implementation of a real-time notification system. The code is well-architected, properly tested, and follows best practices. Here's my detailed review: ✅ Strengths1. Architecture & Design
2. Security
3. Robustness
4. Testing
5. Documentation
🔍 Issues & SuggestionsCritical Issues: None ✅Medium Priority1. Potential Memory Leak in Frontend HookLocation: The Recommendation: // Option 1: Use useRef for callbacks to stabilize dependencies
const onNotificationCreatedRef = useRef(onNotificationCreated);
useEffect(() => {
onNotificationCreatedRef.current = onNotificationCreated;
}, [onNotificationCreated]);
// Option 2: Remove handleMessage from deps and ensure it's stable via useCallback with empty deps2. Missing Rate Limiting DocumentationLocation: The docstring mentions rate limiting isn't implemented but should be considered. While this is read-only, malicious clients could spam ping/heartbeat messages. Recommendation:
3. Unclear Notification Buffer BehaviorLocation: The buffer is cleared on reconnection, which may surprise developers expecting persistence across reconnects. While the comment explains this is intentional, the behavior could be confusing. Recommendation:
Low Priority / Nitpicks4. Inconsistent Error LoggingLocation: Various signal handlers in Some error logs include
This is actually good - all the important ones include it. Just noting for consistency. 5. Hardcoded Magic NumbersLocation: recentNotificationsRef.current = [notification, ...recentNotificationsRef.current].slice(0, 50);The buffer size (50) is hardcoded. Per CLAUDE.md guidelines, consider moving to a constant or hook option. Recommendation: const DEFAULT_NOTIFICATION_BUFFER_SIZE = 50;
export interface UseNotificationWebSocketOptions {
// ... existing options
bufferSize?: number; // Default: 50
}6. TypeScript Type SafetyLocation: createdAt: data.createdAt || new Date().toISOString(),This fallback should never be hit (server always sends createdAt), but having it masks potential bugs. Consider logging a warning if the fallback is used: createdAt: data.createdAt || (() => {
console.warn('[useNotificationWebSocket] Missing createdAt, using fallback');
return new Date().toISOString();
})(),📊 Performance ConsiderationsExcellent:
Future Optimization Opportunities:
🔒 Security AssessmentStrong Points:
Potential Concerns:
🧪 Test CoverageExcellent coverage overall. Consider adding:
📝 Documentation & Code QualityExcellent:
Minor Suggestion:Consider adding a high-level architecture diagram to the docs showing:
This would help future developers understand the system quickly. 🎯 Final Recommendation✅ APPROVE with minor suggestions This PR is production-ready. The suggestions above are optimizations and improvements, not blockers. The code is:
Before Merge Checklist:
💡 Impact AssessmentZero latency notifications vs 0-30s polling delay is a massive UX improvement. The architecture is solid and scales well. Great work on this implementation! 🎉 Reviewed by: Claude Code |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Documents the WebSocket-based notification system (Issue #637): - Architecture diagram and data flow - All notification types and triggers - Backend components (consumer, signals, broadcasting) - Frontend hooks (useNotificationWebSocket, useBadgeNotifications) - Message protocol with JSON schemas - Testing and debugging guides - Performance considerations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request Review: Real-Time Notification System via WebSocketThis is an excellent implementation that successfully migrates the badge notification system from polling to WebSocket-based real-time updates. The code quality is high, the architecture is well-designed, and the documentation is exceptional. Here's my detailed review: ✅ StrengthsArchitecture & Design
Security
Code Quality
Testing
Performance
Documentation
🔍 Issues & ConcernsCritical IssuesNone found - This is production-ready code. Minor Issues & Suggestions1. Potential Memory Leak in Frontend HookLocation: The Suggestion: This is likely fine in practice, but consider adding a comment explaining why this edge case is acceptable. 2. Recent Notifications Buffer Cleared on ReconnectLocation: The comment says "Clear recent notifications buffer for clean state on new connection" but this means notifications received just before a reconnect are lost. Question: Is this intentional? If the connection drops briefly, users might miss notifications in the UI. Consider whether you want to persist Current behavior: Likely acceptable since badges are shown via Apollo cache anyway, but worth documenting the intended behavior. 3. Hardcoded Reconnection DelayLocation: The exponential backoff caps at 8x the base delay ( Suggestion: Consider making the max backoff configurable, or document why 24s is the chosen limit. For mobile users with intermittent connections, this seems reasonable. 4. Signal Broadcasting Exception HandlingLocation: The broad Current implementation: Acceptable - logging with 5. Missing Rate LimitingLocation: The docstring mentions "Currently no rate limiting is implemented for ping/heartbeat messages" and suggests adding it if needed. Suggestion: For production, consider basic rate limiting on client → server messages to prevent abuse (e.g., max 10 pings per minute). Current implementation is fine for read-only consumers, but document that this should be monitored. 6. Type Annotation InconsistencyLocation: Uses Current status: Likely already running 3.10+, but worth verifying. 7. Frontend Connection State Not Used in UILocation: The Suggestion: Consider adding a small UI indicator (e.g., toast notification or status icon) when WebSocket is disconnected, so users know their notifications might be delayed. Not critical, but improves UX transparency. 🎯 Best Practices Adherence✅ CLAUDE.md Compliance:
✅ Security:
✅ Performance:
📋 Testing RecommendationsBackend✅ Excellent coverage - Tests cover:
Suggested additions (optional):
FrontendRecommended tests (based on CLAUDE.md patterns):
Example approach (based on CLAUDE.md): // frontend/tests/hooks/useNotificationWebSocket.test.ts
// Mount through test wrapper with mocked WebSocket🚀 Performance ImpactPositive impacts:
Potential concerns:
🎨 Code Style✅ Excellent:
Minor style note: Some long lines in documentation (e.g., 📊 Final Assessment
Overall: 9.4/10 - Excellent work, ready for merge with minor suggestions. ✅ RecommendationAPPROVE - This PR is production-ready. The implementation is solid, secure, and well-documented. The minor suggestions above are enhancements, not blockers. Before merging:
Migration notes (from CHANGELOG.md):
Excellent work on this feature! The zero-latency notification delivery will significantly improve UX, especially for badge celebrations. 🎉 |
The WebSocket was failing to connect because: 1. App loads and useBadgeNotifications() runs before auth completes 2. Initial connection attempt has no token → 403 error 3. Token change effect only reconnected if connectionState === "connected" 4. Since initial connection failed, state was never "connected" → ignored Fix: Reconnect when token becomes available regardless of current state.
Pull Request Review: Real-Time Notification System via WebSocketSummaryThis PR successfully migrates the notification system from Apollo Client polling (30s intervals) to a real-time WebSocket-based architecture. The implementation is high-quality, well-architected, and production-ready with excellent security considerations, comprehensive testing, and thorough documentation. Recommendation: ✅ Approve with minor suggestions Strengths1. Excellent Architecture & Design
2. Strong Security Implementation
3. Comprehensive TestingThe test suite (
Test coverage appears complete for this feature. 4. Production-Ready Error Handling
5. Excellent Documentation
Issues & Suggestions🟡 Medium Priority1. Potential Race Condition in Bulk Notification Broadcasting (
|
| Aspect | Rating | Notes |
|---|---|---|
| Architecture | ⭐⭐⭐⭐⭐ | Excellent separation, clean abstractions |
| Security | ⭐⭐⭐⭐⭐ | IDOR prevention, auth validation, user isolation |
| Testing | ⭐⭐⭐⭐⭐ | Comprehensive coverage, edge cases handled |
| Documentation | ⭐⭐⭐⭐⭐ | Thorough docs, clear comments |
| Error Handling | ⭐⭐⭐⭐☆ | Excellent resilience, minor UX gap |
| Performance | ⭐⭐⭐⭐⭐ | User-specific channels, efficient broadcasting |
| DRY Principle | ⭐⭐⭐⭐⭐ | No code duplication, good reuse |
| Type Safety | ⭐⭐⭐⭐⭐ | Excellent TypeScript types, Python type hints |
Adherence to CLAUDE.md Guidelines
✅ Tests: Comprehensive backend test suite
✅ TypeScript: Compiles cleanly (assumed from PR description)
✅ Security: Follows IDOR prevention patterns from docs/permissioning/
✅ Documentation: CHANGELOG.md updated with file locations and line numbers
✅ Signal Handlers: Properly imported (would be in apps.py)
✅ DRY: Excellent code reuse and abstraction
✅ Single Responsibility: Each module has clear purpose
Performance Considerations
Impact Analysis (from PR description):
- ✅ Zero latency for notifications (vs 0-30s polling)
- ✅ Reduced server load (eliminated ~60 requests/user/hour)
- ✅ User-specific channels prevent broadcast storms
- ✅ Exponential backoff prevents reconnection spam
Potential Concerns:
- WebSocket connection scaling: Each connected user maintains a persistent connection. Django Channels + Redis should handle this well, but monitor connection counts in production.
- Channel layer performance: Redis should handle the throughput fine, but consider monitoring
channel_layer.group_send()latency.
Recommendation: Add application metrics for:
- Active WebSocket connections count
- Channel layer message latency
- Reconnection rate
Final Recommendation
✅ APPROVE - This is excellent work that follows best practices and significantly improves UX. The suggestions above are minor enhancements and not blockers.
Before Merge (Optional):
- Address the bulk notification timestamp issue (Bump postgres from 14.5 to 15.0 in /compose/production/postgres #1) - verify current behavior is acceptable
- Consider adding UI feedback for connection failures (Bump actions/setup-node from 3.4.1 to 3.5.1 #3)
- Verify
NOTIFICATION_UPDATED/NOTIFICATION_DELETEDsignal handlers exist or remove from docs (Bump mypy from 0.971 to 0.982 #7)
Post-Merge (Optional):
- Monitor WebSocket connection counts and channel layer performance in production
- Consider adding rate limiting if ping spam becomes an issue
- Add UI indicator for notification delivery status
Great work on this feature! The migration from polling to WebSocket is well-executed with strong attention to security, testing, and documentation. This is production-ready code.
Reviewed by: Claude Code
Commit: Based on PR diff at review time
When clicking "View Your Badges" in the badge celebration modal,
the app navigated to /badges which returned a 404. Added a redirect
route from /badges to /profile, where the user's badges are displayed.
Follows existing pattern where /profile redirects to /users/{slug}.
PR Review: WebSocket-Based Real-Time NotificationsThis is an excellent implementation that successfully migrates from polling to WebSocket-based real-time notifications. The code quality is high, with comprehensive testing, proper security measures, and excellent documentation. Here's my detailed review: ✅ Strengths1. Architecture & Design
2. Security 🔒
3. Performance ⚡
4. Testing Coverage 🧪
5. Documentation 📚
🔍 Areas for Improvement1. Frontend Hook Dependencies (Minor)Location: The hook has two // Lines 405-415: eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (enabled) {
connect();
} else {
disconnect();
}
return () => { disconnect(); };
}, [enabled]); // eslint-disable-line react-hooks/exhaustive-depsSuggestion: Consider using const connectStable = useCallback(() => {
// Connection logic here
}, [enabled, token, autoReconnect, reconnectDelay, heartbeatInterval]);2. Error Handling in Broadcast Function (Minor)Location: The Suggestion: Consider logging different error types at different levels (WARNING for missing channel layer vs ERROR for unexpected exceptions). 3. Rate Limiting (Future Enhancement)Location: The consumer notes that no rate limiting is implemented for ping/heartbeat messages. While current implementation is read-only and low-risk, high-frequency pings could be used for abuse. Suggestion: For production at scale, consider adding simple rate limiting: # Track last ping time per connection
if hasattr(self, 'last_ping'):
if time.time() - self.last_ping < 1: # Max 1 ping per second
return
self.last_ping = time.time()4. WebSocket URL Construction (Minor)Location: The Suggestion: Extract common URL construction logic to a shared utility function (though this is a minor issue and can be addressed in a future refactor). 5. Bulk Notification Broadcasting (Edge Case)Location: When bulk creating thread participant notifications, the code broadcasts each notification individually in a loop. For threads with many participants (50+), this could create a burst of channel layer messages. Current implementation: created_notifications = Notification.objects.bulk_create(notifications_to_create)
for notification in created_notifications:
broadcast_notification_via_websocket(notification) # N broadcastsSuggestion: Consider batching channel layer sends or documenting the expected max participant count. This is only a concern for very active threads. 🛡️ Security Considerations✅ Properly Handled
|
The test for "blocks rendering until auth is fully initialized" used setTimeout to simulate async token fetch. This timer could fire after the test completed, polluting subsequent tests with the wrong token value. Using vi.useFakeTimers() and vi.advanceTimersByTimeAsync() ensures the timer is controlled and doesn't leak between tests.
Pull Request Review: Real-Time Notification System via WebSocketThis is an excellent, well-architected implementation that successfully migrates from polling to WebSocket-based notifications. The code demonstrates strong engineering practices with comprehensive testing, security considerations, and excellent documentation. ✅ StrengthsArchitecture & Design
Security
Performance
Test Coverage
Code Quality
🔍 Issues & RecommendationsCritical IssuesNone found - This is production-ready code. Medium Priority Suggestions1. Frontend Test Coverage GapWhile backend tests are comprehensive, there are no frontend tests for the new Recommendation: Add unit tests for:
Example test structure: // frontend/src/hooks/__tests__/useNotificationWebSocket.test.ts
describe('useNotificationWebSocket', () => {
it('should connect on mount when enabled', () => { /* ... */ });
it('should handle NOTIFICATION_CREATED messages', () => { /* ... */ });
it('should reconnect with exponential backoff', () => { /* ... */ });
it('should clear notifications on disconnect', () => { /* ... */ });
});2. Race Condition in Signal HandlersIn Current mitigation (line 65): You handle this with Recommendation: Consider explicitly setting # Before bulk_create
now = timezone.now()
for notification in notifications_to_create:
notification.created_at = nowThis ensures all notifications in a batch have the same timestamp and avoids fallback logic. 3. Heartbeat Interval ConsistencyThe frontend heartbeat interval is hardcoded to 30 seconds ( Recommendation: Consider adding a configurable stale connection timeout on the backend that's synchronized with the frontend heartbeat interval. This would allow the server to detect and close stale connections. # config/websocket/consumers/notification_updates.py
HEARTBEAT_TIMEOUT = 60 # 2x frontend heartbeat interval
async def check_heartbeat(self):
if time.time() - self.last_heartbeat > HEARTBEAT_TIMEOUT:
await self.close(code=4003) # Stale connectionLow Priority / Nice-to-Haves4. Connection State DebuggingThe Suggestion: Consider adding a visual indicator for WebSocket connection status in development mode (similar to how some apps show "Connected" / "Reconnecting" indicators). 5. Rate Limiting DocumentationThe consumer documentation mentions rate limiting may be needed in the future ( Suggestion: Add a commented-out example of how rate limiting could be implemented using Redis or a simple counter for future reference. 6. Notification Buffer SizeThe recent notifications buffer is hardcoded to 50 ( Minor suggestion: Consider making this configurable via hook options if different components need different buffer sizes. 🧪 Testing RecommendationsRun These Tests Before Merge# Backend tests (parallel for speed)
docker compose -f test.yml run django pytest opencontractserver/tests/test_notification_websocket.py -n 4 --dist loadscope -v
# Frontend component tests (use --reporter=list to prevent hanging per CLAUDE.md)
cd frontend && yarn test:ct --reporter=list -g "badge"
# Pre-commit hooks (code quality)
pre-commit run --all-filesManual Testing Checklist
📊 Performance ImpactBefore:
After:
Estimated load reduction: For 1000 concurrent users, this reduces from 2000 requests/min to near-zero polling requests, with only heartbeat overhead. 🎯 CLAUDE.md Compliance✅ Baseline Commit Rules: All rules followed 🚀 VerdictAPPROVED - This is excellent work that significantly improves the platform's UX and performance. The implementation is well-tested, secure, and follows all project conventions. Pre-Merge Checklist
The only recommendation I'd strongly encourage is adding frontend unit tests for the new hook, but this is not a blocker for merging. The backend coverage is comprehensive enough to ensure correctness. Great job! This PR closes #637 with a robust, production-ready solution. 🎉 |
…624) Extends the notification system (established in PR #727) to notify users when background jobs complete. New notification types: - DOCUMENT_PROCESSED: Document ingestion complete (notifies creator + corpus owners) - EXTRACT_COMPLETE: Data extraction job finished - ANALYSIS_COMPLETE: Analysis succeeded - ANALYSIS_FAILED: Analysis failed - EXPORT_COMPLETE: Corpus export ready for download Backend changes: - opencontractserver/notifications/models.py: Added 5 new notification types - opencontractserver/tasks/doc_tasks.py: Document processing notifications - opencontractserver/tasks/extract_orchestrator_tasks.py: Extract notifications - opencontractserver/analyzer/views.py: Analysis success/failure notifications - opencontractserver/tasks/export_tasks.py: Export completion notifications Frontend changes: - frontend/src/hooks/useNotificationWebSocket.ts: Added new types to union - frontend/src/components/notifications/NotificationItem.tsx: Display config Tests: - Added 5 new WebSocket broadcast tests for new notification types Closes #624
Overview
Replaces Apollo Client polling (30s intervals) with real-time WebSocket-based
notification delivery for ALL notification types (BADGE, REPLY, MENTION,
THREAD_REPLY, moderation actions). Provides instant notification delivery
with zero latency and dramatically reduces server load.
Backend Changes
WebSocket Consumer
config/websocket/consumers/notification_updates.pyNotificationUpdatesConsumer: Async WebSocket consumer for real-time notificationsnotification_user_{user_id}) prevent IDORSignal Broadcasting
opencontractserver/notifications/signals.pybroadcast_notification_via_websocket()helper functioncreate_reply_notification(REPLY notifications)create_mention_notification(MENTION notifications)create_badge_notification(BADGE notifications)create_moderation_notification(moderation actions)ASGI Routing
config/asgi.pyws/notification-updates/WebSocket endpointFrontend Changes
WebSocket Hook
frontend/src/hooks/useNotificationWebSocket.tsuseNotificationWebSocket: Complete WebSocket connection lifecycle managementuseNetworkStatusfor mobile screen unlock reconnectionBadge Notifications Migration
frontend/src/hooks/useBadgeNotifications.tsnewBadges,clearNewBadges)connectionStateexport for debugging/UI feedbackWebSocket URL Helper
frontend/src/components/chat/get_websockets.tsgetNotificationUpdatesWebSocket()functionTesting
Backend Tests
opencontractserver/tests/test_notification_websocket.pySecurity
Performance
Impact
Documentation
CHANGELOG.mdCloses #637