Esempio n. 1
0
        /// <summary>
        /// Update a relationship between a user and a topic
        /// </summary>
        /// <param name="processType">Process type</param>
        /// <param name="relationshipOperation">Relationship operation</param>
        /// <param name="relationshipHandle">Relationship handle</param>
        /// <param name="followerUserHandle">Follower user handle</param>
        /// <param name="followingTopicHandle">Following topic handle</param>
        /// <param name="appHandle">App handle</param>
        /// <param name="lastUpdatedTime">Last updated time</param>
        /// <param name="followerRelationshipLookupEntity">Follower relationship lookup entity</param>
        /// <param name="followingRelationshipLookupEntity">Following relationship lookup entity</param>
        /// <returns>Update relationship task</returns>
        public async Task UpdateRelationshipToTopic(
            ProcessType processType,
            RelationshipOperation relationshipOperation,
            string relationshipHandle,
            string followerUserHandle,
            string followingTopicHandle,
            string appHandle,
            DateTime lastUpdatedTime,
            ITopicRelationshipLookupEntity followerRelationshipLookupEntity,
            ITopicRelationshipLookupEntity followingRelationshipLookupEntity)
        {
            TopicRelationshipStatus topicRelationshipStatus = this.GetTopicRelationshipStatus(relationshipOperation);

            if (processType == ProcessType.Frontend)
            {
                await this.topicRelationshipsStore.UpdateTopicFollowerRelationship(
                    StorageConsistencyMode.Strong,
                    relationshipHandle,
                    followingTopicHandle,
                    followerUserHandle,
                    appHandle,
                    topicRelationshipStatus,
                    lastUpdatedTime,
                    followerRelationshipLookupEntity);

                await this.topicRelationshipsStore.UpdateTopicFollowingRelationship(
                    StorageConsistencyMode.Strong,
                    relationshipHandle,
                    followerUserHandle,
                    followingTopicHandle,
                    appHandle,
                    topicRelationshipStatus,
                    lastUpdatedTime,
                    followingRelationshipLookupEntity);

                // fanout an activity indicating that the followerUser is now following the followingTopicHandle
                await this.fanoutActivitiesQueue.SendFanoutActivityMessage(
                    followerUserHandle,
                    appHandle,
                    relationshipHandle,
                    ActivityType.Following,
                    followerUserHandle,
                    null,
                    ContentType.Topic,
                    followingTopicHandle,
                    lastUpdatedTime);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Get topic relationship status
        /// </summary>
        /// <param name="relationshipOperation">Relationship operation</param>
        /// <returns>Topic relationship status</returns>
        private TopicRelationshipStatus GetTopicRelationshipStatus(RelationshipOperation relationshipOperation)
        {
            TopicRelationshipStatus topicRelationshipStatus = TopicRelationshipStatus.None;

            if (relationshipOperation == RelationshipOperation.UnfollowTopic)
            {
                topicRelationshipStatus = TopicRelationshipStatus.None;
            }
            else if (relationshipOperation == RelationshipOperation.FollowTopic)
            {
                topicRelationshipStatus = TopicRelationshipStatus.Follow;
            }
            else
            {
                this.log.LogError("Invalid relationship operation on topic");
            }

            return(topicRelationshipStatus);
        }
Esempio n. 3
0
        /// <summary>
        /// Get target relationship status
        /// </summary>
        /// <param name="relationshipOperation">Relationship operation</param>
        /// <returns>Relationship status</returns>
        private UserRelationshipStatus GetRelationshipStatus(RelationshipOperation relationshipOperation)
        {
            UserRelationshipStatus userRelationshipStatus = UserRelationshipStatus.None;

            switch (relationshipOperation)
            {
            case RelationshipOperation.BlockUser:
                userRelationshipStatus = UserRelationshipStatus.Blocked;
                break;

            case RelationshipOperation.UnblockUser:
                userRelationshipStatus = UserRelationshipStatus.None;
                break;

            case RelationshipOperation.AcceptUser:
                userRelationshipStatus = UserRelationshipStatus.Follow;
                break;

            case RelationshipOperation.RejectUser:
                userRelationshipStatus = UserRelationshipStatus.None;
                break;

            case RelationshipOperation.FollowUser:
                userRelationshipStatus = UserRelationshipStatus.Follow;
                break;

            case RelationshipOperation.PendingUser:
                userRelationshipStatus = UserRelationshipStatus.Pending;
                break;

            case RelationshipOperation.UnfollowUser:
                userRelationshipStatus = UserRelationshipStatus.None;
                break;

            case RelationshipOperation.DeleteFollower:
                userRelationshipStatus = UserRelationshipStatus.None;
                break;
            }

            return(userRelationshipStatus);
        }
Esempio n. 4
0
        /// <summary>
        /// Send relationship message
        /// </summary>
        /// <param name="relationshipOperation">User relationship operation</param>
        /// <param name="relationshipHandle">Relationship handle</param>
        /// <param name="followerKeyUserHandle">Follower key user handle</param>
        /// <param name="followingKeyUserHandle">Following key user handle</param>
        /// <param name="appHandle">App handle</param>
        /// <param name="lastUpdatedTime">Last updated time</param>
        /// <returns>Send message task</returns>
        public async Task SendRelationshipMessage(
            RelationshipOperation relationshipOperation,
            string relationshipHandle,
            string followerKeyUserHandle,
            string followingKeyUserHandle,
            string appHandle,
            DateTime lastUpdatedTime)
        {
            RelationshipMessage message = new RelationshipMessage()
            {
                RelationshipOperation  = relationshipOperation,
                RelationshipHandle     = relationshipHandle,
                FollowerKeyUserHandle  = followerKeyUserHandle,
                FollowingKeyUserHandle = followingKeyUserHandle,
                AppHandle       = appHandle,
                LastUpdatedTime = lastUpdatedTime
            };

            Queue queue = await this.QueueManager.GetQueue(this.QueueIdentifier);

            await queue.SendAsync(message);
        }
        /// <summary> 
        /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
        /// </summary>
        /// <param name="relationships">collection of relationships to visit</param> 
        /// <param name="visit">function to call with each relationship in the list</param>
        /// <param name="context">context object - may be null</param>
        private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit, Object context)
        { 
            // make a local copy that will not be invalidated by any activity of the visitor function
            List<PackageRelationship> relationshipsToVisit = new List<PackageRelationship>(relationships); 
 
            // now invoke the delegate for each member
            for (int i = 0; i < relationshipsToVisit.Count; i++) 
            {
                // exit if visitor wants us to
                if (!visit(relationshipsToVisit[i], context))
                    break; 
            }
        } 
 /// <summary>
 /// Visit relationships without disturbing the PackageRelationshipCollection iterator 
 /// </summary> 
 /// <param name="relationships">collection of relationships to visit</param>
 /// <param name="visit">function to call with each relationship in the list</param> 
 private void SafeVisitRelationships(PackageRelationshipCollection relationships, RelationshipOperation visit)
 {
     SafeVisitRelationships(relationships, visit, null);
 } 
Esempio n. 7
0
        /// <summary>
        /// Update a relationship between two users
        /// </summary>
        /// <param name="processType">Process type</param>
        /// <param name="relationshipOperation">User relationship operation</param>
        /// <param name="relationshipHandle">Relationship handle</param>
        /// <param name="followerKeyUserHandle">Follower key user handle</param>
        /// <param name="followingKeyUserHandle">Following key user handle</param>
        /// <param name="appHandle">App handle</param>
        /// <param name="lastUpdatedTime">Last updated time</param>
        /// <param name="followerRelationshipLookupEntity">Follower relationship lookup entity</param>
        /// <param name="followingRelationshipLookupEntity">Following relationship lookup entity</param>
        /// <returns>Update relationship task</returns>
        public async Task UpdateRelationshipToUser(
            ProcessType processType,
            RelationshipOperation relationshipOperation,
            string relationshipHandle,
            string followerKeyUserHandle,
            string followingKeyUserHandle,
            string appHandle,
            DateTime lastUpdatedTime,
            IUserRelationshipLookupEntity followerRelationshipLookupEntity,
            IUserRelationshipLookupEntity followingRelationshipLookupEntity)
        {
            UserRelationshipStatus userRelationshipStatus = this.GetRelationshipStatus(relationshipOperation);

            if (processType == ProcessType.Frontend)
            {
                await this.userRelationshipsStore.UpdateFollowerRelationship(
                    StorageConsistencyMode.Strong,
                    relationshipHandle,
                    followerKeyUserHandle,
                    followingKeyUserHandle,
                    appHandle,
                    userRelationshipStatus,
                    lastUpdatedTime,
                    followerRelationshipLookupEntity);

                await this.userRelationshipsStore.UpdateFollowingRelationship(
                    StorageConsistencyMode.Strong,
                    relationshipHandle,
                    followingKeyUserHandle,
                    followerKeyUserHandle,
                    appHandle,
                    userRelationshipStatus,
                    lastUpdatedTime,
                    followingRelationshipLookupEntity);

                await this.relationshipsQueue.SendRelationshipMessage(
                    relationshipOperation,
                    relationshipHandle,
                    followerKeyUserHandle,
                    followingKeyUserHandle,
                    appHandle,
                    lastUpdatedTime);
            }
            else if (processType == ProcessType.Backend || processType == ProcessType.BackendRetry)
            {
                if (relationshipOperation == RelationshipOperation.FollowUser)
                {
                    await this.notificationsManager.CreateNotification(
                        processType,
                        followerKeyUserHandle,
                        appHandle,
                        relationshipHandle,
                        ActivityType.Following,
                        followingKeyUserHandle,
                        followerKeyUserHandle,
                        ContentType.Unknown,
                        null,
                        lastUpdatedTime);

                    await this.fanoutActivitiesQueue.SendFanoutActivityMessage(
                        followingKeyUserHandle,
                        appHandle,
                        relationshipHandle,
                        ActivityType.Following,
                        followingKeyUserHandle,
                        followerKeyUserHandle,
                        ContentType.Unknown,
                        null,
                        lastUpdatedTime);

                    await this.followingImportsQueue.SendFollowingImportMessage(
                        followingKeyUserHandle,
                        appHandle,
                        followerKeyUserHandle);
                }
                else if (relationshipOperation == RelationshipOperation.PendingUser)
                {
                    await this.notificationsManager.CreateNotification(
                        processType,
                        followerKeyUserHandle,
                        appHandle,
                        relationshipHandle,
                        ActivityType.FollowRequest,
                        followingKeyUserHandle,
                        followerKeyUserHandle,
                        ContentType.Unknown,
                        null,
                        lastUpdatedTime);
                }
                else if (relationshipOperation == RelationshipOperation.AcceptUser)
                {
                    await this.notificationsManager.CreateNotification(
                        processType,
                        followingKeyUserHandle,
                        appHandle,
                        relationshipHandle,
                        ActivityType.FollowAccept,
                        followerKeyUserHandle,
                        followingKeyUserHandle,
                        ContentType.Unknown,
                        null,
                        lastUpdatedTime);

                    await this.followingImportsQueue.SendFollowingImportMessage(
                        followingKeyUserHandle,
                        appHandle,
                        followerKeyUserHandle);
                }

                // Update popular user feed when follower count changes
                if (relationshipOperation == RelationshipOperation.FollowUser ||
                    relationshipOperation == RelationshipOperation.UnfollowUser ||
                    relationshipOperation == RelationshipOperation.DeleteFollower ||
                    relationshipOperation == RelationshipOperation.BlockUser ||
                    relationshipOperation == RelationshipOperation.AcceptUser)
                {
                    long?followersCount = await this.userRelationshipsStore.QueryFollowersCount(followerKeyUserHandle, appHandle);

                    long followersCountValue = followersCount.HasValue ? followersCount.Value : 0;
                    if (followersCountValue % PopularUsersUpdateFollowersCount == 0)
                    {
                        await this.popularUsersManager.UpdatePopularUser(processType, followerKeyUserHandle, appHandle, followersCountValue);
                    }
                }
            }
        }
        /// <summary>
        /// Update a relationship with another user
        /// </summary>
        /// <param name="callerClassName">name of the controller class of the caller</param>
        /// <param name="callerMethodName">name of method insider controller class of the caller (should correspond to an HTTP action)</param>
        /// <param name="relationshipOperation">Relationship operation</param>
        /// <param name="actedOnUserHandle">Acted on user handle</param>
        /// <returns>No content on success</returns>
        protected async Task <IHttpActionResult> UpdateRelationshipToUser(
            string callerClassName,
            string callerMethodName,
            RelationshipOperation relationshipOperation,
            string actedOnUserHandle)
        {
            string   actorUserHandle = this.UserHandle;
            DateTime currentTime     = DateTime.UtcNow;

            if (actorUserHandle == actedOnUserHandle)
            {
                this.BadRequest(ResponseStrings.NotAllowed);
            }

            IUserProfileEntity userProfileEntity = await this.usersManager.ReadUserProfile(actedOnUserHandle, this.AppHandle);

            if (userProfileEntity == null)
            {
                this.NotFound(ResponseStrings.UserNotFound);
            }

            if (relationshipOperation == RelationshipOperation.FollowUser &&
                userProfileEntity.Visibility == UserVisibilityStatus.Private)
            {
                relationshipOperation = RelationshipOperation.PendingUser;
            }

            string followerKeyUserHandle  = actorUserHandle;
            string followingKeyUserHandle = actedOnUserHandle;

            if (relationshipOperation == RelationshipOperation.FollowUser ||
                relationshipOperation == RelationshipOperation.PendingUser ||
                relationshipOperation == RelationshipOperation.UnfollowUser)
            {
                followerKeyUserHandle  = actedOnUserHandle;
                followingKeyUserHandle = actorUserHandle;
            }

            IUserRelationshipLookupEntity followerRelationshipLookupEntity
                = await this.relationshipsManager.ReadFollowerRelationship(followerKeyUserHandle, followingKeyUserHandle, this.AppHandle);

            if (followerRelationshipLookupEntity != null && followerRelationshipLookupEntity.LastUpdatedTime > currentTime)
            {
                return(this.Conflict(ResponseStrings.NewerItemExists));
            }

            IUserRelationshipLookupEntity followingRelationshipLookupEntity
                = await this.relationshipsManager.ReadFollowingRelationshipToUser(followingKeyUserHandle, followerKeyUserHandle, this.AppHandle);

            if (followingRelationshipLookupEntity != null && followingRelationshipLookupEntity.LastUpdatedTime > currentTime)
            {
                return(this.Conflict(ResponseStrings.NewerItemExists));
            }

            if (relationshipOperation == RelationshipOperation.AcceptUser)
            {
                if (followerRelationshipLookupEntity == null ||
                    followerRelationshipLookupEntity.UserRelationshipStatus != UserRelationshipStatus.Pending)
                {
                    return(this.Forbidden(ResponseStrings.NotAllowed));
                }
            }

            string relationshipHandle = null;

            if (relationshipOperation == RelationshipOperation.AcceptUser ||
                relationshipOperation == RelationshipOperation.BlockUser ||
                relationshipOperation == RelationshipOperation.FollowUser ||
                relationshipOperation == RelationshipOperation.PendingUser)
            {
                relationshipHandle = this.handleGenerator.GenerateShortHandle();
            }

            await this.relationshipsManager.UpdateRelationshipToUser(
                ProcessType.Frontend,
                relationshipOperation,
                relationshipHandle,
                followerKeyUserHandle,
                followingKeyUserHandle,
                this.AppHandle,
                currentTime,
                followerRelationshipLookupEntity,
                followingRelationshipLookupEntity);

            string logEntry = $"ActedOnUserHandle = {actedOnUserHandle}, RelationshipOperation = {relationshipOperation.ToString()}, RelationshipHandle = {relationshipHandle}";

            logEntry += $", OldRelationshipStatus = {followingRelationshipLookupEntity?.UserRelationshipStatus.ToString()}, NewRelationshipStatus = {relationshipOperation.ToString()}";
            this.LogControllerEnd(this.log, callerClassName, callerMethodName, logEntry);
            return(this.NoContent());
        }
        /// <summary>
        /// Update a relationship with a topic
        /// </summary>
        /// <param name="callerClassName">name of the controller class of the caller</param>
        /// <param name="callerMethodName">name of method insider controller class of the caller (should correspond to an HTTP action)</param>
        /// <param name="relationshipOperation">Relationship operation</param>
        /// <param name="actedOnTopicHandle">Acted on topic handle</param>
        /// <returns>No content on success</returns>
        protected async Task <IHttpActionResult> UpdateRelationshipToTopic(
            string callerClassName,
            string callerMethodName,
            RelationshipOperation relationshipOperation,
            string actedOnTopicHandle)
        {
            string   actorUserHandle = this.UserHandle;
            DateTime currentTime     = DateTime.UtcNow;

            if (relationshipOperation != RelationshipOperation.FollowTopic && relationshipOperation != RelationshipOperation.UnfollowTopic)
            {
                // the caller should never specify a operation other than FollowTopic or UnfollowTopic
                return(this.InternalServerError());
            }

            // get the topic being followed
            TopicView topicView = await this.viewsManager.GetTopicView(actedOnTopicHandle, actorUserHandle);

            if (topicView == null)
            {
                // This could mean one of three situations:
                // (1) the topic has been banned and hence is no longer accessible to anyone,
                // (2) the topic has been deleted,
                // (3) the topic is from a private user that the actor user is not following; this should
                //     not happen because the actor user should not be able to get a topicHandle for a topic
                //     posted by a private user that they are not following
                return(this.NotFound(ResponseStrings.TopicNotFound));
            }

            // lookup the existing relationships
            ITopicRelationshipLookupEntity followerRelationshipLookupEntity
                = await this.relationshipsManager.ReadTopicFollowerRelationship(actedOnTopicHandle, actorUserHandle, this.AppHandle);

            if (followerRelationshipLookupEntity != null && followerRelationshipLookupEntity.LastUpdatedTime > currentTime)
            {
                // this relationship has been updated more recently than this request
                return(this.Conflict(ResponseStrings.NewerItemExists));
            }

            ITopicRelationshipLookupEntity followingRelationshipLookupEntity =
                await this.relationshipsManager.ReadFollowingRelationshipToTopic(actorUserHandle, actedOnTopicHandle, this.AppHandle);

            if (followingRelationshipLookupEntity != null && followingRelationshipLookupEntity.LastUpdatedTime > currentTime)
            {
                // this relationship has been updated more recently than this request
                return(this.Conflict(ResponseStrings.NewerItemExists));
            }

            // if following a topic
            string relationshipHandle = null;

            if (relationshipOperation == RelationshipOperation.FollowTopic)
            {
                // create a relationship handle
                relationshipHandle = this.handleGenerator.GenerateShortHandle();

                // insert the topic into the user's following topic feed
                await this.topicsManager.CreateFollowingTopic(actorUserHandle, this.AppHandle, actedOnTopicHandle);
            }

            if (relationshipOperation == RelationshipOperation.UnfollowTopic)
            {
                try
                {
                    // remove the topic from the user's following topic feed
                    await this.topicsManager.DeleteFollowingTopic(actorUserHandle, this.AppHandle, actedOnTopicHandle);
                }
                catch (NotFoundException)
                {
                    return(this.NotFound(ResponseStrings.NotFollowingTopic));
                }
            }

            // submit the request to the relationship manager
            await this.relationshipsManager.UpdateRelationshipToTopic(
                ProcessType.Frontend,
                relationshipOperation,
                relationshipHandle,
                actorUserHandle,
                actedOnTopicHandle,
                this.AppHandle,
                currentTime,
                followerRelationshipLookupEntity,
                followingRelationshipLookupEntity);

            string logEntry = $"TopicHandle = {topicView?.TopicHandle}, RelationshipHandle = {relationshipHandle}, RelationshipOperation = {relationshipOperation.ToString()}";

            this.LogControllerEnd(this.log, callerClassName, callerMethodName, logEntry);

            return(this.NoContent());
        }