def _Update(self):
        """Updates the database:
       1. Revives any followers that have removed the viewpoint.
       2. Creates new episodes and posts.
       3. Creates cover photo, if needed.
    """
        # Revive any REMOVED followers.
        yield gen.Task(Follower.ReviveRemovedFollowers, self._client,
                       self._followers)

        # Create episode and posts that did not exist at the beginning of the operation.
        yield self._CreateNewEpisodesAndPosts(self._new_ep_dicts,
                                              self._new_ids)

        # Create cover photo if one is needed.
        if self._need_cover_photo:
            self._viewpoint.cover_photo = Viewpoint.SelectCoverPhotoFromEpDicts(
                self._new_ep_dicts)
            yield gen.Task(self._viewpoint.Update, self._client)
Beispiel #2
0
    def _Check(self):
        """Gathers pre-mutation information:
       1. Checkpoints list of contacts that need to be made prospective users.
       2. Cover photo, if not specified.

       Validates the following:
       1. Max follower limit.
       2. Permissions to share from source episodes.
       3. Permission to create new viewpoint.
       4. Cover photo is contained in the request.

    Returns True if all checks succeeded and operation execution should continue, or False
    if the operation should end immediately.
    """
        if len(
                self._contact_dicts
        ) + 1 > Viewpoint.MAX_FOLLOWERS:  # +1 to account for user creating viewpoint.
            raise LimitExceededError(
                'User %d attempted to exceed follower limit on viewpoint "%s" by creating a viewpoint with %d followers.'
                % (self._user_id, self._viewpoint_id,
                   len(self._contact_dicts) + 1))

        # Validate source episodes and posts.
        source_ep_posts_list = yield ViewfinderOperation._CheckCopySources(
            'share', self._client, self._user_id, self._ep_dicts)

        # Get dicts describing the target episodes and posts.
        target_ep_ids = [
            ep_dict['new_episode_id'] for ep_dict in self._ep_dicts
        ]
        self._new_ep_dicts = ViewfinderOperation._CreateCopyTargetDicts(
            self._op.timestamp, self._user_id, self._viewpoint_id,
            source_ep_posts_list, target_ep_ids)

        # Does request explicitly set a cover photo?
        if self._vp_dict.has_key('cover_photo'):
            if self._vp_dict['type'] == Viewpoint.DEFAULT:
                # cover_photo isn't supported creating default viewpoint.
                raise InvalidRequestError(
                    'cover_photo is invalid in share_new request for default viewpoint.'
                )
            # Make sure the designated cover photo is contained in the request.
            elif not Viewpoint.IsCoverPhotoContainedInEpDicts(
                    self._vp_dict['cover_photo']['episode_id'],
                    self._vp_dict['cover_photo']['photo_id'],
                    self._new_ep_dicts):
                logging.warning(
                    'cover_photo is specified but not contained in request: vp_dict: %s, ep_dicts: %s',
                    self._vp_dict, self._ep_dicts)
                raise InvalidRequestError(
                    'cover_photo is specified but not contained in request.')
        else:
            # Select cover photo from the set being shared.
            self._vp_dict[
                'cover_photo'] = Viewpoint.SelectCoverPhotoFromEpDicts(
                    self._new_ep_dicts)

        # Start populating the checkpoint if this the first time the operation has been run.
        if self._op.checkpoint is None:
            # If viewpoint already exists, then just warn and do nothing. We do not raise an error
            # because sometimes the client resubmits the same operation with different ids.
            viewpoint = yield gen.Task(Viewpoint.Query,
                                       self._client,
                                       self._viewpoint_id,
                                       None,
                                       must_exist=False)
            if viewpoint is not None:
                logging.warning('target viewpoint "%s" already exists',
                                self._viewpoint_id)
                raise gen.Return(False)

            # Get a tuple for each contact: (user_exists?, user_id, webapp_dev_id).
            self._contact_ids = yield self._ResolveContactIds(
                self._contact_dicts)

            # Set checkpoint.
            # List of contacts need to be check-pointed because it may change in the UPDATE phase (when contacts
            # can be bound to prospective users). If we fail after UPDATE, but before NOTIFY, we would not send
            # correct notifications on retry.
            checkpoint = {'contacts': self._contact_ids}
            yield self._op.SetCheckpoint(self._client, checkpoint)
        else:
            # Restore state from checkpoint.
            self._contact_ids = self._op.checkpoint['contacts']

        self._contact_user_ids = [
            user_id
            for user_exists, user_id, webapp_dev_id in self._contact_ids
        ]

        raise gen.Return(True)