Skip to content
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

added functionality for sending notificaiton through websockets #180

Open
wants to merge 2 commits into
base: django-channels
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions mumblebackend/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
import notification.routing

from channels.security.websocket import AllowedHostsOriginValidator
from .channelsmiddleware import JwtAuthMiddlewareStack
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mumblebackend.settings")

application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(notification.routing.websocket_urlpatterns)
"websocket": AllowedHostsOriginValidator(JwtAuthMiddlewareStack(
URLRouter(notification.routing.websocket_urlpatterns))
),
}
)
69 changes: 69 additions & 0 deletions mumblebackend/channelsmiddleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""General web socket middlewares
written by github.com/nasir733
"""

from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
from rest_framework_simplejwt.tokens import UntypedToken
from rest_framework_simplejwt.authentication import JWTTokenUserAuthentication
from rest_framework_simplejwt.state import User
from channels.middleware import BaseMiddleware
from channels.auth import AuthMiddlewareStack
from django.db import close_old_connections
from urllib.parse import parse_qs
from jwt import decode as jwt_decode
from django.conf import settings


@database_sync_to_async
def get_user(validated_token):
try:
user = get_user_model().objects.get(id=validated_token["user_id"])
# return get_user_model().objects.get(id=toke_id)
print(f"{user}")
return user

except User.DoesNotExist:
return AnonymousUser()


class JwtAuthMiddleware(BaseMiddleware):
def __init__(self, inner):
self.inner = inner

async def __call__(self, scope, receive, send):
# Close old database connections to prevent usage of timed out connections
close_old_connections()

# Get the token
token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]

# Try to authenticate the user
try:
# This will automatically validate the token and raise an error if token is invalid
UntypedToken(token)
except (InvalidToken, TokenError) as e:
# Token is invalid
print(e)
return None
else:
# Then token is valid, decode it
decoded_data = jwt_decode(token, settings.SECRET_KEY, algorithms=["HS256"])
print(decoded_data)
# Will return a dictionary like -
# {
# "token_type": "access",
# "exp": 1568770772,
# "jti": "5c15e80d65b04c20ad34d77b6703251b",
# "user_id": 6
# }

# Get the user using ID
scope["user"] = await get_user(validated_token=decoded_data)
return await super().__call__(scope, receive, send)


def JwtAuthMiddlewareStack(inner):
return JwtAuthMiddleware(AuthMiddlewareStack(inner))
1 change: 1 addition & 0 deletions mumblebackend/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
'rest_framework',
'corsheaders',
'ckeditor',
'channels',
]

REST_FRAMEWORK = {
Expand Down
73 changes: 61 additions & 12 deletions notification/consumers.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,67 @@
import asyncio
import json
from channels.generic.websocket import WebsocketConsumer
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from channels.db import database_sync_to_async
from notification.exceptions import ClientError
from .serializers import NotificationSerializer
import json
from django.db.models.signals import post_save
from .models import Notification
from django.dispatch import receiver

class NotificationConsumer(WebsocketConsumer):
class NotificationConsumer(AsyncJsonWebsocketConsumer):
""" Notification Consumer """
def connect(self):
self.accept()
async def connect(self):
print("new connection request ")
await self.accept()
while True:
await asyncio.sleep(3)
payload = await get_latest_notifications(self, self.scope["user"])
if payload == None:
pass
else:
await self.get_notifications(payload)

async def disconnect(self, close_code):
print(close_code)

async def receive_json(self, content):
command = content.get("command", None)
print('command recived----',command)
try:
if command == "get_notifications":
print("get_notificaitons ran")
print(self.scope["user"])
payload = await get_latest_notifications(self,self.scope["user"])
if payload == None:
pass
else:
await self.get_notifications(payload)
except ClientError as e:
pass
@receiver(post_save,sender=Notification)
async def get_continuous_notifications(instance,**kwargs):
"""Called by the postSave Signal """
print(instance)
print(**kwargs)
# payload = await get_latest_notifications(self, self.scope["user"])
# if payload == None:
# pass
# else:
# await self.get_notifications(payload)

async def get_notifications(self,notifications,**kwargs):
await self.send(json.dumps({
"notificaitons":notifications
}))


# post_save.connect(get_continuous_notifications(self),sender=Notification)
@database_sync_to_async
def get_latest_notifications(self,user):
notifications = user.notifications.order_by('-created')
serialized_data = NotificationSerializer(notifications,many=True)
return serialized_data.data

def disconnect(self, close_code):
pass

def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']

self.send(text_data=json.dumps({
'message': message
}))
10 changes: 10 additions & 0 deletions notification/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class ClientError(Exception):
"""
Custom exception class that is caught by the websocket receive()
handler and translated into a send back to the client.
"""
def __init__(self, code, message):
super().__init__(code)
self.code = code
if message:
self.message = message
4 changes: 2 additions & 2 deletions notification/routing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.urls import re_path
from django.urls import re_path,path

from . import consumers

websocket_urlpatterns = [
re_path(r'ws/notifications/$', consumers.NotificationConsumer.as_asgi()),
path('', consumers.NotificationConsumer.as_asgi()),
]