Exemplo n.º 1
0
def user_logged_out(shard, user_id):
    """Notifies other users that the given user has logged out of a shard."""
    def txn():
        login_record = models.LoginRecord.get_by_id(user_id)

        if not login_record:
            raise ndb.Rollback()

        if not login_record.online:
            raise ndb.Rollback()

        login_record.online = False
        login_record.put()
        return login_record

    login_record = ndb.transaction(txn)

    if not login_record:
        logging.warning('Tried to log out user_id=%r from shard=%r, '
                        'but LoginRecord did not exist', user_id, shard)
        return

    posts.insert_post(
        shard,
        archive_type=models.Post.USER_LOGOUT,
        nickname=login_record.nickname,
        user_id=user_id,
        body='%s has left' % login_record.nickname)

    invalidate_user_cache(shard)
    logging.debug('Logged out user_id=%r from shard=%r', user_id, shard)
Exemplo n.º 2
0
    def testReceiptExists(self):
        """Tests that post receipts prevent duplicate PostReferences."""
        shard = models.Shard(id='my-shard-name')
        shard.put()

        already_posted_key = posts.insert_post(
            shard.shard_id,
            post_id='my-id-1234',
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='Here is my message')

        models.Receipt(id=shard.shard_id, parent=already_posted_key).put()

        new_post_key = posts.insert_post(
            shard.shard_id,
            post_id='my-id-7890',
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='My second message')

        posts.apply_posts(shard.shard_id)

        ref_list = list(models.PostReference.query())
        self.assertEquals(1, len(ref_list))
        found_ref = ref_list[0]
        self.assertEquals(new_post_key.id(), found_ref.post_id)
Exemplo n.º 3
0
    def testReplicate(self):
        """Tests replicating a post to a topic shard."""
        shard = models.Shard(id='my-shard-name')
        shard.put()

        # This post was before the topic change and won't be replicated
        first_post = posts.insert_post(
            shard.shard_id,
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='This will not be replicated')
        posts.apply_posts(shard.shard_id)

        topic_shard_id, change_topic_post = topics.start_topic(
            shard.shard_id, 'my-user-id', 'my-post-id', 'my name',
            'topic title', 'topic description')

        after_shard = shard.key.get()
        self.assertEquals(None, after_shard.current_topic)

        posts.apply_posts(shard.shard_id)

        after_shard = shard.key.get()
        self.assertEquals(topic_shard_id, after_shard.current_topic)

        # The post that caused the topic change will be replicated
        shard_ref_list = list(models.PostReference.query(ancestor=shard.key))
        shard_post_ids = [r.post_id for r in shard_ref_list]
        self.assertEquals([first_post.id(), change_topic_post.id()],
                          shard_post_ids)

        topic_shard = models.Shard.get_by_id(topic_shard_id)
        self.assertEquals(
            None, models.PostReference.query(ancestor=topic_shard.key).get())

        posts.apply_posts(topic_shard_id)

        topic_ref_list = list(models.PostReference.query(
            ancestor=topic_shard.key))
        topic_post_ids = [r.post_id for r in topic_ref_list]
        self.assertEquals([change_topic_post.id()], topic_post_ids)

        # This post is after the topic change and will be replicated
        replicated_post = post_key = posts.insert_post(
            shard.shard_id,
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='Here is my message')

        posts.apply_posts(shard.shard_id)
        posts.apply_posts(topic_shard_id)

        topic_ref_list = list(models.PostReference.query(
            ancestor=topic_shard.key))
        topic_post_ids = [r.post_id for r in topic_ref_list]
        self.assertEquals([change_topic_post.id(), replicated_post.id()],
                          topic_post_ids)
Exemplo n.º 4
0
    def testMultiple(self):
        """Tests inserting multiple posts and applying them together."""
        shard = models.Shard(id='my-shard-name')
        shard.put()

        post_key_list = []
        for i in xrange(5):
            post_key_list.append(posts.insert_post(
                shard.shard_id,
                post_id='my-id-%d' % i,
                archive_type=models.Post.CHAT,
                nickname='My name',
                user_id='abc',
                body='Here is my message %d' % i))

        self.assertEquals(5, models.Post.query().count())
        self.assertEquals(None, models.PostReference.query().get())

        posts.apply_posts(shard.shard_id)
        ref_list = list(models.PostReference.query())
        ref_ids = [r.key.id() for r in ref_list]
        self.assertEquals([1, 2, 3, 4, 5], ref_ids)

        receipt_list = list(models.Receipt.query())
        receipt_parents = [r.key.parent() for r in receipt_list]
        self.assertEquals(post_key_list, receipt_parents)

        shard_after = shard.key.get()
        self.assertEquals(6, shard_after.sequence_number)
Exemplo n.º 5
0
    def testSingle(self):
        """Tests successfully inserting and applying a single Post."""
        shard = models.Shard(id='my-shard-name')
        shard.put()

        post_key = posts.insert_post(
            shard.shard_id,
            post_id='my-id-1234',
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='Here is my message')

        found_post = models.Post.query().get()
        self.assertEquals(post_key, found_post.key)

        self.assertEquals(None, models.PostReference.query().get())
        self.assertEquals(None, models.Receipt.query().get())

        posts.apply_posts(shard.shard_id)

        found_ref = models.PostReference.query().get()
        self.assertEquals(1, found_ref.key.id())
        self.assertEquals(found_post.post_id, found_ref.post_id)

        found_receipt = models.Receipt.query().get()
        self.assertEquals(post_key, found_receipt.key.parent())

        shard_after = shard.key.get()
        self.assertEquals(2, shard_after.sequence_number)
Exemplo n.º 6
0
    def make_post(self, post_id, message, shard_id=None):
        """Makes a test post."""
        if shard_id is None:
            shard_id = self.shard.shard_id

        post_key = posts.insert_post(
            shard_id,
            post_id=post_id,
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id=self.user_id,
            body=message)
        posts.apply_posts(shard_id)
        return post_key
Exemplo n.º 7
0
def start_topic(root_shard_id, user_id, post_id, nickname, title, description):
    """Starts a new topic under a root shard."""
    shard = models.Shard(
        id=models.human_uuid(),
        title=title,
        description=description,
        creation_nickname=nickname,
        root_shard=root_shard_id)
    shard.put()

    post_key = posts.insert_post(
        root_shard_id,
        post_id=post_id,
        archive_type=models.Post.TOPIC_START,
        nickname=nickname,
        user_id=user_id,
        title=title,
        body=description,
        new_topic=shard.shard_id)

    return shard.shard_id, post_key
Exemplo n.º 8
0
    def testChannelMessage(self):
        """Tests that post and apply both send message updates."""
        shard = models.Shard(id='my-shard-name')
        shard.put()

        channel_stub = self.testbed.get_stub(testbed.CHANNEL_SERVICE_NAME)
        user_id = 'my-user-id'
        presence.user_logged_in(shard.shard_id, user_id)
        _, browser_token = presence.change_presence(
            shard.shard_id, user_id, 'name here', True, True, False)
        channel_stub.connect_channel(browser_token)

        # This clears the presence Post from change_presence()
        posts.apply_posts(shard.shard_id)
        channel_stub.pop_first_message(browser_token)

        post_key = posts.insert_post(
            shard.shard_id,
            archive_type=models.Post.CHAT,
            nickname='My name',
            user_id='abc',
            body='Here is my message')

        message = channel_stub.pop_first_message(browser_token)
        found_posts = json.loads(message)['posts']
        post = post_key.get()
        expected_posts = posts.marshal_posts(shard.shard_id, [post])
        self.assertEquals(expected_posts, found_posts)
        self.assertEquals(None, expected_posts[0]['sequenceId'])

        posts.apply_posts(shard.shard_id)
        post = post_key.get()
        message = channel_stub.pop_first_message(browser_token)
        found_posts = json.loads(message)['posts']
        self.assertEquals(2, found_posts[0]['sequenceId'])
        post = post_key.get()
        post.sequence = 2  # Pretend to do what apply_posts does
        expected_posts = posts.marshal_posts(shard.shard_id, [post])
        self.assertEquals(expected_posts, found_posts)
Exemplo n.º 9
0
def change_presence(shard, user_id, nickname, accepted_terms,
                    sounds_enabled, retrying, email_address):
    """Changes the presence for a user."""
    def txn():
        last_nickname = None
        user_connected = True

        login = models.LoginRecord.get_by_id(user_id)
        if not login:
            login = models.LoginRecord(
                key=ndb.Key(models.LoginRecord._get_kind(), user_id),
                shard_id=shard)
        elif only_active_users(login):
            # This is a heartbeat presence check
            user_connected = False

        if maybe_update_token(login, force=retrying):
            logging.debug(
                'Issuing channel token: user_id=%r, shard=%r, force=%r',
                user_id, shard, retrying)

        if nickname:
            # This is a potential nickname change. Right now the client
            # always sends the nickname on every request, so we need to
            # check for the difference to detect a rename.
            last_nickname = login.nickname
            login.nickname = nickname

        if accepted_terms:
            # This is a ToS acceptance
            login.accepted_terms_version = config.terms_version

        login.online = True
        login.sounds_enabled = sounds_enabled
        login.email_address = email_address or None
        login.put()

        return last_nickname, user_connected, login.browser_token

    last_nickname, user_connected, browser_token = ndb.transaction(txn)

    # Invalidate the cache so the nickname will be updated next time
    # someone requests the roster.
    invalidate_user_cache(shard)

    message = None
    archive_type = None

    if nickname and last_nickname and last_nickname != nickname:
        message = '%s has changed their nickname to %s' % (
            last_nickname, nickname)
        archive_type = models.Post.USER_UPDATE
        logging.debug('User update user_id=%r, shard=%r', user_id, shard)
    elif user_connected:
        message = '%s has joined' % nickname
        archive_type = models.Post.USER_LOGIN
        logging.debug('User joined: user_id=%r, shard=%r', user_id, shard)
    else:
        logging.debug('User heartbeat: user_id=%r to shard=%r',
                      user_id, shard)

    if archive_type:
        posts.insert_post(
            shard,
            archive_type=archive_type,
            nickname=nickname,
            user_id=user_id,
            body=message)
    else:
        # As long as users are heart-beating, we should be running a
        # cleanup task for this shard.
        enqueue_cleanup_task(shard)

    return user_connected, browser_token