def post(self, request, *args, **kwargs): """ Returns a token identifying the user in Centrifugo. """ current_timestamp = "%.0f" % time.time() user_id_str = u"{0}".format(request.user.id) token = generate_token(settings.CENTRIFUGE_SECRET, user_id_str, "{0}".format(current_timestamp), info="") # we get all the channels to which the user can subscribe participant = Participant.objects.get(id=request.user.id) # we use the threads as channels ids channels = [] for thread in Thread.managers.get_threads_where_participant_is_active(participant_id=participant.id): channels.append( build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id, thread.participants.all()) ) # we also have a channel to alert us about new threads threads_channel = build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, request.user.id, [request.user.id]) # he is the only one to have access to the channel channels.append(threads_channel) # we return the information to_return = { 'user': user_id_str, 'timestamp': current_timestamp, 'token': token, 'connection_url': "{0}connection/".format(settings.CENTRIFUGE_ADDRESS), 'channels': channels, 'debug': settings.DEBUG, } return HttpResponse(json.dumps(to_return), content_type='application/json; charset=utf-8')
def publish_message_to_centrifugo(sender, instance, created, **kwargs): """ Publishes each saved message to Centrifugo. """ if created is True: client = Client( "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET")) # we ensure the client is still in the thread (he may have left or have been removed) active_participants = [ participation.participant.id for participation in Participation.objects.filter( thread=instance.thread, date_left__isnull=True).select_related( 'participant') ] client.publish( build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.thread.id, active_participants), { "id": instance.id, "body": instance.body, "sender": instance.sender.id, "thread": instance.thread.id, "sent_at": str(instance.sent_at), "is_notification": True, # ATTENTION: check against sender too to be sure to not notify him his message })
def test_token(self): # an unauthenticated client will get no token response = self.client_unauthenticated.post(reverse('rest_messaging_centrifugo:authentication'), data={}) self.assertEqual(302, response.status_code) # an authenticated client will get a token response = self.client_authenticated.post(reverse('rest_messaging_centrifugo:authentication'), data={}) self.assertEqual(200, response.status_code) content = json.loads(compat_json_resp_content(response.content)) self.assertEqual(int(content["user"]), self.user.id) self.assertTrue(content["timestamp"]) self.assertTrue(content["token"]) channels = [build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id, thread.participants.all()) for thread in [self.thread1, self.thread2]] +\ [build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, self.user.id, [self.user.id])] self.assertEqual(set(content["channels"]), set(channels)) # get method does not work response = self.client_authenticated.get(reverse('rest_messaging_centrifugo:authentication')) self.assertEqual(405, response.status_code)
def post(self, request, *args, **kwargs): """ Returns a token identifying the user in Centrifugo. """ current_timestamp = "%.0f" % time.time() user_id_str = u"{0}".format(request.user.id) token = generate_token(settings.CENTRIFUGE_SECRET, user_id_str, "{0}".format(current_timestamp), info="") # we get all the channels to which the user can subscribe participant = Participant.objects.get(id=request.user.id) # we use the threads as channels ids channels = [] for thread in Thread.managers.get_threads_where_participant_is_active( participant_id=participant.id): channels.append( build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, thread.id, thread.participants.all())) # we also have a channel to alert us about new threads threads_channel = build_channel( settings.CENTRIFUGO_THREAD_NAMESPACE, request.user.id, [request.user.id ]) # he is the only one to have access to the channel channels.append(threads_channel) # we return the information to_return = { 'user': user_id_str, 'timestamp': current_timestamp, 'token': token, 'connection_url': "{0}connection/".format(settings.CENTRIFUGE_ADDRESS), 'channels': channels, 'debug': settings.DEBUG, } return HttpResponse(json.dumps(to_return), content_type='application/json; charset=utf-8')
def publish_participation_to_thread(sender, instance, created, **kwargs): """ Warns users everytime a thread including them is published. This is done via channel subscription. """ if kwargs.get("created_and_add_participants") is True: request_participant_id = kwargs.get("request_participant_id") if request_participant_id is not None: client = Client( "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET") ) active_participants = [ participation.participant for participation in Participation.objects.filter( thread=instance, date_left__isnull=True ).select_related("participant") ] for participant in active_participants: client.publish( build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, participant.id, [participant.id]), { "message_channel_to_connect_to": build_channel( settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.id, [p.id for p in active_participants] ) }, )
def publish_participation_to_thread(sender, instance, created, **kwargs): """ Warns users everytime a thread including them is published. This is done via channel subscription. """ if kwargs.get('created_and_add_participants') is True: request_participant_id = kwargs.get('request_participant_id') if request_participant_id is not None: client = Client( "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET")) active_participants = [ participation.participant for participation in Participation.objects.filter( thread=instance, date_left__isnull=True).select_related( 'participant') ] for participant in active_participants: client.publish( build_channel(settings.CENTRIFUGO_THREAD_NAMESPACE, participant.id, [participant.id]), { "message_channel_to_connect_to": build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.id, [p.id for p in active_participants]) })
def publish_message_to_centrifugo(sender, instance, created, **kwargs): """ Publishes each saved message to Centrifugo. """ if created is True: client = Client( "{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET") ) # we ensure the client is still in the thread (he may have left or have been removed) active_participants = [ participation.participant.id for participation in Participation.objects.filter( thread=instance.thread, date_left__isnull=True ).select_related("participant") ] client.publish( build_channel(settings.CENTRIFUGO_MESSAGE_NAMESPACE, instance.thread.id, active_participants), { "id": instance.id, "body": instance.body, "sender": instance.sender.id, "thread": instance.thread.id, "sent_at": str(instance.sent_at), "is_notification": True, # ATTENTION: check against sender too to be sure to not notify him his message }, )
def test_build_channel(self): namespace = "message" name = 1 ids = [1, 2, 3] built = build_channel(namespace=namespace, name=name, user_ids=ids) self.assertEqual(built, "message:1#1,2,3".format(namespace, name, ids))
def test_integration(self): # we hit whatever view just to set the cookie self.selenium.get(self.live_server_url + reverse('dummy')) self.selenium.add_cookie(self.cookie) self.selenium.refresh() # we create threads which call signals telling centrifugo to connect self.thread1 = Thread.managers.get_or_create_thread(self.request, "The #1 Thread", self.participant1.id, self.participant2.id) self.thread2 = Thread.managers.get_or_create_thread(self.request, "The #2 Thread", self.participant1.id, self.participant3.id) # the following conversation does not include the current user, we do not want it on the screen! self.thread_unrelated = Thread.objects.create(name="The unrelated Thread") # the conversation does not involve the current user, we do not want it on the screen! self.participationU1 = Participation.objects.create(participant=self.participant2, thread=self.thread_unrelated) self.participationU2 = Participation.objects.create(participant=self.participant3, thread=self.thread_unrelated) # we load the index page which contains the logic (in javascript) self.selenium.get(self.live_server_url + reverse('index')) # we wait a little bit time.sleep(4) # we create a message # this will trigger a publishing signal in django-rest-messaging-centrifugo body11 = "hi #11" body12 = "hi #12" body21 = "hi #21" body22 = "hi #22" bodyU1 = "We do not want to see this! #1" bodyU2 = "We do not want to see this! #2" m11 = Message.objects.create(sender=self.participant1, thread=self.thread1, body=body11) m12 = Message.objects.create(sender=self.participant2, thread=self.thread1, body=body12) m21 = Message.objects.create(sender=self.participant3, thread=self.thread2, body=body21) m22 = Message.objects.create(sender=self.participant1, thread=self.thread2, body=body22) mU1 = Message.objects.create(sender=self.participant2, thread=self.thread_unrelated, body=bodyU1) mU2 = Message.objects.create(sender=self.participant3, thread=self.thread_unrelated, body=bodyU2) # the channels are private # this means that Centrifugo will check the users ids to know if the user may connect # if we query a private channel the user does not belong to, we never see the message client = Client("{0}api/".format(getattr(settings, "CENTRIFUGE_ADDRESS")), getattr(settings, "CENTRIFUGE_SECRET")) forbidden_message = "Message forbidden" client.publish( build_channel(namespace=settings.CENTRIFUGO_MESSAGE_NAMESPACE, name=self.thread_unrelated.id, user_ids=[p.id for p in self.thread_unrelated.participants.all()]), forbidden_message ) # we wait a little bit time.sleep(4) # now the messages should be displayed m11 = self.selenium.find_element_by_id('message__{0}'.format(m11.id)) m12 = self.selenium.find_element_by_id('message__{0}'.format(m12.id)) m21 = self.selenium.find_element_by_id('message__{0}'.format(m21.id)) m22 = self.selenium.find_element_by_id('message__{0}'.format(m22.id)) self.assertTrue(body11 in m11.text) self.assertTrue(body12 in m12.text) self.assertTrue(body21 in m21.text) self.assertTrue(body22 in m22.text) # the following ensures we get the new threads created during the connection self.thread4 = Thread.managers.get_or_create_thread(self.request, "The #4 Thread", self.participant4.id, self.participant1.id) time.sleep(4) message_channel_to_connect_to = 'messages:4#4,1'.format(self.thread4.id, self.thread4.participants.all()[0].id, self.thread4.participants.all()[1].id) thread_messages = self.selenium.find_element_by_id('thread__{0}'.format(message_channel_to_connect_to)) self.assertTrue(message_channel_to_connect_to in thread_messages.text) # we should not find the unrelated messages self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU1.id)) self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU2.id)) self.assertRaises(Exception, self.selenium.find_element_by_id, 'message__{0}'.format(mU2.id)) self.assertEqual([], self.selenium.find_elements_by_xpath("//*[contains(text(), '{0}')]".format(forbidden_message)))
def test_build_channel(self): namespace = "message" name = 1 ids = [1, 2, 3] built = build_channel(namespace=namespace, name=name, user_ids=ids) self.assertEqual(built, "message:1#1,2,3".format(namespace, name, ids))