/// <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());
        }