chore: 支持 LiveQuery
parent
51fe69d7c0
commit
4b30df0ba0
|
@ -0,0 +1,29 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Realtime\Realtime-Unity\Realtime-Unity.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\LiveQuery\Internal\LCLiveQueryHeartBeat.cs">
|
||||
<Link>Internal\LCLiveQueryHeartBeat.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\LiveQuery\Internal\LCLiveQueryConnection.cs">
|
||||
<Link>Internal\LCLiveQueryConnection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\LiveQuery\LCLiveQuery.cs">
|
||||
<Link>LCLiveQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\LiveQuery\LCQueryExtension.cs">
|
||||
<Link>LCQueryExtension.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\..\UnityLibs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<ReleaseVersion>0.1.0</ReleaseVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LiveQuery\LiveQuery.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,180 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using LeanCloud;
|
||||
using LeanCloud.Storage;
|
||||
using LeanCloud.LiveQuery;
|
||||
|
||||
using static NUnit.Framework.TestContext;
|
||||
|
||||
namespace LiveQuery.Test {
|
||||
internal class Account : LCObject {
|
||||
internal int Balance {
|
||||
get {
|
||||
return (int)this["balance"];
|
||||
}
|
||||
set {
|
||||
this["balance"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal Account() : base("Account") { }
|
||||
}
|
||||
|
||||
public class LiveQuery {
|
||||
private LCLiveQuery liveQuery;
|
||||
|
||||
private Account account;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup() {
|
||||
LCLogger.LogDelegate += Print;
|
||||
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz",
|
||||
"NUKmuRbdAhg1vrb2wexYo1jo",
|
||||
"https://ikggdre2.lc-cn-n1-shared.com");
|
||||
|
||||
LCObject.RegisterSubclass("Account", () => new Account());
|
||||
|
||||
LCQuery<LCObject> query = new LCQuery<LCObject>("Account");
|
||||
query.WhereGreaterThan("balance", 100);
|
||||
liveQuery = await query.Subscribe();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown() {
|
||||
LCLogger.LogDelegate -= Print;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(0)]
|
||||
public async Task Create() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
liveQuery.OnCreate = (obj) => {
|
||||
WriteLine($"******** create: {obj}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
account = new Account {
|
||||
Balance = 110
|
||||
};
|
||||
await account.Save();
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(1)]
|
||||
public async Task Update() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
liveQuery.OnUpdate = (obj, updatedKeys) => {
|
||||
WriteLine($"******** update: {obj}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
account.Balance = 120;
|
||||
await account.Save();
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(2)]
|
||||
public async Task Leave() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
liveQuery.OnLeave = (obj, updatedKeys) => {
|
||||
WriteLine($"******** level: {obj}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
account.Balance = 80;
|
||||
await account.Save();
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(3)]
|
||||
public async Task Enter() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
liveQuery.OnEnter = (obj, updatedKeys) => {
|
||||
WriteLine($"******** enter: {obj}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
account.Balance = 120;
|
||||
await account.Save();
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(4)]
|
||||
public async Task Delete() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
liveQuery.OnDelete = (objId) => {
|
||||
WriteLine($"******** delete: {objId}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
await account.Delete();
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Order(5)]
|
||||
public async Task Login() {
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
await LCUser.Login("hello", "world");
|
||||
LCQuery<LCUser> userQuery = LCUser.GetQuery();
|
||||
userQuery.WhereEqualTo("username", "hello");
|
||||
LCLiveQuery userLiveQuery = await userQuery.Subscribe();
|
||||
userLiveQuery.OnLogin = (user) => {
|
||||
WriteLine($"login: {user}");
|
||||
tcs.SetResult(null);
|
||||
};
|
||||
|
||||
// 模拟 REST API
|
||||
string url = "https://ikggdre2.lc-cn-n1-shared.com/1.1/login";
|
||||
HttpRequestMessage request = new HttpRequestMessage {
|
||||
RequestUri = new Uri(url),
|
||||
Method = HttpMethod.Post
|
||||
};
|
||||
request.Headers.Add("X-LC-Id", "ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz");
|
||||
request.Headers.Add("X-LC-Key", "NUKmuRbdAhg1vrb2wexYo1jo");
|
||||
string content = JsonConvert.SerializeObject(new Dictionary<string, object> {
|
||||
{ "username", "hello" },
|
||||
{ "password", "world" }
|
||||
});
|
||||
StringContent requestContent = new StringContent(content);
|
||||
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
request.Content = requestContent;
|
||||
HttpClient client = new HttpClient();
|
||||
await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
||||
private static void Print(LCLogLevel level, string info) {
|
||||
switch (level) {
|
||||
case LCLogLevel.Debug:
|
||||
WriteLine($"[DEBUG] {info}\n");
|
||||
break;
|
||||
case LCLogLevel.Warn:
|
||||
WriteLine($"[WARNING] {info}\n");
|
||||
break;
|
||||
case LCLogLevel.Error:
|
||||
WriteLine($"[ERROR] {info}\n");
|
||||
break;
|
||||
default:
|
||||
WriteLine(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,55 @@
|
|||
using System;
|
||||
namespace LiveQuery.Internal {
|
||||
public class LCLiveQueryHeartBeat {
|
||||
public LCLiveQueryHeartBeat() {
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LeanCloud.LiveQuery.Internal {
|
||||
/// <summary>
|
||||
/// LiveQuery 心跳控制器
|
||||
/// </summary>
|
||||
internal class LCLiveQueryHeartBeat {
|
||||
private const int PING_INTERVAL = 5000;
|
||||
private const int PONG_INTERVAL = 5000;
|
||||
|
||||
private readonly LCLiveQueryConnection connection;
|
||||
|
||||
private CancellationTokenSource pingCTS;
|
||||
private CancellationTokenSource pongCTS;
|
||||
|
||||
internal LCLiveQueryHeartBeat(LCLiveQueryConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
internal async Task Refresh(Action onTimeout) {
|
||||
LCLogger.Debug("LiveQuery HeartBeat refresh");
|
||||
Stop();
|
||||
|
||||
pingCTS = new CancellationTokenSource();
|
||||
Task delayTask = Task.Delay(PING_INTERVAL, pingCTS.Token);
|
||||
await delayTask;
|
||||
if (delayTask.IsCanceled) {
|
||||
return;
|
||||
}
|
||||
// 发送 Ping 包
|
||||
LCLogger.Debug("Ping ~~~");
|
||||
_ = connection.SendText("{}");
|
||||
|
||||
// 等待 Pong
|
||||
pongCTS = new CancellationTokenSource();
|
||||
Task timeoutTask = Task.Delay(PONG_INTERVAL, pongCTS.Token);
|
||||
await timeoutTask;
|
||||
if (timeoutTask.IsCanceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 超时
|
||||
LCLogger.Debug("Ping timeout");
|
||||
onTimeout?.Invoke();
|
||||
}
|
||||
|
||||
internal void Stop() {
|
||||
pingCTS?.Cancel();
|
||||
pongCTS?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
using System;
|
||||
namespace LiveQuery {
|
||||
public class LCQueryExtension {
|
||||
public LCQueryExtension() {
|
||||
using System.Threading.Tasks;
|
||||
using LeanCloud.Storage;
|
||||
|
||||
namespace LeanCloud.LiveQuery {
|
||||
public static class LCQueryExtension {
|
||||
public static async Task<LCLiveQuery> Subscribe(this LCQuery query) {
|
||||
LCLiveQuery liveQuery = new LCLiveQuery {
|
||||
Query = query
|
||||
};
|
||||
await liveQuery.Subscribe();
|
||||
return liveQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<ReleaseVersion>0.1.0</ReleaseVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Realtime\Realtime\Realtime.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Internal\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue