Example #1
0
    def test_publish_notification(self):
        """
        Go through and set up a notification and publish it
        """

        msg = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'foo': 'bar'
            }
        )

        # make sure it asserts that user_id is an integer
        with self.assertRaises(ContractNotRespected):
            publish_notification_to_user('bad-id', msg)

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # make sure type checking is happening
        with self.assertRaises(ContractNotRespected):
            get_notifications_count_for_user('bad-type')

        # now query back the notification to make sure it got stored
        # and we can retrieve it

        self.assertEqual(
            get_notifications_count_for_user(self.test_user_id),
            1
        )

        # make sure it asserts that user_id is an integer
        with self.assertRaises(ContractNotRespected):
            get_notifications_for_user('bad-id')

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        self.assertEqual(read_user_msg.msg, sent_user_msg.msg)
Example #2
0
    def test_multipayload(self):
        """
        Test that a channel will use the right payload
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        msg.add_payload({'one': 'two'}, channel_name='durable')

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # now query back the notification to make sure it got stored
        # and we can retrieve it
        self.assertEquals(get_notifications_count_for_user(self.test_user_id),
                          1)

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        # make sure the message that got persisted contains only
        # the default payload
        self.assertEqual(read_user_msg.msg.payload,
                         msg.get_payload(channel_name='durable'))
Example #3
0
    def test_bulk_publish_list_exclude(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list, and also pass in an exclusion list to
        make sure the people in the exclude list does not get
        the notification
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        user_ids = list(
            range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1))
        exclude_user_ids = list(
            range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1, 2))

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(user_ids,
                                           msg,
                                           exclude_user_ids=exclude_user_ids)

        # now read them all back
        for user_id in range(
                1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications),
                             1 if user_id not in exclude_user_ids else 0)
            if user_id not in exclude_user_ids:
                self.assertTrue(isinstance(notifications[0], UserNotification))
Example #4
0
    def test_bulk_publish_list(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list
        """

        msg = NotificationMessage(
            namespace='test-runner',
            msg_type=self.msg_type,
            payload={
                'foo': 'bar'
            }
        )

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(
            list(range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1)),
            msg
        )

        # now read them all back
        for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #5
0
    def test_publish_to_scope(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        register_user_scope_resolver("list_scope", TestListScopeResolver())

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        bulk_publish_notification_to_scope(
            scope_name="list_scope",
            # the TestListScopeResolver expects a "range" property in the context
            scope_context={"range": 5},
            msg=msg,
        )

        for user_id in range(4):
            # have to fudge this a bit as the contract on user_id
            # says > 0 only allowed
            user_id = user_id + 1
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #6
0
    def test_bulk_publish_orm_query(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        # set up some test users in Django User's model
        User(username="******").save()
        User(username="******").save()
        User(username="******").save()

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        resultset = User.objects.values_list("id", flat=True).all()

        num_sent = bulk_publish_notification_to_users(resultset, msg)

        # make sure we sent 3
        self.assertEqual(num_sent, 3)

        # now read them back
        for user in User.objects.all():
            notifications = get_notifications_for_user(user.id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #7
0
    def test_bulk_publish_generator(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a generator function
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        def _user_id_generator():
            """
            Just spit our an generator that goes from 1 to 100
            """
            for user_id in range(1, 100):
                yield user_id

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(_user_id_generator(), msg)

        # now read them all back
        for user_id in range(1, 100):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #8
0
    def test_bulk_publish_orm_query(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        # set up some test users in Django User's model
        User(username='******').save()
        User(username='******').save()
        User(username='******').save()

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        resultset = User.objects.values_list('id', flat=True).all()  # pylint: disable=no-member

        num_sent = bulk_publish_notification_to_users(resultset, msg)

        # make sure we sent 3
        self.assertEqual(num_sent, 3)

        # now read them back
        for user in User.objects.all():  # pylint: disable=no-member
            notifications = get_notifications_for_user(user.id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #9
0
    def test_publish_to_scope(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a resultset from a Django ORM query
        """

        register_user_scope_resolver("list_scope", TestListScopeResolver())

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        bulk_publish_notification_to_scope(
            scope_name="list_scope",
            # the TestListScopeResolver expects a "range" property in the context
            scope_context={"range": 5},
            msg=msg)

        for user_id in range(4):
            # have to fudge this a bit as the contract on user_id
            # says > 0 only allowed
            user_id = user_id + 1
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #10
0
    def test_multipayload(self):
        """
        Test that a channel will use the right payload
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        msg.add_payload({"one": "two"}, channel_name="durable")

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # now query back the notification to make sure it got stored
        # and we can retrieve it
        self.assertEquals(get_notifications_count_for_user(self.test_user_id), 1)

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        # make sure the message that got persisted contains only
        # the default payload
        self.assertEqual(read_user_msg.msg.payload, msg.get_payload(channel_name="durable"))
Example #11
0
    def test_bulk_publish_generator(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a generator function
        """

        msg = NotificationMessage(namespace='test-runner',
                                  msg_type=self.msg_type,
                                  payload={'foo': 'bar'})

        def _user_id_generator():
            """
            Just spit our an generator that goes from 1 to 100
            """
            for user_id in range(1, 100):
                yield user_id

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(_user_id_generator(), msg)

        # now read them all back
        for user_id in range(1, 100):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #12
0
    def test_publish_notification(self):
        """
        Go through and set up a notification and publish it
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        # make sure it asserts that user_id is an integer
        with self.assertRaises(ContractNotRespected):
            publish_notification_to_user("bad-id", msg)

        # now do happy path
        sent_user_msg = publish_notification_to_user(self.test_user_id, msg)

        # make sure type checking is happening
        with self.assertRaises(ContractNotRespected):
            get_notifications_count_for_user("bad-type")

        # now query back the notification to make sure it got stored
        # and we can retrieve it

        self.assertEquals(get_notifications_count_for_user(self.test_user_id), 1)

        # make sure it asserts that user_id is an integer
        with self.assertRaises(ContractNotRespected):
            get_notifications_for_user("bad-id")

        notifications = get_notifications_for_user(self.test_user_id)

        self.assertTrue(isinstance(notifications, list))
        self.assertEqual(len(notifications), 1)
        self.assertTrue(isinstance(notifications[0], UserNotification))

        read_user_msg = notifications[0]
        self.assertEqual(read_user_msg.user_id, self.test_user_id)
        self.assertIsNone(read_user_msg.read_at)  # should be unread

        self.assertEqual(read_user_msg, sent_user_msg)
        self.assertEqual(read_user_msg.msg, sent_user_msg.msg)
Example #13
0
    def get(self, request):
        """
        HTTP GET Handler
        """

        try:
            filters, options = _get_filter_and_options(request)
        except ValueError:
            return Response({}, status.HTTP_400_BAD_REQUEST)

        user_msgs = get_notifications_for_user(
            int(request.user.id),
            filters=filters,
            options=options,
        )

        resultset = [user_msg.get_fields() for user_msg in user_msgs]

        return Response(resultset, status.HTTP_200_OK)
Example #14
0
    def get(self, request):
        """
        HTTP GET Handler
        """

        try:
            filters, options = _get_filter_and_options(request)
        except ValueError:
            return Response({}, status.HTTP_400_BAD_REQUEST)

        user_msgs = get_notifications_for_user(
            int(request.user.id),
            filters=filters,
            options=options,
        )

        resultset = [user_msg.get_fields() for user_msg in user_msgs]

        return Response(resultset, status.HTTP_200_OK)
Example #15
0
    def test_bulk_publish_list(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(
            [user_id for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1)], msg
        )

        # now read them all back
        for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1)
            self.assertTrue(isinstance(notifications[0], UserNotification))
Example #16
0
    def test_bulk_publish_list_exclude(self):
        """
        Make sure we can bulk publish to a number of users
        passing in a list, and also pass in an exclusion list to
        make sure the people in the exclude list does not get
        the notification
        """

        msg = NotificationMessage(namespace="test-runner", msg_type=self.msg_type, payload={"foo": "bar"})

        user_ids = [user_id for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1)]
        exclude_user_ids = [user_id for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1, 2)]

        # now send to more than our internal chunking size
        bulk_publish_notification_to_users(user_ids, msg, exclude_user_ids=exclude_user_ids)

        # now read them all back
        for user_id in range(1, const.NOTIFICATION_BULK_PUBLISH_CHUNK_SIZE * 2 + 1):
            notifications = get_notifications_for_user(user_id)

            self.assertTrue(isinstance(notifications, list))
            self.assertEqual(len(notifications), 1 if user_id not in exclude_user_ids else 0)
            if user_id not in exclude_user_ids:
                self.assertTrue(isinstance(notifications[0], UserNotification))
Example #17
0
def _send_user_digest(namespace_info, from_timestamp, to_timestamp, user_id,
                      email, first_name, last_name, subject, from_email, unread_only=True):
    """
    This will send a digest of unread notifications to a given user. Note, it is assumed here
    that the user's preference has already been checked. namespace_info will contain
    a 'display_name' entry which will be a human readable string that can be put
    in the digest email
    """

    # query all unread notifications for this user since the timestamp
    notifications = get_notifications_for_user(
        user_id,
        filters={
            'read': not unread_only,
            'unread': True,
            'start_date': from_timestamp,
            'end_timestamp': to_timestamp,
            'namespace': namespace_info['namespace'],
        },
        options={
            'select_related': True,  # make sure we do JOINs on the initial query
        }
    )

    notification_groups = render_notifications_by_type(notifications)

    # As an option, don't send an email at all if there are no
    # unread notifications
    if not notification_groups and const.NOTIFICATION_DONT_SEND_EMPTY_DIGEST:
        log.debug('Digest email for {email} is empty. Not sending...'.format(email=email))
        return 0

    context = {
        'namespace_display_name': namespace_info['display_name'],
        'grouped_user_notifications': notification_groups
    }

    # render the notifications html template
    notifications_html = render_to_string("django/digests/unread_notifications_inner.html", context)

    # create the image dictionary to store the
    # img_path, unique id and title for the image.
    branded_logo = dict(title='Logo', path=const.NOTIFICATION_BRANDED_DEFAULT_LOGO, cid=str(uuid.uuid4()))

    context = {
        'branded_logo': branded_logo['cid'],
        'user_first_name': first_name,
        'user_last_name': last_name,
        'namespace': namespace_info['display_name'],
        'count': len(notifications),
        'rendered_notifications': notifications_html
    }
    # render the mail digest template.
    email_body = with_inline_css(
        render_to_string("django/digests/branded_notifications_outer.html", context)
    )

    html_part = MIMEMultipart(_subtype='related')
    html_part.attach(MIMEText(email_body, _subtype='html'))
    logo_image = attach_image(branded_logo, 'Header Logo')
    if logo_image:
        html_part.attach(logo_image)

    log.info('Sending Notification Digest email to {email}'.format(email=email))

    # do a formatting pass on the email subject in case we need
    # to also present the namespace display_name
    subject = subject.format(display_name=namespace_info['display_name'])

    msg = EmailMessage(subject, None, from_email, [email])
    msg.attach(html_part)
    msg.send()

    return 1
def _send_user_digest(namespace_info, from_timestamp, to_timestamp, user_id,
                      email, first_name, last_name, subject, from_email, unread_only=True):
    """
    This will send a digest of unread notifications to a given user. Note, it is assumed here
    that the user's preference has already been checked. namespace_info will contain
    a 'display_name' entry which will be a human readable string that can be put
    in the digest email
    """

    # query all unread notifications for this user since the timestamp
    notifications = get_notifications_for_user(
        user_id,
        filters={
            'read': not unread_only,
            'unread': True,
            'start_date': from_timestamp,
            'end_timestamp': to_timestamp,
            'namespace': namespace_info['namespace'],
        },
        options={
            'select_related': True,  # make sure we do JOINs on the initial query
        }
    )

    notification_groups = render_notifications_by_type(notifications)

    # As an option, don't send an email at all if there are no
    # unread notifications
    if not notification_groups and const.NOTIFICATION_DONT_SEND_EMPTY_DIGEST:
        log.debug('Digest email for {email} is empty. Not sending...'.format(email=email))
        return 0

    context = {
        'namespace_display_name': namespace_info['display_name'],
        'grouped_user_notifications': notification_groups
    }

    # render the notifications html template
    notifications_html = render_to_string("django/digests/unread_notifications_inner.html", context)

    # create the image dictionary to store the
    # img_path, unique id and title for the image.
    branded_logo = dict(title='Logo', path=const.NOTIFICATION_BRANDED_DEFAULT_LOGO, cid=str(uuid.uuid4()))

    context = {
        'branded_logo': branded_logo['cid'],
        'user_first_name': first_name,
        'user_last_name': last_name,
        'namespace': namespace_info['display_name'],
        'count': len(notifications),
        'rendered_notifications': notifications_html
    }
    # render the mail digest template.
    email_body = with_inline_css(
        render_to_string("django/digests/branded_notifications_outer.html", context)
    )

    html_part = MIMEMultipart(_subtype='related')
    html_part.attach(MIMEText(email_body, _subtype='html'))
    logo_image = attach_image(branded_logo, 'Header Logo')
    if logo_image:
        html_part.attach(logo_image)

    log.info('Sending Notification Digest email to {email}'.format(email=email))

    # do a formatting pass on the email subject in case we need
    # to also present the namespace display_name
    subject = subject.format(display_name=namespace_info['display_name'])

    msg = EmailMessage(subject, None, from_email, [email])
    msg.attach(html_part)
    msg.send()

    return 1