From 0a225434c8d398fd6e90abca7e5e58d9987acc58 Mon Sep 17 00:00:00 2001 From: oneRain Date: Tue, 12 Jan 2021 18:11:08 +0800 Subject: [PATCH 1/6] feat: friendship --- Storage/Storage/LCFollowersAndFollowees.cs | 21 +++++ Storage/Storage/LCFriendship.cs | 47 ++++++++++ Storage/Storage/LCFriendshipRequest.cs | 8 ++ Storage/Storage/LCUser.cs | 99 ++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 Storage/Storage/LCFollowersAndFollowees.cs create mode 100644 Storage/Storage/LCFriendship.cs create mode 100644 Storage/Storage/LCFriendshipRequest.cs diff --git a/Storage/Storage/LCFollowersAndFollowees.cs b/Storage/Storage/LCFollowersAndFollowees.cs new file mode 100644 index 0000000..288b1b5 --- /dev/null +++ b/Storage/Storage/LCFollowersAndFollowees.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace LeanCloud.Storage { + public class LCFollowersAndFollowees { + public List Followers { + get; internal set; + } + + public List Followees { + get; internal set; + } + + public int FollowersCount { + get; internal set; + } + + public int FolloweeCount { + get; internal set; + } + } +} diff --git a/Storage/Storage/LCFriendship.cs b/Storage/Storage/LCFriendship.cs new file mode 100644 index 0000000..40c0d13 --- /dev/null +++ b/Storage/Storage/LCFriendship.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage { + public static class LCFriendship { + public static async Task Request(string userId, Dictionary attributes = null) { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + string path = "users/friendshipRequests"; + LCObject friend = LCObject.CreateWithoutData("_User", userId); + Dictionary data = new Dictionary { + { "user", LCEncoder.EncodeLCObject(user) }, + { "friend", LCEncoder.EncodeLCObject(friend) }, + }; + if (attributes != null) { + data["friendship"] = attributes; + } + await LCApplication.HttpClient.Post>(path, data: data); + } + + public static async Task AcceptRequest(LCFriendshipRequest request, Dictionary attributes = null) { + if (request == null) { + throw new ArgumentNullException(nameof(request)); + } + string path = $"users/friendshipRequests/{request.ObjectId}/accept"; + Dictionary data = null; + if (attributes != null) { + data = new Dictionary { + { "friendship", attributes } + }; + } + await LCApplication.HttpClient.Put>(path, data: data); + } + + public static async Task DeclineRequest(LCFriendshipRequest request) { + if (request == null) { + throw new ArgumentNullException(nameof(request)); + } + string path = $"users/friendshipRequests/{request.ObjectId}/decline"; + await LCApplication.HttpClient.Put>(path); + } + } +} diff --git a/Storage/Storage/LCFriendshipRequest.cs b/Storage/Storage/LCFriendshipRequest.cs new file mode 100644 index 0000000..6306a62 --- /dev/null +++ b/Storage/Storage/LCFriendshipRequest.cs @@ -0,0 +1,8 @@ +namespace LeanCloud.Storage { + public class LCFriendshipRequest : LCObject { + public const string CLASS_NAME = "_FriendshipRequest"; + + public LCFriendshipRequest() : base(CLASS_NAME) { + } + } +} diff --git a/Storage/Storage/LCUser.cs b/Storage/Storage/LCUser.cs index af1aa03..46ad157 100644 --- a/Storage/Storage/LCUser.cs +++ b/Storage/Storage/LCUser.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using System.Collections; using System.Collections.Generic; using LeanCloud.Storage.Internal.Object; @@ -589,5 +590,103 @@ namespace LeanCloud.Storage { }; await LCApplication.HttpClient.Post>(path, data: data); } + + /// + /// Follows a user. + /// + /// + /// + /// + public async Task Follow(string targetId, Dictionary attrs = null) { + if (string.IsNullOrEmpty(targetId)) { + throw new ArgumentNullException(nameof(targetId)); + } + string path = $"users/self/friendship/{targetId}"; + await LCApplication.HttpClient.Post>(path, data: attrs); + } + + /// + /// Unfollows a user. + /// + /// + /// + public async Task Unfollow(string targetId) { + if (string.IsNullOrEmpty(targetId)) { + throw new ArgumentNullException(nameof(targetId)); + } + string path = $"users/self/friendship/{targetId}"; + await LCApplication.HttpClient.Delete(path); + } + + /// + /// Constructs a follower query. + /// + /// + public LCQuery FollowerQuery() { + return new LCQuery("_Follower") + .WhereEqualTo("user", this) + .Include("follower"); + } + + /// + /// Constructs a followee query. + /// + /// + public LCQuery FolloweeQuery() { + return new LCQuery("_Followee") + .WhereEqualTo("user", this) + .Include("followee"); + } + + public async Task GetFollowersAndFollowees(bool includeFollower = false, + bool includeFollowee = false, bool returnCount = false) { + Dictionary queryParams = new Dictionary(); + if (returnCount) { + queryParams["count"] = 1; + } + if (includeFollower || includeFollowee) { + List includes = new List(); + if (includeFollower) { + includes.Add("follower"); + } + if (includeFollowee) { + includes.Add("followee"); + } + queryParams["include"] = string.Join(",", includes); + } + string path = $"users/{ObjectId}/followersAndFollowees"; + Dictionary response = await LCApplication.HttpClient.Get>(path, + queryParams: queryParams); + LCFollowersAndFollowees result = new LCFollowersAndFollowees(); + if (response.TryGetValue("followers", out object followersObj) && + (followersObj is List followers)) { + result.Followers = new List(); + foreach (object followerObj in followers) { + LCObjectData objectData = LCObjectData.Decode(followerObj as IDictionary); + LCObject follower = new LCObject("_Follower"); + follower.Merge(objectData); + result.Followers.Add(follower); + } + } + if (response.TryGetValue("followees", out object followeesObj) && + (followeesObj is List followees)) { + result.Followees = new List(); + foreach (object followeeObj in followees) { + LCObjectData objectData = LCObjectData.Decode(followeeObj as IDictionary); + LCObject followee = new LCObject("_Followee"); + followee.Merge(objectData); + result.Followees.Add(followee); + } + } + if (response.TryGetValue("followers_count", out object followersCountObj) && + (followersCountObj is int followersCount)) { + result.FollowersCount = followersCount; + } + if (response.TryGetValue("followees_count", out object followeesCountObj) && + (followeesCountObj is int followeesCount)) { + result.FolloweeCount = followeesCount; + } + return result; + } } } From cf87916b8c7a0f71936aca33207779090d3ba29e Mon Sep 17 00:00:00 2001 From: oneRain Date: Wed, 13 Jan 2021 16:30:30 +0800 Subject: [PATCH 2/6] feat: friend test --- Storage/Storage.Test/FriendTest.cs | 103 +++++++++++++++++++++ Storage/Storage.Test/StatusTest.cs | 98 ++++++++++++++++++++ Storage/Storage.Test/UserTest.cs | 30 ++++++ Storage/Storage/LCApplication.cs | 1 + Storage/Storage/LCFollowersAndFollowees.cs | 2 +- Storage/Storage/LCUser.cs | 2 +- 6 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 Storage/Storage.Test/FriendTest.cs create mode 100644 Storage/Storage.Test/StatusTest.cs diff --git a/Storage/Storage.Test/FriendTest.cs b/Storage/Storage.Test/FriendTest.cs new file mode 100644 index 0000000..80dff68 --- /dev/null +++ b/Storage/Storage.Test/FriendTest.cs @@ -0,0 +1,103 @@ +using NUnit.Framework; +using System; +using System.Linq; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using LeanCloud; +using LeanCloud.Storage; + +namespace Storage.Test { + public class FriendTest { + async Task SignUp() { + LCUser user = new LCUser { + Username = Guid.NewGuid().ToString(), + Password = "world" + }; + return await user.SignUp(); + } + + async Task GetRequest() { + LCUser user = await LCUser.GetCurrent(); + LCQuery query = new LCQuery("_FriendshipRequest") + .WhereEqualTo("friend", user) + .WhereEqualTo("status", "pending"); + return await query.First(); + } + + async Task> GetFriends() { + LCUser user = await LCUser.GetCurrent(); + LCQuery query = new LCQuery("_Followee") + .WhereEqualTo("user", user) + .WhereEqualTo("friendStatus", true); + return await query.Find(); + } + + private LCUser user1; + private LCUser user2; + + [SetUp] + public void SetUp() { + LCLogger.LogDelegate += Utils.Print; + LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", + "https://ikggdre2.lc-cn-n1-shared.com"); + } + + [TearDown] + public void TearDown() { + LCLogger.LogDelegate -= Utils.Print; + } + + [Test] + [Order(0)] + public async Task Init() { + user1 = await SignUp(); + user2 = await SignUp(); + Dictionary attrs = new Dictionary { + { "group", "sport" } + }; + await LCFriendship.Request(user1.ObjectId, attrs); + + await SignUp(); + await LCFriendship.Request(user1.ObjectId); + + await LCUser.BecomeWithSessionToken(user1.SessionToken); + } + + [Test] + [Order(1)] + public async Task Accept() { + // 查询好友请求 + LCFriendshipRequest request = await GetRequest(); + // 接受 + await LCFriendship.AcceptRequest(request); + // 查询好友 + Assert.Greater((await GetFriends()).Count, 0); + } + + [Test] + [Order(2)] + public async Task Decline() { + // 查询好友请求 + LCFriendshipRequest request = await GetRequest(); + // 拒绝 + await LCFriendship.DeclineRequest(request); + } + + [Test] + [Order(3)] + public async Task Attributes() { + LCObject followee = (await GetFriends()).First(); + followee["group"] = "friend"; + await followee.Save(); + } + + [Test] + [Order(4)] + public async Task Delete() { + await user1.Unfollow(user2.ObjectId); + // 查询好友 + Assert.AreEqual((await GetFriends()).Count, 0); + } + } +} diff --git a/Storage/Storage.Test/StatusTest.cs b/Storage/Storage.Test/StatusTest.cs new file mode 100644 index 0000000..002b653 --- /dev/null +++ b/Storage/Storage.Test/StatusTest.cs @@ -0,0 +1,98 @@ +using NUnit.Framework; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using LeanCloud; +using LeanCloud.Storage; + +namespace Storage.Test { + public class StatusTest { + private LCUser user1; + private LCUser user2; + private LCUser user3; + + [SetUp] + public void SetUp() { + LCLogger.LogDelegate += Utils.Print; + LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", + "https://ikggdre2.lc-cn-n1-shared.com"); + } + + [TearDown] + public void TearDown() { + LCLogger.LogDelegate -= Utils.Print; + } + + [Test] + [Order(0)] + public async Task Init() { + user1 = new LCUser { + Username = Guid.NewGuid().ToString(), + Password = "world" + }; + await user1.SignUp(); + + user2 = new LCUser { + Username = Guid.NewGuid().ToString(), + Password = "world" + }; + await user2.SignUp(); + + user3 = new LCUser { + Username = Guid.NewGuid().ToString(), + Password = "world" + }; + await user3.SignUp(); + } + + [Test] + [Order(1)] + public async Task Follow() { + await LCUser.BecomeWithSessionToken(user2.SessionToken); + Dictionary attrs = new Dictionary { + { "score", 100 } + }; + await user2.Follow(user1.ObjectId, attrs); + + await LCUser.BecomeWithSessionToken(user3.SessionToken); + await user3.Follow(user2.ObjectId); + } + + [Test] + [Order(2)] + public async Task QueryFollowersAndFollowees() { + await LCUser.BecomeWithSessionToken(user2.SessionToken); + + LCQuery query = user2.FolloweeQuery(); + ReadOnlyCollection results = await query.Find(); + Assert.Greater(results.Count, 0); + foreach (LCObject item in results) { + Assert.IsTrue(item["followee"] is LCObject); + Assert.AreEqual(user1.ObjectId, (item["followee"] as LCObject).ObjectId); + } + + query = user2.FollowerQuery(); + results = await query.Find(); + Assert.Greater(results.Count, 0); + foreach (LCObject item in results) { + Assert.IsTrue(item["follower"] is LCObject); + Assert.AreEqual(user3.ObjectId, (item["follower"] as LCObject).ObjectId); + } + + LCFollowersAndFollowees followersAndFollowees = await user2.GetFollowersAndFollowees(true, true, true); + Assert.AreEqual(followersAndFollowees.FollowersCount, 1); + Assert.AreEqual(followersAndFollowees.FolloweesCount, 1); + } + + [Test] + [Order(5)] + public async Task Unfollow() { + await LCUser.BecomeWithSessionToken(user2.SessionToken); + await user2.Unfollow(user1.ObjectId); + + await LCUser.BecomeWithSessionToken(user3.SessionToken); + await user3.Unfollow(user1.ObjectId); + } + } +} diff --git a/Storage/Storage.Test/UserTest.cs b/Storage/Storage.Test/UserTest.cs index 56cfaca..06ea54a 100644 --- a/Storage/Storage.Test/UserTest.cs +++ b/Storage/Storage.Test/UserTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using LeanCloud; using LeanCloud.Storage; +using Newtonsoft.Json; namespace Storage.Test { public class UserTest { @@ -241,5 +242,34 @@ namespace Storage.Test { await LCUser.Login("hello", "world"); await LCUser.VerifyCodeForUpdatingPhoneNumber("15101006007", "055595"); } + + [Test] + public async Task AuthData() { + string uuid = Guid.NewGuid().ToString(); + Dictionary authData = new Dictionary { + { "expires_in", 7200 }, + { "openid", uuid }, + { "access_token", uuid } + }; + LCUser currentUser = await LCUser.LoginWithAuthData(authData, "weixin"); + TestContext.WriteLine(currentUser.SessionToken); + Assert.NotNull(currentUser.SessionToken); + string userId = currentUser.ObjectId; + TestContext.WriteLine($"userId: {userId}"); + TestContext.WriteLine(JsonConvert.SerializeObject(currentUser.AuthData)); + + try { + authData = new Dictionary { + { "expires_in", 7200 }, + { "openid", uuid }, + { "access_token", uuid } + }; + await currentUser.AssociateAuthData(authData, "qq"); + TestContext.WriteLine(JsonConvert.SerializeObject(currentUser.AuthData)); + } catch (LCException e) { + TestContext.WriteLine($"{e.Code} : {e.Message}"); + TestContext.WriteLine(JsonConvert.SerializeObject(currentUser.AuthData)); + } + } } } diff --git a/Storage/Storage/LCApplication.cs b/Storage/Storage/LCApplication.cs index 694e7cd..6256fde 100644 --- a/Storage/Storage/LCApplication.cs +++ b/Storage/Storage/LCApplication.cs @@ -61,6 +61,7 @@ namespace LeanCloud { LCObject.RegisterSubclass(LCUser.CLASS_NAME, () => new LCUser()); LCObject.RegisterSubclass(LCRole.CLASS_NAME, () => new LCRole()); LCObject.RegisterSubclass(LCFile.CLASS_NAME, () => new LCFile()); + LCObject.RegisterSubclass(LCFriendshipRequest.CLASS_NAME, () => new LCFriendshipRequest()); AppRouter = new LCAppRouter(appId, server); diff --git a/Storage/Storage/LCFollowersAndFollowees.cs b/Storage/Storage/LCFollowersAndFollowees.cs index 288b1b5..3677490 100644 --- a/Storage/Storage/LCFollowersAndFollowees.cs +++ b/Storage/Storage/LCFollowersAndFollowees.cs @@ -14,7 +14,7 @@ namespace LeanCloud.Storage { get; internal set; } - public int FolloweeCount { + public int FolloweesCount { get; internal set; } } diff --git a/Storage/Storage/LCUser.cs b/Storage/Storage/LCUser.cs index 46ad157..bb50f86 100644 --- a/Storage/Storage/LCUser.cs +++ b/Storage/Storage/LCUser.cs @@ -684,7 +684,7 @@ namespace LeanCloud.Storage { } if (response.TryGetValue("followees_count", out object followeesCountObj) && (followeesCountObj is int followeesCount)) { - result.FolloweeCount = followeesCount; + result.FolloweesCount = followeesCount; } return result; } From aec1551cb7c84e8ef9efafcec3d2550c71c63840 Mon Sep 17 00:00:00 2001 From: oneRain Date: Wed, 13 Jan 2021 17:55:17 +0800 Subject: [PATCH 3/6] feat: status --- Storage/Storage.Test/StatusTest.cs | 44 +++++ Storage/Storage/Internal/Http/LCHttpClient.cs | 20 +- Storage/Storage/LCApplication.cs | 1 + Storage/Storage/LCStatus.cs | 176 ++++++++++++++++++ Storage/Storage/LCStatusCount.cs | 11 ++ Storage/Storage/LCStatusQuery.cs | 61 ++++++ 6 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 Storage/Storage/LCStatus.cs create mode 100644 Storage/Storage/LCStatusCount.cs create mode 100644 Storage/Storage/LCStatusQuery.cs diff --git a/Storage/Storage.Test/StatusTest.cs b/Storage/Storage.Test/StatusTest.cs index 002b653..930fb0f 100644 --- a/Storage/Storage.Test/StatusTest.cs +++ b/Storage/Storage.Test/StatusTest.cs @@ -85,6 +85,50 @@ namespace Storage.Test { Assert.AreEqual(followersAndFollowees.FolloweesCount, 1); } + [Test] + [Order(3)] + public async Task Send() { + await LCUser.BecomeWithSessionToken(user1.SessionToken); + + // 给粉丝发送状态 + LCStatus status = new LCStatus { + Data = new Dictionary { + { "image", "xxx.jpg" }, + { "content", "hello, world" } + } + }; + await LCStatus.SendToFollowers(status); + + // 给某个用户发送私信 + LCStatus privateStatus = new LCStatus { + Data = new Dictionary { + { "image", "xxx.jpg" }, + { "content", "hello, game" } + } + }; + await LCStatus.SendPrivately(status, user2.ObjectId); + } + + [Test] + [Order(4)] + public async Task Query() { + await LCUser.BecomeWithSessionToken(user2.SessionToken); + + LCStatusCount statusCount = await LCStatus.GetCount(LCStatus.InboxTypeDefault); + Assert.Greater(statusCount.Total, 0); + LCStatusCount privateCount = await LCStatus.GetCount(LCStatus.InboxTypePrivate); + Assert.Greater(privateCount.Total, 0); + + LCStatusQuery query = new LCStatusQuery(LCStatus.InboxTypeDefault); + ReadOnlyCollection statuses = await query.Find(); + foreach (LCStatus status in statuses) { + Assert.AreEqual((status["source"] as LCObject).ObjectId, user1.ObjectId); + await status.Delete(); + } + + await LCStatus.ResetUnreadCount(LCStatus.InboxTypePrivate); + } + [Test] [Order(5)] public async Task Unfollow() { diff --git a/Storage/Storage/Internal/Http/LCHttpClient.cs b/Storage/Storage/Internal/Http/LCHttpClient.cs index 85b4d1f..b5c1086 100644 --- a/Storage/Storage/Internal/Http/LCHttpClient.cs +++ b/Storage/Storage/Internal/Http/LCHttpClient.cs @@ -136,15 +136,25 @@ namespace LeanCloud.Storage.Internal.Http { throw HandleErrorResponse(response.StatusCode, resultString); } - public async Task Delete(string path) { - string url = await BuildUrl(path); + public async Task Delete(string path, + Dictionary headers = null, + object data = null, + Dictionary queryParams = null) { + string url = await BuildUrl(path, queryParams); HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(url), Method = HttpMethod.Delete }; - await FillHeaders(request.Headers); + await FillHeaders(request.Headers, headers); - LCHttpUtils.PrintRequest(client, request); + string content = null; + if (data != null) { + content = JsonConvert.SerializeObject(data); + StringContent requestContent = new StringContent(content); + requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + request.Content = requestContent; + } + LCHttpUtils.PrintRequest(client, request, content); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); request.Dispose(); @@ -153,7 +163,7 @@ namespace LeanCloud.Storage.Internal.Http { LCHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { - Dictionary ret = JsonConvert.DeserializeObject>(resultString, + _ = JsonConvert.DeserializeObject>(resultString, LCJsonConverter.Default); return; } diff --git a/Storage/Storage/LCApplication.cs b/Storage/Storage/LCApplication.cs index 6256fde..d659ed7 100644 --- a/Storage/Storage/LCApplication.cs +++ b/Storage/Storage/LCApplication.cs @@ -61,6 +61,7 @@ namespace LeanCloud { LCObject.RegisterSubclass(LCUser.CLASS_NAME, () => new LCUser()); LCObject.RegisterSubclass(LCRole.CLASS_NAME, () => new LCRole()); LCObject.RegisterSubclass(LCFile.CLASS_NAME, () => new LCFile()); + LCObject.RegisterSubclass(LCStatus.CLASS_NAME, () => new LCStatus()); LCObject.RegisterSubclass(LCFriendshipRequest.CLASS_NAME, () => new LCFriendshipRequest()); AppRouter = new LCAppRouter(appId, server); diff --git a/Storage/Storage/LCStatus.cs b/Storage/Storage/LCStatus.cs new file mode 100644 index 0000000..97030dc --- /dev/null +++ b/Storage/Storage/LCStatus.cs @@ -0,0 +1,176 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; +using LeanCloud.Storage.Internal.Object; +using Newtonsoft.Json; + +namespace LeanCloud.Storage { + public class LCStatus : LCObject { + public const string CLASS_NAME = "_Status"; + + /// Public, shown on followees' timeline. + public const string InboxTypeDefault = "default"; + + /// Private. + public const string InboxTypePrivate = "private"; + + /// Keys + public const string SourceKey = "source"; + public const string InboxTypeKey = "inboxType"; + public const string OwnerKey = "owner"; + public const string MessageIdKey = "messageId"; + + public int MessageId { + get; internal set; + } + + public string InboxType { + get; internal set; + } + + private LCQuery query; + + public Dictionary Data { + get; set; + } + + public LCStatus() : base(CLASS_NAME) { + InboxType = InboxTypeDefault; + Data = new Dictionary(); + } + + public static async Task SendToFollowers(LCStatus status) { + if (status == null) { + throw new ArgumentNullException(nameof(status)); + } + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + status.Data[SourceKey] = user; + + LCQuery query = new LCQuery("_Follower") + .WhereEqualTo("user", user) + .Select("follower"); + status.query = query; + + status.InboxType = InboxTypeDefault; + + return await status.Send(); + } + + public static async Task SendPrivately(LCStatus status, string targetId) { + if (status == null) { + throw new ArgumentNullException(nameof(status)); + } + if (string.IsNullOrEmpty(targetId)) { + throw new ArgumentNullException(nameof(targetId)); + } + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + status.Data[SourceKey] = user; + LCQuery query = new LCQuery("_User") + .WhereEqualTo("objectId", targetId); + status.query = query; + + status.InboxType = InboxTypePrivate; + + return await status.Send(); + } + + public async Task Send() { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + Dictionary formData = new Dictionary { + { InboxTypeKey, InboxType } + }; + if (Data != null) { + formData["data"] = LCEncoder.Encode(Data); + } + if (query != null) { + Dictionary queryData = new Dictionary { + { "className", query.ClassName } + }; + Dictionary ps = query.BuildParams(); + if (ps.TryGetValue("where", out object whereObj) && + whereObj is string where) { + queryData["where"] = JsonConvert.DeserializeObject(where); + } + if (ps.TryGetValue("keys", out object keys)) { + queryData["keys"] = keys; + } + formData["query"] = queryData; + } + Dictionary response = await LCApplication.HttpClient.Post>("statuses", + data: formData); + LCObjectData objectData = LCObjectData.Decode(response); + Merge(objectData); + + return this; + } + + public new async Task Delete() { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + LCUser source = (Data[SourceKey] ?? this[SourceKey]) as LCUser; + if (source != null && source.ObjectId == user.ObjectId) { + await LCApplication.HttpClient.Delete($"statuses/{ObjectId}"); + } else { + Dictionary data = new Dictionary { + { OwnerKey, JsonConvert.SerializeObject(LCEncoder.Encode(user)) }, + { InboxTypeKey, InboxType }, + { MessageIdKey, MessageId } + }; + await LCApplication.HttpClient.Delete("subscribe/statuses/inbox", queryParams: data); + } + } + + public static async Task GetCount(string inboxType) { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + Dictionary queryParams = new Dictionary { + { OwnerKey, JsonConvert.SerializeObject(LCEncoder.Encode(user)) } + }; + if (!string.IsNullOrEmpty(inboxType)) { + queryParams[InboxTypeKey] = inboxType; + } + Dictionary response = await LCApplication.HttpClient.Get>("subscribe/statuses/count", + queryParams: queryParams); + LCStatusCount statusCount = new LCStatusCount { + Total = (int)response["total"], + Unread = (int)response["unread"] + }; + return statusCount; + } + + public static async Task ResetUnreadCount(string inboxType = null) { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + Dictionary queryParams = new Dictionary { + { OwnerKey, JsonConvert.SerializeObject(LCEncoder.Encode(user)) } + }; + if (!string.IsNullOrEmpty(inboxType)) { + queryParams[InboxTypeKey] = inboxType; + } + await LCApplication.HttpClient.Post>("subscribe/statuses/resetUnreadCount", + queryParams:queryParams); + } + } +} diff --git a/Storage/Storage/LCStatusCount.cs b/Storage/Storage/LCStatusCount.cs new file mode 100644 index 0000000..56ce6b4 --- /dev/null +++ b/Storage/Storage/LCStatusCount.cs @@ -0,0 +1,11 @@ +namespace LeanCloud.Storage { + public class LCStatusCount { + public int Total { + get; set; + } + + public int Unread { + get; set; + } + } +} diff --git a/Storage/Storage/LCStatusQuery.cs b/Storage/Storage/LCStatusQuery.cs new file mode 100644 index 0000000..473cd6a --- /dev/null +++ b/Storage/Storage/LCStatusQuery.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Generic; +using Newtonsoft.Json; +using LeanCloud.Storage.Internal.Codec; +using LeanCloud.Storage.Internal.Object; + +namespace LeanCloud.Storage { + public class LCStatusQuery : LCQuery { + public string InboxType { + get; set; + } + + public int SinceId { + get; set; + } + + public int MaxId { + get; set; + } + + public LCStatusQuery(string inboxType = LCStatus.InboxTypeDefault) : base("_Status") { + InboxType = inboxType; + SinceId = 0; + MaxId = 0; + } + + public async Task> Find() { + LCUser user = await LCUser.GetCurrent(); + if (user == null) { + throw new ArgumentNullException("current user"); + } + + Dictionary queryParams = new Dictionary { + { LCStatus.OwnerKey, JsonConvert.SerializeObject(LCEncoder.Encode(user)) }, + { LCStatus.InboxTypeKey, InboxType }, + { "where", BuildWhere() }, + { "sinceId", SinceId }, + { "maxId", MaxId }, + { "limit", Condition.Limit } + }; + Dictionary response = await LCApplication.HttpClient.Get>("subscribe/statuses", + queryParams: queryParams); + List results = response["results"] as List; + List statuses = new List(); + foreach (object item in results) { + LCObjectData objectData = LCObjectData.Decode(item as IDictionary); + LCStatus status = new LCStatus(); + status.Merge(objectData); + status.MessageId = (int)objectData.CustomPropertyDict[LCStatus.MessageIdKey]; + status.Data = objectData.CustomPropertyDict; + status.InboxType = objectData.CustomPropertyDict[LCStatus.InboxTypeKey] as string; + statuses.Add(status); + } + + return statuses.AsReadOnly(); + } + } +} From 940f6a70ab37ba6eb64f5e1bab153bbc8827746c Mon Sep 17 00:00:00 2001 From: oneRain Date: Thu, 14 Jan 2021 14:36:02 +0800 Subject: [PATCH 4/6] chore --- Storage/Storage.Test/StatusTest.cs | 2 +- Storage/Storage/LCStatusQuery.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Storage/Storage.Test/StatusTest.cs b/Storage/Storage.Test/StatusTest.cs index 930fb0f..a789d0a 100644 --- a/Storage/Storage.Test/StatusTest.cs +++ b/Storage/Storage.Test/StatusTest.cs @@ -106,7 +106,7 @@ namespace Storage.Test { { "content", "hello, game" } } }; - await LCStatus.SendPrivately(status, user2.ObjectId); + await LCStatus.SendPrivately(privateStatus, user2.ObjectId); } [Test] diff --git a/Storage/Storage/LCStatusQuery.cs b/Storage/Storage/LCStatusQuery.cs index 473cd6a..7688295 100644 --- a/Storage/Storage/LCStatusQuery.cs +++ b/Storage/Storage/LCStatusQuery.cs @@ -27,7 +27,7 @@ namespace LeanCloud.Storage { MaxId = 0; } - public async Task> Find() { + public new async Task> Find() { LCUser user = await LCUser.GetCurrent(); if (user == null) { throw new ArgumentNullException("current user"); From 164b59d95d0808d79e94ca4ecb212dd781474d86 Mon Sep 17 00:00:00 2001 From: oneRain Date: Thu, 14 Jan 2021 15:14:31 +0800 Subject: [PATCH 5/6] chore: simplify request. --- Storage/Storage.Test/StatusTest.cs | 1 + Storage/Storage.Test/UserTest.cs | 6 +- Storage/Storage/Internal/Http/LCHttpClient.cs | 118 ++++-------------- 3 files changed, 30 insertions(+), 95 deletions(-) diff --git a/Storage/Storage.Test/StatusTest.cs b/Storage/Storage.Test/StatusTest.cs index a789d0a..cdec3f7 100644 --- a/Storage/Storage.Test/StatusTest.cs +++ b/Storage/Storage.Test/StatusTest.cs @@ -112,6 +112,7 @@ namespace Storage.Test { [Test] [Order(4)] public async Task Query() { + await Task.Delay(5000); await LCUser.BecomeWithSessionToken(user2.SessionToken); LCStatusCount statusCount = await LCStatus.GetCount(LCStatus.InboxTypeDefault); diff --git a/Storage/Storage.Test/UserTest.cs b/Storage/Storage.Test/UserTest.cs index 06ea54a..7d1ba8b 100644 --- a/Storage/Storage.Test/UserTest.cs +++ b/Storage/Storage.Test/UserTest.cs @@ -27,7 +27,8 @@ namespace Storage.Test { user.Password = "world"; string email = $"{unixTime}@qq.com"; user.Email = email; - string mobile = $"{unixTime / 100}"; + Random random = new Random(); + string mobile = $"151{random.Next(10000000, 99999999)}"; user.Mobile = mobile; await user.SignUp(); @@ -73,7 +74,6 @@ namespace Storage.Test { LCObject account = new LCObject("Account"); account["user"] = user; await account.Save(); - Assert.AreEqual(user.ObjectId, "5e0d5c667d5774006a5c1177"); } [Test] @@ -240,7 +240,7 @@ namespace Storage.Test { [Test] public async Task VerifyCodeForUpdatingPhoneNumber() { await LCUser.Login("hello", "world"); - await LCUser.VerifyCodeForUpdatingPhoneNumber("15101006007", "055595"); + await LCUser.VerifyCodeForUpdatingPhoneNumber("15101006007", "969327"); } [Test] diff --git a/Storage/Storage/Internal/Http/LCHttpClient.cs b/Storage/Storage/Internal/Http/LCHttpClient.cs index b5c1086..04e2562 100644 --- a/Storage/Storage/Internal/Http/LCHttpClient.cs +++ b/Storage/Storage/Internal/Http/LCHttpClient.cs @@ -42,40 +42,42 @@ namespace LeanCloud.Storage.Internal.Http { md5 = MD5.Create(); } - public async Task Get(string path, + public Task Get(string path, Dictionary headers = null, Dictionary queryParams = null) { - string url = await BuildUrl(path, queryParams); - HttpRequestMessage request = new HttpRequestMessage { - RequestUri = new Uri(url), - Method = HttpMethod.Get - }; - await FillHeaders(request.Headers, headers); - - LCHttpUtils.PrintRequest(client, request); - HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - request.Dispose(); - - string resultString = await response.Content.ReadAsStringAsync(); - response.Dispose(); - LCHttpUtils.PrintResponse(response, resultString); - - if (response.IsSuccessStatusCode) { - T ret = JsonConvert.DeserializeObject(resultString, - LCJsonConverter.Default); - return ret; - } - throw HandleErrorResponse(response.StatusCode, resultString); + return Request(path, HttpMethod.Get, headers, null, queryParams); } - public async Task Post(string path, + public Task Post(string path, + Dictionary headers = null, + object data = null, + Dictionary queryParams = null) { + return Request(path, HttpMethod.Post, headers, data, queryParams); + } + + public Task Put(string path, + Dictionary headers = null, + object data = null, + Dictionary queryParams = null) { + return Request(path, HttpMethod.Put, headers, data, queryParams); + } + + public Task Delete(string path, + Dictionary headers = null, + object data = null, + Dictionary queryParams = null) { + return Request>(path, HttpMethod.Delete, headers, data, queryParams); + } + + async Task Request(string path, + HttpMethod method, Dictionary headers = null, object data = null, Dictionary queryParams = null) { string url = await BuildUrl(path, queryParams); HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(url), - Method = HttpMethod.Post, + Method = method, }; await FillHeaders(request.Headers, headers); @@ -102,74 +104,6 @@ namespace LeanCloud.Storage.Internal.Http { throw HandleErrorResponse(response.StatusCode, resultString); } - public async Task Put(string path, - Dictionary headers = null, - object data = null, - Dictionary queryParams = null) { - string url = await BuildUrl(path, queryParams); - HttpRequestMessage request = new HttpRequestMessage { - RequestUri = new Uri(url), - Method = HttpMethod.Put, - }; - await FillHeaders(request.Headers, headers); - - string content = null; - if (data != null) { - content = JsonConvert.SerializeObject(data); - StringContent requestContent = new StringContent(content); - requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - request.Content = requestContent; - } - LCHttpUtils.PrintRequest(client, request, content); - HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - request.Dispose(); - - string resultString = await response.Content.ReadAsStringAsync(); - response.Dispose(); - LCHttpUtils.PrintResponse(response, resultString); - - if (response.IsSuccessStatusCode) { - T ret = JsonConvert.DeserializeObject(resultString, - LCJsonConverter.Default); - return ret; - } - throw HandleErrorResponse(response.StatusCode, resultString); - } - - public async Task Delete(string path, - Dictionary headers = null, - object data = null, - Dictionary queryParams = null) { - string url = await BuildUrl(path, queryParams); - HttpRequestMessage request = new HttpRequestMessage { - RequestUri = new Uri(url), - Method = HttpMethod.Delete - }; - await FillHeaders(request.Headers, headers); - - string content = null; - if (data != null) { - content = JsonConvert.SerializeObject(data); - StringContent requestContent = new StringContent(content); - requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - request.Content = requestContent; - } - LCHttpUtils.PrintRequest(client, request, content); - HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - request.Dispose(); - - string resultString = await response.Content.ReadAsStringAsync(); - response.Dispose(); - LCHttpUtils.PrintResponse(response, resultString); - - if (response.IsSuccessStatusCode) { - _ = JsonConvert.DeserializeObject>(resultString, - LCJsonConverter.Default); - return; - } - throw HandleErrorResponse(response.StatusCode, resultString); - } - LCException HandleErrorResponse(HttpStatusCode statusCode, string responseContent) { int code = (int)statusCode; string message = responseContent; From 91e281a14b1a85b727473886cb6e5ec64cb93380 Mon Sep 17 00:00:00 2001 From: oneRain Date: Thu, 14 Jan 2021 15:25:48 +0800 Subject: [PATCH 6/6] chore --- Storage/Storage-Unity/Storage-Unity.csproj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Storage/Storage-Unity/Storage-Unity.csproj b/Storage/Storage-Unity/Storage-Unity.csproj index eb1c493..518e1d1 100644 --- a/Storage/Storage-Unity/Storage-Unity.csproj +++ b/Storage/Storage-Unity/Storage-Unity.csproj @@ -130,6 +130,24 @@ Leaderboard\LCRanking.cs + + LCFollowersAndFollowees.cs + + + LCFriendship.cs + + + LCFriendshipRequest.cs + + + LCStatus.cs + + + LCStatusCount.cs + + + LCStatusQuery.cs +