def _Check(self): """Gathers pre-mutation information: 1. List of requested episodes and photos to save. 2. Checkpoints list of episode and post ids that need to be (re)created. 3. Acquires locks for all source viewpoints. Validates the following: 1. Permissions to share from source episodes. """ # Create save episode dicts from episodes and viewpoints in the request. self._save_ep_dicts = yield self._CreateSaveEpisodeDicts() # Validate episodes and posts to save. source_ep_posts_list = yield ViewfinderOperation._CheckCopySources('save', self._client, self._user.user_id, self._save_ep_dicts) # Get dicts describing the target episodes and posts. target_ep_ids = [ep_dict['new_episode_id'] for ep_dict in self._save_ep_dicts] self._target_ep_dicts = ViewfinderOperation._CreateCopyTargetDicts(self._op.timestamp, self._user.user_id, self._user.private_vp_id, source_ep_posts_list, target_ep_ids) # Lock all source viewpoints. for source_vp_id in set(episode.viewpoint_id for episode, posts in source_ep_posts_list): yield self._lock_tracker.AcquireViewpointLock(source_vp_id) # Start populating the checkpoint if this the first time the operation has been run. if self._op.checkpoint is None: # Get subset of target episodes and posts that need to be saved. self._new_ids = yield ViewfinderOperation._CheckCopyTargets('save', self._client, self._user.user_id, self._user.private_vp_id, self._target_ep_dicts) # Set checkpoint. # List of new episode/post ids need to be check-pointed because they may change in the # UPDATE phase. If we fail after UPDATE, but before NOTIFY, we would not send correct # notifications on retry. checkpoint = {'new': list(self._new_ids)} yield self._op.SetCheckpoint(self._client, checkpoint) else: # Restore state from checkpoint. self._new_ids = set(self._op.checkpoint['new']) raise gen.Return(True)
def _Check(self): """Gathers pre-mutation information: 1. Existing viewpoint and owner follower. 2. Followers of the existing viewpoint. 3. List of requested episodes and photos to share. 4. Checkpoints list of episode and post ids that need to be (re)created. 5. Checkpoints list of followers that need to be revived. 6. Checkpoints boolean indicating whether cover photo needs to be set. Validates the following: 1. Permissions to share from source episodes. 2. Permission to share into existing viewpoint. """ self._viewpoint, self._follower = yield gen.Task( Viewpoint.QueryWithFollower, self._client, self._user_id, self._viewpoint_id) # Checks permission to share into viewpoint. if self._follower is None or not self._follower.CanContribute(): raise PermissionError( 'User %d does not have permission to contribute to viewpoint "%s".' % (self._user_id, self._viewpoint_id)) assert self._viewpoint is not None, self._viewpoint_id # Get all existing followers. self._followers, _ = yield gen.Task(Viewpoint.QueryFollowers, self._client, self._viewpoint_id, limit=Viewpoint.MAX_FOLLOWERS) # Validate source episodes and posts and save the list for possible later use. self._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, self._source_ep_posts_list, target_ep_ids) # Start populating the checkpoint if this the first time the operation has been run. if self._op.checkpoint is None: # Get subset of target episodes and posts that need to be shared. self._new_ids = yield self._CheckCopyTargets( 'share', self._client, self._user_id, self._viewpoint_id, self._new_ep_dicts) # Get list of followers which have removed themselves from the viewpoint and will need to be revived. self._revive_follower_ids = self._GetRevivableFollowers( self._followers) # Check whether a cover photo needs to be set on the viewpoint. self._need_cover_photo = not self._viewpoint.IsDefault( ) and not self._viewpoint.IsCoverPhotoSet() # Set checkpoint. # List of new episode/post ids and followers to revive need to be check-pointed because they may change # in the UPDATE phase. If we fail after UPDATE, but before NOTIFY, we would not send correct notifications # on retry. checkpoint = { 'new': list(self._new_ids), 'revive': self._revive_follower_ids, 'cover': self._need_cover_photo } yield self._op.SetCheckpoint(self._client, checkpoint) else: # Restore state from checkpoint. self._new_ids = set(self._op.checkpoint['new']) self._revive_follower_ids = self._op.checkpoint['revive'] self._need_cover_photo = self._op.checkpoint['cover'] raise gen.Return(True)
def _Check(self): """Gathers pre-mutation information: 1. Existing viewpoint and owner follower. 2. Followers of the existing viewpoint. 3. List of requested episodes and photos to share. 4. Checkpoints list of episode and post ids that need to be (re)created. 5. Checkpoints list of followers that need to be revived. 6. Checkpoints boolean indicating whether cover photo needs to be set. Validates the following: 1. Permissions to share from source episodes. 2. Permission to share into existing viewpoint. """ self._viewpoint, self._follower = yield gen.Task(Viewpoint.QueryWithFollower, self._client, self._user_id, self._viewpoint_id) # Checks permission to share into viewpoint. if self._follower is None or not self._follower.CanContribute(): raise PermissionError('User %d does not have permission to contribute to viewpoint "%s".' % (self._user_id, self._viewpoint_id)) assert self._viewpoint is not None, self._viewpoint_id # Get all existing followers. self._followers, _ = yield gen.Task(Viewpoint.QueryFollowers, self._client, self._viewpoint_id, limit=Viewpoint.MAX_FOLLOWERS) # Validate source episodes and posts and save the list for possible later use. self._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, self._source_ep_posts_list, target_ep_ids) # Start populating the checkpoint if this the first time the operation has been run. if self._op.checkpoint is None: # Get subset of target episodes and posts that need to be shared. self._new_ids = yield self._CheckCopyTargets('share', self._client, self._user_id, self._viewpoint_id, self._new_ep_dicts) # Get list of followers which have removed themselves from the viewpoint and will need to be revived. self._revive_follower_ids = self._GetRevivableFollowers(self._followers) # Check whether a cover photo needs to be set on the viewpoint. self._need_cover_photo = not self._viewpoint.IsDefault() and not self._viewpoint.IsCoverPhotoSet() # Set checkpoint. # List of new episode/post ids and followers to revive need to be check-pointed because they may change # in the UPDATE phase. If we fail after UPDATE, but before NOTIFY, we would not send correct notifications # on retry. checkpoint = {'new': list(self._new_ids), 'revive': self._revive_follower_ids, 'cover': self._need_cover_photo} yield self._op.SetCheckpoint(self._client, checkpoint) else: # Restore state from checkpoint. self._new_ids = set(self._op.checkpoint['new']) self._revive_follower_ids = self._op.checkpoint['revive'] self._need_cover_photo = self._op.checkpoint['cover'] raise gen.Return(True)
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)
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)