/// <summary> /// Update following relationship. /// Following user : someone who i am following. /// </summary> /// <param name="storageConsistencyMode">Consistency mode</param> /// <param name="relationshipHandle">Relationship handle</param> /// <param name="userHandle">User handle</param> /// <param name="relationshipUserHandle">Relationship user handle</param> /// <param name="appHandle">App handle</param> /// <param name="userRelationshipStatus">User relationship status</param> /// <param name="lastUpdatedTime">Last updated time</param> /// <param name="readUserRelationshipLookupEntity">Read user relationship lookup entity</param> /// <returns>Update following relationship task</returns> public async Task UpdateFollowingRelationship( StorageConsistencyMode storageConsistencyMode, string relationshipHandle, string userHandle, string relationshipUserHandle, string appHandle, UserRelationshipStatus userRelationshipStatus, DateTime lastUpdatedTime, IUserRelationshipLookupEntity readUserRelationshipLookupEntity) { CTStore store = await this.tableStoreManager.GetStore(ContainerIdentifier.UserFollowing); ObjectTable lookupTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingLookup) as ObjectTable; FeedTable feedTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingFeed) as FeedTable; CountTable countTable = this.tableStoreManager.GetTable(ContainerIdentifier.UserFollowing, TableIdentifier.FollowingCount) as CountTable; await this.UpdateUserRelationship( storageConsistencyMode, relationshipHandle, userHandle, relationshipUserHandle, appHandle, userRelationshipStatus, lastUpdatedTime, readUserRelationshipLookupEntity, store, lookupTable, feedTable, countTable); }
/// <summary> /// Process message /// </summary> /// <param name="message">Queue message</param> /// <returns>Process message task</returns> protected override async Task Process(IMessage message) { if (message is RelationshipMessage) { RelationshipMessage relationshipMessage = message as RelationshipMessage; ProcessType processType = ProcessType.Backend; if (relationshipMessage.DequeueCount > 1) { processType = ProcessType.BackendRetry; } IUserRelationshipLookupEntity followerRelationshipLookupEntity = await this.relationshipsManager.ReadFollowerRelationship( relationshipMessage.FollowerKeyUserHandle, relationshipMessage.FollowingKeyUserHandle, relationshipMessage.AppHandle); if (followerRelationshipLookupEntity != null && followerRelationshipLookupEntity.LastUpdatedTime > relationshipMessage.LastUpdatedTime) { return; } IUserRelationshipLookupEntity followingRelationshipLookupEntity = await this.relationshipsManager.ReadFollowingRelationshipToUser( relationshipMessage.FollowingKeyUserHandle, relationshipMessage.FollowerKeyUserHandle, relationshipMessage.AppHandle); if (followingRelationshipLookupEntity != null && followingRelationshipLookupEntity.LastUpdatedTime > relationshipMessage.LastUpdatedTime) { return; } await this.relationshipsManager.UpdateRelationshipToUser( processType, relationshipMessage.RelationshipOperation, relationshipMessage.RelationshipHandle, relationshipMessage.FollowerKeyUserHandle, relationshipMessage.FollowingKeyUserHandle, relationshipMessage.AppHandle, relationshipMessage.LastUpdatedTime, followerRelationshipLookupEntity, followingRelationshipLookupEntity); } }
/// <summary> /// Update user relationship /// </summary> /// <param name="storageConsistencyMode">Consistency mode</param> /// <param name="relationshipHandle">Relationship handle</param> /// <param name="userHandle">User handle</param> /// <param name="relationshipUserHandle">Relationship user handle</param> /// <param name="appHandle">App handle</param> /// <param name="userRelationshipStatus">Relationship status</param> /// <param name="lastUpdatedTime">Last updated time</param> /// <param name="readUserRelationshipLookupEntity">Read user relationship lookup entity</param> /// <param name="store">store object</param> /// <param name="lookupTable">lookup table</param> /// <param name="feedTable">feed table</param> /// <param name="countTable">count table</param> /// <returns>Update follower relationship task</returns> private async Task UpdateUserRelationship( StorageConsistencyMode storageConsistencyMode, string relationshipHandle, string userHandle, string relationshipUserHandle, string appHandle, UserRelationshipStatus userRelationshipStatus, DateTime lastUpdatedTime, IUserRelationshipLookupEntity readUserRelationshipLookupEntity, CTStore store, ObjectTable lookupTable, FeedTable feedTable, CountTable countTable) { string objectKey = this.GetObjectKey(appHandle, relationshipUserHandle); string feedKey = this.GetFeedKey(appHandle, userRelationshipStatus); string countKey = this.GetCountKey(appHandle, userRelationshipStatus); Transaction transaction = new Transaction(); UserRelationshipFeedEntity relationshipFeedEntity = new UserRelationshipFeedEntity() { RelationshipHandle = relationshipHandle, UserHandle = relationshipUserHandle }; if (readUserRelationshipLookupEntity == null) { UserRelationshipLookupEntity newRelationshipLookupEntity = new UserRelationshipLookupEntity() { RelationshipHandle = relationshipHandle, LastUpdatedTime = lastUpdatedTime, UserRelationshipStatus = userRelationshipStatus, }; transaction.Add(Operation.Insert(lookupTable, userHandle, objectKey, newRelationshipLookupEntity)); if (userRelationshipStatus != UserRelationshipStatus.None) { transaction.Add(Operation.Insert(feedTable, userHandle, feedKey, relationshipHandle, relationshipFeedEntity)); transaction.Add(Operation.InsertOrIncrement(countTable, userHandle, countKey)); } } else { UserRelationshipStatus oldUserRelationshipStatus = readUserRelationshipLookupEntity.UserRelationshipStatus; string oldRelationshipHandle = readUserRelationshipLookupEntity.RelationshipHandle; readUserRelationshipLookupEntity.RelationshipHandle = relationshipHandle; readUserRelationshipLookupEntity.UserRelationshipStatus = userRelationshipStatus; readUserRelationshipLookupEntity.LastUpdatedTime = lastUpdatedTime; string oldFeedKey = this.GetFeedKey(appHandle, oldUserRelationshipStatus); string oldCountKey = this.GetCountKey(appHandle, oldUserRelationshipStatus); transaction.Add(Operation.Replace(lookupTable, userHandle, objectKey, readUserRelationshipLookupEntity as UserRelationshipLookupEntity)); if (userRelationshipStatus == oldUserRelationshipStatus) { if (userRelationshipStatus != UserRelationshipStatus.None && relationshipHandle != oldRelationshipHandle) { transaction.Add(Operation.Delete(feedTable, userHandle, oldFeedKey, oldRelationshipHandle)); transaction.Add(Operation.Insert(feedTable, userHandle, feedKey, relationshipHandle, relationshipFeedEntity)); } } else { if (userRelationshipStatus != UserRelationshipStatus.None) { transaction.Add(Operation.Insert(feedTable, userHandle, feedKey, relationshipHandle, relationshipFeedEntity)); transaction.Add(Operation.InsertOrIncrement(countTable, userHandle, countKey)); } if (oldUserRelationshipStatus != UserRelationshipStatus.None) { transaction.Add(Operation.Delete(feedTable, userHandle, oldFeedKey, oldRelationshipHandle)); transaction.Add(Operation.Increment(countTable, userHandle, oldCountKey, -1.0)); } } } await store.ExecuteTransactionAsync(transaction, storageConsistencyMode.ToConsistencyMode()); }
/// <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()); }